/* * Humax EPG Tool * by af123, 2011 - 2024 */ #define UNUSED_PARAMETER __attribute__((unused)) #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_SQLITE3 #include #endif #include #include "xgetopt.h" #include "lint.h" int debug = 0; const char *version = "1.2.9"; unsigned long sysopts = 0; unsigned long filterflags = 0; static time_t latest_stamp = 0; #ifdef HAVE_SQLITE3 sqlite3 *db; sqlite3_stmt *stmt; #endif int syntax() { fprintf(stderr, "Humax EPG Tool v%s, by af123, 2011-2024.\n\n", version); fprintf(stderr, "Syntax: epg [options] [filters] ...\n\n"); fprintf(stderr, " Options:\n" " -b Brief output.\n" " -d[level] Set debug level.\n" " -f Specify alternate EPG data file.\n" " -h Show help text.\n" " -p Parsable output (see *).\n" "\n" ); fprintf(stderr, " Filters: (can be specified multiple times, all must be true)\n" " -C Show only events with this CRID.\n" " -D Show only selected descriptor type.\n" " -E Show only selected event.\n" " -R Show only events with this Series ID.\n" " -S Show only selected service.\n" " -G Show only records with this guidance " "type.\n" ); fprintf(stderr, " -T Show only selected content types.\n" " -@ Show only programmes at time.\n" " -/ Show only programmes on day.\n" " -/ Show only programmes on day.\n" " -=: Show only programmes in time period.\n" "\n" ); fprintf(stderr, " Commands:\n" " dump Show a parsed summary of the EPG.\n" " dumpraw Show raw data from the EPG.\n" " sqldump Produce SQL statements from EPG data.\n" " json Produce JSON formatted EPG data.\n" #ifdef HAVE_SQLITE3 " sqlitedump Create SQLite database from EPG data.\n" #endif ); fprintf(stderr, " now Show what is currently on.\n" " first Show the time of the earliest record.\n" " last Show the time of the latest record.\n" " d78 Check for records with d78.\n" " parse Parse the EPG, no output.\n" " search Search programme names for text.\n" " searchall " "Search programme names/descriptions for text.\n" ); fprintf(stderr, "\n" "* Parseable output is tab delimited and contains the following fields:\n" " Service ID, Event ID, Start, Duration, Encrypted,\n" " Title, Synopsis, Warning, Content Code, Content Type,\n" " Event CRID, Series CRID, Recommended CRID, Guidance Mode\n" " Content Mgmt\n" ); fprintf(stderr, "\n"); return 0; } #define DECOMPRESS(str, len) \ if (str && *(str) == 0x1f) uncompress_epg(&(str), &(len)) void pass(struct epg *epg UNUSED_PARAMETER, struct section *s UNUSED_PARAMETER, struct data *d UNUSED_PARAMETER, struct descriptor **ds UNUSED_PARAMETER, void *var UNUSED_PARAMETER) { /* Do nothing. */ } void latest(struct epg *epg UNUSED_PARAMETER, struct section *s UNUSED_PARAMETER, struct data *d UNUSED_PARAMETER, struct descriptor **ds UNUSED_PARAMETER, void *var UNUSED_PARAMETER) { time_t tm; tm = mjd(d->start_date, d->start_hour, d->start_min, d->start_sec); if (tm > latest_stamp) latest_stamp = tm; } void earliest(struct epg *epg UNUSED_PARAMETER, struct section *s UNUSED_PARAMETER, struct data *d UNUSED_PARAMETER, struct descriptor **ds UNUSED_PARAMETER, void *var UNUSED_PARAMETER) { time_t tm; tm = mjd(d->start_date, d->start_hour, d->start_min, d->start_sec); if (!latest_stamp || tm < latest_stamp) latest_stamp = tm; } void d78check(struct epg *epg UNUSED_PARAMETER, struct section *s UNUSED_PARAMETER, struct data *d UNUSED_PARAMETER, struct descriptor **ds UNUSED_PARAMETER, void *var UNUSED_PARAMETER) { if (ds[PARSER_EXTENDED_EVENT]) { printf("Found descriptor 78 in event.\n"); exit(1); } } void dumpraw(struct epg *epg UNUSED_PARAMETER, struct section *s, struct data *d, struct descriptor **ds, void *var UNUSED_PARAMETER) { dump_section(s); dump_data(d); if (ds[PARSER_SHORT_EVENT]) dump_descriptor(ds[PARSER_SHORT_EVENT], 1); if (ds[PARSER_USER_DEFINED]) dump_descriptor(ds[PARSER_USER_DEFINED], 1); if (ds[PARSER_CONTENT]) dump_descriptor(ds[PARSER_CONTENT], 1); if (ds[PARSER_CRID_EVENT]) dump_descriptor(ds[PARSER_CRID_EVENT], 1); if (ds[PARSER_CRID_SERIES]) dump_descriptor(ds[PARSER_CRID_SERIES], 1); if (ds[PARSER_CRID_REC]) dump_descriptor(ds[PARSER_CRID_REC], 1); if (ds[PARSER_FTA_CONTENT_MGMT]) dump_descriptor(ds[PARSER_FTA_CONTENT_MGMT], 1); } /* Strings should all be safe now the huffman module is in place... */ #define safeprintf printf static char * sql_escape_len(unsigned int len, char *txt) { static char buf[0x1000]; char *p; unsigned int i; p = buf; for (i = 0; i < len && *txt; i++) { if (*txt == '\'') *p++ = '\\'; *p++ = *txt++; } *p = '\0'; return buf; } static char * json_escape_len(unsigned int len, char *txt) { static char buf[0x1000]; char *p; unsigned int i; p = buf; for (i = 0; i < len && *txt; i++) { if (*txt == '\n') { *p++ = '\\'; *p++ = 'n'; txt++; continue; } if (*txt == '"') *p++ = '\\'; *p++ = *txt++; } *p = '\0'; return buf; } #ifdef HAVE_SQLITE3 #define EXEC(xx) if (sqlite3_exec(db, xx, NULL, NULL, &error) != SQLITE_OK) \ do { \ printf("Failed: %s - %s (%s)\n", xx, error, sqlite3_errmsg(db)); \ sqlite3_free(error); \ exit(0); \ } while (0) static void sqlitedumpstart(char *file) { char buf[MAXPATHLEN + 1]; char *error; sqlite3_config(SQLITE_CONFIG_SINGLETHREAD); sprintf(buf, "%s.load", file); unlink(buf); if (sqlite3_open_v2(buf, &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL) != SQLITE_OK) { fprintf(stderr, "Problem creating %s, %s", buf, sqlite3_errmsg(db)); exit(1); } EXEC("pragma journal_mode = off"); EXEC("pragma synchronous = off"); EXEC( "create table epg (" "[service_id] integer, [event_id] integer, " "[start] integer, [end] integer, [duration] integer, " "[name] text, [text] text, [warning] text, [warning_mode] integer, " "[content_code] integer, [content_type] text, " "[content_mgmt] integer, " "[event_crid] text, [series_crid] text, [rec_crid] text)" ); EXEC("create index tm on epg(start,end)"); EXEC("create index start on epg(start)"); EXEC("create index service_id on epg(service_id)"); EXEC("create index se on epg(service_id, event_id)"); if (sqlite3_prepare_v2(db, "insert into epg values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", -1, &stmt, NULL) != SQLITE_OK) { fprintf(stderr, "Problem preparing statement %s", sqlite3_errmsg(db)); exit(1); } EXEC("begin transaction"); } static void sqlitedumpend(char *file, int commit) { char buf[MAXPATHLEN + 1]; char *error; sqlite3_finalize(stmt); EXEC(commit ? "commit" : "rollback"); EXEC("analyze epg"); sqlite3_close(db); sprintf(buf, "%s.load", file); if (commit) rename(buf, file); else unlink(buf); } void sqlitedump(struct epg *epg UNUSED_PARAMETER, struct section *s, struct data *d, struct descriptor **ds, void *var UNUSED_PARAMETER) { time_t tm, dur; tm = mjd(d->start_date, d->start_hour, d->start_min, d->start_sec); dur = d->dur_hour * 3600 + d->dur_min * 60 + d->dur_sec; sqlite3_bind_int(stmt, 1, s->service_id); sqlite3_bind_int(stmt, 2, d->event_id); sqlite3_bind_int64(stmt, 3, tm); sqlite3_bind_int64(stmt, 4, tm + dur); sqlite3_bind_int64(stmt, 5, dur); if (ds[PARSER_SHORT_EVENT]) { struct descriptor *d = ds[PARSER_SHORT_EVENT]; DECOMPRESS(d->content.d77.name, d->content.d77.namelen); DECOMPRESS(d->content.d77.text, d->content.d77.textlen); iso6937_convert(&d->content.d77.name, &d->content.d77.namelen); iso6937_convert(&d->content.d77.text, &d->content.d77.textlen); sqlite3_bind_text(stmt, 6, d->content.d77.name, -1, NULL); sqlite3_bind_text(stmt, 7, d->content.d77.text, -1, NULL); } if (ds[PARSER_USER_DEFINED]) { struct descriptor *d = ds[PARSER_USER_DEFINED]; DECOMPRESS(d->content.d137.warning, d->content.d137.warninglen); sqlite3_bind_text(stmt, 8, d->content.d137.warning, -1, NULL); sqlite3_bind_int(stmt, 9, d->content.d137.guidance_mode); } if (ds[PARSER_CONTENT]) { sqlite3_bind_int(stmt, 10, ds[PARSER_CONTENT]->content.d84.level1); sqlite3_bind_text(stmt, 11, content_type(ds[PARSER_CONTENT]), -1, NULL); } if (ds[PARSER_FTA_CONTENT_MGMT]) { sqlite3_bind_int(stmt, 12, !ds[PARSER_FTA_CONTENT_MGMT]->content.d126.no_scramble); } else sqlite3_bind_int(stmt, 12, 0); if (ds[PARSER_CRID_EVENT]) { struct descriptor *d = ds[PARSER_CRID_EVENT]; sqlite3_bind_text(stmt, 13, d->content.d118.crids[0].crid, -1, NULL); } if (ds[PARSER_CRID_SERIES]) { struct descriptor *d = ds[PARSER_CRID_SERIES]; sqlite3_bind_text(stmt, 14, d->content.d118.crids[0].crid, -1, NULL); } if (ds[PARSER_CRID_REC]) { struct descriptor *d = ds[PARSER_CRID_REC]; sqlite3_bind_text(stmt, 15, d->content.d118.crids[0].crid, -1, NULL); } if (sqlite3_step(stmt) != SQLITE_DONE) { fprintf(stderr, "Statement failed, %s", sqlite3_errmsg(db)); exit(1); } sqlite3_reset(stmt); sqlite3_clear_bindings(stmt); } #endif /* HAVE_SQLITE3 */ static unsigned int json_service_id = 0; void jsonstart() { printf("{\n"); } void jsonend() { if (json_service_id) printf("\n }\n"); printf("}\n"); } void json(struct epg *epg UNUSED_PARAMETER, struct section *s, struct data *d, struct descriptor **ds, void *var UNUSED_PARAMETER) { time_t tm, dur; tm = mjd(d->start_date, d->start_hour, d->start_min, d->start_sec); dur = d->dur_hour * 3600 + d->dur_min * 60 + d->dur_sec; if (s->service_id != json_service_id) { /* New service. */ if (json_service_id) printf("\n },\n"); printf(" \"%d\": {\n", s->service_id); json_service_id = s->service_id; } else printf(",\n"); printf(" \"%d\": {\n", d->event_id); printf(" \"start\": %ld,\n", tm); printf(" \"end\": %ld,\n", tm + dur); printf(" \"duration\": %ld", dur); if (ds[PARSER_SHORT_EVENT]) { struct descriptor *d = ds[PARSER_SHORT_EVENT]; DECOMPRESS(d->content.d77.name, d->content.d77.namelen); DECOMPRESS(d->content.d77.text, d->content.d77.textlen); printf(",\n \"name\": \"%s\"", json_escape_len(d->content.d77.namelen, d->content.d77.name)); printf(",\n \"text\": \"%s\"", json_escape_len(d->content.d77.textlen, d->content.d77.text)); } if (ds[PARSER_USER_DEFINED]) { struct descriptor *d = ds[PARSER_USER_DEFINED]; DECOMPRESS(d->content.d137.warning, d->content.d137.warninglen); printf(",\n \"warning\": \"%s\"", json_escape_len(d->content.d137.warninglen, d->content.d137.warning)); printf(",\n \"guidance_type\": \"%d\"", d->content.d137.guidance_type); printf(",\n \"guidance_mode\": \"%d\"", d->content.d137.guidance_mode); } if (ds[PARSER_CONTENT]) { printf(",\n \"content_code\": \"%d\"", ds[PARSER_CONTENT]->content.d84.level1); printf(",\n \"content_type\": \"%s\"", content_type(ds[PARSER_CONTENT])); } if (ds[PARSER_FTA_CONTENT_MGMT]) { struct descriptor *d = ds[PARSER_FTA_CONTENT_MGMT]; printf(",\n \"content_mgmt\": \"%d\"", !d->content.d126.no_scramble); } if (ds[PARSER_CRID_EVENT]) { struct descriptor *d = ds[PARSER_CRID_EVENT]; printf(",\n \"event_crid\": \"%.*s\"", d->content.d118.crids[0].cridlen, d->content.d118.crids[0].crid); } if (ds[PARSER_CRID_SERIES]) { struct descriptor *d = ds[PARSER_CRID_SERIES]; printf(",\n \"series_crid\": \"%.*s\"", d->content.d118.crids[0].cridlen, d->content.d118.crids[0].crid); } if (ds[PARSER_CRID_REC]) { struct descriptor *d = ds[PARSER_CRID_REC]; printf(",\n \"rec_crid\": \"%.*s\"", d->content.d118.crids[0].cridlen, d->content.d118.crids[0].crid); } printf("\n }"); } void sqldumpstart() { printf("create table if not exists epg_load\n"); printf("(\n"); printf("service_id bigint unsigned not null default 0,\n"); printf("event_id bigint unsigned not null default 0,\n"); printf("start bigint unsigned not null,\n"); printf("end bigint unsigned not null,\n"); printf("duration bigint unsigned not null,\n"); printf("name varchar(255),\n"); printf("text varchar(255),\n"); printf("warning varchar(255),\n"); printf("guidance_mode tinyint unsigned not null,\n"); printf("content_code bigint unsigned not null,\n"); printf("content_type varchar(255),\n"); printf("content_mgmt bigint unsigned not null default 0,\n"); printf("event_crid varchar(255),\n"); printf("series_crid varchar(255),\n"); printf("rec_crid varchar(255),\n"); printf("primary key (service_id, event_id),\n"); printf("index time (start,end),\n"); printf("index start (start),\n"); printf("index event_crid (event_crid),\n"); printf("index series_crid (series_crid)\n"); printf(");\n"); printf("truncate epg_load;\n"); printf("lock tables epg_load write;\n"); } void sqldumpend() { printf("unlock tables;\n"); printf("alter table epg_load add fulltext(name);\n"); printf("alter table epg_load add fulltext(text);\n"); printf("alter table epg_load add fulltext(name,text);\n"); printf("analyze table epg_load;\n"); printf("drop table if exists epg;\n"); printf("alter table epg_load rename as epg;\n"); } void sqldump(struct epg *epg UNUSED_PARAMETER, struct section *s, struct data *d, struct descriptor **ds, void *var UNUSED_PARAMETER) { time_t tm, dur; tm = mjd(d->start_date, d->start_hour, d->start_min, d->start_sec); dur = d->dur_hour * 3600 + d->dur_min * 60 + d->dur_sec; printf("replace into epg_load set \n"); printf(" service_id = %d,\n", s->service_id); printf(" event_id = %d,\n", d->event_id); printf(" start = %ld,\n", tm); printf(" end = %ld,\n", tm + dur); printf(" duration = %ld", dur); if (ds[PARSER_SHORT_EVENT]) { struct descriptor *d = ds[PARSER_SHORT_EVENT]; DECOMPRESS(d->content.d77.name, d->content.d77.namelen); DECOMPRESS(d->content.d77.text, d->content.d77.textlen); printf(",\n name = '%s'", sql_escape_len(d->content.d77.namelen, d->content.d77.name)); printf(",\n text = '%s'", sql_escape_len(d->content.d77.textlen, d->content.d77.text)); } if (ds[PARSER_USER_DEFINED]) { struct descriptor *d = ds[PARSER_USER_DEFINED]; DECOMPRESS(d->content.d137.warning, d->content.d137.warninglen); printf(",\n warning = '%s'", sql_escape_len(d->content.d137.warninglen, d->content.d137.warning)); printf(",\n guidance_mode = '%d'", d->content.d137.guidance_mode); } if (ds[PARSER_CONTENT]) { printf(",\n content_code = %d", ds[PARSER_CONTENT]->content.d84.level1); printf(",\n content_type = '%s'", content_type(ds[PARSER_CONTENT])); } if (ds[PARSER_FTA_CONTENT_MGMT]) { printf(",\n content_mgmt = %d", !ds[PARSER_FTA_CONTENT_MGMT]->content.d126.no_scramble); } if (ds[PARSER_CRID_EVENT]) { struct descriptor *d = ds[PARSER_CRID_EVENT]; printf(",\n event_crid = '%.*s'", d->content.d118.crids[0].cridlen, d->content.d118.crids[0].crid); } if (ds[PARSER_CRID_SERIES]) { struct descriptor *d = ds[PARSER_CRID_SERIES]; printf(",\n series_crid = '%.*s'", d->content.d118.crids[0].cridlen, d->content.d118.crids[0].crid); } if (ds[PARSER_CRID_REC]) { struct descriptor *d = ds[PARSER_CRID_REC]; printf(",\n rec_crid = '%.*s'", d->content.d118.crids[0].cridlen, d->content.d118.crids[0].crid); } printf(";\n"); } void dump(struct epg *epg UNUSED_PARAMETER, struct section *s, struct data *d, struct descriptor **ds, void *var UNUSED_PARAMETER) { time_t tm; int cm; tm = mjd(d->start_date, d->start_hour, d->start_min, d->start_sec); if (ds[PARSER_SHORT_EVENT]) { struct descriptor *d77 = ds[PARSER_SHORT_EVENT]; DECOMPRESS(d77->content.d77.name, d77->content.d77.namelen); DECOMPRESS(d77->content.d77.text, d77->content.d77.textlen); } if (ds[PARSER_USER_DEFINED]) { struct descriptor *d = ds[PARSER_USER_DEFINED]; DECOMPRESS(d->content.d137.warning, d->content.d137.warninglen); } if (sysopts & SYSOPT_PARSABLE) { /* service_id, event_id, start, duration, encrypted, name, text * warning, content code, content type, * event CRID, series CRID, rec CRID, * guidance mode, content management */ printf("%d\t%d\t%ld\t%d\t%d\t", s->service_id, d->event_id, tm, d->dur_hour * 3600 + d->dur_min * 60 + d->dur_sec, d->u1.u.free_CA_mode); if (ds[PARSER_SHORT_EVENT]) { struct descriptor *d77 = ds[PARSER_SHORT_EVENT]; safeprintf("%.*s\t", d77->content.d77.namelen, d77->content.d77.name); safeprintf("%.*s\t", d77->content.d77.textlen, d77->content.d77.text); } else printf("\t\t"); if (ds[PARSER_USER_DEFINED]) { struct descriptor *d137 = ds[PARSER_USER_DEFINED]; safeprintf("%.*s\t", d137->content.d137.warninglen, d137->content.d137.warning); } else printf("\t"); if (ds[PARSER_CONTENT]) { printf("%d\t", ds[PARSER_CONTENT]->content.d84.level1); printf("%s\t", content_type(ds[PARSER_CONTENT])); } else printf("\t\t"); if (ds[PARSER_CRID_EVENT]) { struct descriptor *d = ds[PARSER_CRID_EVENT]; printf("%.*s\t", d->content.d118.crids[0].cridlen, d->content.d118.crids[0].crid); } else printf("\t"); if (ds[PARSER_CRID_SERIES]) { struct descriptor *d = ds[PARSER_CRID_SERIES]; printf("%.*s\t", d->content.d118.crids[0].cridlen, d->content.d118.crids[0].crid); } else printf("\t"); if (ds[PARSER_CRID_REC]) { struct descriptor *d = ds[PARSER_CRID_REC]; printf("%.*s\t", d->content.d118.crids[0].cridlen, d->content.d118.crids[0].crid); } else printf("\t"); if (ds[PARSER_USER_DEFINED]) { struct descriptor *d137 = ds[PARSER_USER_DEFINED]; printf("%d\t", d137->content.d137.guidance_mode); } else printf("\t"); if (ds[PARSER_FTA_CONTENT_MGMT]) { printf("%d\t", !ds[PARSER_FTA_CONTENT_MGMT]-> content.d126.no_scramble); } else printf("\t"); printf("\n"); return; } printf("----------------------------------------------------------\n"); if (sysopts & SYSOPT_BRIEF) { safeprintf("%d/%d: %s+%d\n", s->service_id, d->event_id, ctime_nl(&tm), d->dur_hour * 3600 + d->dur_min * 60 + d->dur_sec); if (ds[PARSER_SHORT_EVENT]) { struct descriptor *d77 = ds[PARSER_SHORT_EVENT]; safeprintf("Name:%.*s\n", d77->content.d77.namelen, d77->content.d77.name); safeprintf("Text:%.*s\n", d77->content.d77.textlen, d77->content.d77.text); } if (ds[PARSER_USER_DEFINED]) { struct descriptor *d137 = ds[PARSER_USER_DEFINED]; safeprintf("Warning:%.*s\n", d137->content.d137.warninglen, d137->content.d137.warning); } return; } printf("%30s: %d\n", "Service ID", s->service_id); printf("%30s: %d\n", "Event ID", d->event_id); printf("%30s: %#x %d:%02d:%02d (%s)\n", "Start Date", d->start_date, d->start_hour, d->start_min, d->start_sec, ctime_nl(&tm)); printf("%30s: %d:%02d:%02d\n", "Duration", d->dur_hour, d->dur_min, d->dur_sec); printf("%30s: %d\n", "Encrypted", d->u1.u.free_CA_mode); cm = 1; if (ds[PARSER_FTA_CONTENT_MGMT]) { cm = ds[PARSER_FTA_CONTENT_MGMT]->content.d126.no_scramble; } printf("%30s: %s\n", "Content Mgmt", cm ? "No" : "Yes"); if (ds[PARSER_SHORT_EVENT]) { struct descriptor *d77 = ds[PARSER_SHORT_EVENT]; safeprintf("%30s: %.*s\n", "Name", d77->content.d77.namelen, d77->content.d77.name); safeprintf("%30s: %.*s\n", "Text", d77->content.d77.textlen, d77->content.d77.text); } if (ds[PARSER_USER_DEFINED]) { struct descriptor *d137 = ds[PARSER_USER_DEFINED]; safeprintf("%30s: %.*s\n", "Warning", d137->content.d137.warninglen, d137->content.d137.warning); printf("%30s: %d\n", "Guidance Type", d137->content.d137.guidance_type); printf("%30s: %d\n", "Guidance Mode", d137->content.d137.guidance_mode); } if (ds[PARSER_CONTENT]) printf("%30s: %s (%d)\n", "Content Type", content_type(ds[PARSER_CONTENT]), ds[PARSER_CONTENT]->content.d84.level1); if (ds[PARSER_CRID_EVENT]) printf("%30s: %.*s\n", "Event CRID", ds[PARSER_CRID_EVENT]->content.d118.crids[0].cridlen, ds[PARSER_CRID_EVENT]->content.d118.crids[0].crid); if (ds[PARSER_CRID_SERIES]) printf("%30s: %.*s\n", "Series CRID", ds[PARSER_CRID_SERIES]->content.d118.crids[0].cridlen, ds[PARSER_CRID_SERIES]->content.d118.crids[0].crid); if (ds[PARSER_CRID_REC]) { struct descriptor *d118 = ds[PARSER_CRID_REC]; int i; for (i = 0; i < d118->content.d118.i; i++) { struct crid *crid = &d118->content.d118.crids[i]; printf("%30s: %.*s\n", "Recommended CRID", crid->cridlen, crid->crid); } } } void search(struct epg *epg UNUSED_PARAMETER, struct section *s, struct data *d, struct descriptor **ds, void *var) { if (ds[PARSER_SHORT_EVENT]) { struct descriptor *d77 = ds[PARSER_SHORT_EVENT]; DECOMPRESS(d77->content.d77.name, d77->content.d77.namelen); if (strcasestr(ds[PARSER_SHORT_EVENT]->content.d77.name, (char *)var)) dump(epg, s, d, ds, NULL); } } void searchall(struct epg *epg UNUSED_PARAMETER, struct section *s, struct data *d, struct descriptor **ds, void *var) { if (ds[PARSER_SHORT_EVENT]) { struct descriptor *d77 = ds[PARSER_SHORT_EVENT]; DECOMPRESS(d77->content.d77.name, d77->content.d77.namelen); DECOMPRESS(d77->content.d77.text, d77->content.d77.textlen); if (strcasestr(ds[PARSER_SHORT_EVENT]->content.d77.name, (char *)var) || strcasestr(ds[PARSER_SHORT_EVENT]->content.d77.text, (char *)var)) dump(epg, s, d, ds, NULL); } } int main(int argc, char **argv) { char *epgpath = DEFAULT_EPG_FILE; struct epgfilter *filter = NULL; char *cp; argc--, argv++; while (argc > 0 && *argv[0] == '-') { for (cp = &argv[0][1]; *cp; cp++) { char opt; switch (opt = *cp) { case 'b': sysopts |= SYSOPT_BRIEF; break; case 'd': GETOPTINTOPT; if (*cp == '\0') debug = 1; else debug = atoi(cp); goto nextopt; case 'f': GETOPT; epgpath = strdup(cp); goto nextopt; case 'h': return syntax(); case 'p': sysopts |= SYSOPT_PARSABLE; break; /* Filters */ case '@': GETOPT; add_epgfilter(&filter, FILTER_TIMESTAMP, atoi(cp), 0, NULL, FT_EQUAL); goto nextopt; case '/': { unsigned long dstart, dend; time_t dat; struct tm *tm; GETOPT; dat = atoi(cp); /* If specified as number of days, then convert * to timestamp. */ if (dat < 1000) dat = time(NULL) + dat * 86400; tm = localtime(&dat); tm->tm_hour = tm->tm_min = tm->tm_sec = 0; dstart = (unsigned long) mktime(tm); tm->tm_hour = 23; tm->tm_min = tm->tm_sec = 59; dend = (unsigned long) mktime(tm); add_epgfilter(&filter, FILTER_TIMESTAMP, dstart, dend, NULL, FT_RANGE); goto nextopt; } case '=': { unsigned long dstart, dend; char *p; GETOPT; if (!(p = strchr(cp, ':'))) { fprintf(stderr, "Bad argument to -=\n"); exit(1); } *p++ = '\0'; dstart = strtoul(cp, (char **)NULL, 10); dend = strtoul(p, (char **)NULL, 10); if (dstart >= dend) { fprintf(stderr, "Bad time range to -=\n"); exit(1); } add_epgfilter(&filter, FILTER_TIMERANGE, dstart, dend, NULL, FT_RANGE); goto nextopt; } case 'C': GETOPT; add_epgfilter(&filter, FILTER_CRID, 0, 0, cp, FT_EQUAL); goto nextopt; case 'D': GETOPT; add_epgfilter(&filter, FILTER_DESCRIPTOR, atoi(cp), 0, NULL, FT_EQUAL); goto nextopt; case 'E': GETOPT; add_epgfilter(&filter, FILTER_EVENT, atoi(cp), 0, NULL, FT_EQUAL); goto nextopt; case 'S': GETOPT; add_epgfilter(&filter, FILTER_SERVICE, atoi(cp), 0, NULL, FT_EQUAL); goto nextopt; case 'R': GETOPT; add_epgfilter(&filter, FILTER_SCRID, 0, 0, cp, FT_EQUAL); goto nextopt; case 'T': GETOPT; add_epgfilter(&filter, FILTER_CONTENT, atoi(cp), 0, NULL, FT_EQUAL); goto nextopt; case 'G': GETOPT; add_epgfilter(&filter, FILTER_GUIDETYPE, atoi(cp), 0, NULL, FT_EQUAL); goto nextopt; } } nextopt: argc--, argv++; } if (argc < 1) return syntax(); if (!strcmp(argv[0], "dump")) parse(epgpath, dump, NULL, filter); else if (!strcmp(argv[0], "parse")) parse(epgpath, pass, NULL, filter); else if (!strcmp(argv[0], "last")) { parse(epgpath, latest, NULL, filter); printf("%ld\n", latest_stamp); } else if (!strcmp(argv[0], "d78")) parse(epgpath, d78check, NULL, filter); else if (!strcmp(argv[0], "first")) { parse(epgpath, earliest, NULL, filter); printf("%ld\n", latest_stamp); } else if (!strcmp(argv[0], "dumpraw")) parse(epgpath, dumpraw, NULL, filter); else if (!strcmp(argv[0], "sqldump")) { sqldumpstart(); parse(epgpath, sqldump, NULL, filter); sqldumpend(); } else if (!strcmp(argv[0], "json")) { jsonstart(); parse(epgpath, json, NULL, filter); jsonend(); } #ifdef HAVE_SQLITE3 else if (!strcmp(argv[0], "sqlitedump") && argc == 2) { int r; sqlitedumpstart(argv[1]); r = parse(epgpath, sqlitedump, NULL, filter); sqlitedumpend(argv[1], r); } else if (!strcmp(argv[0], "sqlitedumpd") && argc == 2) { time_t last = 0; for (;;) { struct stat st; while (stat(epgpath, &st) == -1) sleep(30); printf("epgd: DB time %ld, last %ld\n", st.st_mtime, last); fflush(stdout); if (st.st_mtime > last) { time_t tm = time(NULL); int r; printf("epgd: Regenerating.\n"); fflush(stdout); sqlitedumpstart(argv[1]); r = parse(epgpath, sqlitedump, NULL, filter); sqlitedumpend(argv[1], r); printf("epgd: Done in %ld seconds.\n", time(NULL) - tm); fflush(stdout); } last = st.st_mtime; sleep(900); } } #endif else if (!strcmp(argv[0], "now")) { time_t tm; time(&tm); add_epgfilter(&filter, FILTER_TIMESTAMP, (unsigned long) tm, 0, NULL, FT_EQUAL); parse(epgpath, dump, NULL, filter); } else if (!strcmp(argv[0], "search") && argc > 1) parse(epgpath, search, (void *)argv[1], filter); else if (!strcmp(argv[0], "searchall") && argc > 1) parse(epgpath, searchall, (void *)argv[1], filter); else syntax(); return 0; }