Compare commits
56 Commits
Author | SHA1 | Date |
---|---|---|
prpr | b7ebf6861e | |
prpr | b0eb6d5fb6 | |
prpr | 09b1fc7466 | |
prpr | edf589944c | |
prpr | e8b6d30585 | |
prpr | f0ff884255 | |
prpr | 3ccb5332d2 | |
prpr | be6a274dd0 | |
prpr | 869d4d24d2 | |
prpr | 179834a5f5 | |
HummyPkg | 44e991b36e | |
hummypkg | 708b081168 | |
hummypkg | 000c750b19 | |
hummypkg | abe92a3135 | |
hummypkg | 2ffb32bcb4 | |
hummypkg | 792ac397f4 | |
hummypkg | 2b0eeebc30 | |
hummypkg | 18e2ae9938 | |
hummypkg | 0af5061493 | |
hummypkg | ede8b85152 | |
hummypkg | f31cfea03b | |
hummypkg | e8232ddc86 | |
hummypkg | 109e0992ab | |
hummypkg | 8992053f18 | |
hummypkg | 19c33c6566 | |
hummypkg | 26f0a4cf64 | |
hummypkg | fba5a52e92 | |
hummypkg | 56825f0502 | |
hummypkg | dcac532f26 | |
hummypkg | a21b64b158 | |
hummypkg | 9ec88c235a | |
hummypkg | 88180e7473 | |
hummypkg | cba368128d | |
hummypkg | c31dc7166f | |
hummypkg | 5b98b8e21a | |
hummypkg | bb729d2802 | |
hummypkg | 874e8bb6b8 | |
hummypkg | e6e681a43a | |
hummypkg | 603cbd81e5 | |
hummypkg | f0557c57e2 | |
hummypkg | b18b52df1c | |
hummypkg | 1d428500bc | |
hummypkg | 63f96d33e4 | |
hummypkg | d25851546c | |
hummypkg | 8dc3016a79 | |
hummypkg | da83a21b67 | |
hummypkg | 48eec1f3ba | |
hummypkg | 246860b7ec | |
hummypkg | 6b516bf434 | |
hummypkg | d39e1c32b1 | |
hummypkg | 2c021920a3 | |
hummypkg | 68d977c2ba | |
hummypkg | 372759652d | |
hummypkg | c156fc493f | |
hummypkg | 18b82bf36f | |
hummypkg | 1ce97801b6 |
|
@ -0,0 +1,2 @@
|
|||
*.o
|
||||
epg
|
|
@ -0,0 +1,19 @@
|
|||
Copyright 2019 af123
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
13
Makefile
13
Makefile
|
@ -13,7 +13,8 @@ SRCS= descriptor.c \
|
|||
HDRS= descriptor.h \
|
||||
epg.h \
|
||||
lint.h \
|
||||
util.h
|
||||
util.h \
|
||||
xgetopt.h
|
||||
|
||||
OBJS= $(SRCS:.c=.o)
|
||||
CC=gcc
|
||||
|
@ -25,8 +26,18 @@ LIBS=
|
|||
#WARN=-pedantic -Wall -Wnested-externs -Wpointer-arith -Werror -Wno-unused
|
||||
WARN=-pedantic -Wall -W -Wnested-externs -Wpointer-arith -Wno-long-long
|
||||
|
||||
PLATFORM=$(shell uname -s | cut -d- -f1)
|
||||
ifeq ($(PLATFORM),Linux)
|
||||
DEFS=-DHAVE_SQLITE3
|
||||
LIBS=-lsqlite3 -lxconv
|
||||
endif
|
||||
|
||||
all: tags epg
|
||||
|
||||
install: epg
|
||||
strip epg
|
||||
cp epg /mod/bin/epg
|
||||
|
||||
epg: ${OBJS}
|
||||
@echo "Linking..."
|
||||
@-[ -f $@ ] && mv $@ $@~ || exit 0
|
||||
|
|
91
descriptor.c
91
descriptor.c
|
@ -19,7 +19,7 @@ read_descriptor_header(struct epg *epg)
|
|||
{
|
||||
struct descriptor *d;
|
||||
|
||||
d = (struct descriptor *)malloc(sizeof(struct descriptor));
|
||||
d = (struct descriptor *)calloc(sizeof(struct descriptor), 1);
|
||||
d->loaded = 0;
|
||||
|
||||
if (epg->binsize - epg->offset < 2)
|
||||
|
@ -55,9 +55,16 @@ string_to_end(struct epg *epg, struct descriptor *d, int sofar,
|
|||
static char *
|
||||
read_lstring(struct epg *epg, unsigned int *len)
|
||||
{
|
||||
int l = epg->bin[epg->offset++];
|
||||
unsigned int l = epg->bin[epg->offset++];
|
||||
char *c;
|
||||
|
||||
/* Check that there is enough file left. */
|
||||
if (epg->binsize - epg->offset < l)
|
||||
l = epg->binsize - epg->offset;
|
||||
|
||||
if (l < 1)
|
||||
return strdup("Short file");
|
||||
|
||||
c = malloc(l + 1);
|
||||
memcpy(c, epg->bin + epg->offset, l);
|
||||
c[l] = '\0';
|
||||
|
@ -86,6 +93,31 @@ read_descriptor(struct epg *epg, struct descriptor *d)
|
|||
|
||||
break;
|
||||
|
||||
case DS_EXTENDED_EVENT:
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
epg->offset++; /* Skip descriptor number and last descriptor */
|
||||
memcpy(d->content.d78.lang, epg->bin + epg->offset, 3);
|
||||
epg->offset += 3;
|
||||
|
||||
d->content.d78.items = epg->bin[epg->offset++];
|
||||
if (debug > 1 && d->content.d78.items > 0)
|
||||
printf("Extended event items: %d\n",
|
||||
d->content.d78.items);
|
||||
/* Skip items. */
|
||||
for (i = 0; i < d->content.d78.items; i++)
|
||||
{
|
||||
int l = epg->bin[epg->offset++];
|
||||
epg->offset += l;
|
||||
}
|
||||
|
||||
d->content.d78.text =
|
||||
read_lstring(epg, &d->content.d78.textlen);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case DS_COMPONENT:
|
||||
memcpy(&d->content.d80, epg->bin + epg->offset, 6);
|
||||
epg->offset += 6;
|
||||
|
@ -95,15 +127,37 @@ read_descriptor(struct epg *epg, struct descriptor *d)
|
|||
break;
|
||||
|
||||
case DS_USER_DEFINED:
|
||||
d->content.d137.text =
|
||||
read_lstring(epg, &d->content.d137.textlen);
|
||||
{
|
||||
int sofar = 0;
|
||||
|
||||
/* reserved:6 guidance_type:2 */
|
||||
d->content.d137.guidance_type = epg->bin[epg->offset++] & 0x3;
|
||||
sofar++;
|
||||
switch (d->content.d137.guidance_type)
|
||||
{
|
||||
case 0:
|
||||
/* guidance_type 0 means unsuitable for broadcast
|
||||
* prior to the watershed so set mode to 1. */
|
||||
d->content.d137.guidance_mode = 1;
|
||||
break;
|
||||
case 1:
|
||||
/* reserved:7 guidance_mode:1 */
|
||||
d->content.d137.guidance_mode =
|
||||
epg->bin[epg->offset++] & 0x1;
|
||||
sofar++;
|
||||
break;
|
||||
default:
|
||||
/* Unknown guidance type.. */
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy(d->content.d137.lang, epg->bin + epg->offset, 3);
|
||||
epg->offset += 3;
|
||||
d->content.d137.warning = string_to_end(epg, d,
|
||||
4 + d->content.d137.textlen,
|
||||
sofar += 3;
|
||||
d->content.d137.warning = string_to_end(epg, d, sofar,
|
||||
&d->content.d137.warninglen);
|
||||
break;
|
||||
}
|
||||
|
||||
case DS_LINKAGE:
|
||||
memcpy(&d->content.d74, epg->bin + epg->offset,
|
||||
|
@ -216,14 +270,17 @@ free_descriptor(struct descriptor *d)
|
|||
free(d->content.d77.text);
|
||||
break;
|
||||
|
||||
case DS_EXTENDED_EVENT:
|
||||
if (d->content.d78.text)
|
||||
free(d->content.d78.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;
|
||||
|
@ -268,7 +325,6 @@ descriptor_name(struct descriptor *d)
|
|||
case DS_CONTENT_IDENTIFIER: return "content id";
|
||||
case DS_FTA_CONTENT_MGMT: return "content mgmt";
|
||||
case DS_USER_DEFINED: return "user defined";
|
||||
default: return "Unknown";
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
@ -298,10 +354,10 @@ content_type(struct descriptor *d)
|
|||
return "Education/Science/Factual";
|
||||
case 0xa:
|
||||
return "Leisure";
|
||||
default:
|
||||
return "Undefined";
|
||||
case 0xf:
|
||||
return "Drama";
|
||||
}
|
||||
return "Unknown";
|
||||
return "Undefined";
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -325,6 +381,13 @@ dump_descriptor(struct descriptor *d, int content)
|
|||
DUMPHEX(d, content.d77.text, d->content.d77.textlen);
|
||||
break;
|
||||
|
||||
case DS_EXTENDED_EVENT:
|
||||
DUMPNSTR(d, content.d78.lang, 3);
|
||||
DUMPINT(d, content.d78.items);
|
||||
DUMPINT(d, content.d78.textlen);
|
||||
DUMPHEX(d, content.d78.text, d->content.d78.textlen);
|
||||
break;
|
||||
|
||||
case DS_COMPONENT:
|
||||
DUMPINT(d, content.d80.stream_content);
|
||||
DUMPINT(d, content.d80.reserved);
|
||||
|
@ -336,9 +399,9 @@ dump_descriptor(struct descriptor *d, int content)
|
|||
break;
|
||||
|
||||
case DS_USER_DEFINED:
|
||||
DUMPINT(d, content.d137.textlen);
|
||||
DUMPINT(d, content.d137.guidance_type);
|
||||
DUMPINT(d, content.d137.guidance_mode);
|
||||
DUMPNSTR(d, content.d137.lang, 3);
|
||||
DUMPHEX(d, content.d137.text, d->content.d137.textlen);
|
||||
DUMPHEX(d, content.d137.warning, d->content.d137.warninglen);
|
||||
break;
|
||||
|
||||
|
|
33
descriptor.h
33
descriptor.h
|
@ -3,14 +3,15 @@
|
|||
* by af123, 2011
|
||||
*/
|
||||
|
||||
#define DS_LINKAGE 74
|
||||
#define DS_SHORT_EVENT 77
|
||||
#define DS_COMPONENT 80
|
||||
#define DS_CONTENT 84
|
||||
#define DS_PRIVATE_DATA_SPECIFIER 95
|
||||
#define DS_CONTENT_IDENTIFIER 118
|
||||
#define DS_FTA_CONTENT_MGMT 126
|
||||
#define DS_USER_DEFINED 137
|
||||
#define DS_LINKAGE 74 /* 0x4a */
|
||||
#define DS_SHORT_EVENT 77 /* 0x4d */
|
||||
#define DS_EXTENDED_EVENT 78 /* 0x4e */
|
||||
#define DS_COMPONENT 80 /* 0x50 */
|
||||
#define DS_CONTENT 84 /* 0x54 */
|
||||
#define DS_PRIVATE_DATA_SPECIFIER 95 /* 0x5f */
|
||||
#define DS_CONTENT_IDENTIFIER 118 /* 0x76 */
|
||||
#define DS_FTA_CONTENT_MGMT 126 /* 0x7e */
|
||||
#define DS_USER_DEFINED 137 /* 0x89 */
|
||||
|
||||
#define PARSER_SHORT_EVENT 0
|
||||
#define PARSER_USER_DEFINED 1
|
||||
|
@ -18,7 +19,9 @@
|
|||
#define PARSER_CRID_EVENT 3
|
||||
#define PARSER_CRID_SERIES 4
|
||||
#define PARSER_CRID_REC 5
|
||||
#define PARSER_MAX 6
|
||||
#define PARSER_EXTENDED_EVENT 6
|
||||
#define PARSER_FTA_CONTENT_MGMT 7
|
||||
#define PARSER_MAX 8
|
||||
|
||||
#define CRIDT_EVENT '1'
|
||||
#define CRIDT_SERIES '2'
|
||||
|
@ -44,6 +47,12 @@ struct descriptor {
|
|||
char *name;
|
||||
char *text;
|
||||
} d77; /* SHORT_EVENT */
|
||||
struct {
|
||||
char lang[3];
|
||||
unsigned int items;
|
||||
unsigned int textlen;
|
||||
char *text;
|
||||
} d78; /* EXTENDED_EVENT */
|
||||
struct {
|
||||
unsigned int stream_content:4;
|
||||
unsigned int reserved:4;
|
||||
|
@ -59,12 +68,12 @@ struct descriptor {
|
|||
unsigned int user:8;
|
||||
} d84; /* CONTENT */
|
||||
struct {
|
||||
unsigned int textlen;
|
||||
char *text;
|
||||
unsigned int guidance_type;
|
||||
unsigned int guidance_mode;
|
||||
char lang[3];
|
||||
char *warning;
|
||||
unsigned int warninglen;
|
||||
} d137; /* USER_DEFINED - content warnings? */
|
||||
} d137; /* USER_DEFINED - content warnings */
|
||||
struct {
|
||||
unsigned int no_revocation:1;
|
||||
unsigned int control_remote_access:2;
|
||||
|
|
130
epg.c
130
epg.c
|
@ -23,7 +23,7 @@ read_section(struct epg *epg)
|
|||
return NULL;
|
||||
|
||||
memcpy(&h, epg->bin + epg->offset, sizeof(h));
|
||||
if (memcmp(h.magic, SECTION_MAGIC, sizeof(h.magic)))
|
||||
if (memcmp(h.magic, SECTION_MAGIC, sizeof(h.magic) - 1))
|
||||
{
|
||||
printf("Section header magic mismatch.\n");
|
||||
hexdump(epg->bin + epg->offset, 64, 0);
|
||||
|
@ -111,6 +111,28 @@ dump_data(struct data *d)
|
|||
DUMPINT(d, u1.u.descriptors_loop_length);
|
||||
}
|
||||
|
||||
static inline int
|
||||
check_filter_timerange(struct epgfilter *f, enum epgfiltertype type,
|
||||
unsigned long idl, unsigned long idh)
|
||||
{
|
||||
if (!f)
|
||||
return 1;
|
||||
|
||||
for (; f; f = f->next)
|
||||
{
|
||||
if (f->type != type)
|
||||
continue;
|
||||
|
||||
if (!(
|
||||
(idl > f->num && idl < f->num2) ||
|
||||
(idh > f->num && idh < f->num2) ||
|
||||
(idl < f->num && idh > f->num))
|
||||
)
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int
|
||||
check_filter_range(struct epgfilter *f, enum epgfiltertype type,
|
||||
unsigned long idl, unsigned long idh)
|
||||
|
@ -120,7 +142,14 @@ check_filter_range(struct epgfilter *f, enum epgfiltertype type,
|
|||
|
||||
for (; f; f = f->next)
|
||||
{
|
||||
if (f->type == type && (f->num < idl || f->num > idh))
|
||||
if (f->type != type)
|
||||
continue;
|
||||
if (f->match == FT_RANGE)
|
||||
{
|
||||
if (idl < f->num || idl > f->num2)
|
||||
return 0;
|
||||
}
|
||||
else if (f->num < idl || f->num > idh)
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
|
@ -149,7 +178,7 @@ check_filter(struct epgfilter *f, enum epgfiltertype type,
|
|||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
int
|
||||
parse(char *epgpath,
|
||||
void (*callback)(struct epg *, struct section *, struct data *,
|
||||
struct descriptor **, void *), void *val, struct epgfilter *filter)
|
||||
|
@ -159,7 +188,7 @@ parse(char *epgpath,
|
|||
int i;
|
||||
|
||||
if (!(epg = open_file(epgpath)))
|
||||
return;
|
||||
return 0;
|
||||
|
||||
while (epg->offset < epg->binsize)
|
||||
{
|
||||
|
@ -177,7 +206,8 @@ parse(char *epgpath,
|
|||
/* Skip this service. */
|
||||
epg->offset += s->total_length - 14;
|
||||
/* Skip padding bytes... */
|
||||
while (epg->bin[epg->offset] == 'U')
|
||||
while (epg->offset < epg->binsize &&
|
||||
epg->bin[epg->offset] == 'U')
|
||||
epg->offset++;
|
||||
continue;
|
||||
}
|
||||
|
@ -203,10 +233,10 @@ parse(char *epgpath,
|
|||
continue;
|
||||
}
|
||||
|
||||
if ((filterflags & FILTER_TIMESTAMP))
|
||||
if ((filterflags &
|
||||
(FILTER_TIMESTAMP | FILTER_TIMERANGE)))
|
||||
{
|
||||
time_t tm;
|
||||
int dur;
|
||||
time_t tm, dur;
|
||||
|
||||
tm = mjd(d->start_date, d->start_hour,
|
||||
d->start_min, d->start_sec);
|
||||
|
@ -214,14 +244,31 @@ parse(char *epgpath,
|
|||
dur = d->dur_hour * 3600 + d->dur_min * 60 +
|
||||
d->dur_sec;
|
||||
|
||||
if ((filterflags & FILTER_TIMESTAMP))
|
||||
{
|
||||
if (!check_filter_range(filter,
|
||||
FILTER_TIMESTAMP, tm, tm + dur))
|
||||
FILTER_TIMESTAMP,
|
||||
(unsigned long) tm,
|
||||
(unsigned long) (tm + dur)))
|
||||
{
|
||||
/* Skip this event. */
|
||||
epg->offset = dend;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ((filterflags & FILTER_TIMERANGE))
|
||||
{
|
||||
if (!check_filter_timerange(filter,
|
||||
FILTER_TIMERANGE,
|
||||
(unsigned long) tm,
|
||||
(unsigned long) (tm + dur)))
|
||||
{
|
||||
/* Skip this event. */
|
||||
epg->offset = dend;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (debug)
|
||||
hexdump(epg->bin + dstart, dend - dstart,
|
||||
|
@ -244,6 +291,14 @@ parse(char *epgpath,
|
|||
continue;
|
||||
}
|
||||
|
||||
if (debug)
|
||||
printf("Checking descriptor: "
|
||||
"%d/%#x (%d)\n",
|
||||
dstag, dstag, dslen);
|
||||
if (debug > 3)
|
||||
hexdump(epg->bin + epg->offset,
|
||||
dslen + 2, 0);
|
||||
|
||||
switch (dstag)
|
||||
{
|
||||
case DS_SHORT_EVENT:
|
||||
|
@ -253,6 +308,13 @@ parse(char *epgpath,
|
|||
dslist[PARSER_SHORT_EVENT] = ds;
|
||||
break;
|
||||
|
||||
case DS_EXTENDED_EVENT:
|
||||
if (!(ds = read_descriptor_header(epg)))
|
||||
break;
|
||||
read_descriptor(epg, ds);
|
||||
dslist[PARSER_EXTENDED_EVENT] = ds;
|
||||
break;
|
||||
|
||||
case DS_CONTENT_IDENTIFIER:
|
||||
if (!(ds = read_descriptor_header(epg)))
|
||||
break;
|
||||
|
@ -285,12 +347,14 @@ parse(char *epgpath,
|
|||
dslist[PARSER_CONTENT] = ds;
|
||||
break;
|
||||
|
||||
/*
|
||||
case DS_FTA_CONTENT_MGMT:
|
||||
read_descriptor(epg, ds);
|
||||
dump_descriptor(ds, 1);
|
||||
free_descriptor(ds);
|
||||
if (!(ds = read_descriptor_header(epg)))
|
||||
break;
|
||||
read_descriptor(epg, ds);
|
||||
dslist[PARSER_FTA_CONTENT_MGMT] = ds;
|
||||
break;
|
||||
|
||||
/*
|
||||
|
||||
case DS_LINKAGE:
|
||||
read_descriptor(epg, ds);
|
||||
|
@ -300,11 +364,39 @@ parse(char *epgpath,
|
|||
*/
|
||||
|
||||
default:
|
||||
if (debug > 2)
|
||||
printf("! Unhandled.\n");
|
||||
epg->offset += dslen + 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Move synopsis from extended event descriptor into
|
||||
* short description if appropriate. */
|
||||
if (dslist[PARSER_SHORT_EVENT] &&
|
||||
dslist[PARSER_EXTENDED_EVENT] &&
|
||||
dslist[PARSER_EXTENDED_EVENT]->content.d78.textlen
|
||||
> 0 &&
|
||||
dslist[PARSER_SHORT_EVENT]->content.d77.textlen
|
||||
== 2 &&
|
||||
dslist[PARSER_SHORT_EVENT]->content.d77.text[1]
|
||||
== '\0')
|
||||
{
|
||||
if (debug > 3)
|
||||
printf("Moving event text from "
|
||||
"extended to short event.\n");
|
||||
free(dslist[PARSER_SHORT_EVENT]
|
||||
->content.d77.text);
|
||||
dslist[PARSER_SHORT_EVENT]->content.d77.text
|
||||
= dslist[PARSER_EXTENDED_EVENT]->
|
||||
content.d78.text;
|
||||
dslist[PARSER_SHORT_EVENT]->content.d77.textlen
|
||||
= dslist[PARSER_EXTENDED_EVENT]->
|
||||
content.d78.textlen;
|
||||
dslist[PARSER_EXTENDED_EVENT]->content.d78.text
|
||||
= (char *)NULL;
|
||||
}
|
||||
|
||||
if (
|
||||
(
|
||||
!(filterflags & FILTER_CRID) ||
|
||||
|
@ -324,6 +416,13 @@ parse(char *epgpath,
|
|||
check_filter(filter, FILTER_CONTENT,
|
||||
dslist[PARSER_CONTENT]->content.d84.level1,
|
||||
NULL))
|
||||
) && (
|
||||
!(filterflags & FILTER_GUIDETYPE) ||
|
||||
(dslist[PARSER_USER_DEFINED] &&
|
||||
check_filter(filter, FILTER_GUIDETYPE,
|
||||
dslist[PARSER_USER_DEFINED]->
|
||||
content.d137.guidance_type,
|
||||
NULL))
|
||||
)
|
||||
)
|
||||
callback(epg, s, d, dslist, val);
|
||||
|
@ -337,11 +436,14 @@ parse(char *epgpath,
|
|||
epg->offset += 4;
|
||||
|
||||
/* Skip padding bytes... */
|
||||
while (epg->bin[epg->offset] == 'U')
|
||||
while (epg->offset < epg->binsize &&
|
||||
epg->bin[epg->offset] == 'U')
|
||||
epg->offset++;
|
||||
}
|
||||
|
||||
close_file(epg);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
4
epg.h
4
epg.h
|
@ -70,7 +70,9 @@ enum epgfiltertype {
|
|||
FILTER_TIMESTAMP = 0x8,
|
||||
FILTER_CRID = 0x10,
|
||||
FILTER_SCRID = 0x20,
|
||||
FILTER_CONTENT = 0x40
|
||||
FILTER_CONTENT = 0x40,
|
||||
FILTER_TIMERANGE = 0x80,
|
||||
FILTER_GUIDETYPE = 0x100
|
||||
};
|
||||
|
||||
enum matchtype { FT_EQUAL, FT_RANGE, FT_GREATER, FT_LESS };
|
||||
|
|
33
file.c
33
file.c
|
@ -23,7 +23,27 @@ struct epg *
|
|||
open_file(char *filename)
|
||||
{
|
||||
struct epg *epg;
|
||||
struct stat st;
|
||||
struct stat st, st2;
|
||||
|
||||
if (debug)
|
||||
printf("Opening file '%s'\n", filename);
|
||||
|
||||
if (stat(filename, &st) == -1)
|
||||
{
|
||||
perror(filename);
|
||||
return NULL;
|
||||
}
|
||||
sleep(1);
|
||||
if (stat(filename, &st2) == -1)
|
||||
{
|
||||
perror(filename);
|
||||
return NULL;
|
||||
}
|
||||
if (st.st_size != st2.st_size)
|
||||
{
|
||||
printf("EPG data file is updating, try later.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(epg = malloc(sizeof(struct epg))))
|
||||
{
|
||||
|
@ -31,18 +51,7 @@ open_file(char *filename)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
if (debug)
|
||||
printf("Opening file '%s'\n", filename);
|
||||
|
||||
strcpy(epg->fname, filename);
|
||||
|
||||
if (stat(epg->fname, &st) == -1)
|
||||
{
|
||||
perror(epg->fname);
|
||||
free(epg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
epg->binsize = st.st_size;
|
||||
|
||||
if (debug)
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
* by af123, 2011 - 2018
|
||||
*
|
||||
* These tables and the decompression code are derived from MythTV
|
||||
* https://github.com/MythTV/mythtv/tree/master/mythtv/libs/libmythtv/mpeg
|
||||
* freesat_tables.h and freesat_huffman.cpp
|
||||
* https://github.com/MythTV/mythtv/tree/master/mythtv/libs/libmythtv/mpeg/
|
||||
* freesat_tables.cpp and freesat_huffman.cpp
|
||||
*
|
||||
* MythTV is released as free open source software under version 2 or later
|
||||
* of the GPL (GNU General Public License) a copy of which can be viewed at
|
||||
|
@ -5746,7 +5746,7 @@ static unsigned fsat_index_2[] = {
|
|||
#define ESCAPE '\1'
|
||||
|
||||
unsigned char *
|
||||
freeview_huffman_to_string(const unsigned char *src, uint size,
|
||||
freeview_huffman_to_string(const unsigned char *src, unsigned int size,
|
||||
unsigned int *len)
|
||||
{
|
||||
struct fsattab *fsat_table;
|
||||
|
|
7
lint.h
7
lint.h
|
@ -20,9 +20,10 @@ 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 *);
|
||||
time_t mjd(uint16_t, int, int, int);
|
||||
time_t mjd(unsigned int, unsigned int, unsigned int, unsigned int);
|
||||
void safeprintf(char *, ...);
|
||||
void uncompress_epg(char **, unsigned int *);
|
||||
void iso6937_convert(char **, unsigned int *);
|
||||
|
||||
struct epg *open_file(char *);
|
||||
void close_file(struct epg *);
|
||||
|
@ -35,7 +36,7 @@ void dump_section(struct section *);
|
|||
struct data *read_data(struct epg *);
|
||||
void dump_data(struct data *);
|
||||
|
||||
void parse(char *,
|
||||
int parse(char *,
|
||||
void (*)(struct epg *, struct section *, struct data *,
|
||||
struct descriptor **, void *), void *, struct epgfilter *);
|
||||
|
||||
|
@ -46,7 +47,7 @@ void dump_descriptor(struct descriptor *, int);
|
|||
void free_descriptor(struct descriptor *);
|
||||
const char *content_type(struct descriptor *);
|
||||
|
||||
unsigned char *freeview_huffman_to_string(const unsigned char *, uint,
|
||||
unsigned char *freeview_huffman_to_string(const unsigned char *, unsigned int,
|
||||
unsigned int *);
|
||||
|
||||
int is_bst(time_t);
|
||||
|
|
764
main.c
764
main.c
|
@ -1,8 +1,10 @@
|
|||
/*
|
||||
* Humax EPG Tool
|
||||
* by af123, 2011
|
||||
* by af123, 2011 - 2024
|
||||
*/
|
||||
|
||||
#define UNUSED_PARAMETER __attribute__((unused))
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
|
@ -13,18 +15,29 @@
|
|||
#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.0.2";
|
||||
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.\n\n", version);
|
||||
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,
|
||||
|
@ -33,7 +46,7 @@ syntax()
|
|||
" -d[level] Set debug level.\n"
|
||||
" -f<file> Specify alternate EPG data file.\n"
|
||||
" -h Show help text.\n"
|
||||
" -p Parsable output.\n"
|
||||
" -p Parsable output (see *).\n"
|
||||
"\n"
|
||||
);
|
||||
fprintf(stderr,
|
||||
|
@ -43,41 +56,111 @@ syntax()
|
|||
" -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) == 0x1f) uncompress_epg(&(str), &(len))
|
||||
if (str && *(str) == 0x1f) uncompress_epg(&(str), &(len))
|
||||
|
||||
void
|
||||
pass(struct epg *epg __attribute__((unused)),
|
||||
struct section *s __attribute__((unused)),
|
||||
struct data *d __attribute__((unused)),
|
||||
struct descriptor **ds __attribute__((unused)),
|
||||
void *var __attribute__((unused)))
|
||||
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
|
||||
dumpraw(struct epg *epg __attribute__((unused)),
|
||||
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 __attribute__((unused)))
|
||||
void *var UNUSED_PARAMETER)
|
||||
{
|
||||
dump_section(s);
|
||||
dump_data(d);
|
||||
|
@ -94,17 +177,463 @@ dumpraw(struct epg *epg __attribute__((unused)),
|
|||
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
|
||||
dump(struct epg *epg __attribute__((unused)),
|
||||
sqlitedump(struct epg *epg UNUSED_PARAMETER,
|
||||
struct section *s, struct data *d, struct descriptor **ds,
|
||||
void *var __attribute__((unused)))
|
||||
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);
|
||||
|
||||
|
@ -124,7 +653,8 @@ dump(struct epg *epg __attribute__((unused)),
|
|||
{
|
||||
/* service_id, event_id, start, duration, encrypted, name, text
|
||||
* warning, content code, content type,
|
||||
* event CRID, series CRID, rec CRID
|
||||
* 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,
|
||||
|
@ -187,6 +717,23 @@ dump(struct epg *epg __attribute__((unused)),
|
|||
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;
|
||||
}
|
||||
|
@ -223,14 +770,21 @@ dump(struct epg *epg __attribute__((unused)),
|
|||
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",
|
||||
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",
|
||||
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);
|
||||
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])
|
||||
{
|
||||
|
@ -248,6 +802,10 @@ dump(struct epg *epg __attribute__((unused)),
|
|||
|
||||
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])
|
||||
|
@ -281,7 +839,7 @@ dump(struct epg *epg __attribute__((unused)),
|
|||
}
|
||||
|
||||
void
|
||||
search(struct epg *epg __attribute__((unused)),
|
||||
search(struct epg *epg UNUSED_PARAMETER,
|
||||
struct section *s, struct data *d, struct descriptor **ds,
|
||||
void *var)
|
||||
{
|
||||
|
@ -298,7 +856,7 @@ search(struct epg *epg __attribute__((unused)),
|
|||
}
|
||||
|
||||
void
|
||||
searchall(struct epg *epg __attribute__((unused)),
|
||||
searchall(struct epg *epg UNUSED_PARAMETER,
|
||||
struct section *s, struct data *d, struct descriptor **ds,
|
||||
void *var)
|
||||
{
|
||||
|
@ -317,46 +875,6 @@ searchall(struct epg *epg __attribute__((unused)),
|
|||
}
|
||||
}
|
||||
|
||||
#define GETOPTOPT \
|
||||
do { \
|
||||
if (*++cp == '\0' && argc > 1) \
|
||||
{ \
|
||||
argc--, argv++; \
|
||||
cp = argv[0]; \
|
||||
} \
|
||||
while (*cp != '\0' && isspace((int)*cp)) \
|
||||
cp++; \
|
||||
} while (0)
|
||||
|
||||
#define GETOPTINTOPT \
|
||||
do { \
|
||||
if (*++cp == '\0' && argc > 1 && atoi(argv[1]) > 0) \
|
||||
{ \
|
||||
argc--, argv++; \
|
||||
cp = argv[0]; \
|
||||
} \
|
||||
while (*cp != '\0' && isspace((int)*cp)) \
|
||||
cp++; \
|
||||
} while (0)
|
||||
|
||||
#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)
|
||||
{
|
||||
|
@ -403,6 +921,59 @@ main(int argc, char **argv)
|
|||
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,
|
||||
|
@ -438,6 +1009,12 @@ main(int argc, char **argv)
|
|||
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:
|
||||
|
@ -451,14 +1028,79 @@ nextopt:
|
|||
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, tm, 0, NULL, FT_EQUAL);
|
||||
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)
|
||||
|
|
31
util.c
31
util.c
|
@ -13,25 +13,42 @@
|
|||
#include <time.h>
|
||||
#include <strings.h>
|
||||
#include <stdarg.h>
|
||||
#include <xconv.h>
|
||||
|
||||
#include "lint.h"
|
||||
|
||||
void
|
||||
uncompress_epg(char **epg, unsigned int *epglen)
|
||||
{
|
||||
char *new;
|
||||
char *newepg;
|
||||
unsigned int newlen;
|
||||
|
||||
if ((new = (char *)
|
||||
if ((newepg = (char *)
|
||||
freeview_huffman_to_string((unsigned char *)(*epg),
|
||||
*epglen, &newlen)))
|
||||
{
|
||||
free(*epg);
|
||||
*epg = new;
|
||||
*epg = newepg;
|
||||
*epglen = newlen;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
iso6937_convert(char **str, unsigned int *len)
|
||||
{
|
||||
char dst[0x200];
|
||||
int newlen;
|
||||
|
||||
newlen = xconv(*str, dst, sizeof(dst));
|
||||
|
||||
if (newlen)
|
||||
{
|
||||
free(*str);
|
||||
*str = strdup(dst);
|
||||
*len = newlen;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef sun
|
||||
char *
|
||||
strcasestr (char *h, char *n)
|
||||
|
@ -82,16 +99,16 @@ safeprintf(char *fmt, ...)
|
|||
}
|
||||
|
||||
time_t
|
||||
mjd(uint16_t day, int h, int m, int s)
|
||||
mjd(unsigned int day, unsigned int h, unsigned int m, unsigned int s)
|
||||
{
|
||||
time_t tm;
|
||||
struct tm *t;
|
||||
|
||||
tm = MJD_TO_UNIX(day);
|
||||
t = gmtime(&tm);
|
||||
t->tm_hour = h;
|
||||
t->tm_min = m;
|
||||
t->tm_sec = s;
|
||||
t->tm_hour = (int) h;
|
||||
t->tm_min = (int) m;
|
||||
t->tm_sec = (int) s;
|
||||
|
||||
return mktime(t);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
|
||||
#define GETOPTOPT \
|
||||
do { \
|
||||
if (*++cp == '\0' && argc > 1) \
|
||||
{ \
|
||||
argc--, argv++; \
|
||||
cp = argv[0]; \
|
||||
} \
|
||||
while (*cp != '\0' && isspace((int)*cp)) \
|
||||
cp++; \
|
||||
} while (0)
|
||||
|
||||
#define GETOPTINTOPT \
|
||||
do { \
|
||||
if (*++cp == '\0' && argc > 1 && atoi(argv[1]) > 0) \
|
||||
{ \
|
||||
argc--, argv++; \
|
||||
cp = argv[0]; \
|
||||
} \
|
||||
while (*cp != '\0' && isspace((int)*cp)) \
|
||||
cp++; \
|
||||
} while (0)
|
||||
|
||||
#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)
|
||||
|
Loading…
Reference in New Issue