Compare commits

...

56 Commits

Author SHA1 Message Date
prpr b7ebf6861e Update to 1.2.9 2024-04-01 23:19:13 +01:00
prpr b0eb6d5fb6 Ease portability 2024-04-01 23:04:00 +01:00
prpr 09b1fc7466 Fix types 2024-04-01 22:41:05 +01:00
prpr edf589944c Simplify unreachable code. 2024-04-01 19:21:18 +01:00
prpr e8b6d30585 Avoid use of variable "new" 2024-04-01 19:17:34 +01:00
prpr f0ff884255 Add .gitignore 2024-04-01 18:51:32 +01:00
prpr 3ccb5332d2 Use transaction for x2 performance improvement 2024-04-01 18:50:50 +01:00
prpr be6a274dd0 Update 'huffman.c'
Correct MythTV source reference
2023-10-09 01:12:48 +00:00
prpr 869d4d24d2 Use standard types 2023-02-24 15:02:41 +00:00
prpr 179834a5f5 Add missing dependency 2023-02-18 01:42:01 +00:00
HummyPkg 44e991b36e Add licence 2019-01-25 16:07:48 +00:00
hummypkg 708b081168 Add drama for content type 15 2019-01-25 16:07:48 +00:00
hummypkg 000c750b19 fix empty epg.db if regeneration coincides with raw EPG update 2019-01-25 16:07:48 +00:00
hummypkg abe92a3135 add character set conversion 2019-01-25 16:07:47 +00:00
hummypkg 2ffb32bcb4 skip if file size is changing 2019-01-25 16:07:47 +00:00
hummypkg 792ac397f4 stopon truncated file 2019-01-25 16:07:47 +00:00
hummypkg 2b0eeebc30 fix warning 2019-01-25 16:07:47 +00:00
hummypkg 18e2ae9938 spot truncated files during string read 2019-01-25 16:07:47 +00:00
hummypkg 0af5061493 version bump, create extra index 2019-01-25 16:07:46 +00:00
hummypkg ede8b85152 fix sqldump d126 crash 2019-01-25 16:07:46 +00:00
hummypkg f31cfea03b support descriptor 126 2019-01-25 16:07:46 +00:00
hummypkg e8232ddc86 bump version 2019-01-25 16:07:46 +00:00
hummypkg 109e0992ab fix epg parameters 2019-01-25 16:07:45 +00:00
hummypkg 8992053f18 add . 2019-01-25 16:07:45 +00:00
hummypkg 19c33c6566 fix help layout 2019-01-25 16:07:45 +00:00
hummypkg 26f0a4cf64 properly handle guidance, fix sql field names 2019-01-25 16:07:45 +00:00
hummypkg fba5a52e92 skip decompression for null strings 2019-01-25 16:07:45 +00:00
hummypkg 56825f0502 no explicit engine 2019-01-25 16:07:44 +00:00
hummypkg dcac532f26 add indices after load 2019-01-25 16:07:44 +00:00
hummypkg a21b64b158 analyse table once generated 2019-01-25 16:07:44 +00:00
hummypkg 9ec88c235a add service_id index 2019-01-25 16:07:44 +00:00
hummypkg 88180e7473 escape JSON newlines 2019-01-25 16:07:44 +00:00
hummypkg cba368128d fix JSON 2019-01-25 16:07:43 +00:00
hummypkg c31dc7166f add JSON 2019-01-25 16:07:43 +00:00
hummypkg 5b98b8e21a add xgetopt.h 2019-01-25 16:07:43 +00:00
hummypkg bb729d2802 add support for descriptor 78 2019-01-25 16:07:43 +00:00
hummypkg 874e8bb6b8 more debug output 2019-01-25 16:07:42 +00:00
hummypkg e6e681a43a wait for raw database if necessary 2019-01-25 16:07:42 +00:00
hummypkg 603cbd81e5 update version to 1.0.9 2019-01-25 16:07:42 +00:00
hummypkg f0557c57e2 do not check the last character of the second header magic (Austrian EPG has a different value there) 2019-01-25 16:07:42 +00:00
hummypkg b18b52df1c change engine type 2019-01-25 16:07:42 +00:00
hummypkg 1d428500bc add extra index 2019-01-25 16:07:41 +00:00
hummypkg 63f96d33e4 add first command 2019-01-25 16:07:41 +00:00
hummypkg d25851546c change fulltext indices 2019-01-25 16:07:41 +00:00
hummypkg 8dc3016a79 bump version 2019-01-25 16:07:41 +00:00
hummypkg da83a21b67 add last 2019-01-25 16:07:41 +00:00
hummypkg 48eec1f3ba more epgd logging 2019-01-25 16:07:40 +00:00
hummypkg 246860b7ec add pragmas 2019-01-25 16:07:40 +00:00
hummypkg 6b516bf434 add daemon mode 2019-01-25 16:07:40 +00:00
hummypkg d39e1c32b1 add sqlite3 dump 2019-01-25 16:07:40 +00:00
hummypkg 2c021920a3 add time range option 2019-01-25 16:07:40 +00:00
hummypkg 68d977c2ba sql updates 2019-01-25 16:07:39 +00:00
hummypkg 372759652d avoid overrun at end of file 2019-01-25 16:07:39 +00:00
hummypkg c156fc493f bump version 2019-01-25 16:07:39 +00:00
hummypkg 18b82bf36f add -/ 2019-01-25 16:07:39 +00:00
hummypkg 1ce97801b6 add mysqldump 2019-01-25 16:07:38 +00:00
13 changed files with 1050 additions and 133 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*.o
epg

19
LICENCE Normal file
View File

@ -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.

View File

@ -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

View File

@ -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;

View File

@ -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;

136
epg.c
View File

@ -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,12 +244,29 @@ parse(char *epgpath,
dur = d->dur_hour * 3600 + d->dur_min * 60 +
d->dur_sec;
if (!check_filter_range(filter,
FILTER_TIMESTAMP, tm, tm + dur))
if ((filterflags & FILTER_TIMESTAMP))
{
/* Skip this event. */
epg->offset = dend;
continue;
if (!check_filter_range(filter,
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;
}
}
}
@ -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,13 +347,15 @@ parse(char *epgpath,
dslist[PARSER_CONTENT] = ds;
break;
/*
case DS_FTA_CONTENT_MGMT:
if (!(ds = read_descriptor_header(epg)))
break;
read_descriptor(epg, ds);
dump_descriptor(ds, 1);
free_descriptor(ds);
dslist[PARSER_FTA_CONTENT_MGMT] = ds;
break;
/*
case DS_LINKAGE:
read_descriptor(epg, ds);
dump_descriptor(ds, 1);
@ -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
View File

@ -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
View File

@ -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)

View File

@ -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
View File

@ -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);

768
main.c
View File

@ -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 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;
}
@ -196,7 +743,7 @@ dump(struct epg *epg __attribute__((unused)),
if (sysopts & SYSOPT_BRIEF)
{
safeprintf("%d/%d: %s+%d\n",
s->service_id, d->event_id, ctime_nl(&tm),
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])
{
@ -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
View File

@ -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);
}

40
xgetopt.h Normal file
View File

@ -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)