vdr  1.7.31
channels.c
Go to the documentation of this file.
1 /*
2  * channels.c: Channel handling
3  *
4  * See the main source file 'vdr.c' for copyright information and
5  * how to reach the author.
6  *
7  * $Id: channels.c 2.24 2012/07/14 12:34:47 kls Exp $
8  */
9 
10 #include "channels.h"
11 #include <ctype.h>
12 #include "device.h"
13 #include "epg.h"
14 #include "libsi/si.h"
15 #include "timers.h"
16 
17 // IMPORTANT NOTE: in the 'sscanf()' calls there is a blank after the '%d'
18 // format characters in order to allow any number of blanks after a numeric
19 // value!
20 
21 // --- tChannelID ------------------------------------------------------------
22 
24 
26 {
27  char *sourcebuf = NULL;
28  int nid;
29  int tid;
30  int sid;
31  int rid = 0;
32  int fields = sscanf(s, "%a[^-]-%d-%d-%d-%d", &sourcebuf, &nid, &tid, &sid, &rid);
33  if (fields == 4 || fields == 5) {
34  int source = cSource::FromString(sourcebuf);
35  free(sourcebuf);
36  if (source >= 0)
37  return tChannelID(source, nid, tid, sid, rid);
38  }
39  return tChannelID::InvalidID;
40 }
41 
43 {
44  char buffer[256];
45  snprintf(buffer, sizeof(buffer), rid ? "%s-%d-%d-%d-%d" : "%s-%d-%d-%d", *cSource::ToString(source), nid, tid, sid, rid);
46  return buffer;
47 }
48 
50 {
51  while (tid > 100000)
52  tid -= 100000;
53  return *this;
54 }
55 
56 // --- cChannel --------------------------------------------------------------
57 
59 {
60  name = strdup("");
61  shortName = strdup("");
62  provider = strdup("");
63  portalName = strdup("");
64  memset(&__BeginData__, 0, (char *)&__EndData__ - (char *)&__BeginData__);
65  parameters = "";
67  schedule = NULL;
68  linkChannels = NULL;
69  refChannel = NULL;
70 }
71 
73 {
74  name = NULL;
75  shortName = NULL;
76  provider = NULL;
77  portalName = NULL;
78  schedule = NULL;
79  linkChannels = NULL;
80  refChannel = NULL;
81  *this = Channel;
82 }
83 
85 {
86  delete linkChannels;
87  linkChannels = NULL; // more than one channel can link to this one, so we need the following loop
88  for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)) {
89  if (Channel->linkChannels) {
90  for (cLinkChannel *lc = Channel->linkChannels->First(); lc; lc = Channel->linkChannels->Next(lc)) {
91  if (lc->Channel() == this) {
92  Channel->linkChannels->Del(lc);
93  break;
94  }
95  }
96  if (Channel->linkChannels->Count() == 0) {
97  delete Channel->linkChannels;
98  Channel->linkChannels = NULL;
99  }
100  }
101  }
102  free(name);
103  free(shortName);
104  free(provider);
105  free(portalName);
106 }
107 
109 {
110  name = strcpyrealloc(name, Channel.name);
114  memcpy(&__BeginData__, &Channel.__BeginData__, (char *)&Channel.__EndData__ - (char *)&Channel.__BeginData__);
115  nameSource = NULL; // these will be recalculated automatically
116  shortNameSource = NULL;
117  parameters = Channel.parameters;
118  return *this;
119 }
120 
121 const char *cChannel::Name(void) const
122 {
124  if (isempty(nameSource))
126  return nameSource;
127  }
128  return name;
129 }
130 
131 const char *cChannel::ShortName(bool OrName) const
132 {
133  if (OrName && isempty(shortName))
134  return Name();
138  return shortNameSource;
139  }
140  return shortName;
141 }
142 
143 int cChannel::Transponder(int Frequency, char Polarization)
144 {
145  // some satellites have transponders at the same frequency, just with different polarization:
146  switch (toupper(Polarization)) {
147  case 'H': Frequency += 100000; break;
148  case 'V': Frequency += 200000; break;
149  case 'L': Frequency += 300000; break;
150  case 'R': Frequency += 400000; break;
151  default: esyslog("ERROR: invalid value for Polarization '%c'", Polarization);
152  }
153  return Frequency;
154 }
155 
156 int cChannel::Transponder(void) const
157 {
158  int tf = frequency;
159  while (tf > 20000)
160  tf /= 1000;
161  if (IsSat()) {
162  const char *p = strpbrk(parameters, "HVLRhvlr"); // lowercase for backwards compatibility
163  if (p)
164  tf = Transponder(tf, *p);
165  }
166  return tf;
167 }
168 
169 bool cChannel::HasTimer(void) const
170 {
171  for (cTimer *Timer = Timers.First(); Timer; Timer = Timers.Next(Timer)) {
172  if (Timer->Channel() == this)
173  return true;
174  }
175  return false;
176 }
177 
179 {
180  int Result = modification & Mask;
182  return Result;
183 }
184 
186 {
187  if (Channel) {
188  frequency = Channel->frequency;
189  source = Channel->source;
190  srate = Channel->srate;
191  parameters = Channel->parameters;
192  }
193 }
194 
195 bool cChannel::SetTransponderData(int Source, int Frequency, int Srate, const char *Parameters, bool Quiet)
196 {
197  if (strchr(Parameters, ':')) {
198  esyslog("ERROR: parameter string '%s' contains ':'", Parameters);
199  return false;
200  }
201  // Workarounds for broadcaster stupidity:
202  // Some providers broadcast the transponder frequency of their channels with two different
203  // values (like 12551 and 12552), so we need to allow for a little tolerance here
204  if (abs(frequency - Frequency) <= 1)
205  Frequency = frequency;
206  // Sometimes the transponder frequency is set to 0, which is just wrong
207  if (Frequency == 0)
208  return false;
209  // Sometimes the symbol rate is off by one
210  if (abs(srate - Srate) <= 1)
211  Srate = srate;
212 
213  if (source != Source || frequency != Frequency || srate != Srate || strcmp(parameters, Parameters)) {
214  cString OldTransponderData = TransponderDataToString();
215  source = Source;
217  srate = Srate;
219  schedule = NULL;
220  nameSource = NULL;
221  shortNameSource = NULL;
222  if (Number() && !Quiet) {
223  dsyslog("changing transponder data of channel %d from %s to %s", Number(), *OldTransponderData, *TransponderDataToString());
226  }
227  }
228  return true;
229 }
230 
231 void cChannel::SetId(int Nid, int Tid, int Sid, int Rid)
232 {
233  if (nid != Nid || tid != Tid || sid != Sid || rid != Rid) {
234  if (Number()) {
235  dsyslog("changing id of channel %d from %d-%d-%d-%d to %d-%d-%d-%d", Number(), nid, tid, sid, rid, Nid, Tid, Sid, Rid);
238  Channels.UnhashChannel(this);
239  }
240  nid = Nid;
241  tid = Tid;
242  sid = Sid;
243  rid = Rid;
244  if (Number())
245  Channels.HashChannel(this);
246  schedule = NULL;
247  }
248 }
249 
250 void cChannel::SetName(const char *Name, const char *ShortName, const char *Provider)
251 {
252  if (!isempty(Name)) {
253  bool nn = strcmp(name, Name) != 0;
254  bool ns = strcmp(shortName, ShortName) != 0;
255  bool np = strcmp(provider, Provider) != 0;
256  if (nn || ns || np) {
257  if (Number()) {
258  dsyslog("changing name of channel %d from '%s,%s;%s' to '%s,%s;%s'", Number(), name, shortName, provider, Name, ShortName, Provider);
261  }
262  if (nn) {
263  name = strcpyrealloc(name, Name);
264  nameSource = NULL;
265  }
266  if (ns) {
267  shortName = strcpyrealloc(shortName, ShortName);
268  shortNameSource = NULL;
269  }
270  if (np)
271  provider = strcpyrealloc(provider, Provider);
272  }
273  }
274 }
275 
276 void cChannel::SetPortalName(const char *PortalName)
277 {
278  if (!isempty(PortalName) && strcmp(portalName, PortalName) != 0) {
279  if (Number()) {
280  dsyslog("changing portal name of channel %d from '%s' to '%s'", Number(), portalName, PortalName);
283  }
284  portalName = strcpyrealloc(portalName, PortalName);
285  }
286 }
287 
288 #define STRDIFF 0x01
289 #define VALDIFF 0x02
290 
291 static int IntArraysDiffer(const int *a, const int *b, const char na[][MAXLANGCODE2] = NULL, const char nb[][MAXLANGCODE2] = NULL)
292 {
293  int result = 0;
294  for (int i = 0; a[i] || b[i]; i++) {
295  if (!a[i] || !b[i]) {
296  result |= VALDIFF;
297  break;
298  }
299  if (na && nb && strcmp(na[i], nb[i]) != 0)
300  result |= STRDIFF;
301  if (a[i] != b[i])
302  result |= VALDIFF;
303  }
304  return result;
305 }
306 
307 static int IntArrayToString(char *s, const int *a, int Base = 10, const char n[][MAXLANGCODE2] = NULL, const int *t = NULL)
308 {
309  char *q = s;
310  int i = 0;
311  while (a[i] || i == 0) {
312  q += sprintf(q, Base == 16 ? "%s%X" : "%s%d", i ? "," : "", a[i]);
313  const char *Delim = "=";
314  if (a[i]) {
315  if (n && *n[i]) {
316  q += sprintf(q, "%s%s", Delim, n[i]);
317  Delim = "";
318  }
319  if (t && t[i])
320  q += sprintf(q, "%s@%d", Delim, t[i]);
321  }
322  if (!a[i])
323  break;
324  i++;
325  }
326  *q = 0;
327  return q - s;
328 }
329 
330 void cChannel::SetPids(int Vpid, int Ppid, int Vtype, int *Apids, int *Atypes, char ALangs[][MAXLANGCODE2], int *Dpids, int *Dtypes, char DLangs[][MAXLANGCODE2], int *Spids, char SLangs[][MAXLANGCODE2], int Tpid)
331 {
332  int mod = CHANNELMOD_NONE;
333  if (vpid != Vpid || ppid != Ppid || vtype != Vtype || tpid != Tpid)
334  mod |= CHANNELMOD_PIDS;
335  int m = IntArraysDiffer(apids, Apids, alangs, ALangs) | IntArraysDiffer(atypes, Atypes) | IntArraysDiffer(dpids, Dpids, dlangs, DLangs) | IntArraysDiffer(dtypes, Dtypes) | IntArraysDiffer(spids, Spids, slangs, SLangs);
336  if (m & STRDIFF)
337  mod |= CHANNELMOD_LANGS;
338  if (m & VALDIFF)
339  mod |= CHANNELMOD_PIDS;
340  if (mod) {
341  const int BufferSize = (MAXAPIDS + MAXDPIDS) * (5 + 1 + MAXLANGCODE2 + 5) + 10; // 5 digits plus delimiting ',' or ';' plus optional '=cod+cod@type', +10: paranoia
342  char OldApidsBuf[BufferSize];
343  char NewApidsBuf[BufferSize];
344  char *q = OldApidsBuf;
345  q += IntArrayToString(q, apids, 10, alangs, atypes);
346  if (dpids[0]) {
347  *q++ = ';';
348  q += IntArrayToString(q, dpids, 10, dlangs, dtypes);
349  }
350  *q = 0;
351  q = NewApidsBuf;
352  q += IntArrayToString(q, Apids, 10, ALangs, Atypes);
353  if (Dpids[0]) {
354  *q++ = ';';
355  q += IntArrayToString(q, Dpids, 10, DLangs, Dtypes);
356  }
357  *q = 0;
358  const int SBufferSize = MAXSPIDS * (5 + 1 + MAXLANGCODE2) + 10; // 5 digits plus delimiting ',' or ';' plus optional '=cod', +10: paranoia
359  char OldSpidsBuf[SBufferSize];
360  char NewSpidsBuf[SBufferSize];
361  q = OldSpidsBuf;
362  q += IntArrayToString(q, spids, 10, slangs);
363  *q = 0;
364  q = NewSpidsBuf;
365  q += IntArrayToString(q, Spids, 10, SLangs);
366  *q = 0;
367  if (Number())
368  dsyslog("changing pids of channel %d from %d+%d=%d:%s:%s:%d to %d+%d=%d:%s:%s:%d", Number(), vpid, ppid, vtype, OldApidsBuf, OldSpidsBuf, tpid, Vpid, Ppid, Vtype, NewApidsBuf, NewSpidsBuf, Tpid);
369  vpid = Vpid;
370  ppid = Ppid;
371  vtype = Vtype;
372  for (int i = 0; i < MAXAPIDS; i++) {
373  apids[i] = Apids[i];
374  atypes[i] = Atypes[i];
375  strn0cpy(alangs[i], ALangs[i], MAXLANGCODE2);
376  }
377  apids[MAXAPIDS] = 0;
378  for (int i = 0; i < MAXDPIDS; i++) {
379  dpids[i] = Dpids[i];
380  dtypes[i] = Dtypes[i];
381  strn0cpy(dlangs[i], DLangs[i], MAXLANGCODE2);
382  }
383  dpids[MAXDPIDS] = 0;
384  for (int i = 0; i < MAXSPIDS; i++) {
385  spids[i] = Spids[i];
386  strn0cpy(slangs[i], SLangs[i], MAXLANGCODE2);
387  }
388  spids[MAXSPIDS] = 0;
389  tpid = Tpid;
390  modification |= mod;
392  }
393 }
394 
395 void cChannel::SetSubtitlingDescriptors(uchar *SubtitlingTypes, uint16_t *CompositionPageIds, uint16_t *AncillaryPageIds)
396 {
397  if (SubtitlingTypes) {
398  for (int i = 0; i < MAXSPIDS; i++)
399  subtitlingTypes[i] = SubtitlingTypes[i];
400  }
401  if (CompositionPageIds) {
402  for (int i = 0; i < MAXSPIDS; i++)
403  compositionPageIds[i] = CompositionPageIds[i];
404  }
405  if (AncillaryPageIds) {
406  for (int i = 0; i < MAXSPIDS; i++)
407  ancillaryPageIds[i] = AncillaryPageIds[i];
408  }
409 }
410 
412 {
413  int mod = CHANNELMOD_NONE;
414  if (totalTtxtSubtitlePages != (fixedTtxtSubtitlePages + numberOfPages))
415  mod |= CHANNELMOD_PIDS;
417  for (int i = 0; (i < numberOfPages) && (totalTtxtSubtitlePages < MAXTXTPAGES); i++) {
418  if (teletextSubtitlePages[totalTtxtSubtitlePages].ttxtMagazine != pages[i].ttxtMagazine ||
419  teletextSubtitlePages[totalTtxtSubtitlePages].ttxtPage != pages[i].ttxtPage ||
420  teletextSubtitlePages[totalTtxtSubtitlePages].ttxtType != pages[i].ttxtType ||
421  strcmp(teletextSubtitlePages[totalTtxtSubtitlePages].ttxtLanguage, pages[i].ttxtLanguage)) {
422  mod |= CHANNELMOD_PIDS;
424  }
426  }
427  modification |= mod;
429 }
430 
431 void cChannel::SetCaIds(const int *CaIds)
432 {
433  if (caids[0] && caids[0] <= CA_USER_MAX)
434  return; // special values will not be overwritten
435  if (IntArraysDiffer(caids, CaIds)) {
436  char OldCaIdsBuf[MAXCAIDS * 5 + 10]; // 5: 4 digits plus delimiting ',', 10: paranoia
437  char NewCaIdsBuf[MAXCAIDS * 5 + 10];
438  IntArrayToString(OldCaIdsBuf, caids, 16);
439  IntArrayToString(NewCaIdsBuf, CaIds, 16);
440  if (Number())
441  dsyslog("changing caids of channel %d from %s to %s", Number(), OldCaIdsBuf, NewCaIdsBuf);
442  for (int i = 0; i <= MAXCAIDS; i++) { // <= to copy the terminating 0
443  caids[i] = CaIds[i];
444  if (!CaIds[i])
445  break;
446  }
449  }
450 }
451 
453 {
454  if (Level > 0) {
457  if (Number() && Level > 1)
458  dsyslog("changing ca descriptors of channel %d", Number());
459  }
460 }
461 
463 {
464  if (!linkChannels && !LinkChannels)
465  return;
466  if (linkChannels && LinkChannels) {
467  cLinkChannel *lca = linkChannels->First();
468  cLinkChannel *lcb = LinkChannels->First();
469  while (lca && lcb) {
470  if (lca->Channel() != lcb->Channel()) {
471  lca = NULL;
472  break;
473  }
474  lca = linkChannels->Next(lca);
475  lcb = LinkChannels->Next(lcb);
476  }
477  if (!lca && !lcb) {
478  delete LinkChannels;
479  return; // linkage has not changed
480  }
481  }
482  char buffer[((linkChannels ? linkChannels->Count() : 0) + (LinkChannels ? LinkChannels->Count() : 0)) * 6 + 256]; // 6: 5 digit channel number plus blank, 256: other texts (see below) plus reserve
483  char *q = buffer;
484  q += sprintf(q, "linking channel %d from", Number());
485  if (linkChannels) {
486  for (cLinkChannel *lc = linkChannels->First(); lc; lc = linkChannels->Next(lc)) {
487  lc->Channel()->SetRefChannel(NULL);
488  q += sprintf(q, " %d", lc->Channel()->Number());
489  }
490  delete linkChannels;
491  }
492  else
493  q += sprintf(q, " none");
494  q += sprintf(q, " to");
496  if (linkChannels) {
497  for (cLinkChannel *lc = linkChannels->First(); lc; lc = linkChannels->Next(lc)) {
498  lc->Channel()->SetRefChannel(this);
499  q += sprintf(q, " %d", lc->Channel()->Number());
500  //dsyslog("link %4d -> %4d: %s", Number(), lc->Channel()->Number(), lc->Channel()->Name());
501  }
502  }
503  else
504  q += sprintf(q, " none");
505  if (Number())
506  dsyslog("%s", buffer);
507 }
508 
510 {
512 }
513 
515 {
516  if (cSource::IsTerr(source))
518  return cString::sprintf("%d:%s:%s:%d", frequency, *parameters, *cSource::ToString(source), srate);
519 }
520 
522 {
523  char FullName[strlen(Channel->name) + 1 + strlen(Channel->shortName) + 1 + strlen(Channel->provider) + 1 + 10]; // +10: paranoia
524  char *q = FullName;
525  q += sprintf(q, "%s", Channel->name);
526  if (!isempty(Channel->shortName))
527  q += sprintf(q, ",%s", Channel->shortName);
528  else if (strchr(Channel->name, ','))
529  q += sprintf(q, ",");
530  if (!isempty(Channel->provider))
531  q += sprintf(q, ";%s", Channel->provider);
532  *q = 0;
533  strreplace(FullName, ':', '|');
534  cString buffer;
535  if (Channel->groupSep) {
536  if (Channel->number)
537  buffer = cString::sprintf(":@%d %s\n", Channel->number, FullName);
538  else
539  buffer = cString::sprintf(":%s\n", FullName);
540  }
541  else {
542  char vpidbuf[32];
543  char *q = vpidbuf;
544  q += snprintf(q, sizeof(vpidbuf), "%d", Channel->vpid);
545  if (Channel->ppid && Channel->ppid != Channel->vpid)
546  q += snprintf(q, sizeof(vpidbuf) - (q - vpidbuf), "+%d", Channel->ppid);
547  if (Channel->vpid && Channel->vtype)
548  q += snprintf(q, sizeof(vpidbuf) - (q - vpidbuf), "=%d", Channel->vtype);
549  *q = 0;
550  const int ABufferSize = (MAXAPIDS + MAXDPIDS) * (5 + 1 + MAXLANGCODE2 + 5) + 10; // 5 digits plus delimiting ',' or ';' plus optional '=cod+cod@type', +10: paranoia
551  char apidbuf[ABufferSize];
552  q = apidbuf;
553  q += IntArrayToString(q, Channel->apids, 10, Channel->alangs, Channel->atypes);
554  if (Channel->dpids[0]) {
555  *q++ = ';';
556  q += IntArrayToString(q, Channel->dpids, 10, Channel->dlangs, Channel->dtypes);
557  }
558  *q = 0;
559  const int TBufferSize = (MAXTXTPAGES * MAXSPIDS) * (5 + 1 + MAXLANGCODE2) + 10; // 5 digits plus delimiting ',' or ';' plus optional '=cod+cod', +10: paranoia and tpid
560  char tpidbuf[TBufferSize];
561  q = tpidbuf;
562  q += snprintf(q, sizeof(tpidbuf), "%d", Channel->tpid);
563  if (Channel->fixedTtxtSubtitlePages > 0) {
564  *q++ = '+';
565  for (int i = 0; i < Channel->fixedTtxtSubtitlePages; ++i) {
566  tTeletextSubtitlePage page = Channel->teletextSubtitlePages[i];
567  q += snprintf(q, sizeof(tpidbuf) - (q - tpidbuf), "%d=%s", page.PageNumber(), page.ttxtLanguage);
568  }
569  }
570  if (Channel->spids[0]) {
571  *q++ = ';';
572  q += IntArrayToString(q, Channel->spids, 10, Channel->slangs);
573  }
574  char caidbuf[MAXCAIDS * 5 + 10]; // 5: 4 digits plus delimiting ',', 10: paranoia
575  q = caidbuf;
576  q += IntArrayToString(q, Channel->caids, 16);
577  *q = 0;
578  buffer = cString::sprintf("%s:%d:%s:%s:%d:%s:%s:%s:%s:%d:%d:%d:%d\n", FullName, Channel->frequency, *Channel->parameters, *cSource::ToString(Channel->source), Channel->srate, vpidbuf, apidbuf, tpidbuf, caidbuf, Channel->sid, Channel->nid, Channel->tid, Channel->rid);
579  }
580  return buffer;
581 }
582 
584 {
585  return ToText(this);
586 }
587 
588 bool cChannel::Parse(const char *s)
589 {
590  bool ok = true;
591  if (*s == ':') {
592  groupSep = true;
593  if (*++s == '@' && *++s) {
594  char *p = NULL;
595  errno = 0;
596  int n = strtol(s, &p, 10);
597  if (!errno && p != s && n > 0) {
598  number = n;
599  s = p;
600  }
601  }
603  strreplace(name, '|', ':');
604  }
605  else {
606  groupSep = false;
607  char *namebuf = NULL;
608  char *sourcebuf = NULL;
609  char *parambuf = NULL;
610  char *vpidbuf = NULL;
611  char *apidbuf = NULL;
612  char *tpidbuf = NULL;
613  char *caidbuf = NULL;
614  int fields = sscanf(s, "%a[^:]:%d :%a[^:]:%a[^:] :%d :%a[^:]:%a[^:]:%a[^:]:%a[^:]:%d :%d :%d :%d ", &namebuf, &frequency, &parambuf, &sourcebuf, &srate, &vpidbuf, &apidbuf, &tpidbuf, &caidbuf, &sid, &nid, &tid, &rid);
615  if (fields >= 9) {
616  if (fields == 9) {
617  // allow reading of old format
618  sid = atoi(caidbuf);
619  delete caidbuf;
620  caidbuf = NULL;
621  if (sscanf(tpidbuf, "%d", &tpid) != 1)
622  return false;
623  caids[0] = tpid;
624  caids[1] = 0;
625  tpid = 0;
626  }
627  vpid = ppid = 0;
628  vtype = 0;
629  apids[0] = 0;
630  atypes[0] = 0;
631  dpids[0] = 0;
632  dtypes[0] = 0;
633  spids[0] = 0;
634  ok = false;
635  if (parambuf && sourcebuf && vpidbuf && apidbuf) {
636  parameters = parambuf;
637  ok = (source = cSource::FromString(sourcebuf)) >= 0;
638 
639  char *p;
640  if ((p = strchr(vpidbuf, '=')) != NULL) {
641  *p++ = 0;
642  if (sscanf(p, "%d", &vtype) != 1)
643  return false;
644  }
645  if ((p = strchr(vpidbuf, '+')) != NULL) {
646  *p++ = 0;
647  if (sscanf(p, "%d", &ppid) != 1)
648  return false;
649  }
650  if (sscanf(vpidbuf, "%d", &vpid) != 1)
651  return false;
652  if (!ppid)
653  ppid = vpid;
654  if (vpid && !vtype)
655  vtype = 2; // default is MPEG-2
656 
657  char *dpidbuf = strchr(apidbuf, ';');
658  if (dpidbuf)
659  *dpidbuf++ = 0;
660  p = apidbuf;
661  char *q;
662  int NumApids = 0;
663  char *strtok_next;
664  while ((q = strtok_r(p, ",", &strtok_next)) != NULL) {
665  if (NumApids < MAXAPIDS) {
666  atypes[NumApids] = 4; // backwards compatibility
667  char *l = strchr(q, '=');
668  if (l) {
669  *l++ = 0;
670  char *t = strchr(l, '@');
671  if (t) {
672  *t++ = 0;
673  atypes[NumApids] = strtol(t, NULL, 10);
674  }
675  strn0cpy(alangs[NumApids], l, MAXLANGCODE2);
676  }
677  else
678  *alangs[NumApids] = 0;
679  if ((apids[NumApids] = strtol(q, NULL, 10)) != 0)
680  NumApids++;
681  }
682  else
683  esyslog("ERROR: too many APIDs!"); // no need to set ok to 'false'
684  p = NULL;
685  }
686  apids[NumApids] = 0;
687  atypes[NumApids] = 0;
688  if (dpidbuf) {
689  char *p = dpidbuf;
690  char *q;
691  int NumDpids = 0;
692  char *strtok_next;
693  while ((q = strtok_r(p, ",", &strtok_next)) != NULL) {
694  if (NumDpids < MAXDPIDS) {
695  dtypes[NumDpids] = SI::AC3DescriptorTag; // backwards compatibility
696  char *l = strchr(q, '=');
697  if (l) {
698  *l++ = 0;
699  char *t = strchr(l, '@');
700  if (t) {
701  *t++ = 0;
702  dtypes[NumDpids] = strtol(t, NULL, 10);
703  }
704  strn0cpy(dlangs[NumDpids], l, MAXLANGCODE2);
705  }
706  else
707  *dlangs[NumDpids] = 0;
708  if ((dpids[NumDpids] = strtol(q, NULL, 10)) != 0)
709  NumDpids++;
710  }
711  else
712  esyslog("ERROR: too many DPIDs!"); // no need to set ok to 'false'
713  p = NULL;
714  }
715  dpids[NumDpids] = 0;
716  dtypes[NumDpids] = 0;
717  }
718  int NumSpids = 0;
719  if ((p = strchr(tpidbuf, ';')) != NULL) {
720  *p++ = 0;
721  char *q;
722  char *strtok_next;
723  while ((q = strtok_r(p, ",", &strtok_next)) != NULL) {
724  if (NumSpids < MAXSPIDS) {
725  char *l = strchr(q, '=');
726  if (l) {
727  *l++ = 0;
728  strn0cpy(slangs[NumSpids], l, MAXLANGCODE2);
729  }
730  else
731  *slangs[NumSpids] = 0;
732  spids[NumSpids++] = strtol(q, NULL, 10);
733  }
734  else
735  esyslog("ERROR: too many SPIDs!"); // no need to set ok to 'false'
736  p = NULL;
737  }
738  spids[NumSpids] = 0;
739  }
741  if ((p = strchr(tpidbuf, '+')) != NULL) {
742  *p++ = 0;
743  char *q;
744  char *strtok_next;
745  while ((q = strtok_r(p, ",", &strtok_next)) != NULL) {
747  int page;
748  char *l = strchr(q, '=');
749  if (l)
750  *l++ = 0;
751  if (sscanf(q, "%d", &page) == 1) {
753  if (l)
756  }
757  else
758  esyslog("ERROR: invalid Teletext page!"); // no need to set ok to 'false'
759  }
760  else
761  esyslog("ERROR: too many Teletext pages!"); // no need to set ok to 'false'
762  p = NULL;
763  }
765  }
766  if (sscanf(tpidbuf, "%d", &tpid) != 1)
767  return false;
768  if (caidbuf) {
769  char *p = caidbuf;
770  char *q;
771  int NumCaIds = 0;
772  char *strtok_next;
773  while ((q = strtok_r(p, ",", &strtok_next)) != NULL) {
774  if (NumCaIds < MAXCAIDS) {
775  caids[NumCaIds++] = strtol(q, NULL, 16) & 0xFFFF;
776  if (NumCaIds == 1 && caids[0] <= CA_USER_MAX)
777  break;
778  }
779  else
780  esyslog("ERROR: too many CA ids!"); // no need to set ok to 'false'
781  p = NULL;
782  }
783  caids[NumCaIds] = 0;
784  }
785  }
786  strreplace(namebuf, '|', ':');
787 
788  char *p = strchr(namebuf, ';');
789  if (p) {
790  *p++ = 0;
792  }
793  p = strrchr(namebuf, ','); // long name might contain a ',', so search for the rightmost one
794  if (p) {
795  *p++ = 0;
797  }
798  name = strcpyrealloc(name, namebuf);
799 
800  free(parambuf);
801  free(sourcebuf);
802  free(vpidbuf);
803  free(apidbuf);
804  free(tpidbuf);
805  free(caidbuf);
806  free(namebuf);
807  nameSource = NULL;
808  shortNameSource = NULL;
809  if (!GetChannelID().Valid()) {
810  esyslog("ERROR: channel data results in invalid ID!");
811  return false;
812  }
813  }
814  else
815  return false;
816  }
817  return ok;
818 }
819 
820 bool cChannel::Save(FILE *f)
821 {
822  return fprintf(f, "%s", *ToText()) > 0;
823 }
824 
825 // --- cChannelSorter --------------------------------------------------------
826 
827 class cChannelSorter : public cListObject {
828 public:
832  channel = Channel;
834  }
835  virtual int Compare(const cListObject &ListObject) const {
836  cChannelSorter *cs = (cChannelSorter *)&ListObject;
837  return memcmp(&channelID, &cs->channelID, sizeof(channelID));
838  }
839  };
840 
841 // --- cChannels -------------------------------------------------------------
842 
844 
846 {
847  maxNumber = 0;
851 }
852 
854 {
855  cList<cChannelSorter> ChannelSorter;
856  for (cChannel *channel = First(); channel; channel = Next(channel)) {
857  if (!channel->GroupSep())
858  ChannelSorter.Add(new cChannelSorter(channel));
859  }
860  ChannelSorter.Sort();
861  cChannelSorter *cs = ChannelSorter.First();
862  while (cs) {
863  cChannelSorter *next = ChannelSorter.Next(cs);
864  if (next && cs->channelID == next->channelID) {
865  dsyslog("deleting duplicate channel %s", *next->channel->ToText());
866  Del(next->channel);
867  }
868  cs = next;
869  }
870 }
871 
872 bool cChannels::Load(const char *FileName, bool AllowComments, bool MustExist)
873 {
874  if (cConfig<cChannel>::Load(FileName, AllowComments, MustExist)) {
876  ReNumber();
877  return true;
878  }
879  return false;
880 }
881 
883 {
884  channelsHashSid.Add(Channel, Channel->Sid());
885 }
886 
888 {
889  channelsHashSid.Del(Channel, Channel->Sid());
890 }
891 
893 {
894  cChannel *channel = Get(++Idx);
895  while (channel && !(channel->GroupSep() && *channel->Name()))
896  channel = Get(++Idx);
897  return channel ? Idx : -1;
898 }
899 
901 {
902  cChannel *channel = Get(--Idx);
903  while (channel && !(channel->GroupSep() && *channel->Name()))
904  channel = Get(--Idx);
905  return channel ? Idx : -1;
906 }
907 
909 {
910  cChannel *channel = Get(++Idx);
911  while (channel && channel->GroupSep())
912  channel = Get(++Idx);
913  return channel ? Idx : -1;
914 }
915 
917 {
918  cChannel *channel = Get(--Idx);
919  while (channel && channel->GroupSep())
920  channel = Get(--Idx);
921  return channel ? Idx : -1;
922 }
923 
925 {
927  maxNumber = 0;
928  int Number = 1;
929  for (cChannel *channel = First(); channel; channel = Next(channel)) {
930  if (channel->GroupSep()) {
931  if (channel->Number() > Number)
932  Number = channel->Number();
933  }
934  else {
935  HashChannel(channel);
936  maxNumber = Number;
937  channel->SetNumber(Number++);
938  }
939  }
940 }
941 
942 cChannel *cChannels::GetByNumber(int Number, int SkipGap)
943 {
944  cChannel *previous = NULL;
945  for (cChannel *channel = First(); channel; channel = Next(channel)) {
946  if (!channel->GroupSep()) {
947  if (channel->Number() == Number)
948  return channel;
949  else if (SkipGap && channel->Number() > Number)
950  return SkipGap > 0 ? channel : previous;
951  previous = channel;
952  }
953  }
954  return NULL;
955 }
956 
957 cChannel *cChannels::GetByServiceID(int Source, int Transponder, unsigned short ServiceID)
958 {
959  cList<cHashObject> *list = channelsHashSid.GetList(ServiceID);
960  if (list) {
961  for (cHashObject *hobj = list->First(); hobj; hobj = list->Next(hobj)) {
962  cChannel *channel = (cChannel *)hobj->Object();
963  if (channel->Sid() == ServiceID && channel->Source() == Source && ISTRANSPONDER(channel->Transponder(), Transponder))
964  return channel;
965  }
966  }
967  return NULL;
968 }
969 
970 cChannel *cChannels::GetByChannelID(tChannelID ChannelID, bool TryWithoutRid, bool TryWithoutPolarization)
971 {
972  int sid = ChannelID.Sid();
974  if (list) {
975  for (cHashObject *hobj = list->First(); hobj; hobj = list->Next(hobj)) {
976  cChannel *channel = (cChannel *)hobj->Object();
977  if (channel->Sid() == sid && channel->GetChannelID() == ChannelID)
978  return channel;
979  }
980  if (TryWithoutRid) {
981  ChannelID.ClrRid();
982  for (cHashObject *hobj = list->First(); hobj; hobj = list->Next(hobj)) {
983  cChannel *channel = (cChannel *)hobj->Object();
984  if (channel->Sid() == sid && channel->GetChannelID().ClrRid() == ChannelID)
985  return channel;
986  }
987  }
988  if (TryWithoutPolarization) {
989  ChannelID.ClrPolarization();
990  for (cHashObject *hobj = list->First(); hobj; hobj = list->Next(hobj)) {
991  cChannel *channel = (cChannel *)hobj->Object();
992  if (channel->Sid() == sid && channel->GetChannelID().ClrPolarization() == ChannelID)
993  return channel;
994  }
995  }
996  }
997  return NULL;
998 }
1000 {
1001  int source = ChannelID.Source();
1002  int nid = ChannelID.Nid();
1003  int tid = ChannelID.Tid();
1004  for (cChannel *channel = First(); channel; channel = Next(channel)) {
1005  if (channel->Tid() == tid && channel->Nid() == nid && channel->Source() == source)
1006  return channel;
1007  }
1008  return NULL;
1009 }
1010 
1011 bool cChannels::HasUniqueChannelID(cChannel *NewChannel, cChannel *OldChannel)
1012 {
1013  tChannelID NewChannelID = NewChannel->GetChannelID();
1014  for (cChannel *channel = First(); channel; channel = Next(channel)) {
1015  if (!channel->GroupSep() && channel != OldChannel && channel->GetChannelID() == NewChannelID)
1016  return false;
1017  }
1018  return true;
1019 }
1020 
1021 bool cChannels::SwitchTo(int Number)
1022 {
1023  cChannel *channel = GetByNumber(Number);
1024  return channel && cDevice::PrimaryDevice()->SwitchChannel(channel, true);
1025 }
1026 
1028 {
1029  if (!maxChannelNameLength) {
1030  for (cChannel *channel = First(); channel; channel = Next(channel)) {
1031  if (!channel->GroupSep())
1033  }
1034  }
1035  return maxChannelNameLength;
1036 }
1037 
1039 {
1041  for (cChannel *channel = First(); channel; channel = Next(channel)) {
1042  if (!channel->GroupSep())
1043  maxShortChannelNameLength = max(Utf8StrLen(channel->ShortName(true)), maxShortChannelNameLength);
1044  }
1045  }
1047 }
1048 
1049 void cChannels::SetModified(bool ByUser)
1050 {
1053 }
1054 
1056 {
1057  int Result = modified;
1059  return Result;
1060 }
1061 
1062 cChannel *cChannels::NewChannel(const cChannel *Transponder, const char *Name, const char *ShortName, const char *Provider, int Nid, int Tid, int Sid, int Rid)
1063 {
1064  if (Transponder) {
1065  dsyslog("creating new channel '%s,%s;%s' on %s transponder %d with id %d-%d-%d-%d", Name, ShortName, Provider, *cSource::ToString(Transponder->Source()), Transponder->Transponder(), Nid, Tid, Sid, Rid);
1066  cChannel *NewChannel = new cChannel;
1067  NewChannel->CopyTransponderData(Transponder);
1068  NewChannel->SetId(Nid, Tid, Sid, Rid);
1069  NewChannel->SetName(Name, ShortName, Provider);
1070  Add(NewChannel);
1071  ReNumber();
1072  return NewChannel;
1073  }
1074  return NULL;
1075 }
1076 
1077 cString ChannelString(const cChannel *Channel, int Number)
1078 {
1079  char buffer[256];
1080  if (Channel) {
1081  if (Channel->GroupSep())
1082  snprintf(buffer, sizeof(buffer), "%s", Channel->Name());
1083  else
1084  snprintf(buffer, sizeof(buffer), "%d%s %s", Channel->Number(), Number ? "-" : "", Channel->Name());
1085  }
1086  else if (Number)
1087  snprintf(buffer, sizeof(buffer), "%d-", Number);
1088  else
1089  snprintf(buffer, sizeof(buffer), "%s", tr("*** Invalid Channel ***"));
1090  return buffer;
1091 }