| // Copyright 2011 Google Inc. All Rights Reserved. |
| // Author: qianzhang@google.com (Qiann Zhang) |
| |
| #define _GNU_SOURCE |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <sys/socket.h> |
| #include <netinet/in.h> |
| #include <arpa/inet.h> |
| #include <errno.h> |
| #include <netdb.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| #include <limits.h> |
| #include <assert.h> |
| #include <signal.h> |
| #include <sys/errno.h> |
| #include <features.h> |
| #include <sys/time.h> |
| #include <math.h> |
| #include <time.h> |
| #include <sys/timeb.h> |
| #include <sys/un.h> |
| #include <poll.h> |
| #include <pthread.h> |
| #include <dirent.h> |
| #include <dlfcn.h> |
| #include <stdarg.h> |
| #include <inttypes.h> |
| #include "channel_buffer.h" |
| |
| #define sage_log(x) _sagelog x |
| void _sagelog( int type, int level, const char* cstr, ... ); |
| |
| static const char* delimiter = " \t,;\r\n:=|'\"#"; |
| |
| static int is_delimiter( char ch ) |
| { |
| int i; |
| if ( ch == 0 ) return 1; |
| for ( i = 0; delimiter[i]; i++ ) |
| if ( ch == delimiter[i] ) return 1; |
| return 0; |
| } |
| |
| //format name:zzz (or name=zzzz) |
| int get_string( char*p, const char* name, char* string, int max_len, char **next ) |
| { |
| char *s, *e; |
| e = p; |
| |
| while ( e != NULL && *e ) { |
| s = strstr( e, name ); |
| if ( s == NULL ) |
| return 0; |
| //is a token name |
| if ( ((s > p && is_delimiter(*(s-1))) || s == p ) && is_delimiter( *(s+strlen(name)) ) ) { |
| s += (int)strlen(name); |
| while( *s && ( *s == ' ' || *s == '\t' ) ) |
| s++; //skip white space |
| if ( *s == '=' || *s == ':' ) { |
| s++; |
| while( *s && ( *s == ' ' || *s == '\t' ) ) |
| s++; //skip white space |
| if ( *s ) { |
| int i=0; |
| while ( i++<max_len && *s && *s != ' ' && *s != '\t' && *s != '\n' && *s != '\r') |
| *string++ = *s++; |
| |
| *string = 0x0; //terminator |
| if ( next != NULL ) { |
| while ( *s && *s != ' ' && *s != '\t' && *s != '\n' && *s != '\r') |
| s++; |
| *next = s; |
| } |
| return 1; |
| } |
| } |
| } |
| e = ++s; |
| } |
| return 0; |
| } |
| |
| //format name:"zzz" (or name="zzzz") |
| int get_string_with_quote( char*p, const char* name, char* string, int max_len, char **next ) |
| { |
| char *s, *e; |
| e = p; |
| |
| while ( e != NULL && *e ) { |
| s = strstr( e, name ); |
| if ( s == NULL ) return 0; |
| //is a token name |
| if ( ((s > p && is_delimiter(*(s-1))) || s == p ) && is_delimiter( *(s+strlen(name)) ) ) { |
| s += (int)strlen(name); |
| while( *s && ( *s == ' ' || *s == '\t' ) ) |
| s++; //skip white space |
| if ( *s == '=' || *s == ':' ) { |
| s++; |
| while( *s && ( *s == ' ' || *s == '\t' ) ) |
| s++; //skip white space |
| if ( *s == '\"' ) { |
| int i=0; |
| s++; |
| while ( i++<max_len && *s && *s != '\"' ) |
| *string++ = *s++; |
| |
| *string = 0x0; //terminator |
| |
| if ( next != NULL) { |
| while ( *s && *s != '\"' ) |
| s++; |
| *next = s+1; |
| } |
| return 1; |
| } else |
| return 0; |
| } |
| } |
| e = ++s; |
| } |
| return 0; |
| } |
| |
| int get_string_token( char*p, int num, char* string, int max_len, char **next ) |
| { |
| int count = 0; |
| char *s; |
| s = p; |
| if ( s == NULL ) |
| return 0; |
| while ( *s ) { |
| while( *s && ( *s == ' ' || *s == '\t' || *s == '\r' || *s == '\n' ) ) |
| s++; //skip white space |
| if ( *s == 0 ) |
| break; |
| |
| if ( count++ != num ) { |
| while( *s && !( *s == ' ' || *s == '\t' || *s == ';' || *s == ',' || *s == '\r' || *s == '\n' ) ) |
| s++; //skip a token |
| } else { |
| if ( *s ) { |
| int i=0; |
| while ( i++<max_len && *s && !( *s == ' ' || *s == '\t' || *s == ';' || *s == ',' || *s == '\r' || *s == '\n' ) ) |
| *string++ = *s++; |
| |
| *string = 0x0; //terminator |
| if ( next != NULL ) { |
| while ( *s && !( *s == ' ' || *s == '\t' || *s == '\r' || *s == '\n' ) ) |
| s++; |
| *next = s; |
| } |
| return 1; |
| } |
| } |
| if ( *s ) |
| s++; |
| } |
| return 0; |
| } |
| |
| int get_int_val( char*p, const char* name, int* val, char** next ) |
| { |
| char *s, *e; |
| e = p; |
| while( e != NULL && *e ) { |
| s = strstr( e, name ); |
| if ( s == NULL ) |
| return 0; |
| //is a token name |
| if ( ( (s > p && is_delimiter(*(s-1)) ) || s == p ) && is_delimiter( *(s+strlen(name)) ) ) { |
| s += strlen(name); |
| while( *s && ( *s == ' ' || *s == '\t' ) ) |
| s++; //skip white space |
| if ( *s == '=' || *s == ':' ) { |
| s++; |
| while( *s && ( *s == ' ' || *s == '\t' ) ) |
| s++; //skip white space |
| if ( *s ) { |
| *val = atoi( s ); |
| if (*val < 0 && *s == '-' ) |
| s++; |
| while( *s >= '0' && *s <= '9') |
| s++; |
| if ( next != NULL ) |
| *next = s; |
| return 1; |
| } |
| } |
| } |
| e = ++s; |
| } |
| return 0; |
| } |
| |
| //format: name val; ext |
| int get_cfg_val( char* string, char* name, int max_name_lenn, int* Val, char* ext, int max_ext_len ) |
| { |
| const char* delima = " \t,;\r\n"; |
| char *token, *saved_ptr; |
| if ( name == NULL || ext == NULL || max_name_lenn == 0 || max_ext_len == 0 ) |
| return 0; |
| |
| name[0] = 0; |
| *Val = 0; |
| ext[0] = 0;; |
| token = (char*)strtok_r( string, delima, &saved_ptr ); |
| if ( token != NULL ) |
| strncpy( name, token, max_name_lenn ); |
| token = (char*)strtok_r( saved_ptr, delima, &saved_ptr ); |
| if ( token != NULL ) { |
| *Val = atoi( token); strncpy( ext, token, max_ext_len ); } |
| token = (char*)strtok_r( saved_ptr, delima, &saved_ptr ); |
| if ( token != NULL ) |
| strncpy( ext, token, max_ext_len ); |
| return 1; |
| } |
| |
| int make_channel_entry_buffer( struct channel_entry_t *ce, char *buf, int size ) |
| { |
| int pos = 0; |
| pos += snprintf( buf+pos, size-pos, "ch:%03d state:%d ", ce->ch, ce->state ); |
| if ( ce->ctrl_bits ) |
| pos += snprintf( buf+pos, size-pos, "ctrl:%d ", ce->ctrl_bits ); |
| if ( ce->ch_name[0] ) |
| pos += snprintf( buf+pos, size-pos, "name:%s ", ce->ch_name ); |
| if ( ce->src_ip[0] ) |
| pos += snprintf( buf+pos, size-pos, "ip:%s port:%d ", ce->src_ip, ce->src_port); |
| if ( ce->epg_id[0] ) |
| pos += snprintf( buf+pos, size-pos, "epg:%s ", ce->epg_id ); |
| if ( ce->dev_name[0] ) |
| pos += snprintf( buf+pos, size-pos, "dev:%s ", ce->dev_name ); |
| if ( ce->dev_tuning[0] ) |
| pos += snprintf( buf+pos, size-pos, "tune:%s ", ce->dev_tuning ); |
| if ( ce->av_inf[0] ) |
| pos += snprintf( buf+pos, size-pos, "av:\"%s\" ", ce->av_inf ); |
| if ( ce->ch_desc[0] ) |
| pos += snprintf( buf+pos, size-pos, "desc:%s ", ce->ch_desc ); |
| pos += snprintf( buf+pos, size-pos, "\n" ); |
| return pos; |
| } |
| |
| int parse_channel_entry_t_buffer( struct channel_entry_t *ce, char* buf ) |
| { |
| int item = 0; |
| char *p = buf; |
| if ( !get_int_val( p, "ch", &ce->ch, NULL ) ) |
| ce->ch = -1; |
| else |
| if ( !get_int_val( p, "state", &ce->state, NULL ) ) |
| ce->ch = 1; |
| else |
| item++; |
| if ( !get_int_val( p, "ctrl", &ce->ctrl_bits, NULL ) ) |
| ce->ctrl_bits = 0; |
| else |
| item++; |
| if ( !get_string( p, "name", ce->ch_name, sizeof(ce->ch_name)-1, NULL ) ) |
| ce->ch_name[0] = 0x0; |
| else |
| item++; |
| if ( !get_string( p, "desc", ce->ch_desc, sizeof(ce->ch_desc)-1, NULL ) ) |
| ce->ch_desc[0] = 0x0; |
| else |
| item++; |
| if ( !get_string( p, "ip", ce->src_ip, sizeof(ce->src_ip)-1, NULL ) ) |
| ce->src_ip[0] = 0x0; |
| else |
| item++; |
| if ( !get_int_val( p, "port", &ce->src_port, NULL ) ) |
| ce->src_port = -1; |
| else |
| item++; |
| if ( !get_string( p, "epg", ce->epg_id, sizeof(ce->epg_id)-1, NULL ) ) |
| ce->epg_id[0] = 0x0; |
| else |
| item++; |
| if ( !get_string( p, "dev", ce->dev_name, sizeof(ce->dev_name)-1, NULL ) ) |
| ce->dev_name[0] = 0x0; |
| else |
| item++; |
| if ( !get_string( p, "tuning", ce->dev_tuning, sizeof(ce->dev_tuning)-1, NULL ) ) |
| ce->dev_tuning[0] = 0x0; |
| else |
| item++; |
| if ( !get_string_with_quote( p, "av", ce->av_inf, sizeof(ce->av_inf)-1, NULL ) ) |
| ce->av_inf[0] = 0x0; |
| else |
| item++; |
| if ( !get_string_with_quote( p, "desc", ce->ch_desc, sizeof(ce->ch_desc)-1, NULL ) ) |
| ce->ch_desc[0] = 0x0; |
| else |
| item++; |
| |
| return item; |
| } |
| |
| struct channel_list_t* create_channel_list( int num ) |
| { |
| if ( num <= 0 ) |
| return NULL; |
| struct channel_list_t *cl = malloc( sizeof( struct channel_list_t)); |
| if ( cl == NULL ) |
| return NULL; |
| cl->num = num; |
| cl->channel_num = 0; |
| cl->ce = malloc( sizeof(struct channel_entry_t)*num ); |
| if ( cl->ce == NULL ) { |
| free( cl ); |
| return NULL; |
| } |
| memset( cl->ce, 0x0, sizeof(struct channel_entry_t)*num ); |
| return cl; |
| } |
| |
| void release_channel_list( struct channel_list_t *cl ) |
| { |
| if ( cl ) { |
| if ( cl->ce ) |
| free( cl->ce ); |
| free( cl ); |
| } |
| } |
| |
| struct channel_list_t* join_channel_list( struct channel_list_t* cl1, struct channel_list_t* cl2 ) |
| { |
| int i, n, channel_num; |
| if ( cl1 == NULL ) |
| return cl2; |
| if ( cl2 == NULL ) |
| return cl1; |
| |
| channel_num = cl1->channel_num + cl2->channel_num; |
| if ( channel_num < cl1->num ) { |
| n = 0; |
| for ( i = cl1->channel_num; i<channel_num; i++ ) { |
| cl1->ce[i] = cl2->ce[n++]; |
| cl1->ce[i].ch = i; |
| } |
| cl2->channel_num = channel_num; |
| release_channel_list( cl2 ); |
| return cl1; |
| } else if ( channel_num < cl2->num ) { |
| n = 0; |
| for ( i = cl2->channel_num; i<channel_num; i++ ) { |
| cl2->ce[i] = cl1->ce[n++]; |
| cl2->ce[i].ch = i; |
| } |
| cl2->channel_num = channel_num; |
| release_channel_list( cl1 ); |
| return cl2; |
| } else { |
| struct channel_list_t* cl = create_channel_list( channel_num ); |
| for ( i = 0; i<cl1->channel_num; i++ ) { |
| cl->ce[i] = cl1->ce[i]; |
| cl->ce[i].ch = i; |
| } |
| for ( n = 0; n<cl2->channel_num; n++, i++ ) { |
| cl->ce[i] = cl2->ce[n]; |
| cl->ce[i].ch = i; |
| } |
| release_channel_list( cl1 ); |
| release_channel_list( cl2 ); |
| cl->channel_num = channel_num; |
| return cl; |
| } |
| return NULL; |
| } |
| |
| |
| struct channel_list_t* load_channel_entry( char* file_name ) |
| { |
| int item; |
| int count = 0; |
| char buf[2048]={0}; |
| struct channel_list_t *cl; |
| FILE *fp; |
| fp = fopen( file_name, "r" ); |
| if ( fp == NULL ) { |
| //printf( "Can't open file %s to load channel table, errno:%d\n", file_name, errno ); |
| return NULL; |
| } |
| //count entry number |
| while ( !feof(fp) ) { |
| char* p = fgets( buf, sizeof(buf)-1, fp ); |
| if ( p == NULL ) |
| break; |
| if ( buf[0] != '#' && strlen(buf) > 4 ) |
| count++; |
| } |
| fseek( fp, 0, SEEK_SET ); |
| cl = create_channel_list( count ); |
| while ( !feof(fp) && cl->channel_num < cl->num ) { |
| char *p = fgets( buf, sizeof(buf)-1, fp ); |
| if ( p == NULL ) |
| break; |
| |
| //skip comments |
| if ( buf[0] == '#' ) |
| continue; |
| |
| item = parse_channel_entry_t_buffer( &cl->ce[cl->channel_num], buf ); |
| if ( item > 0 ) { |
| cl->channel_num++; |
| } |
| } |
| fclose( fp ); |
| return cl; |
| } |
| |
| int save_channel_entry( char* file_name, struct channel_list_t *cl ) |
| { |
| int i; |
| char buf[2048]; |
| FILE *fp; |
| if ( cl == NULL || cl->channel_num == 0 ) |
| return 0; |
| |
| fp = fopen( file_name, "w" ); |
| if ( fp == NULL ) { |
| //printf( "Can't open file %s to save channel table, errno:%d\n", file_name, errno ); |
| return -1; |
| } |
| fputs( "#Google sagetv channel table ver 1.0., maxium 2048 bytes per line\n", fp ); |
| fprintf( fp, "total_channel:%d\n", cl->channel_num ); |
| for ( i = 0; i<cl->channel_num; i++ ) { |
| make_channel_entry_buffer( &cl->ce[i], buf, sizeof(buf) ); |
| fputs( buf, fp ); |
| } |
| fclose( fp ); |
| return 1; |
| } |
| |
| char* join_alloc_string( char* str1, char* str2 ) |
| { |
| int len, len1, len2; |
| char* str; |
| if ( str1 == NULL ) |
| return str2; |
| if ( str2 == NULL ) |
| return str1; |
| len1 = strlen( str1 ); |
| len2 = strlen( str2 ); |
| len = len1 + len2; |
| str = (char*)malloc( len+1 ); |
| strncpy( str, str1, len1 ); |
| strncpy( str+len1, str2, len2 ); |
| str[len] = 0x0; |
| free( str1 ); |
| free( str2 ); |
| return str; |
| } |
| |