diff mbox

New utility program atsc_epg added to dvb-apps utility suite.

Message ID 1245465487.14312.17.camel@core2duo.localdomain (mailing list archive)
State Not Applicable
Headers show

Commit Message

Yufei Yuan June 20, 2009, 2:38 a.m. UTC
Ok, let me first summarize what I have done in order not to waste your time again. I used Evolution client, 
used preformatted option, sent it to my other email box, forwarded it back and saved it as text file, then
patched the original tree, so far everything looks okay. Hopefully you guys can do start to do next step.

I do apologize for your wasted time.

Signed-off-by: Yufei Yuan <yfyuan@gmail.com>



On Sat, 2009-06-20 at 03:28 +0200, hermann pitton wrote: 
> Hi,
> 
> Am Freitag, den 19.06.2009, 20:00 -0500 schrieb Yufei Yuan:
> > Thanks for your time. It's my first time to do this, so I have been
> > trying to follow literally on the wiki page to do it right. If you can
> > elaborate a bit about what is broken? Is it the patch created
> > incorrectly, or it is pasted incorrectly, or the style is still
> > problematic?
> > 
> > I noticed that cutting and pasting from my console to the gmail
> > compose window does not seem working alright. How do you normally do
> > the inlining?
> > 
> > I have a full weekend to do this, and I do realize from the wiki page
> > that it does not appear to be simple, :)
> > 
> > I now simply disable the footer, don't worry.
> 
> Just keep it on your decision. I'm not against to learn from the past.
> 
> It starts with lots of broken lines.
> 
> Cheers,
> Hermann


--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Comments

hermann pitton June 20, 2009, 3:04 a.m. UTC | #1
Am Freitag, den 19.06.2009, 21:38 -0500 schrieb Yufei Yuan:
> Ok, let me first summarize what I have done in order not to waste your time again. I used Evolution client, 
> used preformatted option, sent it to my other email box, forwarded it back and saved it as text file, then
> patched the original tree, so far everything looks okay. Hopefully you guys can do start to do next step.
> 
> I do apologize for your wasted time.
> 
> Signed-off-by: Yufei Yuan <yfyuan@gmail.com>

Manu,

please review.

Hermann

