Fix various bugs related to channel <-> service mappings
This commit is contained in:
parent
cb32905c3c
commit
0d7cbe9ac2
15 changed files with 91 additions and 81 deletions
|
@ -413,13 +413,13 @@ ajax_cheditor(http_connection_t *hc, http_reply_t *hr,
|
|||
|
||||
tcp_qprintf(tq, "<div>Sources:</div>");
|
||||
|
||||
LIST_FOREACH(t, &ch->ch_transports, tht_channel_link) {
|
||||
LIST_FOREACH(t, &ch->ch_transports, tht_ch_link) {
|
||||
ajax_box_begin(tq, AJAX_BOX_BORDER, NULL, NULL, NULL);
|
||||
tcp_qprintf(tq, "<div style=\"overflow: auto; width: 100%\">");
|
||||
tcp_qprintf(tq, "<div style=\"float: left; width: 13%%\">%s</div>",
|
||||
val2str(t->tht_type, sourcetypetab) ?: "???");
|
||||
tcp_qprintf(tq, "<div style=\"float: left; width: 87%%\">\"%s\"%s</div>",
|
||||
t->tht_servicename, t->tht_scrambled ? " - (scrambled)" : "");
|
||||
t->tht_svcname, t->tht_scrambled ? " - (scrambled)" : "");
|
||||
s = t->tht_sourcename ? t->tht_sourcename(t) : NULL;
|
||||
|
||||
tcp_qprintf(tq, "</div><div style=\"overflow: auto; width: 100%\">");
|
||||
|
|
|
@ -134,7 +134,7 @@ ajax_transport_build_list(http_connection_t *hc, tcp_queue_t *tq,
|
|||
ajax_table_cell(&ta, NULL, "%d", t->tht_dvb_service_id);
|
||||
ajax_table_cell(&ta, NULL, "%s", t->tht_scrambled ? "Yes" : "No");
|
||||
ajax_table_cell(&ta, NULL, "%s", transport_servicetype_txt(t));
|
||||
ajax_table_cell(&ta, NULL, "%s", t->tht_servicename ?: "");
|
||||
ajax_table_cell(&ta, NULL, "%s", t->tht_svcname ?: "");
|
||||
|
||||
ajax_table_cell(&ta, NULL,
|
||||
"<a href=\"javascript:void(0)\" "
|
||||
|
@ -142,9 +142,9 @@ ajax_transport_build_list(http_connection_t *hc, tcp_queue_t *tq,
|
|||
"{parameters: {'%s': 'selected'}})\">"
|
||||
"<img id=\"map_%s\" src=\"/gfx/%smapped.png\"></a>",
|
||||
t->tht_identifier, t->tht_identifier,
|
||||
t->tht_channel ? "" : "un");
|
||||
t->tht_ch ? "" : "un");
|
||||
|
||||
if(t->tht_channel == NULL) {
|
||||
if(t->tht_ch == NULL) {
|
||||
/* Unmapped */
|
||||
ajax_table_cell(&ta, "chname",
|
||||
"<a href=\"javascript:void(0)\" "
|
||||
|
@ -152,9 +152,9 @@ ajax_transport_build_list(http_connection_t *hc, tcp_queue_t *tq,
|
|||
"'/ajax/transport_rename_channel/%s', '%s')\">"
|
||||
"%s</a>",
|
||||
t->tht_identifier, t->tht_identifier,
|
||||
t->tht_channelname, t->tht_channelname);
|
||||
t->tht_chname, t->tht_chname);
|
||||
} else {
|
||||
ajax_table_cell(&ta, "chname", "%s", t->tht_channel->ch_name);
|
||||
ajax_table_cell(&ta, "chname", "%s", t->tht_ch->ch_name);
|
||||
}
|
||||
|
||||
ajax_table_cell_checkbox(&ta);
|
||||
|
@ -254,8 +254,8 @@ ajax_transport_rename_channel(http_connection_t *hc, http_reply_t *hr,
|
|||
if((newname = http_arg_get(&hc->hc_req_args, "newname")) == NULL)
|
||||
return HTTP_STATUS_BAD_REQUEST;
|
||||
|
||||
free((void *)t->tht_channelname);
|
||||
t->tht_channelname = strdup(newname);
|
||||
free((void *)t->tht_chname);
|
||||
t->tht_chname = strdup(newname);
|
||||
|
||||
ajax_a_jsfuncf(tq, newname,
|
||||
"tentative_chname('chname_%s', "
|
||||
|
@ -273,12 +273,12 @@ ajax_transport_rename_channel(http_connection_t *hc, http_reply_t *hr,
|
|||
static void
|
||||
dvb_map_channel(th_transport_t *t, tcp_queue_t *tq)
|
||||
{
|
||||
transport_map_channel(t);
|
||||
transport_map_channel(t, NULL);
|
||||
|
||||
tcp_qprintf(tq,
|
||||
"$('chname_%s').innerHTML='%s';\n\r"
|
||||
"$('map_%s').src='/gfx/mapped.png';\n\r",
|
||||
t->tht_identifier, t->tht_channel->ch_name,
|
||||
t->tht_identifier, t->tht_ch->ch_name,
|
||||
t->tht_identifier);
|
||||
}
|
||||
|
||||
|
@ -299,7 +299,7 @@ dvb_unmap_channel(th_transport_t *t, tcp_queue_t *tq)
|
|||
"';\n\r"
|
||||
"$('map_%s').src='/gfx/unmapped.png';\n\r",
|
||||
t->tht_identifier, t->tht_identifier, t->tht_identifier,
|
||||
t->tht_channelname, t->tht_channelname, t->tht_identifier);
|
||||
t->tht_chname, t->tht_chname, t->tht_identifier);
|
||||
}
|
||||
|
||||
|
||||
|
@ -327,13 +327,13 @@ ajax_transport_op(http_connection_t *hc, http_reply_t *hr,
|
|||
continue;
|
||||
|
||||
if(!strcmp(op, "toggle")) {
|
||||
if(t->tht_channel)
|
||||
if(t->tht_ch)
|
||||
dvb_unmap_channel(t, tq);
|
||||
else
|
||||
dvb_map_channel(t, tq);
|
||||
} else if(!strcmp(op, "map") && t->tht_channel == NULL) {
|
||||
} else if(!strcmp(op, "map") && t->tht_ch == NULL) {
|
||||
dvb_map_channel(t, tq);
|
||||
} else if(!strcmp(op, "unmap") && t->tht_channel != NULL) {
|
||||
} else if(!strcmp(op, "unmap") && t->tht_ch != NULL) {
|
||||
dvb_unmap_channel(t, tq);
|
||||
} else if(!strcmp(op, "probe")) {
|
||||
serviceprobe_add(t);
|
||||
|
|
3
avgen.c
3
avgen.c
|
@ -116,9 +116,8 @@ avgen_init(void)
|
|||
t->tht_provider = strdup("HTS Tvheadend");
|
||||
|
||||
t->tht_identifier = strdup("test1");
|
||||
t->tht_servicename = strdup("test1");
|
||||
|
||||
transport_map_channel(t);
|
||||
transport_map_channel(t, ch);
|
||||
}
|
||||
|
||||
|
||||
|
|
16
channels.c
16
channels.c
|
@ -461,9 +461,9 @@ channel_rename(channel_t *ch, const char *newname)
|
|||
LIST_REMOVE(ch, ch_global_link);
|
||||
channel_set_name(ch, newname);
|
||||
|
||||
LIST_FOREACH(t, &ch->ch_transports, tht_channel_link) {
|
||||
free(t->tht_servicename);
|
||||
t->tht_servicename = strdup(newname);
|
||||
LIST_FOREACH(t, &ch->ch_transports, tht_ch_link) {
|
||||
free(t->tht_chname);
|
||||
t->tht_chname = strdup(newname);
|
||||
t->tht_config_change(t);
|
||||
}
|
||||
|
||||
|
@ -497,6 +497,9 @@ channel_delete(channel_t *ch)
|
|||
|
||||
autorec_destroy_by_channel(ch);
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s/channels/%s", settings_dir, ch->ch_sname);
|
||||
unlink(buf);
|
||||
|
||||
free((void *)ch->ch_name);
|
||||
free((void *)ch->ch_sname);
|
||||
free(ch->ch_icon);
|
||||
|
@ -505,8 +508,6 @@ channel_delete(channel_t *ch)
|
|||
LIST_REMOVE(ch, ch_global_link);
|
||||
free(ch);
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s/channels/%s", settings_dir, ch->ch_sname);
|
||||
unlink(buf);
|
||||
}
|
||||
|
||||
|
||||
|
@ -524,10 +525,7 @@ channel_merge(channel_t *dst, channel_t *src)
|
|||
while((t = LIST_FIRST(&src->ch_transports)) != NULL) {
|
||||
transport_unmap_channel(t);
|
||||
|
||||
free(t->tht_servicename);
|
||||
t->tht_servicename = strdup(dst->ch_name);
|
||||
|
||||
transport_map_channel(t);
|
||||
transport_map_channel(t, dst);
|
||||
t->tht_config_change(t);
|
||||
}
|
||||
|
||||
|
|
4
cwc.c
4
cwc.c
|
@ -503,7 +503,7 @@ cwc_dispatch_running_reply(cwc_t *cwc, uint8_t msgtype, uint8_t *msg, int len)
|
|||
if(ct->ct_keystate != CT_FORBIDDEN) {
|
||||
syslog(LOG_ERR,
|
||||
"Can not descramble \"%s\" for service \"%s\", access denied",
|
||||
t->tht_identifier, t->tht_servicename);
|
||||
t->tht_identifier, t->tht_svcname);
|
||||
ct->ct_keystate = CT_FORBIDDEN;
|
||||
}
|
||||
|
||||
|
@ -513,7 +513,7 @@ cwc_dispatch_running_reply(cwc_t *cwc, uint8_t msgtype, uint8_t *msg, int len)
|
|||
if(ct->ct_keystate != CT_RESOLVED)
|
||||
syslog(LOG_INFO,
|
||||
"Obtained key for \"%s\" for service \"%s\"",
|
||||
t->tht_identifier, t->tht_servicename);
|
||||
t->tht_identifier, t->tht_svcname);
|
||||
|
||||
ct->ct_keystate = CT_RESOLVED;
|
||||
set_control_words(ct->ct_keys, msg + 3, msg + 3 + 8);
|
||||
|
|
34
dvb.c
34
dvb.c
|
@ -581,13 +581,13 @@ dvb_tdmi_save(th_dvb_mux_instance_t *tdmi)
|
|||
if(t->tht_provider != NULL)
|
||||
fprintf(fp, "\tprovider = %s\n", t->tht_provider);
|
||||
|
||||
if(t->tht_servicename)
|
||||
fprintf(fp, "\tservicename = %s\n", t->tht_servicename);
|
||||
if(t->tht_svcname)
|
||||
fprintf(fp, "\tservicename = %s\n", t->tht_svcname);
|
||||
|
||||
if(t->tht_channelname)
|
||||
fprintf(fp, "\tchannelname = %s\n", t->tht_channelname);
|
||||
if(t->tht_chname)
|
||||
fprintf(fp, "\tchannelname = %s\n", t->tht_chname);
|
||||
|
||||
fprintf(fp, "\tmapped = %d\n", t->tht_channel ? 1 : 0);
|
||||
fprintf(fp, "\tmapped = %d\n", t->tht_ch ? 1 : 0);
|
||||
|
||||
psi_save_transport(fp, t);
|
||||
|
||||
|
@ -634,21 +634,21 @@ dvb_tdmi_load(th_dvb_mux_instance_t *tdmi)
|
|||
t->tht_provider = strdup(v);
|
||||
|
||||
v = config_get_str_sub(&ce->ce_sub, "servicename", "unknown");
|
||||
free((void *)t->tht_servicename);
|
||||
t->tht_servicename = strdup(v);
|
||||
free((void *)t->tht_svcname);
|
||||
t->tht_svcname = strdup(v);
|
||||
|
||||
v = config_get_str_sub(&ce->ce_sub, "channelname", NULL);
|
||||
if(v != NULL) {
|
||||
free((void *)t->tht_channelname);
|
||||
t->tht_channelname = strdup(v);
|
||||
free((void *)t->tht_chname);
|
||||
t->tht_chname = strdup(v);
|
||||
} else {
|
||||
t->tht_channelname = strdup(t->tht_servicename);
|
||||
t->tht_chname = strdup(t->tht_svcname);
|
||||
}
|
||||
|
||||
psi_load_transport(&ce->ce_sub, t);
|
||||
|
||||
if(atoi(config_get_str_sub(&ce->ce_sub, "mapped", "0"))) {
|
||||
transport_map_channel(t);
|
||||
transport_map_channel(t, NULL);
|
||||
}
|
||||
}
|
||||
config_free0(&cl);
|
||||
|
@ -738,14 +738,14 @@ dvb_tda_clone(th_dvb_adapter_t *dst, th_dvb_adapter_t *src)
|
|||
if(t_src->tht_provider != NULL)
|
||||
t_dst->tht_provider = strdup(t_src->tht_provider);
|
||||
|
||||
if(t_src->tht_servicename != NULL)
|
||||
t_dst->tht_servicename = strdup(t_src->tht_servicename);
|
||||
if(t_src->tht_svcname != NULL)
|
||||
t_dst->tht_svcname = strdup(t_src->tht_svcname);
|
||||
|
||||
if(t_src->tht_channelname != NULL)
|
||||
t_dst->tht_channelname = strdup(t_src->tht_channelname);
|
||||
if(t_src->tht_chname != NULL)
|
||||
t_dst->tht_chname = strdup(t_src->tht_chname);
|
||||
|
||||
if(t_src->tht_channel != NULL)
|
||||
transport_map_channel(t_dst);
|
||||
if(t_src->tht_ch != NULL)
|
||||
transport_map_channel(t_dst, t_src->tht_ch);
|
||||
|
||||
|
||||
|
||||
|
|
12
dvb_tables.c
12
dvb_tables.c
|
@ -255,7 +255,7 @@ dvb_eit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
|
|||
if(t == NULL)
|
||||
return;
|
||||
|
||||
ch = t->tht_channel;
|
||||
ch = t->tht_ch;
|
||||
if(ch == NULL)
|
||||
return;
|
||||
|
||||
|
@ -410,7 +410,7 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
|
|||
t->tht_servicetype != stype ||
|
||||
t->tht_scrambled != free_ca_mode ||
|
||||
strcmp(t->tht_provider ?: "", provider) ||
|
||||
strcmp(t->tht_servicename ?: "", chname );
|
||||
strcmp(t->tht_svcname ?: "", chname );
|
||||
|
||||
t->tht_servicetype = stype;
|
||||
t->tht_scrambled = free_ca_mode;
|
||||
|
@ -418,11 +418,11 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
|
|||
free((void *)t->tht_provider);
|
||||
t->tht_provider = strdup(provider);
|
||||
|
||||
free((void *)t->tht_servicename);
|
||||
t->tht_servicename = strdup(chname);
|
||||
free((void *)t->tht_svcname);
|
||||
t->tht_svcname = strdup(chname);
|
||||
|
||||
if(t->tht_channelname == NULL)
|
||||
t->tht_channelname = strdup(chname);
|
||||
if(t->tht_chname == NULL)
|
||||
t->tht_chname = strdup(chname);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -156,8 +156,7 @@ file_input_init(void)
|
|||
t->tht_provider = strdup("HTS Tvheadend");
|
||||
t->tht_identifier = strdup(ch->ch_name);
|
||||
t->tht_file_input = fi;
|
||||
t->tht_servicename = strdup(ch->ch_name);
|
||||
transport_map_channel(t);
|
||||
transport_map_channel(t, ch);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -236,7 +236,7 @@ iptv_configure_transport(th_transport_t *t, const char *iptv_type,
|
|||
inet_ntoa(t->tht_iptv_group_addr), t->tht_iptv_port);
|
||||
t->tht_identifier = strdup(buf);
|
||||
|
||||
t->tht_channelname = strdup(channel_name);
|
||||
t->tht_chname = strdup(channel_name);
|
||||
|
||||
LIST_INSERT_HEAD(&iptv_probing_transports, t, tht_active_link);
|
||||
startupcounter++;
|
||||
|
@ -278,7 +278,7 @@ iptv_probe_done(th_transport_t *t, int timeout)
|
|||
iptv_stop_feed(t);
|
||||
|
||||
if(!timeout)
|
||||
transport_map_channel(t);
|
||||
transport_map_channel(t, NULL);
|
||||
else
|
||||
LIST_INSERT_HEAD(&iptv_stale_transports, t, tht_active_link);
|
||||
|
||||
|
|
|
@ -84,6 +84,7 @@ sp_timeout(void *aux, int64_t now)
|
|||
sp_t *sp = aux;
|
||||
th_transport_t *t = sp->sp_s->ths_transport;
|
||||
const char *errtxt;
|
||||
channel_t *ch;
|
||||
|
||||
switch(sp->sp_error) {
|
||||
case 0:
|
||||
|
@ -103,11 +104,13 @@ sp_timeout(void *aux, int64_t now)
|
|||
break;
|
||||
}
|
||||
|
||||
syslog(LOG_INFO, "Probed \"%s\" -- %s\n", t->tht_servicename, errtxt);
|
||||
syslog(LOG_INFO, "Probed \"%s\" -- %s\n", t->tht_svcname, errtxt);
|
||||
|
||||
if(sp->sp_error == 0) {
|
||||
if(t->tht_channel == NULL && t->tht_servicename != NULL) {
|
||||
transport_map_channel(t);
|
||||
if(t->tht_ch == NULL && t->tht_svcname != NULL) {
|
||||
ch = channel_find(t->tht_svcname, 1, NULL);
|
||||
transport_map_channel(t, ch);
|
||||
|
||||
t->tht_config_change(t);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -180,7 +180,7 @@ tt_decode_line(th_transport_t *t, uint8_t *buf)
|
|||
uint8_t mpag, line, s12, s34, c;
|
||||
int page, magidx, i;
|
||||
tt_mag_t *mag;
|
||||
channel_t *ch = t->tht_channel;
|
||||
channel_t *ch = t->tht_ch;
|
||||
tt_decoder_t *ttd = &ch->ch_tt;
|
||||
tt_page_t *ttp;
|
||||
|
||||
|
|
35
transports.c
35
transports.c
|
@ -281,13 +281,13 @@ transport_find(channel_t *ch, unsigned int weight)
|
|||
|
||||
/* First, sort all transports in order */
|
||||
|
||||
LIST_FOREACH(t, &ch->ch_transports, tht_channel_link)
|
||||
LIST_FOREACH(t, &ch->ch_transports, tht_ch_link)
|
||||
if(!t->tht_disabled)
|
||||
cnt++;
|
||||
|
||||
vec = alloca(cnt * sizeof(th_transport_t *));
|
||||
i = 0;
|
||||
LIST_FOREACH(t, &ch->ch_transports, tht_channel_link)
|
||||
LIST_FOREACH(t, &ch->ch_transports, tht_ch_link)
|
||||
if(!t->tht_disabled)
|
||||
vec[i++] = t;
|
||||
|
||||
|
@ -433,9 +433,9 @@ transport_destroy(th_transport_t *t)
|
|||
|
||||
free((void *)t->tht_name);
|
||||
|
||||
if(t->tht_channel != NULL) {
|
||||
t->tht_channel = NULL;
|
||||
LIST_REMOVE(t, tht_channel_link);
|
||||
if(t->tht_ch != NULL) {
|
||||
t->tht_ch = NULL;
|
||||
LIST_REMOVE(t, tht_ch_link);
|
||||
}
|
||||
|
||||
LIST_REMOVE(t, tht_mux_link);
|
||||
|
@ -444,8 +444,8 @@ transport_destroy(th_transport_t *t)
|
|||
transport_flush_subscribers(t);
|
||||
|
||||
free(t->tht_identifier);
|
||||
free(t->tht_servicename);
|
||||
free(t->tht_channelname);
|
||||
free(t->tht_svcname);
|
||||
free(t->tht_chname);
|
||||
free(t->tht_provider);
|
||||
|
||||
while((st = LIST_FIRST(&t->tht_streams)) != NULL) {
|
||||
|
@ -524,19 +524,26 @@ transport_add_stream(th_transport_t *t, int pid, tv_streamtype_t type)
|
|||
*
|
||||
*/
|
||||
void
|
||||
transport_map_channel(th_transport_t *t)
|
||||
transport_map_channel(th_transport_t *t, channel_t *ch)
|
||||
{
|
||||
channel_t *ch = channel_find(t->tht_servicename, 1, NULL);
|
||||
assert(t->tht_ch == NULL);
|
||||
|
||||
assert(t->tht_channel == NULL);
|
||||
if(ch == NULL) {
|
||||
if(t->tht_chname == NULL)
|
||||
return;
|
||||
ch = channel_find(t->tht_chname, 1, NULL);
|
||||
} else {
|
||||
free(t->tht_chname);
|
||||
t->tht_chname = strdup(ch->ch_name);
|
||||
}
|
||||
|
||||
avgstat_init(&t->tht_cc_errors, 3600);
|
||||
avgstat_init(&t->tht_rate, 10);
|
||||
|
||||
assert(t->tht_identifier != NULL);
|
||||
t->tht_channel = ch;
|
||||
t->tht_ch = ch;
|
||||
|
||||
LIST_INSERT_HEAD(&ch->ch_transports, t, tht_channel_link);
|
||||
LIST_INSERT_HEAD(&ch->ch_transports, t, tht_ch_link);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -545,8 +552,8 @@ transport_map_channel(th_transport_t *t)
|
|||
void
|
||||
transport_unmap_channel(th_transport_t *t)
|
||||
{
|
||||
t->tht_channel = NULL;
|
||||
LIST_REMOVE(t, tht_channel_link);
|
||||
t->tht_ch = NULL;
|
||||
LIST_REMOVE(t, tht_ch_link);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ th_transport_t *transport_create(const char *identifier, int type,
|
|||
|
||||
th_transport_t *transport_find_by_identifier(const char *identifier);
|
||||
|
||||
void transport_map_channel(th_transport_t *t);
|
||||
void transport_map_channel(th_transport_t *t, channel_t *ch);
|
||||
|
||||
void transport_unmap_channel(th_transport_t *t);
|
||||
|
||||
|
|
14
tvhead.h
14
tvhead.h
|
@ -418,9 +418,6 @@ typedef struct th_transport {
|
|||
|
||||
LIST_ENTRY(th_transport) tht_active_link;
|
||||
|
||||
LIST_ENTRY(th_transport) tht_channel_link;
|
||||
struct channel *tht_channel;
|
||||
|
||||
LIST_HEAD(, th_subscription) tht_subscriptions;
|
||||
|
||||
int (*tht_start_feed)(struct th_transport *t, unsigned int weight,
|
||||
|
@ -474,8 +471,7 @@ typedef struct th_transport {
|
|||
} u;
|
||||
|
||||
char *tht_identifier;
|
||||
char *tht_servicename;
|
||||
char *tht_channelname;
|
||||
char *tht_svcname;
|
||||
char *tht_provider;
|
||||
|
||||
enum {
|
||||
|
@ -508,6 +504,14 @@ typedef struct th_transport {
|
|||
TAILQ_ENTRY(th_transport) tht_probe_link;
|
||||
int tht_on_probe_queue;
|
||||
|
||||
/**
|
||||
* Channel mapping
|
||||
*/
|
||||
|
||||
LIST_ENTRY(th_transport) tht_ch_link;
|
||||
struct channel *tht_ch;
|
||||
char *tht_chname;
|
||||
|
||||
} th_transport_t;
|
||||
|
||||
|
||||
|
|
4
v4l.c
4
v4l.c
|
@ -97,9 +97,9 @@ v4l_configure_transport(th_transport_t *t, const char *muxname,
|
|||
snprintf(buf, sizeof(buf), "analog_%u", t->tht_v4l_frequency);
|
||||
t->tht_identifier = strdup(buf);
|
||||
|
||||
t->tht_servicename = strdup(channel_name);
|
||||
t->tht_chname = strdup(channel_name);
|
||||
|
||||
transport_map_channel(t);
|
||||
transport_map_channel(t, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue