vdr  2.0.4
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.1.1 2013/10/11 11:40:02 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 (!Channel->groupSep) {
527  if (!isempty(Channel->shortName))
528  q += sprintf(q, ",%s", Channel->shortName);
529  else if (strchr(Channel->name, ','))
530  q += sprintf(q, ",");
531  if (!isempty(Channel->provider))
532  q += sprintf(q, ";%s", Channel->provider);
533  }
534  *q = 0;
535  strreplace(FullName, ':', '|');
536  cString buffer;
537  if (Channel->groupSep) {
538  if (Channel->number)
539  buffer = cString::sprintf(":@%d %s\n", Channel->number, FullName);
540  else
541  buffer = cString::sprintf(":%s\n", FullName);
542  }
543  else {
544  char vpidbuf[32];
545  char *q = vpidbuf;
546  q += snprintf(q, sizeof(vpidbuf), "%d", Channel->vpid);
547  if (Channel->ppid && Channel->ppid != Channel->vpid)
548  q += snprintf(q, sizeof(vpidbuf) - (q - vpidbuf), "+%d", Channel->ppid);
549  if (Channel->vpid && Channel->vtype)
550  q += snprintf(q, sizeof(vpidbuf) - (q - vpidbuf), "=%d", Channel->vtype);
551  *q = 0;
552  const int ABufferSize = (MAXAPIDS + MAXDPIDS) * (5 + 1 + MAXLANGCODE2 + 5) + 10; // 5 digits plus delimiting ',' or ';' plus optional '=cod+cod@type', +10: paranoia
553  char apidbuf[ABufferSize];
554  q = apidbuf;
555  q += IntArrayToString(q, Channel->apids, 10, Channel->alangs, Channel->atypes);
556  if (Channel->dpids[0]) {
557  *q++ = ';';
558  q += IntArrayToString(q, Channel->dpids, 10, Channel->dlangs, Channel->dtypes);
559  }
560  *q = 0;
561  const int TBufferSize = (MAXTXTPAGES * MAXSPIDS) * (5 + 1 + MAXLANGCODE2) + 10; // 5 digits plus delimiting ',' or ';' plus optional '=cod+cod', +10: paranoia and tpid
562  char tpidbuf[TBufferSize];
563  q = tpidbuf;
564  q += snprintf(q, sizeof(tpidbuf), "%d", Channel->tpid);
565  if (Channel->fixedTtxtSubtitlePages > 0) {
566  *q++ = '+';
567  for (int i = 0; i < Channel->fixedTtxtSubtitlePages; ++i) {
568  tTeletextSubtitlePage page = Channel->teletextSubtitlePages[i];
569  q += snprintf(q, sizeof(tpidbuf) - (q - tpidbuf), "%d=%s", page.PageNumber(), page.ttxtLanguage);
570  }
571  }
572  if (Channel->spids[0]) {
573  *q++ = ';';
574  q += IntArrayToString(q, Channel->spids, 10, Channel->slangs);
575  }
576  char caidbuf[MAXCAIDS * 5 + 10]; // 5: 4 digits plus delimiting ',', 10: paranoia
577  q = caidbuf;
578  q += IntArrayToString(q, Channel->caids, 16);
579  *q = 0;
580  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);
581  }
582  return buffer;
583 }
584 
586 {
587  return ToText(this);
588 }
589 
590 bool cChannel::Parse(const char *s)
591 {
592  bool ok = true;
593  if (*s == ':') {
594  groupSep = true;
595  if (*++s == '@' && *++s) {
596  char *p = NULL;
597  errno = 0;
598  int n = strtol(s, &p, 10);
599  if (!errno && p != s && n > 0) {
600  number = n;
601  s = p;
602  }
603  }
605  strreplace(name, '|', ':');
606  }
607  else {
608  groupSep = false;
609  char *namebuf = NULL;
610  char *sourcebuf = NULL;
611  char *parambuf = NULL;
612  char *vpidbuf = NULL;
613  char *apidbuf = NULL;
614  char *tpidbuf = NULL;
615  char *caidbuf = NULL;
616  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);
617  if (fields >= 9) {
618  if (fields == 9) {
619  // allow reading of old format
620  sid = atoi(caidbuf);
621  delete caidbuf;
622  caidbuf = NULL;
623  if (sscanf(tpidbuf, "%d", &tpid) != 1)
624  return false;
625  caids[0] = tpid;
626  caids[1] = 0;
627  tpid = 0;
628  }
629  vpid = ppid = 0;
630  vtype = 0;
631  apids[0] = 0;
632  atypes[0] = 0;
633  dpids[0] = 0;
634  dtypes[0] = 0;
635  spids[0] = 0;
636  ok = false;
637  if (parambuf && sourcebuf && vpidbuf && apidbuf) {
638  parameters = parambuf;
639  ok = (source = cSource::FromString(sourcebuf)) >= 0;
640 
641  char *p;
642  if ((p = strchr(vpidbuf, '=')) != NULL) {
643  *p++ = 0;
644  if (sscanf(p, "%d", &vtype) != 1)
645  return false;
646  }
647  if ((p = strchr(vpidbuf, '+')) != NULL) {
648  *p++ = 0;
649  if (sscanf(p, "%d", &ppid) != 1)
650  return false;
651  }
652  if (sscanf(vpidbuf, "%d", &vpid) != 1)
653  return false;
654  if (!ppid)
655  ppid = vpid;
656  if (vpid && !vtype)
657  vtype = 2; // default is MPEG-2
658 
659  char *dpidbuf = strchr(apidbuf, ';');
660  if (dpidbuf)
661  *dpidbuf++ = 0;
662  p = apidbuf;
663  char *q;
664  int NumApids = 0;
665  char *strtok_next;
666  while ((q = strtok_r(p, ",", &strtok_next)) != NULL) {
667  if (NumApids < MAXAPIDS) {
668  atypes[NumApids] = 4; // backwards compatibility
669  char *l = strchr(q, '=');
670  if (l) {
671  *l++ = 0;
672  char *t = strchr(l, '@');
673  if (t) {
674  *t++ = 0;
675  atypes[NumApids] = strtol(t, NULL, 10);
676  }
677  strn0cpy(alangs[NumApids], l, MAXLANGCODE2);
678  }
679  else
680  *alangs[NumApids] = 0;
681  if ((apids[NumApids] = strtol(q, NULL, 10)) != 0)
682  NumApids++;
683  }
684  else
685  esyslog("ERROR: too many APIDs!"); // no need to set ok to 'false'
686  p = NULL;
687  }
688  apids[NumApids] = 0;
689  atypes[NumApids] = 0;
690  if (dpidbuf) {
691  char *p = dpidbuf;
692  char *q;
693  int NumDpids = 0;
694  char *strtok_next;
695  while ((q = strtok_r(p, ",", &strtok_next)) != NULL) {
696  if (NumDpids < MAXDPIDS) {
697  dtypes[NumDpids] = SI::AC3DescriptorTag; // backwards compatibility
698  char *l = strchr(q, '=');
699  if (l) {
700  *l++ = 0;
701  char *t = strchr(l, '@');
702  if (t) {
703  *t++ = 0;
704  dtypes[NumDpids] = strtol(t, NULL, 10);
705  }
706  strn0cpy(dlangs[NumDpids], l, MAXLANGCODE2);
707  }
708  else
709  *dlangs[NumDpids] = 0;
710  if ((dpids[NumDpids] = strtol(q, NULL, 10)) != 0)
711  NumDpids++;
712  }
713  else
714  esyslog("ERROR: too many DPIDs!"); // no need to set ok to 'false'
715  p = NULL;
716  }
717  dpids[NumDpids] = 0;
718  dtypes[NumDpids] = 0;
719  }
720  int NumSpids = 0;
721  if ((p = strchr(tpidbuf, ';')) != NULL) {
722  *p++ = 0;
723  char *q;
724  char *strtok_next;
725  while ((q = strtok_r(p, ",", &strtok_next)) != NULL) {
726  if (NumSpids < MAXSPIDS) {
727  char *l = strchr(q, '=');
728  if (l) {
729  *l++ = 0;
730  strn0cpy(slangs[NumSpids], l, MAXLANGCODE2);
731  }
732  else
733  *slangs[NumSpids] = 0;
734  spids[NumSpids++] = strtol(q, NULL, 10);
735  }
736  else
737  esyslog("ERROR: too many SPIDs!"); // no need to set ok to 'false'
738  p = NULL;
739  }
740  spids[NumSpids] = 0;
741  }
743  if ((p = strchr(tpidbuf, '+')) != NULL) {
744  *p++ = 0;
745  char *q;
746  char *strtok_next;
747  while ((q = strtok_r(p, ",", &strtok_next)) != NULL) {
749  int page;
750  char *l = strchr(q, '=');
751  if (l)
752  *l++ = 0;
753  if (sscanf(q, "%d", &page) == 1) {
755  if (l)
758  }
759  else
760  esyslog("ERROR: invalid Teletext page!"); // no need to set ok to 'false'
761  }
762  else
763  esyslog("ERROR: too many Teletext pages!"); // no need to set ok to 'false'
764  p = NULL;
765  }
767  }
768  if (sscanf(tpidbuf, "%d", &tpid) != 1)
769  return false;
770  if (caidbuf) {
771  char *p = caidbuf;
772  char *q;
773  int NumCaIds = 0;
774  char *strtok_next;
775  while ((q = strtok_r(p, ",", &strtok_next)) != NULL) {
776  if (NumCaIds < MAXCAIDS) {
777  caids[NumCaIds++] = strtol(q, NULL, 16) & 0xFFFF;
778  if (NumCaIds == 1 && caids[0] <= CA_USER_MAX)
779  break;
780  }
781  else
782  esyslog("ERROR: too many CA ids!"); // no need to set ok to 'false'
783  p = NULL;
784  }
785  caids[NumCaIds] = 0;
786  }
787  }
788  strreplace(namebuf, '|', ':');
789 
790  char *p = strchr(namebuf, ';');
791  if (p) {
792  *p++ = 0;
794  }
795  p = strrchr(namebuf, ','); // long name might contain a ',', so search for the rightmost one
796  if (p) {
797  *p++ = 0;
799  }
800  name = strcpyrealloc(name, namebuf);
801 
802  free(parambuf);
803  free(sourcebuf);
804  free(vpidbuf);
805  free(apidbuf);
806  free(tpidbuf);
807  free(caidbuf);
808  free(namebuf);
809  nameSource = NULL;
810  shortNameSource = NULL;
811  if (!GetChannelID().Valid()) {
812  esyslog("ERROR: channel data results in invalid ID!");
813  return false;
814  }
815  }
816  else
817  return false;
818  }
819  return ok;
820 }
821 
822 bool cChannel::Save(FILE *f)
823 {
824  return fprintf(f, "%s", *ToText()) > 0;
825 }
826 
827 // --- cChannelSorter --------------------------------------------------------
828 
829 class cChannelSorter : public cListObject {
830 public:
834  channel = Channel;
836  }
837  virtual int Compare(const cListObject &ListObject) const {
838  cChannelSorter *cs = (cChannelSorter *)&ListObject;
839  return memcmp(&channelID, &cs->channelID, sizeof(channelID));
840  }
841  };
842 
843 // --- cChannels -------------------------------------------------------------
844 
846 
848 {
849  maxNumber = 0;
853 }
854 
856 {
857  cList<cChannelSorter> ChannelSorter;
858  for (cChannel *channel = First(); channel; channel = Next(channel)) {
859  if (!channel->GroupSep())
860  ChannelSorter.Add(new cChannelSorter(channel));
861  }
862  ChannelSorter.Sort();
863  cChannelSorter *cs = ChannelSorter.First();
864  while (cs) {
865  cChannelSorter *next = ChannelSorter.Next(cs);
866  if (next && cs->channelID == next->channelID) {
867  dsyslog("deleting duplicate channel %s", *next->channel->ToText());
868  Del(next->channel);
869  }
870  cs = next;
871  }
872 }
873 
874 bool cChannels::Load(const char *FileName, bool AllowComments, bool MustExist)
875 {
876  if (cConfig<cChannel>::Load(FileName, AllowComments, MustExist)) {
878  ReNumber();
879  return true;
880  }
881  return false;
882 }
883 
885 {
886  channelsHashSid.Add(Channel, Channel->Sid());
887 }
888 
890 {
891  channelsHashSid.Del(Channel, Channel->Sid());
892 }
893 
895 {
896  cChannel *channel = Get(++Idx);
897  while (channel && !(channel->GroupSep() && *channel->Name()))
898  channel = Get(++Idx);
899  return channel ? Idx : -1;
900 }
901 
903 {
904  cChannel *channel = Get(--Idx);
905  while (channel && !(channel->GroupSep() && *channel->Name()))
906  channel = Get(--Idx);
907  return channel ? Idx : -1;
908 }
909 
911 {
912  cChannel *channel = Get(++Idx);
913  while (channel && channel->GroupSep())
914  channel = Get(++Idx);
915  return channel ? Idx : -1;
916 }
917 
919 {
920  cChannel *channel = Get(--Idx);
921  while (channel && channel->GroupSep())
922  channel = Get(--Idx);
923  return channel ? Idx : -1;
924 }
925 
927 {
929  maxNumber = 0;
930  int Number = 1;
931  for (cChannel *channel = First(); channel; channel = Next(channel)) {
932  if (channel->GroupSep()) {
933  if (channel->Number() > Number)
934  Number = channel->Number();
935  }
936  else {
937  HashChannel(channel);
938  maxNumber = Number;
939  channel->SetNumber(Number++);
940  }
941  }
942 }
943 
944 cChannel *cChannels::GetByNumber(int Number, int SkipGap)
945 {
946  cChannel *previous = NULL;
947  for (cChannel *channel = First(); channel; channel = Next(channel)) {
948  if (!channel->GroupSep()) {
949  if (channel->Number() == Number)
950  return channel;
951  else if (SkipGap && channel->Number() > Number)
952  return SkipGap > 0 ? channel : previous;
953  previous = channel;
954  }
955  }
956  return NULL;
957 }
958 
959 cChannel *cChannels::GetByServiceID(int Source, int Transponder, unsigned short ServiceID)
960 {
961  cList<cHashObject> *list = channelsHashSid.GetList(ServiceID);
962  if (list) {
963  for (cHashObject *hobj = list->First(); hobj; hobj = list->Next(hobj)) {
964  cChannel *channel = (cChannel *)hobj->Object();
965  if (channel->Sid() == ServiceID && channel->Source() == Source && ISTRANSPONDER(channel->Transponder(), Transponder))
966  return channel;
967  }
968  }
969  return NULL;
970 }
971 
972 cChannel *cChannels::GetByChannelID(tChannelID ChannelID, bool TryWithoutRid, bool TryWithoutPolarization)
973 {
974  int sid = ChannelID.Sid();
976  if (list) {
977  for (cHashObject *hobj = list->First(); hobj; hobj = list->Next(hobj)) {
978  cChannel *channel = (cChannel *)hobj->Object();
979  if (channel->Sid() == sid && channel->GetChannelID() == ChannelID)
980  return channel;
981  }
982  if (TryWithoutRid) {
983  ChannelID.ClrRid();
984  for (cHashObject *hobj = list->First(); hobj; hobj = list->Next(hobj)) {
985  cChannel *channel = (cChannel *)hobj->Object();
986  if (channel->Sid() == sid && channel->GetChannelID().ClrRid() == ChannelID)
987  return channel;
988  }
989  }
990  if (TryWithoutPolarization) {
991  ChannelID.ClrPolarization();
992  for (cHashObject *hobj = list->First(); hobj; hobj = list->Next(hobj)) {
993  cChannel *channel = (cChannel *)hobj->Object();
994  if (channel->Sid() == sid && channel->GetChannelID().ClrPolarization() == ChannelID)
995  return channel;
996  }
997  }
998  }
999  return NULL;
1000 }
1002 {
1003  int source = ChannelID.Source();
1004  int nid = ChannelID.Nid();
1005  int tid = ChannelID.Tid();
1006  for (cChannel *channel = First(); channel; channel = Next(channel)) {
1007  if (channel->Tid() == tid && channel->Nid() == nid && channel->Source() == source)
1008  return channel;
1009  }
1010  return NULL;
1011 }
1012 
1013 bool cChannels::HasUniqueChannelID(cChannel *NewChannel, cChannel *OldChannel)
1014 {
1015  tChannelID NewChannelID = NewChannel->GetChannelID();
1016  for (cChannel *channel = First(); channel; channel = Next(channel)) {
1017  if (!channel->GroupSep() && channel != OldChannel && channel->GetChannelID() == NewChannelID)
1018  return false;
1019  }
1020  return true;
1021 }
1022 
1023 bool cChannels::SwitchTo(int Number)
1024 {
1025  cChannel *channel = GetByNumber(Number);
1026  return channel && cDevice::PrimaryDevice()->SwitchChannel(channel, true);
1027 }
1028 
1030 {
1031  if (!maxChannelNameLength) {
1032  for (cChannel *channel = First(); channel; channel = Next(channel)) {
1033  if (!channel->GroupSep())
1035  }
1036  }
1037  return maxChannelNameLength;
1038 }
1039 
1041 {
1043  for (cChannel *channel = First(); channel; channel = Next(channel)) {
1044  if (!channel->GroupSep())
1045  maxShortChannelNameLength = max(Utf8StrLen(channel->ShortName(true)), maxShortChannelNameLength);
1046  }
1047  }
1049 }
1050 
1051 void cChannels::SetModified(bool ByUser)
1052 {
1055 }
1056 
1058 {
1059  int Result = modified;
1061  return Result;
1062 }
1063 
1064 cChannel *cChannels::NewChannel(const cChannel *Transponder, const char *Name, const char *ShortName, const char *Provider, int Nid, int Tid, int Sid, int Rid)
1065 {
1066  if (Transponder) {
1067  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);
1068  cChannel *NewChannel = new cChannel;
1069  NewChannel->CopyTransponderData(Transponder);
1070  NewChannel->SetId(Nid, Tid, Sid, Rid);
1071  NewChannel->SetName(Name, ShortName, Provider);
1072  Add(NewChannel);
1073  ReNumber();
1074  return NewChannel;
1075  }
1076  return NULL;
1077 }
1078 
1079 cString ChannelString(const cChannel *Channel, int Number)
1080 {
1081  char buffer[256];
1082  if (Channel) {
1083  if (Channel->GroupSep())
1084  snprintf(buffer, sizeof(buffer), "%s", Channel->Name());
1085  else
1086  snprintf(buffer, sizeof(buffer), "%d%s %s", Channel->Number(), Number ? "-" : "", Channel->Name());
1087  }
1088  else if (Number)
1089  snprintf(buffer, sizeof(buffer), "%d-", Number);
1090  else
1091  snprintf(buffer, sizeof(buffer), "%s", tr("*** Invalid Channel ***"));
1092  return buffer;
1093 }
1094