398 lines
9.6 KiB
C
398 lines
9.6 KiB
C
/*
|
|
* Humax EPG Tool
|
|
* by af123, 2011
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <ctype.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <sys/time.h>
|
|
#include <sys/stat.h>
|
|
#include <time.h>
|
|
#include "lint.h"
|
|
|
|
int debug = 0;
|
|
const char *version = "1.0.1";
|
|
unsigned long sysopts = 0;
|
|
|
|
int
|
|
syntax()
|
|
{
|
|
fprintf(stderr, "Humax EPG Tool v%s, by af123, 2011.\n\n", version);
|
|
fprintf(stderr,
|
|
"Syntax: epg [options] [filters] <command>...\n\n");
|
|
fprintf(stderr,
|
|
" Options:\n"
|
|
" -b Brief output.\n"
|
|
" -d[level] Set debug level.\n"
|
|
" -f<file> Specify alternate EPG data file.\n"
|
|
" -h Show help text.\n"
|
|
" -p Parsable output.\n"
|
|
"\n"
|
|
);
|
|
fprintf(stderr,
|
|
" Filters: (can be specified multiple times, all must be true)\n"
|
|
" -C<CRID> Show only events with this CRID.\n"
|
|
" -D<descriptor type> Show only selected descriptor type.\n"
|
|
" -E<event id> Show only selected event.\n"
|
|
" -S<service id> Show only selected service.\n"
|
|
" -@<unix timestamp> Show only programmes at time.\n"
|
|
"\n"
|
|
);
|
|
fprintf(stderr,
|
|
" Commands:\n"
|
|
" dump Show a parsed summary of the EPG.\n"
|
|
" now Show what is currently on.\n"
|
|
" search <text> Search programme names for text.\n"
|
|
" searchall <text> "
|
|
"Search programme names/descriptions for text.\n"
|
|
);
|
|
fprintf(stderr, "\n");
|
|
return 0;
|
|
}
|
|
|
|
#define DECOMPRESS(str, len) \
|
|
if (*(str) == 0x1f) uncompress_epg(&(str), &(len))
|
|
|
|
void
|
|
dumpraw(struct epg *epg __attribute__((unused)),
|
|
struct section *s, struct data *d, struct descriptor **ds,
|
|
void *var __attribute__((unused)))
|
|
{
|
|
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_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);
|
|
}
|
|
|
|
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);
|
|
|
|
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 (sysopts & SYSOPT_PARSABLE)
|
|
{
|
|
/* service_id, event_id, start, duration, encrypted, name, text
|
|
* warning, num_crids, crid_type, crid, ...
|
|
*/
|
|
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_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");
|
|
|
|
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);
|
|
|
|
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_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 __attribute__((unused)),
|
|
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 __attribute__((unused)),
|
|
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);
|
|
}
|
|
}
|
|
|
|
#define GETOPT \
|
|
do { \
|
|
if (*++cp == '\0' && argc < 2) \
|
|
{ \
|
|
fprintf(stderr, \
|
|
"No argument supplied for -%c\n", opt); \
|
|
exit(1); \
|
|
} \
|
|
else if (*cp == '\0') \
|
|
{ \
|
|
argc--, argv++; \
|
|
cp = argv[0]; \
|
|
} \
|
|
while (isspace((int)*cp)) \
|
|
cp++; \
|
|
} while (0)
|
|
|
|
|
|
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':
|
|
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);
|
|
/* Global flag to indicate that the
|
|
* timestamp check is required - has overhead
|
|
* so don't want to do it in all cases. */
|
|
sysopts |= SYSOPT_TIMESTAMP;
|
|
goto nextopt;
|
|
|
|
case 'C':
|
|
GETOPT;
|
|
add_epgfilter(&filter, FILTER_CRID,
|
|
0, 0, cp, FT_EQUAL);
|
|
/* Global flag to indicate that CRID filtering
|
|
* has been requested. Required so that
|
|
* events with no event CRID aren't shown
|
|
* when filtering. */
|
|
sysopts |= SYSOPT_CRIDFILTER;
|
|
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;
|
|
}
|
|
}
|
|
nextopt:
|
|
argc--, argv++;
|
|
}
|
|
|
|
if (argc < 1)
|
|
return syntax();
|
|
|
|
if (!strcmp(argv[0], "dump"))
|
|
parse(epgpath, dump, NULL, filter);
|
|
else if (!strcmp(argv[0], "dumpraw"))
|
|
parse(epgpath, dumpraw, NULL, filter);
|
|
else if (!strcmp(argv[0], "now"))
|
|
{
|
|
time_t tm;
|
|
|
|
time(&tm);
|
|
add_epgfilter(&filter, FILTER_TIMESTAMP, 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;
|
|
}
|
|
|