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 <yfy...@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 <yfy...@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 majord...@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 majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to