epg/main.c

1116 lines
27 KiB
C

/*
* Humax EPG Tool
* by af123, 2011 - 2024
*/
#define UNUSED_PARAMETER __attribute__((unused))
#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>
#ifdef HAVE_SQLITE3
#include <sqlite3.h>
#endif
#include <time.h>
#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] <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 (see *).\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"
" -R<SCRID> Show only events with this Series ID.\n"
" -S<service id> Show only selected service.\n"
" -G<type> Show only records with this guidance "
"type.\n"
);
fprintf(stderr,
" -T<content type> Show only selected content types.\n"
" -@<unix timestamp> Show only programmes at time.\n"
" -/<unix timestamp> Show only programmes on day.\n"
" -/<days> Show only programmes on day.\n"
" -=<start>:<end> 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 <file> 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 <text> Search programme names for text.\n"
" searchall <text> "
"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;
}