> 
> diff -uprN dvb-apps/util/atsc_epg/atsc_epg.c dvb-apps_new/util/atsc_epg/atsc_epg.c
> --- dvb-apps/util/atsc_epg/atsc_epg.c	1969-12-31 18:00:00.000000000 -0600
> +++ dvb-apps_new/util/atsc_epg/atsc_epg.c	2009-06-19 20:31:17.710924970 -0500
> @@ -0,0 +1,1249 @@
> +/*
> + * atsc_epg utility
> + *
> + * Copyright (C) 2009 Yufei Yuan <yfyuan@gmail.com>
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + *
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <unistd.h>
> +#include <string.h>
> +#include <time.h>
> +#include <signal.h>
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <fcntl.h>
> +#include <sys/ioctl.h>
> +#include <sys/poll.h>
> +#include <errno.h>
> +#include <getopt.h>
> +#include <stdarg.h>
> +#include <libdvbapi/dvbfe.h>
> +#include <libdvbapi/dvbdemux.h>
> +#include <libucsi/dvb/section.h>
> +#include <libucsi/atsc/section.h>
> +#include <libucsi/atsc/types.h>
> +
> +#define TIMEOUT				60
> +#define RRT_TIMEOUT			60
> +#define MAX_NUM_EVENT_TABLES		128
> +#define TITLE_BUFFER_LEN		4096
> +#define MESSAGE_BUFFER_LEN		(16 * 1024)
> +#define MAX_NUM_CHANNELS		16
> +#define MAX_NUM_EVENTS_PER_CHANNEL	(4 * 24 * 7)
> +
> +static int atsc_scan_table(int dmxfd, uint16_t pid, enum atsc_section_tag tag,
> +	void **table_section);
> +
> +static const char *program;
> +static int adapter = 0;
> +static int period = 12; /* hours */
> +static int frequency;
> +static int enable_ett = 0;
> +static int ctrl_c = 0;
> +static const char *modulation = NULL;
> +static char separator[80];
> +void (*old_handler)(int);
> +
> +struct atsc_string_buffer {
> +	int buf_len;
> +	int buf_pos;
> +	char *string;
> +};
> +
> +struct atsc_event_info {
> +	uint16_t id;
> +	struct tm start;
> +	struct tm end;
> +	int title_pos;
> +	int title_len;
> +	int msg_pos;
> +	int msg_len;
> +};
> +
> +struct atsc_eit_section_info {
> +	uint8_t section_num;
> +	uint8_t num_events;
> +	uint8_t num_etms;
> +	uint8_t num_received_etms;
> +	struct atsc_event_info **events;
> +};
> +
> +struct atsc_eit_info {
> +	int num_eit_sections;
> +	struct atsc_eit_section_info *section;
> +};
> +
> +struct atsc_channel_info {
> +	uint8_t num_eits;
> +	uint8_t service_type;
> +	char short_name[8];
> +	uint16_t major_num;
> +	uint16_t minor_num;
> +	uint16_t tsid;
> +	uint16_t prog_num;
> +	uint16_t src_id;
> +	struct atsc_eit_info *eit;
> +	struct atsc_event_info *last_event;
> +	int event_info_index;
> +	struct atsc_event_info e[MAX_NUM_EVENTS_PER_CHANNEL];
> +	struct atsc_string_buffer title_buf;
> +	struct atsc_string_buffer msg_buf;
> +};
> +
> +struct atsc_virtual_channels_info {
> +	int num_channels;
> +	uint16_t eit_pid[MAX_NUM_EVENT_TABLES];
> +	uint16_t ett_pid[MAX_NUM_EVENT_TABLES];
> +	struct atsc_channel_info ch[MAX_NUM_CHANNELS];
> +} guide;
> +
> +struct mgt_table_name {
> +	uint16_t range;
> +	const char *string;
> +};
> +
> +struct mgt_table_name mgt_tab_name_array[] = {
> +	{0x0000, "terrestrial VCT with current_next_indictor=1"},
> +	{0x0001, "terrestrial VCT with current_next_indictor=0"},
> +	{0x0002, "cable VCT with current_next_indictor=1"},
> +	{0x0003, "cable VCT with current_next_indictor=0"},
> +	{0x0004, "channel ETT"},
> +	{0x0005, "DCCSCT"},
> +	{0x00FF, "reserved for future ATSC use"},
> +	{0x017F, "EIT"},
> +	{0x01FF, "reserved for future ATSC use"},
> +	{0x027F, "event ETT"},
> +	{0x02FF, "reserved for future ATSC use"}, /* FIXME */
> +	{0x03FF, "RRT with rating region"},
> +	{0x0FFF, "user private"},
> +	{0x13FF, "reserved for future ATSC use"},
> +	{0x14FF, "DCCT with dcc_id"},
> +	{0xFFFF, "reserved for future ATSC use"}
> +};
> +
> +const char *channel_modulation_mode[] = {
> +	"",
> +	"analog",
> +	"SCTE mode 1",
> +	"SCTE mode 2",
> +	"ATSC 8VSB",
> +	"ATSC 16VSB"
> +};
> +
> +const char *channel_service_type[] = {
> +	"",
> +	"analog TV",
> +	"ATSC digital TV",
> +	"ATSC audio",
> +	"ATSC data-only"
> +};
> +
> +void *(*table_callback[16])(struct atsc_section_psip *) =
> +{
> +	NULL, NULL, NULL, NULL, NULL, NULL, NULL,
> +	(void *(*)(struct atsc_section_psip *))atsc_mgt_section_codec,
> +	(void *(*)(struct atsc_section_psip *))atsc_tvct_section_codec,
> +	(void *(*)(struct atsc_section_psip *))atsc_cvct_section_codec,
> +	(void *(*)(struct atsc_section_psip *))atsc_rrt_section_codec,
> +	(void *(*)(struct atsc_section_psip *))atsc_eit_section_codec,
> +	(void *(*)(struct atsc_section_psip *))atsc_ett_section_codec,
> +	(void *(*)(struct atsc_section_psip *))atsc_stt_section_codec,
> +	NULL, NULL
> +};
> +
> +static void int_handler(int sig_num)
> +{
> +	if(SIGINT != sig_num) {
> +		return;
> +	}
> +	ctrl_c = 1;
> +}
> +
> +/* shamelessly stolen from dvbsnoop, but almost not modified */
> +static uint32_t get_bits(const uint8_t *buf, int startbit, int bitlen)
> +{
> +	const uint8_t *b;
> +	uint32_t mask,tmp_long;
> +	int bitHigh,i;
> +
> +	b = &buf[startbit / 8];
> +	startbit %= 8;
> +
> +	bitHigh = 8;
> +	tmp_long = b[0];
> +	for (i = 0; i < ((bitlen-1) >> 3); i++) {
> +		tmp_long <<= 8;
> +		tmp_long  |= b[i+1];
> +		bitHigh   += 8;
> +	}
> +
> +	startbit = bitHigh - startbit - bitlen;
> +	tmp_long = tmp_long >> startbit;
> +	mask     = (1ULL << bitlen) - 1;
> +	return tmp_long & mask;
> +}
> +
> +static void usage(void)
> +{
> +	fprintf(stderr, "usage: %s [-a <n>] -f <frequency> [-p <period>]"
> +		" [-m <modulation>] [-t] [-h]\n", program);
> +}
> +
> +static void help(void)
> +{
> +	fprintf(stderr,
> +	"\nhelp:\n"
> +	"%s [-a <n>] -f <frequency> [-p <period>] [-m <modulation>] [-t] [-h]\n"
> +	"  -a: adapter index to use, (default 0)\n"
> +	"  -f: tuning frequency\n"
> +	"  -p: period in hours, (default 12)\n"
> +	"  -m: modulation ATSC vsb_8|vsb_16 (default vsb_8)\n"
> +	"  -t: enable ETT to receive program details, if available\n"
> +	"  -h: display this message\n", program);
> +}
> +
> +static int close_frontend(struct dvbfe_handle *fe)
> +{
> +	if(NULL == fe) {
> +		fprintf(stderr, "%s(): NULL pointer detected\n", __FUNCTION__);
> +	}
> +
> +	dvbfe_close(fe);
> +
> +	return 0;
> +}
> +
> +static int open_frontend(struct dvbfe_handle **fe)
> +{
> +	struct dvbfe_info fe_info;
> +
> +	if(NULL == (*fe = dvbfe_open(adapter, 0, 0))) {
> +		fprintf(stderr, "%s(): error calling dvbfe_open()\n",
> +			__FUNCTION__);
> +		return -1;
> +	}
> +	dvbfe_get_info(*fe, 0, &fe_info, DVBFE_INFO_QUERYTYPE_IMMEDIATE, 0);
> +	if(DVBFE_TYPE_ATSC != fe_info.type) {
> +		fprintf(stderr, "%s(): only ATSC frontend supported currently\n",
> +			__FUNCTION__);
> +		return -1;
> +	}
> +	fe_info.feparams.frequency = frequency;
> +	fe_info.feparams.inversion = DVBFE_INVERSION_AUTO;
> +	fe_info.feparams.u.atsc.modulation = DVBFE_ATSC_MOD_VSB_8;
> +	fprintf(stdout, "tuning to %d Hz, please wait...\n", frequency);
> +	if(dvbfe_set(*fe, &fe_info.feparams, TIMEOUT * 1000)) {
> +		fprintf(stderr, "%s(): cannot lock to %d Hz in %d seconds\n",
> +			__FUNCTION__, frequency, TIMEOUT);
> +		return -1;
> +	}
> +	fprintf(stdout, "tuner locked.\n");
> +
> +	return 0;
> +}
> +
> +#ifdef ENABLE_RRT
> +/* this is untested as since this part of the library is broken */
> +static int parse_rrt(int dmxfd)
> +{
> +	const enum atsc_section_tag tag = stag_atsc_rating_region;
> +	struct atsc_rrt_section *rrt;
> +	struct atsc_text *region_name;
> +	struct atsc_text_string *atsc_str;
> +	int i, j, ret;
> +
> +	i = 0;
> +	fprintf(stdout, "waiting for RRT: ");
> +	fflush(stdout);
> +	while(i < RRT_TIMEOUT) {
> +		ret = atsc_scan_table(dmxfd, ATSC_BASE_PID, tag, (void **)&rrt);
> +		if(0 > ret) {
> +			fprintf(stderr, "%s(): error calling atsc_scan_table()\n",
> +				__FUNCTION__);
> +			return -1;
> +		}
> +		if(0 == ret) {
> +			if(RRT_TIMEOUT > i) {
> +				fprintf(stdout, ".");
> +				fflush(stdout);
> +			} else {
> +				fprintf(stdout, "\nno RRT in %d seconds\n",
> +					RRT_TIMEOUT);
> +				return 0;
> +			}
> +			i += TIMEOUT;
> +		} else {
> +			fprintf(stdout, "\n");
> +			fflush(stdout);
> +			break;
> +		}
> +	}
> +
> +	region_name = atsc_rrt_section_rating_region_name_text(rrt);
> +	atsc_text_strings_for_each(region_name, atsc_str, i) {
> +		struct atsc_text_string_segment *seg;
> +
> +		atsc_text_string_segments_for_each(atsc_str, seg, j) {
> +			const char *c;
> +			int k;
> +			if(seg->mode < 0x3E) {
> +				fprintf(stderr, "%s(): text mode of 0x%02X "
> +					"not supported yet\n",
> +					__FUNCTION__, seg->mode);
> +				return -1;
> +			}
> +			c = (const char *)atsc_text_string_segment_bytes(seg);
> +			for(k = 0; k < seg->number_bytes; k++) {
> +				fprintf(stdout, "%c", c[k]);
> +			}
> +		}
> +	}
> +
> +	return 0;
> +}
> +#endif
> +
> +static int parse_stt(int dmxfd)
> +{
> +	const enum atsc_section_tag tag = stag_atsc_system_time;
> +	const struct atsc_stt_section *stt;
> +	time_t rx_time;
> +	time_t sys_time;
> +	int ret;
> +
> +	ret = atsc_scan_table(dmxfd, ATSC_BASE_PID, tag, (void **)&stt);
> +	if(0 > ret) {
> +		fprintf(stderr, "%s(): error calling atsc_scan_table()\n",
> +			__FUNCTION__);
> +		return -1;
> +	}
> +	if(0 == ret) {
> +		fprintf(stdout, "no STT in %d seconds\n", TIMEOUT);
> +		return 0;
> +	}
> +
> +	rx_time = atsctime_to_unixtime(stt->system_time);
> +	time(&sys_time);
> +	fprintf(stdout, "system time: %s", ctime(&sys_time));
> +	fprintf(stdout, "TS STT time: %s", ctime(&rx_time));
> +
> +	return 0;
> +}
> +
> +static int parse_tvct(int dmxfd)
> +{
> +	int num_sections;
> +	uint32_t section_pattern;
> +	const enum atsc_section_tag tag = stag_atsc_terrestrial_virtual_channel;
> +	struct atsc_tvct_section *tvct;
> +	struct atsc_tvct_channel *ch;
> +	struct atsc_channel_info *curr_info;
> +	int i, k, ret;
> +
> +	section_pattern = 0;
> +	num_sections = -1;
> +
> +	do {
> +		ret = atsc_scan_table(dmxfd, ATSC_BASE_PID, tag, (void **)&tvct);
> +		if(0 > ret) {
> +			fprintf(stderr, "%s(): error calling atsc_scan_table()\n",
> +			__FUNCTION__);
> +			return -1;
> +		}
> +		if(0 == ret) {
> +			fprintf(stdout, "no TVCT in %d seconds\n", TIMEOUT);
> +			return 0;
> +		}
> +
> +		if(-1 == num_sections) {
> +			num_sections = 1 + tvct->head.ext_head.last_section_number;
> +			if(32 < num_sections) {
> +				fprintf(stderr, "%s(): no support yet for "
> +					"tables having more than 32 sections\n",
> +					__FUNCTION__);
> +				return -1;
> +			}
> +		} else {
> +			if(num_sections !=
> +				1 + tvct->head.ext_head.last_section_number) {
> +				fprintf(stderr,
> +					"%s(): last section number does not match\n",
> +					__FUNCTION__);
> +				return -1;
> +			}
> +		}
> +		if(section_pattern & (1 << tvct->head.ext_head.section_number)) {
> +			continue;
> +		}
> +		section_pattern |= 1 << tvct->head.ext_head.section_number;
> +
> +		if(MAX_NUM_CHANNELS < guide.num_channels +
> +			tvct->num_channels_in_section) {
> +			fprintf(stderr, "%s(): no support for more than %d "
> +				"virtual channels in a pyhsical channel\n",
> +				__FUNCTION__, MAX_NUM_CHANNELS);
> +			return -1;
> +		}
> +		curr_info = &guide.ch[guide.num_channels];
> +		guide.num_channels += tvct->num_channels_in_section;
> +
> +	atsc_tvct_section_channels_for_each(tvct, ch, i) {
> +		/* initialize the curr_info structure */
> +		/* each EIT covers 3 hours */
> +		curr_info->num_eits = (period / 3) + !!(period % 3);
> +		while (curr_info->num_eits &&
> +			(0xFFFF == guide.eit_pid[curr_info->num_eits - 1])) {
> +			curr_info->num_eits -= 1;
> +		}
> +		if(curr_info->eit) {
> +			fprintf(stderr, "%s(): non-NULL pointer detected "
> +				"during initialization", __FUNCTION__);
> +			return -1;
> +		}
> +		if(NULL == (curr_info->eit = calloc(curr_info->num_eits,
> +			sizeof(struct atsc_eit_info)))) {
> +			fprintf(stderr, "%s(): error calling calloc()\n",
> +				__FUNCTION__);
> +			return -1;
> +		}
> +		if(NULL == (curr_info->title_buf.string = calloc(TITLE_BUFFER_LEN,
> +			sizeof(char)))) {
> +			fprintf(stderr, "%s(): error calling calloc()\n",
> +				__FUNCTION__);
> +			return -1;
> +		}
> +		curr_info->title_buf.buf_len = TITLE_BUFFER_LEN;
> +		curr_info->title_buf.buf_pos = 0;
> +
> +		if(NULL == (curr_info->msg_buf.string = calloc(MESSAGE_BUFFER_LEN,
> +			sizeof(char)))) {
> +			fprintf(stderr, "%s(): error calling calloc()\n",
> +				__FUNCTION__);
> +			return -1;
> +		}
> +		curr_info->msg_buf.buf_len = MESSAGE_BUFFER_LEN;
> +		curr_info->msg_buf.buf_pos = 0;
> +
> +		for(k = 0; k < 7; k++) {
> +			curr_info->short_name[k] =
> +				get_bits((const uint8_t *)ch->short_name,
> +				k * 16, 16);
> +		}
> +		curr_info->service_type = ch->service_type;
> +		curr_info->major_num = ch->major_channel_number;
> +		curr_info->minor_num = ch->minor_channel_number;
> +		curr_info->tsid = ch->channel_TSID;
> +		curr_info->prog_num = ch->program_number;
> +		curr_info->src_id = ch->source_id;
> +		curr_info++;
> +		}
> +	} while(section_pattern != (uint32_t)((1 << num_sections) - 1));
> +
> +	return 0;
> +}
> +
> +static int match_event(struct atsc_eit_info *eit, uint16_t event_id,
> +	struct atsc_event_info **event, uint8_t *curr_index)
> +{
> +	int j, k;
> +	struct atsc_eit_section_info *section;
> +
> +	if(NULL == eit || NULL == event || NULL == curr_index) {
> +		fprintf(stderr, "%s(): NULL pointer detected\n", __FUNCTION__);
> +		return -1;
> +	}
> +
> +	for(j = 0; j < eit->num_eit_sections; j++) {
> +		section = &eit->section[j];
> +
> +		for(k = 0; k < section->num_events; k++) {
> +			if(section->events[k] && section->events[k]->id ==
> +				event_id) {
> +				*event = section->events[k];
> +				break;
> +			}
> +		}
> +		if(*event) {
> +			*curr_index = j;
> +			break;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int parse_message(struct atsc_channel_info *channel,
> +	struct atsc_ett_section *ett, struct atsc_event_info *event)
> +{
> +	int i, j;
> +	struct atsc_text *text;
> +	struct atsc_text_string *str;
> +
> +	if(NULL == ett || NULL == event || NULL == channel) {
> +		fprintf(stderr, "%s(): NULL pointer detected\n", __FUNCTION__);
> +		return -1;
> +	}
> +
> +	text = atsc_ett_section_extended_text_message(ett);
> +	atsc_text_strings_for_each(text, str, i) {
> +		struct atsc_text_string_segment *seg;
> +
> +		atsc_text_string_segments_for_each(str, seg, j) {
> +			event->msg_pos = channel->msg_buf.buf_pos;
> +			if(0 > atsc_text_segment_decode(seg,
> +				(uint8_t **)&channel->msg_buf.string,
> +				(size_t *)&channel->msg_buf.buf_len,
> +				(size_t *)&channel->msg_buf.buf_pos)) {
> +				fprintf(stderr, "%s(): error calling "
> +					"atsc_text_segment_decode()\n",
> +					__FUNCTION__);
> +				return -1;
> +			}
> +			event->msg_len = channel->msg_buf.buf_pos -
> +				event->msg_pos;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int parse_ett(int dmxfd, int index, uint16_t pid)
> +{
> +	uint8_t curr_index;
> +	uint32_t section_pattern;
> +	const enum atsc_section_tag tag = stag_atsc_extended_text;
> +	struct atsc_eit_info *eit;
> +	struct atsc_ett_section *ett;
> +	struct atsc_channel_info *channel;
> +	struct atsc_event_info *event;
> +	struct atsc_eit_section_info *section;
> +	uint16_t source_id, event_id;
> +	int c, ret;
> +
> +	if(0xFFFF == guide.ett_pid[index]) {
> +		return 0;
> +	}
> +
> +	for(c = 0; c < guide.num_channels; c++) {
> +		channel = &guide.ch[c];
> +		eit = &channel->eit[index];
> +
> +		section_pattern = 0;
> +		while(section_pattern !=
> +			(uint32_t)((1 << eit->num_eit_sections) - 1)) {
> +			if(ctrl_c) {
> +				return 0;
> +			}
> +			ret = atsc_scan_table(dmxfd, pid, tag, (void **)&ett);
> +			fprintf(stdout, ".");
> +			fflush(stdout);
> +			if(0 > ret) {
> +				fprintf(stderr, "%s(): error calling "
> +					"atsc_scan_table()\n", __FUNCTION__);
> +				return -1;
> +			}
> +			if(0 == ret) {
> +				fprintf(stdout, "no ETT %d in %d seconds\n",
> +					index, TIMEOUT);
> +				return 0;
> +			}
> +
> +			source_id = ett->ETM_source_id;
> +			event_id = ett->ETM_sub_id;
> +			if(source_id != channel->src_id) {
> +				continue;
> +			}
> +
> +			event = NULL;
> +			if(match_event(eit, event_id, &event, &curr_index)) {
> +				fprintf(stderr, "%s(): error calling "
> +					"match_event()\n", __FUNCTION__);
> +				return -1;
> +			}
> +			if(NULL == event) {
> +				continue;
> +			}
> +			if(section_pattern & (1 << curr_index)) {
> +				/* the section has been filled, so skip,
> +				 * not consider version yet
> +				 */
> +				continue;
> +			}
> +			if(event->msg_len) {
> +				/* the message has been filled */
> +				continue;
> +			}
> +
> +			if(parse_message(channel, ett, event)) {
> +				fprintf(stderr, "%s(): error calling "
> +					"parse_message()\n", __FUNCTION__);
> +				return -1;
> +			}
> +			section = &eit->section[curr_index];
> +			if(++section->num_received_etms == section->num_etms) {
> +				section_pattern |= 1 << curr_index;
> +			}
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int parse_events(struct atsc_channel_info *curr_info,
> +	struct atsc_eit_section *eit, struct atsc_eit_section_info *section)
> +{
> +	int i, j, k;
> +	struct atsc_eit_event *e;
> +	time_t start_time, end_time;
> +
> +	if(NULL == curr_info || NULL == eit) {
> +		fprintf(stderr, "%s(): NULL pointer detected\n", __FUNCTION__);
> +		return -1;
> +	}
> +
> +	atsc_eit_section_events_for_each(eit, e, i) {
> +		struct atsc_text *title;
> +		struct atsc_text_string *str;
> +		struct atsc_event_info *e_info =
> +			&curr_info->e[curr_info->event_info_index];
> +
> +		if(0 == i && curr_info->last_event) {
> +			if(e->event_id == curr_info->last_event->id) {
> +				section->events[i] = NULL;
> +				/* skip if it's the same event spanning
> +				 * over sections
> +				 */
> +				continue;
> +			}
> +		}
> +		curr_info->event_info_index += 1;
> +		section->events[i] = e_info;
> +		e_info->id = e->event_id;
> +		start_time = atsctime_to_unixtime(e->start_time);
> +		end_time = start_time + e->length_in_seconds;
> +		localtime_r(&start_time, &e_info->start);
> +		localtime_r(&end_time, &e_info->end);
> +		if(0 != e->ETM_location && 3 != e->ETM_location) {
> +			/* FIXME assume 1 and 2 is interchangable as of now */
> +			section->num_etms++;
> +		}
> +
> +		title = atsc_eit_event_name_title_text(e);
> +		atsc_text_strings_for_each(title, str, j) {
> +			struct atsc_text_string_segment *seg;
> +
> +			atsc_text_string_segments_for_each(str, seg, k) {
> +				e_info->title_pos = curr_info->title_buf.buf_pos;
> +				if(0 > atsc_text_segment_decode(seg,
> +					(uint8_t **)&curr_info->title_buf.string,
> +					(size_t *)&curr_info->title_buf.buf_len,
> +					(size_t *)&curr_info->title_buf.buf_pos)) {
> +					fprintf(stderr, "%s(): error calling "
> +						"atsc_text_segment_decode()\n",
> +						__FUNCTION__);
> +					return -1;
> +				}
> +				e_info->title_len = curr_info->title_buf.buf_pos -
> +					e_info->title_pos + 1;
> +			}
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int parse_eit(int dmxfd, int index, uint16_t pid)
> +{
> +	int num_sections;
> +	uint8_t section_num;
> +	uint8_t curr_channel_index;
> +	uint32_t section_pattern;
> +	const enum atsc_section_tag tag = stag_atsc_event_information;
> +	struct atsc_eit_section *eit;
> +	struct atsc_channel_info *curr_info;
> +	struct atsc_eit_info *eit_info;
> +	struct atsc_eit_section_info *section;
> +	uint16_t source_id;
> +	uint32_t eit_instance_pattern = 0;
> +	int i, k, ret;
> +
> +	while(eit_instance_pattern !=
> +		(uint32_t)((1 << guide.num_channels) - 1)) {
> +		source_id = 0xFFFF;
> +		section_pattern = 0;
> +		num_sections = -1;
> +
> +		do {
> +			ret = atsc_scan_table(dmxfd, pid, tag, (void **)&eit);
> +			fprintf(stdout, ".");
> +			fflush(stdout);
> +			if(0 > ret) {
> +				fprintf(stderr, "%s(): error calling "
> +					"atsc_scan_table()\n", __FUNCTION__);
> +				return -1;
> +			}
> +			if(0 == ret) {
> +				fprintf(stdout, "no EIT %d in %d seconds\n",
> +					index, TIMEOUT);
> +				return 0;
> +			}
> +
> +			if(0xFFFF == source_id) {
> +			source_id = atsc_eit_section_source_id(eit);
> +			for(k = 0; k < guide.num_channels; k++) {
> +				if(source_id == guide.ch[k].src_id) {
> +					curr_info = &guide.ch[k];
> +					curr_channel_index = k;
> +					if(0 == index) {
> +						curr_info->last_event = NULL;
> +					}
> +					break;
> +				}
> +			}
> +			if(k == guide.num_channels) {
> +				fprintf(stderr, "%s(): cannot find source_id "
> +					"0x%04X in the EIT\n",
> +					__FUNCTION__, source_id);
> +				return -1;
> +			}
> +			} else {
> +				if(source_id !=
> +					atsc_eit_section_source_id(eit)) {
> +					continue;
> +				}
> +			}
> +			if(eit_instance_pattern & (1 << curr_channel_index)) {
> +				/* we have received this instance,
> +				 * so quit quick
> +				 */
> +				break;
> +			}
> +
> +			if(-1 == num_sections) {
> +				num_sections = 1 +
> +					eit->head.ext_head.last_section_number;
> +				if(32 < num_sections) {
> +					fprintf(stderr,
> +						"%s(): no support yet for "
> +						"tables having more than "
> +						"32 sections\n", __FUNCTION__);
> +					return -1;
> +				}
> +			} else {
> +				if(num_sections != 1 +
> +					eit->head.ext_head.last_section_number) {
> +					fprintf(stderr,
> +						"%s(): last section number "
> +						"does not match\n",
> +						__FUNCTION__);
> +					return -1;
> +				}
> +			}
> +			if(section_pattern &
> +				(1 << eit->head.ext_head.section_number)) {
> +				continue;
> +			}
> +			section_pattern |= 1 << eit->head.ext_head.section_number;
> +
> +			eit_info = &curr_info->eit[index];
> +			if(NULL == (eit_info->section =
> +				realloc(eit_info->section,
> +				(eit_info->num_eit_sections + 1) *
> +				sizeof(struct atsc_eit_section_info)))) {
> +				fprintf(stderr,
> +					"%s(): error calling realloc()\n",
> +					__FUNCTION__);
> +				return -1;
> +			}
> +			section_num = eit->head.ext_head.section_number;
> +			if(0 == eit_info->num_eit_sections) {
> +				eit_info->num_eit_sections = 1;
> +				section = eit_info->section;
> +			} else {
> +				/* have to sort it into section order
> +				 * (temporal order)
> +				 */
> +				for(i = 0; i < eit_info->num_eit_sections; i++) {
> +					if(eit_info->section[i].section_num >
> +						section_num) {
> +						break;
> +					}
> +				}
> +				memmove(&eit_info->section[i + 1],
> +					&eit_info->section[i],
> +					(eit_info->num_eit_sections - i) *
> +					sizeof(struct atsc_eit_section_info));
> +				section = &eit_info->section[i - 1];
> +				section = &eit_info->section[i];
> +				eit_info->num_eit_sections += 1;
> +			}
> +
> +			section->section_num = section_num;
> +			section->num_events = eit->num_events_in_section;
> +			section->num_etms = 0;
> +			section->num_received_etms = 0;
> +			if(NULL == (section->events = calloc(section->num_events,
> +				sizeof(struct atsc_event_info *)))) {
> +				fprintf(stderr, "%s(): error calling calloc()\n",
> +					__FUNCTION__);
> +				return -1;
> +			}
> +			if(parse_events(curr_info, eit, section)) {
> +				fprintf(stderr, "%s(): error calling "
> +					"parse_events()\n", __FUNCTION__);
> +				return -1;
> +			}
> +		} while(section_pattern != (uint32_t)((1 << num_sections) - 1));
> +		eit_instance_pattern |= 1 << curr_channel_index;
> +	}
> +
> +	for(i = 0; i < guide.num_channels; i++) {
> +		struct atsc_channel_info *channel = &guide.ch[i];
> +		struct atsc_eit_info *ei = &channel->eit[index];
> +		struct atsc_eit_section_info *s;
> +
> +		if(0 == ei->num_eit_sections) {
> +			channel->last_event = NULL;
> +			continue;
> +		}
> +		s = &ei->section[ei->num_eit_sections - 1];
> +		/* BUG: it's incorrect when last section has no event */
> +		if(0 == s->num_events) {
> +			channel->last_event = NULL;
> +			continue;
> +		}
> +		channel->last_event = s->events[s->num_events - 1];
> +	}
> +
> +	return 0;
> +}
> +
> +static int parse_mgt(int dmxfd)
> +{
> +	const enum atsc_section_tag tag = stag_atsc_master_guide;
> +	struct atsc_mgt_section *mgt;
> +	struct atsc_mgt_table *t;
> +	int i, j, ret;
> +
> +	ret = atsc_scan_table(dmxfd, ATSC_BASE_PID, tag, (void **)&mgt);
> +	if(0 > ret) {
> +		fprintf(stderr, "%s(): error calling atsc_scan_table()\n",
> +			__FUNCTION__);
> +		return -1;
> +	}
> +	if(0 == ret) {
> +		fprintf(stdout, "no MGT in %d seconds\n", TIMEOUT);
> +		return 0;
> +	}
> +
> +	fprintf(stdout, "MGT table:\n");
> +	atsc_mgt_section_tables_for_each(mgt, t, i) {
> +		struct mgt_table_name table;
> +
> +	for(j = 0; j < (int)(sizeof(mgt_tab_name_array) /
> +		sizeof(struct mgt_table_name)); j++) {
> +		if(t->table_type > mgt_tab_name_array[j].range) {
> +			continue;
> +		}
> +		table = mgt_tab_name_array[j];
> +		if(0 == j || mgt_tab_name_array[j - 1].range + 1 ==
> +			mgt_tab_name_array[j].range) {
> +			j = -1;
> +		} else {
> +			j = t->table_type - mgt_tab_name_array[j - 1].range - 1;
> +			if(0x017F == table.range) {
> +				guide.eit_pid[j] = t->table_type_PID;
> +			} else if (0x027F == table.range) {
> +				guide.ett_pid[j] = t->table_type_PID;
> +			}
> +		}
> +		break;
> +	}
> +
> +		fprintf(stdout, "  %2d: type = 0x%04X, PID = 0x%04X, %s", i,
> +			t->table_type, t->table_type_PID, table.string);
> +		if(-1 != j) {
> +			fprintf(stdout, " %d", j);
> +		}
> +		fprintf(stdout, "\n");
> +	}
> +
> +	return 0;
> +}
> +
> +static int cleanup_guide(void)
> +{
> +	int i, j, k;
> +
> +	for(i = 0; i < guide.num_channels; i++) {
> +		struct atsc_channel_info *channel = &guide.ch[i];
> +
> +		if(channel->title_buf.string) {
> +			free(channel->title_buf.string);
> +		}
> +		if(channel->msg_buf.string) {
> +			free(channel->msg_buf.string);
> +		}
> +		for(j = 0; j < channel->num_eits; j++) {
> +			struct atsc_eit_info *eit = &channel->eit[j];
> +
> +			for(k = 0; k < eit->num_eit_sections; k++) {
> +				struct atsc_eit_section_info *section =
> +					&eit->section[k];
> +				if(section->num_events) {
> +					free(section->events);
> +				}
> +			}
> +			if(k) {
> +				free(eit->section);
> +			}
> +		}
> +		if(j) {
> +			free(channel->eit);
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int print_events(struct atsc_channel_info *channel,
> +	struct atsc_eit_section_info *section)
> +{
> +	int m;
> +	char line[256];
> +
> +	if(NULL == section) {
> +		fprintf(stderr, "%s(): NULL pointer detected", __FUNCTION__);
> +		return -1;
> +	}
> +	for(m = 0; m < section->num_events; m++) {
> +		struct atsc_event_info *event =
> +			section->events[m];
> +
> +		if(NULL == event) {
> +			continue;
> +		}
> +		fprintf(stdout, "|%02d:%02d--%02d:%02d| ",
> +			event->start.tm_hour, event->start.tm_min,
> +			event->end.tm_hour, event->end.tm_min);
> +		snprintf(line, event->title_len, "%s",
> +			&channel->title_buf.string[event->title_pos]);
> +		line[event->title_len] = '\0';
> +		fprintf(stdout, "%s\n", line);
> +		if(event->msg_len) {
> +			int len = event->msg_len;
> +			int pos = event->msg_pos;
> +			size_t part;
> +
> +			do {
> +				part = len > 255 ? 255 : len;
> +				snprintf(line, part + 1, "%s",
> +					&channel->msg_buf.string[pos]);
> +				line[part] = '\0';
> +				fprintf(stdout, "%s", line);
> +				len -= part;
> +				pos += part;
> +			} while(0 < len);
> +			fprintf(stdout, "\n");
> +		}
> +	}
> +	return 0;
> +}
> +
> +static int print_guide(void)
> +{
> +	int i, j, k;
> +
> +	fprintf(stdout, "%s\n", separator);
> +	for(i = 0; i < guide.num_channels; i++) {
> +		struct atsc_channel_info *channel = &guide.ch[i];
> +
> +		fprintf(stdout, "%d.%d  %s\n", channel->major_num,
> +			channel->minor_num, channel->short_name);
> +		for(j = 0; j < channel->num_eits; j++) {
> +			struct atsc_eit_info *eit = &channel->eit[j];
> +
> +			for(k = 0; k < eit->num_eit_sections; k++) {
> +				struct atsc_eit_section_info *section =
> +					&eit->section[k];
> +				if(print_events(channel, section)) {
> +					fprintf(stderr, "%s(): error calling "
> +						"print_events()\n", __FUNCTION__);
> +					return -1;
> +				}
> +			}
> +		}
> +		fprintf(stdout, "%s\n", separator);
> +	}
> +
> +	return 0;
> +}
> +
> +static int open_demux(int *dmxfd)
> +{
> +	if((*dmxfd = dvbdemux_open_demux(adapter, 0, 0)) < 0) {
> +		fprintf(stderr, "%s(): error calling dvbdemux_open_demux()\n",
> +			__FUNCTION__);
> +		return -1;
> +	}
> +	return 0;
> +}
> +
> +static int close_demux(int dmxfd)
> +{
> +	if(dvbdemux_stop(dmxfd)) {
> +		fprintf(stderr, "%s(): error calling dvbdemux_stop()\n",
> +			__FUNCTION__);
> +		return -1;
> +	}
> +	return 0;
> +}
> +
> +/* used other utilities as template and generalized here */
> +static int atsc_scan_table(int dmxfd, uint16_t pid, enum atsc_section_tag tag,
> +	void **table_section)
> +{
> +	uint8_t filter[18];
> +	uint8_t mask[18];
> +	unsigned char sibuf[4096];
> +	int size;
> +	int ret;
> +	struct pollfd pollfd;
> +	struct section *section;
> +	struct section_ext *section_ext;
> +	struct atsc_section_psip *psip;
> +
> +	/* create a section filter for the table */
> +	memset(filter, 0, sizeof(filter));
> +	memset(mask, 0, sizeof(mask));
> +	filter[0] = tag;
> +	mask[0] = 0xFF;
> +	if(dvbdemux_set_section_filter(dmxfd, pid, filter, mask, 1, 1)) {
> +		fprintf(stderr, "%s(): error calling atsc_scan_table()\n",
> +			__FUNCTION__);
> +		return -1;
> +	}
> +
> +	/* poll for data */
> +	pollfd.fd = dmxfd;
> +	pollfd.events = POLLIN | POLLERR |POLLPRI;
> +	if((ret = poll(&pollfd, 1, TIMEOUT * 1000)) < 0) {
> +		if(ctrl_c) {
> +			return 0;
> +		}
> +		fprintf(stderr, "%s(): error calling poll()\n", __FUNCTION__);
> +		return -1;
> +	}
> +
> +	if(0 == ret) {
> +		return 0;
> +	}
> +
> +	/* read it */
> +	if((size = read(dmxfd, sibuf, sizeof(sibuf))) < 0) {
> +		fprintf(stderr, "%s(): error calling read()\n", __FUNCTION__);
> +		return -1;
> +	}
> +
> +	/* parse section */
> +	section = section_codec(sibuf, size);
> +	if(NULL == section) {
> +		fprintf(stderr, "%s(): error calling section_codec()\n",
> +			__FUNCTION__);
> +		return -1;
> +	}
> +
> +	section_ext = section_ext_decode(section, 0);
> +	if(NULL == section_ext) {
> +		fprintf(stderr, "%s(): error calling section_ext_decode()\n",
> +			__FUNCTION__);
> +		return -1;
> +	}
> +
> +	psip = atsc_section_psip_decode(section_ext);
> +	if(NULL == psip) {
> +		fprintf(stderr,
> +			"%s(): error calling atsc_section_psip_decode()\n",
> +			__FUNCTION__);
> +		return -1;
> +	}
> +
> +	*table_section = table_callback[tag & 0x0F](psip);
> +	if(NULL == *table_section) {
> +		fprintf(stderr, "%s(): error decode table section\n",
> +			__FUNCTION__);
> +		return -1;
> +	}
> +
> +	return 1;
> +}
> +
> +int main(int argc, char *argv[])
> +{
> +	int i, dmxfd;
> +	struct dvbfe_handle *fe;
> +
> +	program = argv[0];
> +
> +	if(1 == argc) {
> +		usage();
> +		exit(-1);
> +	}
> +
> +	for( ; ; ) {
> +		char c;
> +
> +		if(-1 == (c = getopt(argc, argv, "a:f:p:m:th"))) {
> +			break;
> +		}
> +
> +		switch(c) {
> +		case 'a':
> +			adapter = strtoll(optarg, NULL, 0);
> +			break;
> +
> +		case 'f':
> +			frequency = strtol(optarg, NULL, 0);
> +			break;
> +
> +		case 'p':
> +			period = strtol(optarg, NULL, 0);
> +			/* each table covers 3 hours */
> +			if((3 * MAX_NUM_EVENT_TABLES) < period) {
> +				period = 3 * MAX_NUM_EVENT_TABLES;
> +			}
> +			break;
> +
> +		case 'm':
> +			/* just stub, so far ATSC only has VSB_8 */
> +			modulation = optarg;
> +			break;
> +
> +		case 't':
> +			enable_ett = 1;
> +			break;
> +
> +		case 'h':
> +			help();
> +			exit(0);
> +
> +		default:
> +			usage();
> +			exit(-1);
> +		}
> +	}
> +
> +	memset(separator, '-', sizeof(separator));
> +	separator[79] = '\0';
> +	memset(&guide, 0, sizeof(struct atsc_virtual_channels_info));
> +	memset(guide.eit_pid, 0xFF, MAX_NUM_EVENT_TABLES * sizeof(uint16_t));
> +	memset(guide.ett_pid, 0xFF, MAX_NUM_EVENT_TABLES * sizeof(uint16_t));
> +
> +	if(open_frontend(&fe)) {
> +		fprintf(stderr, "%s(): error calling open_frontend()\n",
> +			__FUNCTION__);
> +		return -1;
> +	}
> +
> +	if(open_demux(&dmxfd)) {
> +		fprintf(stderr, "%s(): error calling open_demux()\n",
> +			__FUNCTION__);
> +		return -1;
> +	}
> +
> +	if(parse_stt(dmxfd)) {
> +		fprintf(stderr, "%s(): error calling parse_stt()\n",
> +			__FUNCTION__);
> +		return -1;
> +	}
> +
> +	if(parse_mgt(dmxfd)) {
> +		fprintf(stderr, "%s(): error calling parse_mgt()\n",
> +			__FUNCTION__);
> +		return -1;
> +	}
> +
> +	if(parse_tvct(dmxfd)) {
> +		fprintf(stderr, "%s(): error calling parse_tvct()\n",
> +			__FUNCTION__);
> +		return -1;
> +	}
> +
> +#ifdef ENABLE_RRT
> +	if(parse_rrt(dmxfd)) {
> +		fprintf(stderr, "%s(): error calling parse_rrt()\n",
> +			__FUNCTION__);
> +		return -1;
> +	}
> +#endif
> +
> +	fprintf(stdout, "receiving EIT ");
> +	for(i = 0; i < guide.ch[0].num_eits; i++) {
> +		if(parse_eit(dmxfd, i, guide.eit_pid[i])) {
> +			fprintf(stderr, "%s(): error calling parse_eit()\n",
> +				__FUNCTION__);
> +			return -1;
> +		}
> +	}
> +	fprintf(stdout, "\n");
> +
> +	old_handler = signal(SIGINT, int_handler);
> +	if(enable_ett) {
> +		fprintf(stdout, "receiving ETT ");
> +		for(i = 0; i < guide.ch[0].num_eits; i++) {
> +			if(0xFFFF != guide.ett_pid[i]) {
> +				if(parse_ett(dmxfd, i, guide.ett_pid[i])) {
> +					fprintf(stderr, "%s(): error calling "
> +						"parse_eit()\n", __FUNCTION__);
> +					return -1;
> +				}
> +			}
> +			if(ctrl_c) {
> +				break;
> +			}
> +		}
> +		fprintf(stdout, "\n");
> +	}
> +	signal(SIGINT, old_handler);
> +
> +	if(print_guide()) {
> +		fprintf(stderr, "%s(): error calling print_guide()\n",
> +			__FUNCTION__);
> +		return -1;
> +	}
> +
> +	if(cleanup_guide()) {
> +		fprintf(stderr, "%s(): error calling cleanup_guide()\n",
> +			__FUNCTION__);
> +		return -1;
> +	}
> +
> +	if(close_demux(dmxfd)) {
> +		fprintf(stderr, "%s(): error calling close_demux()\n",
> +			__FUNCTION__);
> +		return -1;
> +	}
> +
> +	if(close_frontend(fe)) {
> +		fprintf(stderr, "%s(): error calling close_demux()\n",
> +			__FUNCTION__);
> +		return -1;
> +	}
> +
> +	return 0;
> +}
> diff -uprN dvb-apps/util/atsc_epg/Makefile dvb-apps_new/util/atsc_epg/Makefile
> --- dvb-apps/util/atsc_epg/Makefile	1969-12-31 18:00:00.000000000 -0600
> +++ dvb-apps_new/util/atsc_epg/Makefile	2009-06-18 20:11:58.362985962 -0500
> @@ -0,0 +1,16 @@
> +# Makefile for linuxtv.org dvb-apps/util/atsc_epg
> +
> +binaries = atsc_epg
> +
> +inst_bin = $(binaries)
> +
> +CPPFLAGS += -I../../lib -std=c99 -D_POSIX_SOURCE
> +#LDFLAGS  += -static -L../../lib/libdvbapi -L../../lib/libucsi
> +LDFLAGS  += -L../../lib/libdvbapi -L../../lib/libucsi
> +LDLIBS   += -ldvbapi -lucsi
> +
> +.PHONY: all
> +
> +all: $(binaries)
> +
> +include ../../Make.rules
> diff -uprN dvb-apps/util/atsc_epg/README dvb-apps_new/util/atsc_epg/README
> --- dvb-apps/util/atsc_epg/README	1969-12-31 18:00:00.000000000 -0600
> +++ dvb-apps_new/util/atsc_epg/README	2009-06-18 20:33:47.836924378 -0500
> @@ -0,0 +1,12 @@
> +Hi there,
> +
> +atsc_epg is a small utility for obtaining information such as programs, EPG 
> +(electronic program guide) from an ATSC channel. 
> +
> +Pulling the detailed information, i.e., option '-t', may take fairly long 
> +time, or never ending, which is a bug of the PSIP generator. Ctrl+C can be 
> +used to abort and the received parts will be printed.
> +
> +Enjoy,
> +Yufei
> +
> diff -uprN dvb-apps/util/Makefile dvb-apps_new/util/Makefile
> --- dvb-apps/util/Makefile	2009-06-18 19:43:30.034986539 -0500
> +++ dvb-apps_new/util/Makefile	2009-06-18 20:11:41.169986806 -0500
> @@ -3,6 +3,7 @@
>  .PHONY: all clean install
>  
>  all clean install:
> +	$(MAKE) -C atsc_epg $@
>  	$(MAKE) -C av7110_loadkeys $@
>  	$(MAKE) -C dib3000-watch $@
>  	$(MAKE) -C dst-utils $@
> 
> 
> On Sat, 2009-06-20 at 03:28 +0200, hermann pitton wrote: 
> > Hi,
> > 
> > Am Freitag, den 19.06.2009, 20:00 -0500 schrieb Yufei Yuan:
> > > Thanks for your time. It's my first time to do this, so I have been
> > > trying to follow literally on the wiki page to do it right. If you can
> > > elaborate a bit about what is broken? Is it the patch created
> > > incorrectly, or it is pasted incorrectly, or the style is still
> > > problematic?
> > > 
> > > I noticed that cutting and pasting from my console to the gmail
> > > compose window does not seem working alright. How do you normally do
> > > the inlining?
> > > 
> > > I have a full weekend to do this, and I do realize from the wiki page
> > > that it does not appear to be simple, :)
> > > 
> > > I now simply disable the footer, don't worry.
> > 
> > Just keep it on your decision. I'm not against to learn from the past.
> > 
> > It starts with lots of broken lines.
> > 
> > Cheers,
> > Hermann
> 
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff -uprN dvb-apps/util/atsc_epg/atsc_epg.c dvb-apps_new/util/atsc_epg/atsc_epg.c
--- dvb-apps/util/atsc_epg/atsc_epg.c	1969-12-31 18:00:00.000000000 -0600
+++ dvb-apps_new/util/atsc_epg/atsc_epg.c	2009-06-19 20:31:17.710924970 -0500
@@ -0,0 +1,1249 @@ 
+/*
+ * atsc_epg utility
+ *
+ * Copyright (C) 2009 Yufei Yuan <yfyuan@gmail.com>
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <time.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/poll.h>
+#include <errno.h>
+#include <getopt.h>
+#include <stdarg.h>
+#include <libdvbapi/dvbfe.h>
+#include <libdvbapi/dvbdemux.h>
+#include <libucsi/dvb/section.h>
+#include <libucsi/atsc/section.h>
+#include <libucsi/atsc/types.h>
+
+#define TIMEOUT				60
+#define RRT_TIMEOUT			60
+#define MAX_NUM_EVENT_TABLES		128
+#define TITLE_BUFFER_LEN		4096
+#define MESSAGE_BUFFER_LEN		(16 * 1024)
+#define MAX_NUM_CHANNELS		16
+#define MAX_NUM_EVENTS_PER_CHANNEL	(4 * 24 * 7)
+
+static int atsc_scan_table(int dmxfd, uint16_t pid, enum atsc_section_tag tag,
+	void **table_section);
+
+static const char *program;
+static int adapter = 0;
+static int period = 12; /* hours */
+static int frequency;
+static int enable_ett = 0;
+static int ctrl_c = 0;
+static const char *modulation = NULL;
+static char separator[80];
+void (*old_handler)(int);
+
+struct atsc_string_buffer {
+	int buf_len;
+	int buf_pos;
+	char *string;
+};
+
+struct atsc_event_info {
+	uint16_t id;
+	struct tm start;
+	struct tm end;
+	int title_pos;
+	int title_len;
+	int msg_pos;
+	int msg_len;
+};
+
+struct atsc_eit_section_info {
+	uint8_t section_num;
+	uint8_t num_events;
+	uint8_t num_etms;
+	uint8_t num_received_etms;
+	struct atsc_event_info **events;
+};
+
+struct atsc_eit_info {
+	int num_eit_sections;
+	struct atsc_eit_section_info *section;
+};
+
+struct atsc_channel_info {
+	uint8_t num_eits;
+	uint8_t service_type;
+	char short_name[8];
+	uint16_t major_num;
+	uint16_t minor_num;
+	uint16_t tsid;
+	uint16_t prog_num;
+	uint16_t src_id;
+	struct atsc_eit_info *eit;
+	struct atsc_event_info *last_event;
+	int event_info_index;
+	struct atsc_event_info e[MAX_NUM_EVENTS_PER_CHANNEL];
+	struct atsc_string_buffer title_buf;
+	struct atsc_string_buffer msg_buf;
+};
+
+struct atsc_virtual_channels_info {
+	int num_channels;
+	uint16_t eit_pid[MAX_NUM_EVENT_TABLES];
+	uint16_t ett_pid[MAX_NUM_EVENT_TABLES];
+	struct atsc_channel_info ch[MAX_NUM_CHANNELS];
+} guide;
+
+struct mgt_table_name {
+	uint16_t range;
+	const char *string;
+};
+
+struct mgt_table_name mgt_tab_name_array[] = {
+	{0x0000, "terrestrial VCT with current_next_indictor=1"},
+	{0x0001, "terrestrial VCT with current_next_indictor=0"},
+	{0x0002, "cable VCT with current_next_indictor=1"},
+	{0x0003, "cable VCT with current_next_indictor=0"},
+	{0x0004, "channel ETT"},
+	{0x0005, "DCCSCT"},
+	{0x00FF, "reserved for future ATSC use"},
+	{0x017F, "EIT"},
+	{0x01FF, "reserved for future ATSC use"},
+	{0x027F, "event ETT"},
+	{0x02FF, "reserved for future ATSC use"}, /* FIXME */
+	{0x03FF, "RRT with rating region"},
+	{0x0FFF, "user private"},
+	{0x13FF, "reserved for future ATSC use"},
+	{0x14FF, "DCCT with dcc_id"},
+	{0xFFFF, "reserved for future ATSC use"}
+};
+
+const char *channel_modulation_mode[] = {
+	"",
+	"analog",
+	"SCTE mode 1",
+	"SCTE mode 2",
+	"ATSC 8VSB",
+	"ATSC 16VSB"
+};
+
+const char *channel_service_type[] = {
+	"",
+	"analog TV",
+	"ATSC digital TV",
+	"ATSC audio",
+	"ATSC data-only"
+};
+
+void *(*table_callback[16])(struct atsc_section_psip *) =
+{
+	NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+	(void *(*)(struct atsc_section_psip *))atsc_mgt_section_codec,
+	(void *(*)(struct atsc_section_psip *))atsc_tvct_section_codec,
+	(void *(*)(struct atsc_section_psip *))atsc_cvct_section_codec,
+	(void *(*)(struct atsc_section_psip *))atsc_rrt_section_codec,
+	(void *(*)(struct atsc_section_psip *))atsc_eit_section_codec,
+	(void *(*)(struct atsc_section_psip *))atsc_ett_section_codec,
+	(void *(*)(struct atsc_section_psip *))atsc_stt_section_codec,
+	NULL, NULL
+};
+
+static void int_handler(int sig_num)
+{
+	if(SIGINT != sig_num) {
+		return;
+	}
+	ctrl_c = 1;
+}
+
+/* shamelessly stolen from dvbsnoop, but almost not modified */
+static uint32_t get_bits(const uint8_t *buf, int startbit, int bitlen)
+{
+	const uint8_t *b;
+	uint32_t mask,tmp_long;
+	int bitHigh,i;
+
+	b = &buf[startbit / 8];
+	startbit %= 8;
+
+	bitHigh = 8;
+	tmp_long = b[0];
+	for (i = 0; i < ((bitlen-1) >> 3); i++) {
+		tmp_long <<= 8;
+		tmp_long  |= b[i+1];
+		bitHigh   += 8;
+	}
+
+	startbit = bitHigh - startbit - bitlen;
+	tmp_long = tmp_long >> startbit;
+	mask     = (1ULL << bitlen) - 1;
+	return tmp_long & mask;
+}
+
+static void usage(void)
+{
+	fprintf(stderr, "usage: %s [-a <n>] -f <frequency> [-p <period>]"
+		" [-m <modulation>] [-t] [-h]\n", program);
+}
+
+static void help(void)
+{
+	fprintf(stderr,
+	"\nhelp:\n"
+	"%s [-a <n>] -f <frequency> [-p <period>] [-m <modulation>] [-t] [-h]\n"
+	"  -a: adapter index to use, (default 0)\n"
+	"  -f: tuning frequency\n"
+	"  -p: period in hours, (default 12)\n"
+	"  -m: modulation ATSC vsb_8|vsb_16 (default vsb_8)\n"
+	"  -t: enable ETT to receive program details, if available\n"
+	"  -h: display this message\n", program);
+}
+
+static int close_frontend(struct dvbfe_handle *fe)
+{
+	if(NULL == fe) {
+		fprintf(stderr, "%s(): NULL pointer detected\n", __FUNCTION__);
+	}
+
+	dvbfe_close(fe);
+
+	return 0;
+}
+
+static int open_frontend(struct dvbfe_handle **fe)
+{
+	struct dvbfe_info fe_info;
+
+	if(NULL == (*fe = dvbfe_open(adapter, 0, 0))) {
+		fprintf(stderr, "%s(): error calling dvbfe_open()\n",
+			__FUNCTION__);
+		return -1;
+	}
+	dvbfe_get_info(*fe, 0, &fe_info, DVBFE_INFO_QUERYTYPE_IMMEDIATE, 0);
+	if(DVBFE_TYPE_ATSC != fe_info.type) {
+		fprintf(stderr, "%s(): only ATSC frontend supported currently\n",
+			__FUNCTION__);
+		return -1;
+	}
+	fe_info.feparams.frequency = frequency;
+	fe_info.feparams.inversion = DVBFE_INVERSION_AUTO;
+	fe_info.feparams.u.atsc.modulation = DVBFE_ATSC_MOD_VSB_8;
+	fprintf(stdout, "tuning to %d Hz, please wait...\n", frequency);
+	if(dvbfe_set(*fe, &fe_info.feparams, TIMEOUT * 1000)) {
+		fprintf(stderr, "%s(): cannot lock to %d Hz in %d seconds\n",
+			__FUNCTION__, frequency, TIMEOUT);
+		return -1;
+	}
+	fprintf(stdout, "tuner locked.\n");
+
+	return 0;
+}
+
+#ifdef ENABLE_RRT
+/* this is untested as since this part of the library is broken */
+static int parse_rrt(int dmxfd)
+{
+	const enum atsc_section_tag tag = stag_atsc_rating_region;
+	struct atsc_rrt_section *rrt;
+	struct atsc_text *region_name;
+	struct atsc_text_string *atsc_str;
+	int i, j, ret;
+
+	i = 0;
+	fprintf(stdout, "waiting for RRT: ");
+	fflush(stdout);
+	while(i < RRT_TIMEOUT) {
+		ret = atsc_scan_table(dmxfd, ATSC_BASE_PID, tag, (void **)&rrt);
+		if(0 > ret) {
+			fprintf(stderr, "%s(): error calling atsc_scan_table()\n",
+				__FUNCTION__);
+			return -1;
+		}
+		if(0 == ret) {
+			if(RRT_TIMEOUT > i) {
+				fprintf(stdout, ".");
+				fflush(stdout);
+			} else {
+				fprintf(stdout, "\nno RRT in %d seconds\n",
+					RRT_TIMEOUT);
+				return 0;
+			}
+			i += TIMEOUT;
+		} else {
+			fprintf(stdout, "\n");
+			fflush(stdout);
+			break;
+		}
+	}
+
+	region_name = atsc_rrt_section_rating_region_name_text(rrt);
+	atsc_text_strings_for_each(region_name, atsc_str, i) {
+		struct atsc_text_string_segment *seg;
+
+		atsc_text_string_segments_for_each(atsc_str, seg, j) {
+			const char *c;
+			int k;
+			if(seg->mode < 0x3E) {
+				fprintf(stderr, "%s(): text mode of 0x%02X "
+					"not supported yet\n",
+					__FUNCTION__, seg->mode);
+				return -1;
+			}
+			c = (const char *)atsc_text_string_segment_bytes(seg);
+			for(k = 0; k < seg->number_bytes; k++) {
+				fprintf(stdout, "%c", c[k]);
+			}
+		}
+	}
+
+	return 0;
+}
+#endif
+
+static int parse_stt(int dmxfd)
+{
+	const enum atsc_section_tag tag = stag_atsc_system_time;
+	const struct atsc_stt_section *stt;
+	time_t rx_time;
+	time_t sys_time;
+	int ret;
+
+	ret = atsc_scan_table(dmxfd, ATSC_BASE_PID, tag, (void **)&stt);
+	if(0 > ret) {
+		fprintf(stderr, "%s(): error calling atsc_scan_table()\n",
+			__FUNCTION__);
+		return -1;
+	}
+	if(0 == ret) {
+		fprintf(stdout, "no STT in %d seconds\n", TIMEOUT);
+		return 0;
+	}
+
+	rx_time = atsctime_to_unixtime(stt->system_time);
+	time(&sys_time);
+	fprintf(stdout, "system time: %s", ctime(&sys_time));
+	fprintf(stdout, "TS STT time: %s", ctime(&rx_time));
+
+	return 0;
+}
+
+static int parse_tvct(int dmxfd)
+{
+	int num_sections;
+	uint32_t section_pattern;
+	const enum atsc_section_tag tag = stag_atsc_terrestrial_virtual_channel;
+	struct atsc_tvct_section *tvct;
+	struct atsc_tvct_channel *ch;
+	struct atsc_channel_info *curr_info;
+	int i, k, ret;
+
+	section_pattern = 0;
+	num_sections = -1;
+
+	do {
+		ret = atsc_scan_table(dmxfd, ATSC_BASE_PID, tag, (void **)&tvct);
+		if(0 > ret) {
+			fprintf(stderr, "%s(): error calling atsc_scan_table()\n",
+			__FUNCTION__);
+			return -1;
+		}
+		if(0 == ret) {
+			fprintf(stdout, "no TVCT in %d seconds\n", TIMEOUT);
+			return 0;
+		}
+
+		if(-1 == num_sections) {
+			num_sections = 1 + tvct->head.ext_head.last_section_number;
+			if(32 < num_sections) {
+				fprintf(stderr, "%s(): no support yet for "
+					"tables having more than 32 sections\n",
+					__FUNCTION__);
+				return -1;
+			}
+		} else {
+			if(num_sections !=
+				1 + tvct->head.ext_head.last_section_number) {
+				fprintf(stderr,
+					"%s(): last section number does not match\n",
+					__FUNCTION__);
+				return -1;
+			}
+		}
+		if(section_pattern & (1 << tvct->head.ext_head.section_number)) {
+			continue;
+		}
+		section_pattern |= 1 << tvct->head.ext_head.section_number;
+
+		if(MAX_NUM_CHANNELS < guide.num_channels +
+			tvct->num_channels_in_section) {
+			fprintf(stderr, "%s(): no support for more than %d "
+				"virtual channels in a pyhsical channel\n",
+				__FUNCTION__, MAX_NUM_CHANNELS);
+			return -1;
+		}
+		curr_info = &guide.ch[guide.num_channels];
+		guide.num_channels += tvct->num_channels_in_section;
+
+	atsc_tvct_section_channels_for_each(tvct, ch, i) {
+		/* initialize the curr_info structure */
+		/* each EIT covers 3 hours */
+		curr_info->num_eits = (period / 3) + !!(period % 3);
+		while (curr_info->num_eits &&
+			(0xFFFF == guide.eit_pid[curr_info->num_eits - 1])) {
+			curr_info->num_eits -= 1;
+		}
+		if(curr_info->eit) {
+			fprintf(stderr, "%s(): non-NULL pointer detected "
+				"during initialization", __FUNCTION__);
+			return -1;
+		}
+		if(NULL == (curr_info->eit = calloc(curr_info->num_eits,
+			sizeof(struct atsc_eit_info)))) {
+			fprintf(stderr, "%s(): error calling calloc()\n",
+				__FUNCTION__);
+			return -1;
+		}
+		if(NULL == (curr_info->title_buf.string = calloc(TITLE_BUFFER_LEN,
+			sizeof(char)))) {
+			fprintf(stderr, "%s(): error calling calloc()\n",
+				__FUNCTION__);
+			return -1;
+		}
+		curr_info->title_buf.buf_len = TITLE_BUFFER_LEN;
+		curr_info->title_buf.buf_pos = 0;
+
+		if(NULL == (curr_info->msg_buf.string = calloc(MESSAGE_BUFFER_LEN,
+			sizeof(char)))) {
+			fprintf(stderr, "%s(): error calling calloc()\n",
+				__FUNCTION__);
+			return -1;
+		}
+		curr_info->msg_buf.buf_len = MESSAGE_BUFFER_LEN;
+		curr_info->msg_buf.buf_pos = 0;
+
+		for(k = 0; k < 7; k++) {
+			curr_info->short_name[k] =
+				get_bits((const uint8_t *)ch->short_name,
+				k * 16, 16);
+		}
+		curr_info->service_type = ch->service_type;
+		curr_info->major_num = ch->major_channel_number;
+		curr_info->minor_num = ch->minor_channel_number;
+		curr_info->tsid = ch->channel_TSID;
+		curr_info->prog_num = ch->program_number;
+		curr_info->src_id = ch->source_id;
+		curr_info++;
+		}
+	} while(section_pattern != (uint32_t)((1 << num_sections) - 1));
+
+	return 0;
+}
+
+static int match_event(struct atsc_eit_info *eit, uint16_t event_id,
+	struct atsc_event_info **event, uint8_t *curr_index)
+{
+	int j, k;
+	struct atsc_eit_section_info *section;
+
+	if(NULL == eit || NULL == event || NULL == curr_index) {
+		fprintf(stderr, "%s(): NULL pointer detected\n", __FUNCTION__);
+		return -1;
+	}
+
+	for(j = 0; j < eit->num_eit_sections; j++) {
+		section = &eit->section[j];
+
+		for(k = 0; k < section->num_events; k++) {
+			if(section->events[k] && section->events[k]->id ==
+				event_id) {
+				*event = section->events[k];
+				break;
+			}
+		}
+		if(*event) {
+			*curr_index = j;
+			break;
+		}
+	}
+
+	return 0;
+}
+
+static int parse_message(struct atsc_channel_info *channel,
+	struct atsc_ett_section *ett, struct atsc_event_info *event)
+{
+	int i, j;
+	struct atsc_text *text;
+	struct atsc_text_string *str;
+
+	if(NULL == ett || NULL == event || NULL == channel) {
+		fprintf(stderr, "%s(): NULL pointer detected\n", __FUNCTION__);
+		return -1;
+	}
+
+	text = atsc_ett_section_extended_text_message(ett);
+	atsc_text_strings_for_each(text, str, i) {
+		struct atsc_text_string_segment *seg;
+
+		atsc_text_string_segments_for_each(str, seg, j) {
+			event->msg_pos = channel->msg_buf.buf_pos;
+			if(0 > atsc_text_segment_decode(seg,
+				(uint8_t **)&channel->msg_buf.string,
+				(size_t *)&channel->msg_buf.buf_len,
+				(size_t *)&channel->msg_buf.buf_pos)) {
+				fprintf(stderr, "%s(): error calling "
+					"atsc_text_segment_decode()\n",
+					__FUNCTION__);
+				return -1;
+			}
+			event->msg_len = channel->msg_buf.buf_pos -
+				event->msg_pos;
+		}
+	}
+
+	return 0;
+}
+
+static int parse_ett(int dmxfd, int index, uint16_t pid)
+{
+	uint8_t curr_index;
+	uint32_t section_pattern;
+	const enum atsc_section_tag tag = stag_atsc_extended_text;
+	struct atsc_eit_info *eit;
+	struct atsc_ett_section *ett;
+	struct atsc_channel_info *channel;
+	struct atsc_event_info *event;
+	struct atsc_eit_section_info *section;
+	uint16_t source_id, event_id;
+	int c, ret;
+
+	if(0xFFFF == guide.ett_pid[index]) {
+		return 0;
+	}
+
+	for(c = 0; c < guide.num_channels; c++) {
+		channel = &guide.ch[c];
+		eit = &channel->eit[index];
+
+		section_pattern = 0;
+		while(section_pattern !=
+			(uint32_t)((1 << eit->num_eit_sections) - 1)) {
+			if(ctrl_c) {
+				return 0;
+			}
+			ret = atsc_scan_table(dmxfd, pid, tag, (void **)&ett);
+			fprintf(stdout, ".");
+			fflush(stdout);
+			if(0 > ret) {
+				fprintf(stderr, "%s(): error calling "
+					"atsc_scan_table()\n", __FUNCTION__);
+				return -1;
+			}
+			if(0 == ret) {
+				fprintf(stdout, "no ETT %d in %d seconds\n",
+					index, TIMEOUT);
+				return 0;
+			}
+
+			source_id = ett->ETM_source_id;
+			event_id = ett->ETM_sub_id;
+			if(source_id != channel->src_id) {
+				continue;
+			}
+
+			event = NULL;
+			if(match_event(eit, event_id, &event, &curr_index)) {
+				fprintf(stderr, "%s(): error calling "
+					"match_event()\n", __FUNCTION__);
+				return -1;
+			}
+			if(NULL == event) {
+				continue;
+			}
+			if(section_pattern & (1 << curr_index)) {
+				/* the section has been filled, so skip,
+				 * not consider version yet
+				 */
+				continue;
+			}
+			if(event->msg_len) {
+				/* the message has been filled */
+				continue;
+			}
+
+			if(parse_message(channel, ett, event)) {
+				fprintf(stderr, "%s(): error calling "
+					"parse_message()\n", __FUNCTION__);
+				return -1;
+			}
+			section = &eit->section[curr_index];
+			if(++section->num_received_etms == section->num_etms) {
+				section_pattern |= 1 << curr_index;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int parse_events(struct atsc_channel_info *curr_info,
+	struct atsc_eit_section *eit, struct atsc_eit_section_info *section)
+{
+	int i, j, k;
+	struct atsc_eit_event *e;
+	time_t start_time, end_time;
+
+	if(NULL == curr_info || NULL == eit) {
+		fprintf(stderr, "%s(): NULL pointer detected\n", __FUNCTION__);
+		return -1;
+	}
+
+	atsc_eit_section_events_for_each(eit, e, i) {
+		struct atsc_text *title;
+		struct atsc_text_string *str;
+		struct atsc_event_info *e_info =
+			&curr_info->e[curr_info->event_info_index];
+
+		if(0 == i && curr_info->last_event) {
+			if(e->event_id == curr_info->last_event->id) {
+				section->events[i] = NULL;
+				/* skip if it's the same event spanning
+				 * over sections
+				 */
+				continue;
+			}
+		}
+		curr_info->event_info_index += 1;
+		section->events[i] = e_info;
+		e_info->id = e->event_id;
+		start_time = atsctime_to_unixtime(e->start_time);
+		end_time = start_time + e->length_in_seconds;
+		localtime_r(&start_time, &e_info->start);
+		localtime_r(&end_time, &e_info->end);
+		if(0 != e->ETM_location && 3 != e->ETM_location) {
+			/* FIXME assume 1 and 2 is interchangable as of now */
+			section->num_etms++;
+		}
+
+		title = atsc_eit_event_name_title_text(e);
+		atsc_text_strings_for_each(title, str, j) {
+			struct atsc_text_string_segment *seg;
+
+			atsc_text_string_segments_for_each(str, seg, k) {
+				e_info->title_pos = curr_info->title_buf.buf_pos;
+				if(0 > atsc_text_segment_decode(seg,
+					(uint8_t **)&curr_info->title_buf.string,
+					(size_t *)&curr_info->title_buf.buf_len,
+					(size_t *)&curr_info->title_buf.buf_pos)) {
+					fprintf(stderr, "%s(): error calling "
+						"atsc_text_segment_decode()\n",
+						__FUNCTION__);
+					return -1;
+				}
+				e_info->title_len = curr_info->title_buf.buf_pos -
+					e_info->title_pos + 1;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int parse_eit(int dmxfd, int index, uint16_t pid)
+{
+	int num_sections;
+	uint8_t section_num;
+	uint8_t curr_channel_index;
+	uint32_t section_pattern;
+	const enum atsc_section_tag tag = stag_atsc_event_information;
+	struct atsc_eit_section *eit;
+	struct atsc_channel_info *curr_info;
+	struct atsc_eit_info *eit_info;
+	struct atsc_eit_section_info *section;
+	uint16_t source_id;
+	uint32_t eit_instance_pattern = 0;
+	int i, k, ret;
+
+	while(eit_instance_pattern !=
+		(uint32_t)((1 << guide.num_channels) - 1)) {
+		source_id = 0xFFFF;
+		section_pattern = 0;
+		num_sections = -1;
+
+		do {
+			ret = atsc_scan_table(dmxfd, pid, tag, (void **)&eit);
+			fprintf(stdout, ".");
+			fflush(stdout);
+			if(0 > ret) {
+				fprintf(stderr, "%s(): error calling "
+					"atsc_scan_table()\n", __FUNCTION__);
+				return -1;
+			}
+			if(0 == ret) {
+				fprintf(stdout, "no EIT %d in %d seconds\n",
+					index, TIMEOUT);
+				return 0;
+			}
+
+			if(0xFFFF == source_id) {
+			source_id = atsc_eit_section_source_id(eit);
+			for(k = 0; k < guide.num_channels; k++) {
+				if(source_id == guide.ch[k].src_id) {
+					curr_info = &guide.ch[k];
+					curr_channel_index = k;
+					if(0 == index) {
+						curr_info->last_event = NULL;
+					}
+					break;
+				}
+			}
+			if(k == guide.num_channels) {
+				fprintf(stderr, "%s(): cannot find source_id "
+					"0x%04X in the EIT\n",
+					__FUNCTION__, source_id);
+				return -1;
+			}
+			} else {
+				if(source_id !=
+					atsc_eit_section_source_id(eit)) {
+					continue;
+				}
+			}
+			if(eit_instance_pattern & (1 << curr_channel_index)) {
+				/* we have received this instance,
+				 * so quit quick
+				 */
+				break;
+			}
+
+			if(-1 == num_sections) {
+				num_sections = 1 +
+					eit->head.ext_head.last_section_number;
+				if(32 < num_sections) {
+					fprintf(stderr,
+						"%s(): no support yet for "
+						"tables having more than "
+						"32 sections\n", __FUNCTION__);
+					return -1;
+				}
+			} else {
+				if(num_sections != 1 +
+					eit->head.ext_head.last_section_number) {
+					fprintf(stderr,
+						"%s(): last section number "
+						"does not match\n",
+						__FUNCTION__);
+					return -1;
+				}
+			}
+			if(section_pattern &
+				(1 << eit->head.ext_head.section_number)) {
+				continue;
+			}
+			section_pattern |= 1 << eit->head.ext_head.section_number;
+
+			eit_info = &curr_info->eit[index];
+			if(NULL == (eit_info->section =
+				realloc(eit_info->section,
+				(eit_info->num_eit_sections + 1) *
+				sizeof(struct atsc_eit_section_info)))) {
+				fprintf(stderr,
+					"%s(): error calling realloc()\n",
+					__FUNCTION__);
+				return -1;
+			}
+			section_num = eit->head.ext_head.section_number;
+			if(0 == eit_info->num_eit_sections) {
+				eit_info->num_eit_sections = 1;
+				section = eit_info->section;
+			} else {
+				/* have to sort it into section order
+				 * (temporal order)
+				 */
+				for(i = 0; i < eit_info->num_eit_sections; i++) {
+					if(eit_info->section[i].section_num >
+						section_num) {
+						break;
+					}
+				}
+				memmove(&eit_info->section[i + 1],
+					&eit_info->section[i],
+					(eit_info->num_eit_sections - i) *
+					sizeof(struct atsc_eit_section_info));
+				section = &eit_info->section[i - 1];
+				section = &eit_info->section[i];
+				eit_info->num_eit_sections += 1;
+			}
+
+			section->section_num = section_num;
+			section->num_events = eit->num_events_in_section;
+			section->num_etms = 0;
+			section->num_received_etms = 0;
+			if(NULL == (section->events = calloc(section->num_events,
+				sizeof(struct atsc_event_info *)))) {
+				fprintf(stderr, "%s(): error calling calloc()\n",
+					__FUNCTION__);
+				return -1;
+			}
+			if(parse_events(curr_info, eit, section)) {
+				fprintf(stderr, "%s(): error calling "
+					"parse_events()\n", __FUNCTION__);
+				return -1;
+			}
+		} while(section_pattern != (uint32_t)((1 << num_sections) - 1));
+		eit_instance_pattern |= 1 << curr_channel_index;
+	}
+
+	for(i = 0; i < guide.num_channels; i++) {
+		struct atsc_channel_info *channel = &guide.ch[i];
+		struct atsc_eit_info *ei = &channel->eit[index];
+		struct atsc_eit_section_info *s;
+
+		if(0 == ei->num_eit_sections) {
+			channel->last_event = NULL;
+			continue;
+		}
+		s = &ei->section[ei->num_eit_sections - 1];
+		/* BUG: it's incorrect when last section has no event */
+		if(0 == s->num_events) {
+			channel->last_event = NULL;
+			continue;
+		}
+		channel->last_event = s->events[s->num_events - 1];
+	}
+
+	return 0;
+}
+
+static int parse_mgt(int dmxfd)
+{
+	const enum atsc_section_tag tag = stag_atsc_master_guide;
+	struct atsc_mgt_section *mgt;
+	struct atsc_mgt_table *t;
+	int i, j, ret;
+
+	ret = atsc_scan_table(dmxfd, ATSC_BASE_PID, tag, (void **)&mgt);
+	if(0 > ret) {
+		fprintf(stderr, "%s(): error calling atsc_scan_table()\n",
+			__FUNCTION__);
+		return -1;
+	}
+	if(0 == ret) {
+		fprintf(stdout, "no MGT in %d seconds\n", TIMEOUT);
+		return 0;
+	}
+
+	fprintf(stdout, "MGT table:\n");
+	atsc_mgt_section_tables_for_each(mgt, t, i) {
+		struct mgt_table_name table;
+
+	for(j = 0; j < (int)(sizeof(mgt_tab_name_array) /
+		sizeof(struct mgt_table_name)); j++) {
+		if(t->table_type > mgt_tab_name_array[j].range) {
+			continue;
+		}
+		table = mgt_tab_name_array[j];
+		if(0 == j || mgt_tab_name_array[j - 1].range + 1 ==
+			mgt_tab_name_array[j].range) {
+			j = -1;
+		} else {
+			j = t->table_type - mgt_tab_name_array[j - 1].range - 1;
+			if(0x017F == table.range) {
+				guide.eit_pid[j] = t->table_type_PID;
+			} else if (0x027F == table.range) {
+				guide.ett_pid[j] = t->table_type_PID;
+			}
+		}
+		break;
+	}
+
+		fprintf(stdout, "  %2d: type = 0x%04X, PID = 0x%04X, %s", i,
+			t->table_type, t->table_type_PID, table.string);
+		if(-1 != j) {
+			fprintf(stdout, " %d", j);
+		}
+		fprintf(stdout, "\n");
+	}
+
+	return 0;
+}
+
+static int cleanup_guide(void)
+{
+	int i, j, k;
+
+	for(i = 0; i < guide.num_channels; i++) {
+		struct atsc_channel_info *channel = &guide.ch[i];
+
+		if(channel->title_buf.string) {
+			free(channel->title_buf.string);
+		}
+		if(channel->msg_buf.string) {
+			free(channel->msg_buf.string);
+		}
+		for(j = 0; j < channel->num_eits; j++) {
+			struct atsc_eit_info *eit = &channel->eit[j];
+
+			for(k = 0; k < eit->num_eit_sections; k++) {
+				struct atsc_eit_section_info *section =
+					&eit->section[k];
+				if(section->num_events) {
+					free(section->events);
+				}
+			}
+			if(k) {
+				free(eit->section);
+			}
+		}
+		if(j) {
+			free(channel->eit);
+		}
+	}
+
+	return 0;
+}
+
+static int print_events(struct atsc_channel_info *channel,
+	struct atsc_eit_section_info *section)
+{
+	int m;
+	char line[256];
+
+	if(NULL == section) {
+		fprintf(stderr, "%s(): NULL pointer detected", __FUNCTION__);
+		return -1;
+	}
+	for(m = 0; m < section->num_events; m++) {
+		struct atsc_event_info *event =
+			section->events[m];
+
+		if(NULL == event) {
+			continue;
+		}
+		fprintf(stdout, "|%02d:%02d--%02d:%02d| ",
+			event->start.tm_hour, event->start.tm_min,
+			event->end.tm_hour, event->end.tm_min);
+		snprintf(line, event->title_len, "%s",
+			&channel->title_buf.string[event->title_pos]);
+		line[event->title_len] = '\0';
+		fprintf(stdout, "%s\n", line);
+		if(event->msg_len) {
+			int len = event->msg_len;
+			int pos = event->msg_pos;
+			size_t part;
+
+			do {
+				part = len > 255 ? 255 : len;
+				snprintf(line, part + 1, "%s",
+					&channel->msg_buf.string[pos]);
+				line[part] = '\0';
+				fprintf(stdout, "%s", line);
+				len -= part;
+				pos += part;
+			} while(0 < len);
+			fprintf(stdout, "\n");
+		}
+	}
+	return 0;
+}
+
+static int print_guide(void)
+{
+	int i, j, k;
+
+	fprintf(stdout, "%s\n", separator);
+	for(i = 0; i < guide.num_channels; i++) {
+		struct atsc_channel_info *channel = &guide.ch[i];
+
+		fprintf(stdout, "%d.%d  %s\n", channel->major_num,
+			channel->minor_num, channel->short_name);
+		for(j = 0; j < channel->num_eits; j++) {
+			struct atsc_eit_info *eit = &channel->eit[j];
+
+			for(k = 0; k < eit->num_eit_sections; k++) {
+				struct atsc_eit_section_info *section =
+					&eit->section[k];
+				if(print_events(channel, section)) {
+					fprintf(stderr, "%s(): error calling "
+						"print_events()\n", __FUNCTION__);
+					return -1;
+				}
+			}
+		}
+		fprintf(stdout, "%s\n", separator);
+	}
+
+	return 0;
+}
+
+static int open_demux(int *dmxfd)
+{
+	if((*dmxfd = dvbdemux_open_demux(adapter, 0, 0)) < 0) {
+		fprintf(stderr, "%s(): error calling dvbdemux_open_demux()\n",
+			__FUNCTION__);
+		return -1;
+	}
+	return 0;
+}
+
+static int close_demux(int dmxfd)
+{
+	if(dvbdemux_stop(dmxfd)) {
+		fprintf(stderr, "%s(): error calling dvbdemux_stop()\n",
+			__FUNCTION__);
+		return -1;
+	}
+	return 0;
+}
+
+/* used other utilities as template and generalized here */
+static int atsc_scan_table(int dmxfd, uint16_t pid, enum atsc_section_tag tag,
+	void **table_section)
+{
+	uint8_t filter[18];
+	uint8_t mask[18];
+	unsigned char sibuf[4096];
+	int size;
+	int ret;
+	struct pollfd pollfd;
+	struct section *section;
+	struct section_ext *section_ext;
+	struct atsc_section_psip *psip;
+
+	/* create a section filter for the table */
+	memset(filter, 0, sizeof(filter));
+	memset(mask, 0, sizeof(mask));
+	filter[0] = tag;
+	mask[0] = 0xFF;
+	if(dvbdemux_set_section_filter(dmxfd, pid, filter, mask, 1, 1)) {
+		fprintf(stderr, "%s(): error calling atsc_scan_table()\n",
+			__FUNCTION__);
+		return -1;
+	}
+
+	/* poll for data */
+	pollfd.fd = dmxfd;
+	pollfd.events = POLLIN | POLLERR |POLLPRI;
+	if((ret = poll(&pollfd, 1, TIMEOUT * 1000)) < 0) {
+		if(ctrl_c) {
+			return 0;
+		}
+		fprintf(stderr, "%s(): error calling poll()\n", __FUNCTION__);
+		return -1;
+	}
+
+	if(0 == ret) {
+		return 0;
+	}
+
+	/* read it */
+	if((size = read(dmxfd, sibuf, sizeof(sibuf))) < 0) {
+		fprintf(stderr, "%s(): error calling read()\n", __FUNCTION__);
+		return -1;
+	}
+
+	/* parse section */
+	section = section_codec(sibuf, size);
+	if(NULL == section) {
+		fprintf(stderr, "%s(): error calling section_codec()\n",
+			__FUNCTION__);
+		return -1;
+	}
+
+	section_ext = section_ext_decode(section, 0);
+	if(NULL == section_ext) {
+		fprintf(stderr, "%s(): error calling section_ext_decode()\n",
+			__FUNCTION__);
+		return -1;
+	}
+
+	psip = atsc_section_psip_decode(section_ext);
+	if(NULL == psip) {
+		fprintf(stderr,
+			"%s(): error calling atsc_section_psip_decode()\n",
+			__FUNCTION__);
+		return -1;
+	}
+
+	*table_section = table_callback[tag & 0x0F](psip);
+	if(NULL == *table_section) {
+		fprintf(stderr, "%s(): error decode table section\n",
+			__FUNCTION__);
+		return -1;
+	}
+
+	return 1;
+}
+
+int main(int argc, char *argv[])
+{
+	int i, dmxfd;
+	struct dvbfe_handle *fe;
+
+	program = argv[0];
+
+	if(1 == argc) {
+		usage();
+		exit(-1);
+	}
+
+	for( ; ; ) {
+		char c;
+
+		if(-1 == (c = getopt(argc, argv, "a:f:p:m:th"))) {
+			break;
+		}
+
+		switch(c) {
+		case 'a':
+			adapter = strtoll(optarg, NULL, 0);
+			break;
+
+		case 'f':
+			frequency = strtol(optarg, NULL, 0);
+			break;
+
+		case 'p':
+			period = strtol(optarg, NULL, 0);
+			/* each table covers 3 hours */
+			if((3 * MAX_NUM_EVENT_TABLES) < period) {
+				period = 3 * MAX_NUM_EVENT_TABLES;
+			}
+			break;
+
+		case 'm':
+			/* just stub, so far ATSC only has VSB_8 */
+			modulation = optarg;
+			break;
+
+		case 't':
+			enable_ett = 1;
+			break;
+
+		case 'h':
+			help();
+			exit(0);
+
+		default:
+			usage();
+			exit(-1);
+		}
+	}
+
+	memset(separator, '-', sizeof(separator));
+	separator[79] = '\0';
+	memset(&guide, 0, sizeof(struct atsc_virtual_channels_info));
+	memset(guide.eit_pid, 0xFF, MAX_NUM_EVENT_TABLES * sizeof(uint16_t));
+	memset(guide.ett_pid, 0xFF, MAX_NUM_EVENT_TABLES * sizeof(uint16_t));
+
+	if(open_frontend(&fe)) {
+		fprintf(stderr, "%s(): error calling open_frontend()\n",
+			__FUNCTION__);
+		return -1;
+	}
+
+	if(open_demux(&dmxfd)) {
+		fprintf(stderr, "%s(): error calling open_demux()\n",
+			__FUNCTION__);
+		return -1;
+	}
+
+	if(parse_stt(dmxfd)) {
+		fprintf(stderr, "%s(): error calling parse_stt()\n",
+			__FUNCTION__);
+		return -1;
+	}
+
+	if(parse_mgt(dmxfd)) {
+		fprintf(stderr, "%s(): error calling parse_mgt()\n",
+			__FUNCTION__);
+		return -1;
+	}
+
+	if(parse_tvct(dmxfd)) {
+		fprintf(stderr, "%s(): error calling parse_tvct()\n",
+			__FUNCTION__);
+		return -1;
+	}
+
+#ifdef ENABLE_RRT
+	if(parse_rrt(dmxfd)) {
+		fprintf(stderr, "%s(): error calling parse_rrt()\n",
+			__FUNCTION__);
+		return -1;
+	}
+#endif
+
+	fprintf(stdout, "receiving EIT ");
+	for(i = 0; i < guide.ch[0].num_eits; i++) {
+		if(parse_eit(dmxfd, i, guide.eit_pid[i])) {
+			fprintf(stderr, "%s(): error calling parse_eit()\n",
+				__FUNCTION__);
+			return -1;
+		}
+	}
+	fprintf(stdout, "\n");
+
+	old_handler = signal(SIGINT, int_handler);
+	if(enable_ett) {
+		fprintf(stdout, "receiving ETT ");
+		for(i = 0; i < guide.ch[0].num_eits; i++) {
+			if(0xFFFF != guide.ett_pid[i]) {
+				if(parse_ett(dmxfd, i, guide.ett_pid[i])) {
+					fprintf(stderr, "%s(): error calling "
+						"parse_eit()\n", __FUNCTION__);
+					return -1;
+				}
+			}
+			if(ctrl_c) {
+				break;
+			}
+		}
+		fprintf(stdout, "\n");
+	}
+	signal(SIGINT, old_handler);
+
+	if(print_guide()) {
+		fprintf(stderr, "%s(): error calling print_guide()\n",
+			__FUNCTION__);
+		return -1;
+	}
+
+	if(cleanup_guide()) {
+		fprintf(stderr, "%s(): error calling cleanup_guide()\n",
+			__FUNCTION__);
+		return -1;
+	}
+
+	if(close_demux(dmxfd)) {
+		fprintf(stderr, "%s(): error calling close_demux()\n",
+			__FUNCTION__);
+		return -1;
+	}
+
+	if(close_frontend(fe)) {
+		fprintf(stderr, "%s(): error calling close_demux()\n",
+			__FUNCTION__);
+		return -1;
+	}
+
+	return 0;
+}
diff -uprN dvb-apps/util/atsc_epg/Makefile dvb-apps_new/util/atsc_epg/Makefile
--- dvb-apps/util/atsc_epg/Makefile	1969-12-31 18:00:00.000000000 -0600
+++ dvb-apps_new/util/atsc_epg/Makefile	2009-06-18 20:11:58.362985962 -0500
@@ -0,0 +1,16 @@ 
+# Makefile for linuxtv.org dvb-apps/util/atsc_epg
+
+binaries = atsc_epg
+
+inst_bin = $(binaries)
+
+CPPFLAGS += -I../../lib -std=c99 -D_POSIX_SOURCE
+#LDFLAGS  += -static -L../../lib/libdvbapi -L../../lib/libucsi
+LDFLAGS  += -L../../lib/libdvbapi -L../../lib/libucsi
+LDLIBS   += -ldvbapi -lucsi
+
+.PHONY: all
+
+all: $(binaries)
+
+include ../../Make.rules
diff -uprN dvb-apps/util/atsc_epg/README dvb-apps_new/util/atsc_epg/README
--- dvb-apps/util/atsc_epg/README	1969-12-31 18:00:00.000000000 -0600
+++ dvb-apps_new/util/atsc_epg/README	2009-06-18 20:33:47.836924378 -0500
@@ -0,0 +1,12 @@ 
+Hi there,
+
+atsc_epg is a small utility for obtaining information such as programs, EPG 
+(electronic program guide) from an ATSC channel. 
+
+Pulling the detailed information, i.e., option '-t', may take fairly long 
+time, or never ending, which is a bug of the PSIP generator. Ctrl+C can be 
+used to abort and the received parts will be printed.
+
+Enjoy,
+Yufei
+
diff -uprN dvb-apps/util/Makefile dvb-apps_new/util/Makefile
--- dvb-apps/util/Makefile	2009-06-18 19:43:30.034986539 -0500
+++ dvb-apps_new/util/Makefile	2009-06-18 20:11:41.169986806 -0500
@@ -3,6 +3,7 @@ 
 .PHONY: all clean install
 
 all clean install:
+	$(MAKE) -C atsc_epg $@
 	$(MAKE) -C av7110_loadkeys $@
 	$(MAKE) -C dib3000-watch $@
 	$(MAKE) -C dst-utils $@