| // 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" |
| |
| 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., maximum 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; |
| } |