diff --git a/descriptor.c b/descriptor.c index e82dd96..f01128b 100644 --- a/descriptor.c +++ b/descriptor.c @@ -12,16 +12,19 @@ struct descriptor * read_descriptor_header(struct epg *epg) { - static struct descriptor d; + struct descriptor *d; + + d = (struct descriptor *)malloc(sizeof(struct descriptor)); + d->loaded = 0; if (epg->binsize - epg->offset < 2) return NULL; - memcpy(&d, epg->bin + epg->offset, 2); + memcpy(d, epg->bin + epg->offset, 2); epg->offset += 2; - return &d; + return d; } static char * @@ -44,25 +47,37 @@ string_to_end(struct epg *epg, struct descriptor *d, int sofar, return str; } +static char * +read_lstring(struct epg *epg, unsigned int *len) +{ + int l = epg->bin[epg->offset++]; + char *c; + + c = malloc(l + 1); + memcpy(c, epg->bin + epg->offset, l); + c[l] = '\0'; + epg->offset += l; + + if (len) + *len = l; + + return c; +} + void read_descriptor(struct epg *epg, struct descriptor *d) { + d->loaded = 1; switch (d->tag) { case DS_SHORT_EVENT: memcpy(d->content.d77.lang, epg->bin + epg->offset, 3); epg->offset += 3; - d->content.d77.namelen = epg->bin[epg->offset++]; - d->content.d77.name = read_string_len(epg->bin + epg->offset, - d->content.d77.namelen); - epg->offset += d->content.d77.namelen; - - d->content.d77.textlen = epg->bin[epg->offset++]; - d->content.d77.text = read_string_len(epg->bin + epg->offset, - d->content.d77.textlen); - epg->offset += d->content.d77.textlen; - + d->content.d77.name = + read_lstring(epg, &d->content.d77.namelen); + d->content.d77.text = + read_lstring(epg, &d->content.d77.textlen); break; case DS_COMPONENT: @@ -74,10 +89,8 @@ read_descriptor(struct epg *epg, struct descriptor *d) break; case DS_USER_DEFINED: - d->content.d137.textlen = epg->bin[epg->offset++]; - d->content.d137.text = read_string_len(epg->bin + epg->offset, - d->content.d137.textlen); - epg->offset += d->content.d137.textlen; + d->content.d137.text = + read_lstring(epg, &d->content.d137.textlen); memcpy(d->content.d137.lang, epg->bin + epg->offset, 3); epg->offset += 3; @@ -87,29 +100,44 @@ read_descriptor(struct epg *epg, struct descriptor *d) break; case DS_CONTENT_IDENTIFIER: - memcpy(&d->content.d118, epg->bin + epg->offset, 1); - epg->offset++; - d->content.d118.cridlen = d->content.d118.ref = 0; - switch (d->content.d118.location) + { + unsigned int end = epg->offset + d->len; + + d->content.d118.i = 0; + + while (epg->offset < end) { - case 0: - d->content.d118.cridlen = epg->bin[epg->offset++]; - d->content.d118.crid = read_string_len( - epg->bin + epg->offset, d->content.d118.cridlen); - epg->offset += d->content.d118.cridlen; - break; - - case 1: - d->content.d118.ref = - _swap16((uint16_t)epg->bin[epg->offset]); - /* Not safe to use post increment in macro */ + struct crid *crid = + &d->content.d118.crids[d->content.d118.i]; + + memcpy(crid, epg->bin + epg->offset, 1); epg->offset++; - break; - default: - printf("Unknown CRID location - %d\n", - d->content.d118.location); + crid->cridlen = crid->ref = 0; + switch (crid->location) + { + case 0: + crid->crid = read_lstring(epg, &crid->cridlen); + break; + + case 1: + crid->ref = + _swap16((uint16_t)epg->bin[epg->offset]); + /* Not safe to use post increment in macro */ + epg->offset++; + break; + + default: + printf("Unknown CRID location - %d\n", + crid->location); + } + if (d->content.d118.i++ >= 3) + { + printf("Too many content IDs.\n"); + exit(0); + } } break; + } default: printf("Default descriptor: %d\n", d->tag); @@ -132,33 +160,45 @@ skip_descriptor(struct epg *epg, struct descriptor *d) void free_descriptor(struct descriptor *d) { - switch (d->tag) + if (d->loaded) { - case DS_SHORT_EVENT: - if (d->content.d77.name) - free(d->content.d77.name); - if (d->content.d77.text) - free(d->content.d77.text); - break; - case DS_COMPONENT: - if (d->content.d80.text) - free(d->content.d80.text); - break; - case DS_USER_DEFINED: - if (d->content.d137.text) - free(d->content.d137.text); - if (d->content.d137.warning) - free(d->content.d137.warning); - break; - case DS_CONTENT_IDENTIFIER: - if (d->content.d118.crid) - free(d->content.d118.crid); - break; - default: - if (d->content.unknown.text) - free(d->content.unknown.text); - break; + switch (d->tag) + { + case DS_SHORT_EVENT: + if (d->content.d77.name) + free(d->content.d77.name); + if (d->content.d77.text) + free(d->content.d77.text); + break; + case DS_COMPONENT: + if (d->content.d80.text) + free(d->content.d80.text); + break; + case DS_USER_DEFINED: + if (d->content.d137.text) + free(d->content.d137.text); + if (d->content.d137.warning) + free(d->content.d137.warning); + break; + case DS_CONTENT_IDENTIFIER: + { + int i; + + for (i = 0; i < d->content.d118.i; i++) + { + struct crid *crid = &d->content.d118.crids[i]; + if (crid->cridlen) + free(crid->crid); + } + break; + } + default: + if (d->content.unknown.text) + free(d->content.unknown.text); + break; + } } + free(d); } char * @@ -187,7 +227,7 @@ dump_descriptor(struct descriptor *d, int content) descriptor_name(d)); DUMPINT(d, len); - if (!content) + if (!content || !d->loaded) return; switch (d->tag) @@ -195,10 +235,8 @@ dump_descriptor(struct descriptor *d, int content) case DS_SHORT_EVENT: DUMPNSTR(d, content.d77.lang, 3); DUMPINT(d, content.d77.namelen); - //DUMPNSTR(d, content.d77.name, d->content.d77.namelen); DUMPHEX(d, content.d77.name, d->content.d77.namelen); DUMPINT(d, content.d77.textlen); - //DUMPNSTR(d, content.d77.text, d->content.d77.textlen); DUMPHEX(d, content.d77.text, d->content.d77.textlen); break; @@ -220,13 +258,23 @@ dump_descriptor(struct descriptor *d, int content) break; case DS_CONTENT_IDENTIFIER: - DUMPINT(d, content.d118.location); - DUMPINT(d, content.d118.type); - DUMPINT(d, content.d118.cridlen); - if (d->content.d118.cridlen) - DUMPHEX(d, content.d118.crid, d->content.d118.cridlen); - DUMPINT(d, content.d118.ref); + { + int i; + + DUMPINT(d, content.d118.i); + + for (i = 0; i < d->content.d118.i; i++) + { + struct crid *crid = &d->content.d118.crids[i]; + DUMPINT(crid, location); + DUMPINT(crid, type); + DUMPINT(crid, cridlen); + if (crid->cridlen) + DUMPHEX(crid, crid, crid->cridlen); + DUMPINT(crid, ref); + } break; + } default: if (d->content.unknown.textlen) diff --git a/descriptor.h b/descriptor.h index 79d56b1..0ce2965 100644 --- a/descriptor.h +++ b/descriptor.h @@ -7,14 +7,27 @@ #define DS_FTA_CONTENT_MGMT 126 #define DS_USER_DEFINED 137 +#define PARSER_SHORT_EVENT 0 +#define PARSER_CONTENT_ID 1 +#define PARSER_USER_DEFINED 2 + +#pragma pack(1) +struct crid { + unsigned int location:2; + unsigned int type:6; + unsigned int cridlen; + char *crid; + unsigned int ref:16; +}; + struct descriptor { unsigned int tag:8; unsigned int len:8; union { struct { char lang[3]; - unsigned int namelen:8; - unsigned int textlen:8; + unsigned int namelen; + unsigned int textlen; char *name; char *text; } d77; /* SHORT_EVENT */ @@ -28,18 +41,15 @@ struct descriptor { unsigned int textlen; } d80; /* COMPONENT */ struct { - unsigned int textlen:8; + unsigned int textlen; char *text; char lang[3]; char *warning; unsigned int warninglen; } d137; /* USER_DEFINED - content warnings? */ struct { - unsigned int location:2; - unsigned int type:6; - unsigned int cridlen:8; - char *crid; - unsigned int ref:16; + struct crid crids[3]; + int i; } d118; /* CONTENT_IDENTIFIER */ struct { char *text; @@ -47,5 +57,6 @@ struct descriptor { } unknown; /* 74, 84, 95, 126, ... */ } content; + int loaded; }; diff --git a/epg.c b/epg.c index 3e0aa29..f6abbbd 100644 --- a/epg.c +++ b/epg.c @@ -71,6 +71,14 @@ read_data(struct epg *epg) d.start_date = _swap16(d.start_date); d.u1.comp = _swap16(d.u1.comp); + d.start_hour = bcd(d.start_hour); + d.start_min = bcd(d.start_min); + d.start_sec = bcd(d.start_sec); + + d.dur_hour = bcd(d.dur_hour); + d.dur_min = bcd(d.dur_min); + d.dur_sec = bcd(d.dur_sec); + epg->offset += sizeof(d); return &d; @@ -84,12 +92,11 @@ dump_data(struct data *d) printf("Data:\n"); DUMPINT(d, event_id); - tm = MJD_TO_UNIX(d->start_date); - printf(" %30s: %#x (%s)\n", "start_date", - d->start_date, ctime_nl(&tm)); + tm = mjd(d->start_date, d->start_hour, d->start_min, d->start_sec); + 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", "start_time", - d->start_hour, d->start_min, d->start_sec); printf(" %30s: %d:%02d:%02d\n", "duration", d->dur_hour, d->dur_min, d->dur_sec); @@ -98,3 +105,85 @@ dump_data(struct data *d) DUMPINT(d, u1.u.descriptors_loop_length); } +void +parse(char *epgpath, + void (*callback)(struct epg *, struct section *, struct data *, + struct descriptor **, void *), void *val) +{ + struct descriptor *dslist[3]; + struct epg *epg; + int i; + + if (!(epg = open_file(epgpath))) + return; + + while (epg->offset < epg->binsize) + { + struct section *s; + uint32_t send; + + if (!(s = read_section(epg))) + break; + + send = epg->offset - 11 + s->u1.u.length - 4; + + while (epg->offset < send) + { + struct data *d; + uint32_t dend; + + if (!(d = read_data(epg))) + break; + + dslist[0] = dslist[1] = dslist[2] = NULL; + + dend = epg->offset + d->u1.u.descriptors_loop_length; + while (epg->offset < dend) + { + struct descriptor *ds; + + if (!(ds = read_descriptor_header(epg))) + continue; + + switch (ds->tag) + { + case DS_SHORT_EVENT: + read_descriptor(epg, ds); + dslist[PARSER_SHORT_EVENT] = ds; + break; + + case DS_CONTENT_IDENTIFIER: + read_descriptor(epg, ds); + dslist[PARSER_CONTENT_ID] = ds; + break; + + case DS_USER_DEFINED: + read_descriptor(epg, ds); + dslist[PARSER_USER_DEFINED] = ds; + break; + + default: + skip_descriptor(epg, ds); + free_descriptor(ds); + break; + } + } + + callback(epg, s, d, dslist, val); + + for (i = 0; i < 3; i++) + if (dslist[i]) + free_descriptor(dslist[i]); + } + + /* Skip CRC */ + epg->offset += 4; + + /* Skip padding bytes... */ + while (epg->bin[epg->offset] == 'U') + epg->offset++; + } + + close_file(epg); +} + diff --git a/epg.h b/epg.h index aa9438e..a0233a7 100644 --- a/epg.h +++ b/epg.h @@ -1,6 +1,8 @@ #include +#define DEFAULT_EPG_FILE "/mnt/hd1/dvbepg/epg.dat" + struct epg { char fname[MAXPATHLEN + 1]; int fd; diff --git a/lint.h b/lint.h index 749e096..c6dc81c 100644 --- a/lint.h +++ b/lint.h @@ -3,6 +3,8 @@ #include "descriptor.h" #include "util.h" +#define SYSOPT_PARSABLE 0x1 + extern int debug; extern const char *version; extern unsigned long sysopts; @@ -12,7 +14,8 @@ inline uint32_t read_uint32(uint8_t *, int); void hexdump(uint8_t *, uint32_t, uint32_t); char *hexstr(uint8_t *, uint32_t); char *ctime_nl(time_t *); -char *read_string_len(uint8_t *, uint32_t); +time_t mjd(uint16_t, int, int, int); +void safeprintf(char *, ...); struct epg *open_file(char *); void close_file(struct epg *); @@ -23,9 +26,17 @@ void dump_section(struct section *); struct data *read_data(struct epg *); void dump_data(struct data *); +void parse(char *, + void (*)(struct epg *, struct section *, struct data *, + struct descriptor **, void *), void *); + struct descriptor *read_descriptor_header(struct epg *); void read_descriptor(struct epg *, struct descriptor *); void skip_descriptor(struct epg *, struct descriptor *); void dump_descriptor(struct descriptor *, int); void free_descriptor(struct descriptor *); +#ifdef sun +char *strcasestr (char *, char *); +#endif + diff --git a/main.c b/main.c index d10df03..fc7ddd5 100644 --- a/main.c +++ b/main.c @@ -25,69 +25,212 @@ syntax() { fprintf(stderr, "Humax EPG Tool v%s, by af123, 2011.\n\n", version); fprintf(stderr, - "Syntax: epg ...\n"); + "Syntax: epg [options] ...\n"); + fprintf(stderr, + " Options:\n" + " -d[level] Set debug level.\n" + " -f Specify alternate EPG data file.\n" + " -h Show help text.\n" + " -p Parsable output.\n" + ); + fprintf(stderr, "\n"); fprintf(stderr, " Commands:\n" + " dump Show a parsed summary of the EPG.\n" + " search Search programme names for text.\n" + " searchall " + "Search programme names/descriptions for text.\n" ); fprintf(stderr, "\n"); return 0; } +void +dump(struct epg *epg __attribute__((unused)), + struct section *s, struct data *d, struct descriptor **ds, + void *var __attribute__((unused))) +{ + time_t tm; + + tm = mjd(d->start_date, d->start_hour, d->start_min, d->start_sec); + + printf("----------------------------------------------------------\n"); + + if (sysopts & SYSOPT_PARSABLE) + { + printf("ServiceID:%d\n", s->service_id); + printf("EventID:%d\n", d->event_id); + printf("Start:%ld\n", tm); + printf("Duration:%d\n", + d->dur_hour * 3600 + d->dur_min * 60 + d->dur_sec); + printf("Encrypted:%d\n", d->u1.u.free_CA_mode); + 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); + } + + if (ds[PARSER_CONTENT_ID]) + { + struct descriptor *d118 = ds[PARSER_CONTENT_ID]; + int i; + + printf("nCRIDs:%d\n", d118->content.d118.i); + + for (i = 0; i < d118->content.d118.i; i++) + { + struct crid *crid = + &d118->content.d118.crids[i]; + + printf("CRID:%d:%.*s\n", crid->type, + crid->cridlen, crid->crid); + } + } + 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); + + 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); + } + + if (ds[PARSER_CONTENT_ID]) + { + struct descriptor *d118 = ds[PARSER_CONTENT_ID]; + int i; + + printf("%30s: %d\n", "CRIDs", d118->content.d118.i); + + for (i = 0; i < d118->content.d118.i; i++) + { + struct crid *crid = &d118->content.d118.crids[i]; + printf("%30s: %d\n", "CRID Type", crid->type); + printf("%30s: %.*s\n", "CRID", + crid->cridlen, crid->crid); + } + } +} + +void +search(struct epg *epg __attribute__((unused)), + struct section *s, struct data *d, struct descriptor **ds, + void *var) +{ + if (ds[PARSER_SHORT_EVENT] && + strcasestr(ds[PARSER_SHORT_EVENT]->content.d77.name, (char *)var)) + dump(epg, s, d, ds, NULL); +} + +void +searchall(struct epg *epg __attribute__((unused)), + struct section *s, struct data *d, struct descriptor **ds, + void *var) +{ + if (ds[PARSER_SHORT_EVENT] && ( + 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) { - struct section *h; - struct epg *epg; - int i = 0; + char *epgpath = DEFAULT_EPG_FILE; + char *cp; - if (!(epg = open_file("../epg.dat"))) - return 0; - - while (epg->offset < epg->binsize) - { - uint32_t send; - - if (!(h = read_section(epg))) - break; - //dump_section(h); - - send = epg->offset - 11 + h->u1.u.length - 4; - - while (epg->offset < send) + argc--, argv++; + while (argc > 0 && *argv[0] == '-') + { + for (cp = &argv[0][1]; *cp; cp++) { - struct data *d; - uint32_t dend; + switch (*cp) + { + case 'd': + if (*++cp == '\0') + debug = 1; + else + debug = atoi(cp); + goto nextopt; - if (!(d = read_data(epg))) + case 'f': + if (*++cp == '\0') + { + fprintf(stderr, + "No argument supplied for -f\n"); + exit(1); + } + else + { + while (isspace((int)*cp)) + cp++; + epgpath = strdup(cp); + } + goto nextopt; + + case 'h': + return syntax(); + + case 'p': + sysopts |= SYSOPT_PARSABLE; break; - //dump_data(d); - - dend = epg->offset + d->u1.u.descriptors_loop_length; - while (epg->offset < dend) - { - struct descriptor *ds; - - ds = read_descriptor_header(epg); - read_descriptor(epg, ds); - -/* - if (ds->tag == DS_CONTENT_IDENTIFIER) - dump_descriptor(ds, 1); -*/ - - free_descriptor(ds); } } - - /* Skip CRC */ - epg->offset += 4; - - /* Skip padding bytes... */ - while (epg->bin[epg->offset] == 'U') - epg->offset++; +nextopt: + argc--, argv++; } - close_file(epg); + + if (argc < 1) + return syntax(); + + if (!strcmp(argv[0], "dump")) + parse(epgpath, dump, NULL); + else if (!strcmp(argv[0], "search") && argc > 1) + parse(epgpath, search, (void *)argv[1]); + else if (!strcmp(argv[0], "searchall") && argc > 1) + parse(epgpath, searchall, (void *)argv[1]); + else + syntax(); return 0; } diff --git a/test b/test deleted file mode 100644 index d55b2d9..0000000 --- a/test +++ /dev/null @@ -1,42 +0,0 @@ -{'CRC_32': 3423408323L, - 'current_next_indicator': 1, - 'datas': [{'descriptors': [{'descriptor_length': 2, - 'descriptor_tag': 84, - 'unknown': '\x00\x00'}, - {'ISO_639_language_code': 'eng', - 'component_tag': 1, - 'component_type': 1, - 'descriptor_length': 6, - 'descriptor_tag': 80, - 'reserved_future_use': 15, - 'stream_content': 2, - 'text': ''}, - {'ISO_639_language_code': 'eng', - 'descriptor_length': 38, - 'descriptor_tag': 77, - 'event_name': '\x1f\x01\x05g60\xe3/U\x86$~\xe8\x80', - 'event_name_length': 14, - 'text': '\x1f\x01\xa9\x89\x1f\xbaS/UO\xaf\xb6\x9dNj\xc8a\xeb\x9d', - 'text_length': 19}], - 'descriptors_loop_length': 52, - 'duration': datetime.timedelta(0, 21600), - 'event_id': 795, - 'free_CA_mode': 0, - 'running_status': 0, - 'start_time': datetime.datetime(2011, 6, 1, 0, 0)}], - 'last_section_number': 248, - 'last_table_id': 97, - 'original_network_id': 9018, - 'pre_header_1': '(Gq\x87\x00\x00\x00\x00\x02\x00\x00\x00\x00\x01', - 'pre_header_2': '\x00\x00\x00\x00', - 'reserved': 3, - 'reserved_future_use': 1, - 'section_length': 79, - 'section_number': 0, - 'section_syntax_indicator': 1, - 'segment_last_section_number': 0, - 'service_id': 26560, - 'table_id': 96, - 'total_length': 84, - 'transport_stream_id': 24576, - 'version_number': 6} diff --git a/util.c b/util.c index 3852d6b..98f9e67 100644 --- a/util.c +++ b/util.c @@ -12,6 +12,70 @@ #include "lint.h" +#ifdef sun +char * +strcasestr (char *h, char *n) +{ + char *hp, *np = n, *match = 0; + + if(!*np) { + return np; + } + + for (hp = h; *hp; hp++) { + if (toupper(*hp) == toupper(*np)) { + if (!match) { + match = hp; + } + if(!*++np) { + return match; + } + } else { + if (match) { + match = 0; + np = n; + } + } + } + + return NULL; +} +#endif + +void +safeprintf(char *fmt, ...) +{ + char buf[0x400]; + char *p; + va_list argp; + int len; + + va_start(argp, fmt); + len = vsprintf(buf, fmt, argp); + va_end(argp); + + for (p = buf; p < buf + len; p++) + if (!isprint(*p)) + *p = '.'; + + printf("%.*s\n", len, buf); +} + +time_t +mjd(uint16_t day, int h, int m, int s) +{ + time_t tm; + struct tm *t; + + tm = MJD_TO_UNIX(day); + t = localtime(&tm); + t->tm_hour = h; + t->tm_min = m; + t->tm_sec = s; + + return mktime(t); +} + char * ctime_nl(time_t *tm) { @@ -23,15 +87,6 @@ ctime_nl(time_t *tm) return buf; } -inline char * -read_string_len(uint8_t *src, uint32_t len) -{ - char *s = malloc(len + 1); - memcpy(s, src, len); - s[len] = '\0'; - return s; -} - inline uint32_t read_uint32(uint8_t *p, int le) { diff --git a/util.h b/util.h index 293e921..afae7fb 100644 --- a/util.h +++ b/util.h @@ -4,6 +4,8 @@ #define _swap16(v) ((((v) >> 8) & 0xff) | (((v) & 0xff) << 8)) +#define bcd(v) (((((v) >> 4) & 0xf) * 10) + ((v) & 0xf)) + #define DUMPINT(ss,xx) printf(" %30s: %d\n", #xx, ss->xx) #define DUMPSTR(ss,xx) printf(" %30s: %s\n", #xx, ss->xx) #define DUMPNSTR(ss,xx,nn) printf(" %30s: %.*s\n", #xx, nn, ss->xx)