vdr  1.7.27
channels.c
Go to the documentation of this file.
00001 /*
00002  * channels.c: Channel handling
00003  *
00004  * See the main source file 'vdr.c' for copyright information and
00005  * how to reach the author.
00006  *
00007  * $Id: channels.c 2.21 2012/03/11 13:29:06 kls Exp $
00008  */
00009 
00010 #include "channels.h"
00011 #include <ctype.h>
00012 #include "device.h"
00013 #include "epg.h"
00014 #include "libsi/si.h"
00015 #include "timers.h"
00016 
00017 // IMPORTANT NOTE: in the 'sscanf()' calls there is a blank after the '%d'
00018 // format characters in order to allow any number of blanks after a numeric
00019 // value!
00020 
00021 // --- tChannelID ------------------------------------------------------------
00022 
00023 const tChannelID tChannelID::InvalidID;
00024 
00025 tChannelID tChannelID::FromString(const char *s)
00026 {
00027   char *sourcebuf = NULL;
00028   int nid;
00029   int tid;
00030   int sid;
00031   int rid = 0;
00032   int fields = sscanf(s, "%a[^-]-%d-%d-%d-%d", &sourcebuf, &nid, &tid, &sid, &rid);
00033   if (fields == 4 || fields == 5) {
00034      int source = cSource::FromString(sourcebuf);
00035      free(sourcebuf);
00036      if (source >= 0)
00037         return tChannelID(source, nid, tid, sid, rid);
00038      }
00039   return tChannelID::InvalidID;
00040 }
00041 
00042 cString tChannelID::ToString(void) const
00043 {
00044   char buffer[256];
00045   snprintf(buffer, sizeof(buffer), rid ? "%s-%d-%d-%d-%d" : "%s-%d-%d-%d", *cSource::ToString(source), nid, tid, sid, rid);
00046   return buffer;
00047 }
00048 
00049 tChannelID &tChannelID::ClrPolarization(void)
00050 {
00051   while (tid > 100000)
00052         tid -= 100000;
00053   return *this;
00054 }
00055 
00056 // --- cChannel --------------------------------------------------------------
00057 
00058 cChannel::cChannel(void)
00059 {
00060   name = strdup("");
00061   shortName = strdup("");
00062   provider = strdup("");
00063   portalName = strdup("");
00064   memset(&__BeginData__, 0, (char *)&__EndData__ - (char *)&__BeginData__);
00065   parameters = "";
00066   modification = CHANNELMOD_NONE;
00067   schedule     = NULL;
00068   linkChannels = NULL;
00069   refChannel   = NULL;
00070 }
00071 
00072 cChannel::cChannel(const cChannel &Channel)
00073 {
00074   name = NULL;
00075   shortName = NULL;
00076   provider = NULL;
00077   portalName = NULL;
00078   schedule     = NULL;
00079   linkChannels = NULL;
00080   refChannel   = NULL;
00081   *this = Channel;
00082 }
00083 
00084 cChannel::~cChannel()
00085 {
00086   delete linkChannels;
00087   linkChannels = NULL; // more than one channel can link to this one, so we need the following loop
00088   for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)) {
00089       if (Channel->linkChannels) {
00090          for (cLinkChannel *lc = Channel->linkChannels->First(); lc; lc = Channel->linkChannels->Next(lc)) {
00091              if (lc->Channel() == this) {
00092                 Channel->linkChannels->Del(lc);
00093                 break;
00094                 }
00095              }
00096          if (Channel->linkChannels->Count() == 0) {
00097             delete Channel->linkChannels;
00098             Channel->linkChannels = NULL;
00099             }
00100          }
00101       }
00102   free(name);
00103   free(shortName);
00104   free(provider);
00105   free(portalName);
00106 }
00107 
00108 cChannel& cChannel::operator= (const cChannel &Channel)
00109 {
00110   name = strcpyrealloc(name, Channel.name);
00111   shortName = strcpyrealloc(shortName, Channel.shortName);
00112   provider = strcpyrealloc(provider, Channel.provider);
00113   portalName = strcpyrealloc(portalName, Channel.portalName);
00114   memcpy(&__BeginData__, &Channel.__BeginData__, (char *)&Channel.__EndData__ - (char *)&Channel.__BeginData__);
00115   parameters = Channel.parameters;
00116   return *this;
00117 }
00118 
00119 int cChannel::Transponder(int Frequency, char Polarization)
00120 {
00121   // some satellites have transponders at the same frequency, just with different polarization:
00122   switch (toupper(Polarization)) {
00123     case 'H': Frequency += 100000; break;
00124     case 'V': Frequency += 200000; break;
00125     case 'L': Frequency += 300000; break;
00126     case 'R': Frequency += 400000; break;
00127     default: esyslog("ERROR: invalid value for Polarization '%c'", Polarization);
00128     }
00129   return Frequency;
00130 }
00131 
00132 int cChannel::Transponder(void) const
00133 {
00134   int tf = frequency;
00135   while (tf > 20000)
00136         tf /= 1000;
00137   if (IsSat()) {
00138      const char *p = strpbrk(parameters, "HVLRhvlr"); // lowercase for backwards compatibility
00139      if (p)
00140         tf = Transponder(tf, *p);
00141      }
00142   return tf;
00143 }
00144 
00145 bool cChannel::HasTimer(void) const
00146 {
00147   for (cTimer *Timer = Timers.First(); Timer; Timer = Timers.Next(Timer)) {
00148       if (Timer->Channel() == this)
00149          return true;
00150       }
00151   return false;
00152 }
00153 
00154 int cChannel::Modification(int Mask)
00155 {
00156   int Result = modification & Mask;
00157   modification = CHANNELMOD_NONE;
00158   return Result;
00159 }
00160 
00161 void cChannel::CopyTransponderData(const cChannel *Channel)
00162 {
00163   if (Channel) {
00164      frequency    = Channel->frequency;
00165      source       = Channel->source;
00166      srate        = Channel->srate;
00167      parameters   = Channel->parameters;
00168      }
00169 }
00170 
00171 bool cChannel::SetTransponderData(int Source, int Frequency, int Srate, const char *Parameters, bool Quiet)
00172 {
00173   if (strchr(Parameters, ':')) {
00174      esyslog("ERROR: parameter string '%s' contains ':'", Parameters);
00175      return false;
00176      }
00177   // Workarounds for broadcaster stupidity:
00178   // Some providers broadcast the transponder frequency of their channels with two different
00179   // values (like 12551 and 12552), so we need to allow for a little tolerance here
00180   if (abs(frequency - Frequency) <= 1)
00181      Frequency = frequency;
00182   // Sometimes the transponder frequency is set to 0, which is just wrong
00183   if (Frequency == 0)
00184      return false;
00185   // Sometimes the symbol rate is off by one
00186   if (abs(srate - Srate) <= 1)
00187      Srate = srate;
00188 
00189   if (source != Source || frequency != Frequency || srate != Srate || strcmp(parameters, Parameters)) {
00190      cString OldTransponderData = TransponderDataToString();
00191      source = Source;
00192      frequency = Frequency;
00193      srate = Srate;
00194      parameters = Parameters;
00195      schedule = NULL;
00196      if (Number() && !Quiet) {
00197         dsyslog("changing transponder data of channel %d from %s to %s", Number(), *OldTransponderData, *TransponderDataToString());
00198         modification |= CHANNELMOD_TRANSP;
00199         Channels.SetModified();
00200         }
00201      }
00202   return true;
00203 }
00204 
00205 void cChannel::SetId(int Nid, int Tid, int Sid, int Rid)
00206 {
00207   if (nid != Nid || tid != Tid || sid != Sid || rid != Rid) {
00208      if (Number()) {
00209         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);
00210         modification |= CHANNELMOD_ID;
00211         Channels.SetModified();
00212         Channels.UnhashChannel(this);
00213         }
00214      nid = Nid;
00215      tid = Tid;
00216      sid = Sid;
00217      rid = Rid;
00218      if (Number())
00219         Channels.HashChannel(this);
00220      schedule = NULL;
00221      }
00222 }
00223 
00224 void cChannel::SetName(const char *Name, const char *ShortName, const char *Provider)
00225 {
00226   if (!isempty(Name)) {
00227      bool nn = strcmp(name, Name) != 0;
00228      bool ns = strcmp(shortName, ShortName) != 0;
00229      bool np = strcmp(provider, Provider) != 0;
00230      if (nn || ns || np) {
00231         if (Number()) {
00232            dsyslog("changing name of channel %d from '%s,%s;%s' to '%s,%s;%s'", Number(), name, shortName, provider, Name, ShortName, Provider);
00233            modification |= CHANNELMOD_NAME;
00234            Channels.SetModified();
00235            }
00236         if (nn)
00237            name = strcpyrealloc(name, Name);
00238         if (ns)
00239            shortName = strcpyrealloc(shortName, ShortName);
00240         if (np)
00241            provider = strcpyrealloc(provider, Provider);
00242         }
00243      }
00244 }
00245 
00246 void cChannel::SetPortalName(const char *PortalName)
00247 {
00248   if (!isempty(PortalName) && strcmp(portalName, PortalName) != 0) {
00249      if (Number()) {
00250         dsyslog("changing portal name of channel %d from '%s' to '%s'", Number(), portalName, PortalName);
00251         modification |= CHANNELMOD_NAME;
00252         Channels.SetModified();
00253         }
00254      portalName = strcpyrealloc(portalName, PortalName);
00255      }
00256 }
00257 
00258 #define STRDIFF 0x01
00259 #define VALDIFF 0x02
00260 
00261 static int IntArraysDiffer(const int *a, const int *b, const char na[][MAXLANGCODE2] = NULL, const char nb[][MAXLANGCODE2] = NULL)
00262 {
00263   int result = 0;
00264   for (int i = 0; a[i] || b[i]; i++) {
00265       if (!a[i] || !b[i]) {
00266          result |= VALDIFF;
00267          break;
00268          }
00269       if (na && nb && strcmp(na[i], nb[i]) != 0)
00270          result |= STRDIFF;
00271       if (a[i] != b[i])
00272          result |= VALDIFF;
00273       }
00274   return result;
00275 }
00276 
00277 static int IntArrayToString(char *s, const int *a, int Base = 10, const char n[][MAXLANGCODE2] = NULL, const int *t = NULL)
00278 {
00279   char *q = s;
00280   int i = 0;
00281   while (a[i] || i == 0) {
00282         q += sprintf(q, Base == 16 ? "%s%X" : "%s%d", i ? "," : "", a[i]);
00283         const char *Delim = "=";
00284         if (a[i]) {
00285            if (n && *n[i]) {
00286               q += sprintf(q, "%s%s", Delim, n[i]);
00287               Delim = "";
00288               }
00289            if (t && t[i])
00290               q += sprintf(q, "%s@%d", Delim, t[i]);
00291            }
00292         if (!a[i])
00293            break;
00294         i++;
00295         }
00296   *q = 0;
00297   return q - s;
00298 }
00299 
00300 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)
00301 {
00302   int mod = CHANNELMOD_NONE;
00303   if (vpid != Vpid || ppid != Ppid || vtype != Vtype || tpid != Tpid)
00304      mod |= CHANNELMOD_PIDS;
00305   int m = IntArraysDiffer(apids, Apids, alangs, ALangs) | IntArraysDiffer(atypes, Atypes) | IntArraysDiffer(dpids, Dpids, dlangs, DLangs) | IntArraysDiffer(dtypes, Dtypes) | IntArraysDiffer(spids, Spids, slangs, SLangs);
00306   if (m & STRDIFF)
00307      mod |= CHANNELMOD_LANGS;
00308   if (m & VALDIFF)
00309      mod |= CHANNELMOD_PIDS;
00310   if (mod) {
00311      const int BufferSize = (MAXAPIDS + MAXDPIDS) * (5 + 1 + MAXLANGCODE2 + 5) + 10; // 5 digits plus delimiting ',' or ';' plus optional '=cod+cod@type', +10: paranoia
00312      char OldApidsBuf[BufferSize];
00313      char NewApidsBuf[BufferSize];
00314      char *q = OldApidsBuf;
00315      q += IntArrayToString(q, apids, 10, alangs, atypes);
00316      if (dpids[0]) {
00317         *q++ = ';';
00318         q += IntArrayToString(q, dpids, 10, dlangs, dtypes);
00319         }
00320      *q = 0;
00321      q = NewApidsBuf;
00322      q += IntArrayToString(q, Apids, 10, ALangs, Atypes);
00323      if (Dpids[0]) {
00324         *q++ = ';';
00325         q += IntArrayToString(q, Dpids, 10, DLangs, Dtypes);
00326         }
00327      *q = 0;
00328      const int SBufferSize = MAXSPIDS * (5 + 1 + MAXLANGCODE2) + 10; // 5 digits plus delimiting ',' or ';' plus optional '=cod', +10: paranoia
00329      char OldSpidsBuf[SBufferSize];
00330      char NewSpidsBuf[SBufferSize];
00331      q = OldSpidsBuf;
00332      q += IntArrayToString(q, spids, 10, slangs);
00333      *q = 0;
00334      q = NewSpidsBuf;
00335      q += IntArrayToString(q, Spids, 10, SLangs);
00336      *q = 0;
00337      if (Number())
00338         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);
00339      vpid = Vpid;
00340      ppid = Ppid;
00341      vtype = Vtype;
00342      for (int i = 0; i < MAXAPIDS; i++) {
00343          apids[i] = Apids[i];
00344          atypes[i] = Atypes[i];
00345          strn0cpy(alangs[i], ALangs[i], MAXLANGCODE2);
00346          }
00347      apids[MAXAPIDS] = 0;
00348      for (int i = 0; i < MAXDPIDS; i++) {
00349          dpids[i] = Dpids[i];
00350          dtypes[i] = Dtypes[i];
00351          strn0cpy(dlangs[i], DLangs[i], MAXLANGCODE2);
00352          }
00353      dpids[MAXDPIDS] = 0;
00354      for (int i = 0; i < MAXSPIDS; i++) {
00355          spids[i] = Spids[i];
00356          strn0cpy(slangs[i], SLangs[i], MAXLANGCODE2);
00357          }
00358      spids[MAXSPIDS] = 0;
00359      tpid = Tpid;
00360      modification |= mod;
00361      Channels.SetModified();
00362      }
00363 }
00364 
00365 void cChannel::SetSubtitlingDescriptors(uchar *SubtitlingTypes, uint16_t *CompositionPageIds, uint16_t *AncillaryPageIds)
00366 {
00367   if (SubtitlingTypes) {
00368      for (int i = 0; i < MAXSPIDS; i++)
00369          subtitlingTypes[i] = SubtitlingTypes[i];
00370      }
00371   if (CompositionPageIds) {
00372      for (int i = 0; i < MAXSPIDS; i++)
00373          compositionPageIds[i] = CompositionPageIds[i];
00374      }
00375   if (AncillaryPageIds) {
00376      for (int i = 0; i < MAXSPIDS; i++)
00377          ancillaryPageIds[i] = AncillaryPageIds[i];
00378      }
00379 }
00380 
00381 void cChannel::SetTeletextSubtitlePages(tTeletextSubtitlePage pages[], int numberOfPages)
00382 {
00383   int mod = CHANNELMOD_NONE;
00384   if (totalTtxtSubtitlePages != (fixedTtxtSubtitlePages + numberOfPages))
00385      mod |= CHANNELMOD_PIDS;
00386   totalTtxtSubtitlePages = fixedTtxtSubtitlePages;
00387   for (int i = 0; (i < numberOfPages) && (totalTtxtSubtitlePages < MAXTXTPAGES); i++) {
00388       if (teletextSubtitlePages[totalTtxtSubtitlePages].ttxtMagazine != pages[i].ttxtMagazine ||
00389           teletextSubtitlePages[totalTtxtSubtitlePages].ttxtPage != pages[i].ttxtPage ||
00390           teletextSubtitlePages[totalTtxtSubtitlePages].ttxtType != pages[i].ttxtType ||
00391           strcmp(teletextSubtitlePages[totalTtxtSubtitlePages].ttxtLanguage, pages[i].ttxtLanguage)) {
00392          mod |= CHANNELMOD_PIDS;
00393          teletextSubtitlePages[totalTtxtSubtitlePages] = pages[i];
00394          }
00395       totalTtxtSubtitlePages++;
00396       }
00397   modification |= mod;
00398   Channels.SetModified();
00399 }
00400 
00401 void cChannel::SetCaIds(const int *CaIds)
00402 {
00403   if (caids[0] && caids[0] <= CA_USER_MAX)
00404      return; // special values will not be overwritten
00405   if (IntArraysDiffer(caids, CaIds)) {
00406      char OldCaIdsBuf[MAXCAIDS * 5 + 10]; // 5: 4 digits plus delimiting ',', 10: paranoia
00407      char NewCaIdsBuf[MAXCAIDS * 5 + 10];
00408      IntArrayToString(OldCaIdsBuf, caids, 16);
00409      IntArrayToString(NewCaIdsBuf, CaIds, 16);
00410      if (Number())
00411         dsyslog("changing caids of channel %d from %s to %s", Number(), OldCaIdsBuf, NewCaIdsBuf);
00412      for (int i = 0; i <= MAXCAIDS; i++) { // <= to copy the terminating 0
00413          caids[i] = CaIds[i];
00414          if (!CaIds[i])
00415             break;
00416          }
00417      modification |= CHANNELMOD_CA;
00418      Channels.SetModified();
00419      }
00420 }
00421 
00422 void cChannel::SetCaDescriptors(int Level)
00423 {
00424   if (Level > 0) {
00425      modification |= CHANNELMOD_CA;
00426      Channels.SetModified();
00427      if (Number() && Level > 1)
00428         dsyslog("changing ca descriptors of channel %d", Number());
00429      }
00430 }
00431 
00432 void cChannel::SetLinkChannels(cLinkChannels *LinkChannels)
00433 {
00434   if (!linkChannels && !LinkChannels)
00435      return;
00436   if (linkChannels && LinkChannels) {
00437      cLinkChannel *lca = linkChannels->First();
00438      cLinkChannel *lcb = LinkChannels->First();
00439      while (lca && lcb) {
00440            if (lca->Channel() != lcb->Channel()) {
00441               lca = NULL;
00442               break;
00443               }
00444            lca = linkChannels->Next(lca);
00445            lcb = LinkChannels->Next(lcb);
00446            }
00447      if (!lca && !lcb) {
00448         delete LinkChannels;
00449         return; // linkage has not changed
00450         }
00451      }
00452   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
00453   char *q = buffer;
00454   q += sprintf(q, "linking channel %d from", Number());
00455   if (linkChannels) {
00456      for (cLinkChannel *lc = linkChannels->First(); lc; lc = linkChannels->Next(lc)) {
00457          lc->Channel()->SetRefChannel(NULL);
00458          q += sprintf(q, " %d", lc->Channel()->Number());
00459          }
00460      delete linkChannels;
00461      }
00462   else
00463      q += sprintf(q, " none");
00464   q += sprintf(q, " to");
00465   linkChannels = LinkChannels;
00466   if (linkChannels) {
00467      for (cLinkChannel *lc = linkChannels->First(); lc; lc = linkChannels->Next(lc)) {
00468          lc->Channel()->SetRefChannel(this);
00469          q += sprintf(q, " %d", lc->Channel()->Number());
00470          //dsyslog("link %4d -> %4d: %s", Number(), lc->Channel()->Number(), lc->Channel()->Name());
00471          }
00472      }
00473   else
00474      q += sprintf(q, " none");
00475   if (Number())
00476      dsyslog("%s", buffer);
00477 }
00478 
00479 void cChannel::SetRefChannel(cChannel *RefChannel)
00480 {
00481   refChannel = RefChannel;
00482 }
00483 
00484 cString cChannel::TransponderDataToString(void) const
00485 {
00486   if (cSource::IsTerr(source))
00487      return cString::sprintf("%d:%s:%s", frequency, *parameters, *cSource::ToString(source));
00488   return cString::sprintf("%d:%s:%s:%d", frequency, *parameters, *cSource::ToString(source), srate);
00489 }
00490 
00491 cString cChannel::ToText(const cChannel *Channel)
00492 {
00493   char FullName[strlen(Channel->name) + 1 + strlen(Channel->shortName) + 1 + strlen(Channel->provider) + 1 + 10]; // +10: paranoia
00494   char *q = FullName;
00495   q += sprintf(q, "%s", Channel->name);
00496   if (!isempty(Channel->shortName))
00497      q += sprintf(q, ",%s", Channel->shortName);
00498   else if (strchr(Channel->name, ','))
00499      q += sprintf(q, ",");
00500   if (!isempty(Channel->provider))
00501      q += sprintf(q, ";%s", Channel->provider);
00502   *q = 0;
00503   strreplace(FullName, ':', '|');
00504   cString buffer;
00505   if (Channel->groupSep) {
00506      if (Channel->number)
00507         buffer = cString::sprintf(":@%d %s\n", Channel->number, FullName);
00508      else
00509         buffer = cString::sprintf(":%s\n", FullName);
00510      }
00511   else {
00512      char vpidbuf[32];
00513      char *q = vpidbuf;
00514      q += snprintf(q, sizeof(vpidbuf), "%d", Channel->vpid);
00515      if (Channel->ppid && Channel->ppid != Channel->vpid)
00516         q += snprintf(q, sizeof(vpidbuf) - (q - vpidbuf), "+%d", Channel->ppid);
00517      if (Channel->vpid && Channel->vtype)
00518         q += snprintf(q, sizeof(vpidbuf) - (q - vpidbuf), "=%d", Channel->vtype);
00519      *q = 0;
00520      const int ABufferSize = (MAXAPIDS + MAXDPIDS) * (5 + 1 + MAXLANGCODE2 + 5) + 10; // 5 digits plus delimiting ',' or ';' plus optional '=cod+cod@type', +10: paranoia
00521      char apidbuf[ABufferSize];
00522      q = apidbuf;
00523      q += IntArrayToString(q, Channel->apids, 10, Channel->alangs, Channel->atypes);
00524      if (Channel->dpids[0]) {
00525         *q++ = ';';
00526         q += IntArrayToString(q, Channel->dpids, 10, Channel->dlangs, Channel->dtypes);
00527         }
00528      *q = 0;
00529      const int TBufferSize = (MAXTXTPAGES * MAXSPIDS) * (5 + 1 + MAXLANGCODE2) + 10; // 5 digits plus delimiting ',' or ';' plus optional '=cod+cod', +10: paranoia and tpid
00530      char tpidbuf[TBufferSize];
00531      q = tpidbuf;
00532      q += snprintf(q, sizeof(tpidbuf), "%d", Channel->tpid);
00533      if (Channel->fixedTtxtSubtitlePages > 0) {
00534         *q++ = '+';
00535         for (int i = 0; i < Channel->fixedTtxtSubtitlePages; ++i) {
00536             tTeletextSubtitlePage page = Channel->teletextSubtitlePages[i];
00537             q += snprintf(q, sizeof(tpidbuf) - (q - tpidbuf), "%d=%s", page.PageNumber(), page.ttxtLanguage);
00538             }
00539         }
00540      if (Channel->spids[0]) {
00541         *q++ = ';';
00542         q += IntArrayToString(q, Channel->spids, 10, Channel->slangs);
00543         }
00544      char caidbuf[MAXCAIDS * 5 + 10]; // 5: 4 digits plus delimiting ',', 10: paranoia
00545      q = caidbuf;
00546      q += IntArrayToString(q, Channel->caids, 16);
00547      *q = 0;
00548      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);
00549      }
00550   return buffer;
00551 }
00552 
00553 cString cChannel::ToText(void) const
00554 {
00555   return ToText(this);
00556 }
00557 
00558 bool cChannel::Parse(const char *s)
00559 {
00560   bool ok = true;
00561   if (*s == ':') {
00562      groupSep = true;
00563      if (*++s == '@' && *++s) {
00564         char *p = NULL;
00565         errno = 0;
00566         int n = strtol(s, &p, 10);
00567         if (!errno && p != s && n > 0) {
00568            number = n;
00569            s = p;
00570            }
00571         }
00572      name = strcpyrealloc(name, skipspace(s));
00573      strreplace(name, '|', ':');
00574      }
00575   else {
00576      groupSep = false;
00577      char *namebuf = NULL;
00578      char *sourcebuf = NULL;
00579      char *parambuf = NULL;
00580      char *vpidbuf = NULL;
00581      char *apidbuf = NULL;
00582      char *tpidbuf = NULL;
00583      char *caidbuf = NULL;
00584      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);
00585      if (fields >= 9) {
00586         if (fields == 9) {
00587            // allow reading of old format
00588            sid = atoi(caidbuf);
00589            delete caidbuf;
00590            caidbuf = NULL;
00591            if (sscanf(tpidbuf, "%d", &tpid) != 1)
00592               return false;
00593            caids[0] = tpid;
00594            caids[1] = 0;
00595            tpid = 0;
00596            }
00597         vpid = ppid = 0;
00598         vtype = 0;
00599         apids[0] = 0;
00600         atypes[0] = 0;
00601         dpids[0] = 0;
00602         dtypes[0] = 0;
00603         spids[0] = 0;
00604         ok = false;
00605         if (parambuf && sourcebuf && vpidbuf && apidbuf) {
00606            parameters = parambuf;
00607            ok = (source = cSource::FromString(sourcebuf)) >= 0;
00608 
00609            char *p;
00610            if ((p = strchr(vpidbuf, '=')) != NULL) {
00611               *p++ = 0;
00612               if (sscanf(p, "%d", &vtype) != 1)
00613                  return false;
00614               }
00615            if ((p = strchr(vpidbuf, '+')) != NULL) {
00616               *p++ = 0;
00617               if (sscanf(p, "%d", &ppid) != 1)
00618                  return false;
00619               }
00620            if (sscanf(vpidbuf, "%d", &vpid) != 1)
00621               return false;
00622            if (!ppid)
00623               ppid = vpid;
00624            if (vpid && !vtype)
00625               vtype = 2; // default is MPEG-2
00626 
00627            char *dpidbuf = strchr(apidbuf, ';');
00628            if (dpidbuf)
00629               *dpidbuf++ = 0;
00630            p = apidbuf;
00631            char *q;
00632            int NumApids = 0;
00633            char *strtok_next;
00634            while ((q = strtok_r(p, ",", &strtok_next)) != NULL) {
00635                  if (NumApids < MAXAPIDS) {
00636                     atypes[NumApids] = 4; // backwards compatibility
00637                     char *l = strchr(q, '=');
00638                     if (l) {
00639                        *l++ = 0;
00640                        char *t = strchr(l, '@');
00641                        if (t) {
00642                           *t++ = 0;
00643                           atypes[NumApids] = strtol(t, NULL, 10);
00644                           }
00645                        strn0cpy(alangs[NumApids], l, MAXLANGCODE2);
00646                        }
00647                     else
00648                        *alangs[NumApids] = 0;
00649                     if ((apids[NumApids] = strtol(q, NULL, 10)) != 0)
00650                        NumApids++;
00651                     }
00652                  else
00653                     esyslog("ERROR: too many APIDs!"); // no need to set ok to 'false'
00654                  p = NULL;
00655                  }
00656            apids[NumApids] = 0;
00657            atypes[NumApids] = 0;
00658            if (dpidbuf) {
00659               char *p = dpidbuf;
00660               char *q;
00661               int NumDpids = 0;
00662               char *strtok_next;
00663               while ((q = strtok_r(p, ",", &strtok_next)) != NULL) {
00664                     if (NumDpids < MAXDPIDS) {
00665                        dtypes[NumDpids] = SI::AC3DescriptorTag; // backwards compatibility
00666                        char *l = strchr(q, '=');
00667                        if (l) {
00668                           *l++ = 0;
00669                           char *t = strchr(l, '@');
00670                           if (t) {
00671                              *t++ = 0;
00672                              dtypes[NumDpids] = strtol(t, NULL, 10);
00673                              }
00674                           strn0cpy(dlangs[NumDpids], l, MAXLANGCODE2);
00675                           }
00676                        else
00677                           *dlangs[NumDpids] = 0;
00678                        if ((dpids[NumDpids] = strtol(q, NULL, 10)) != 0)
00679                           NumDpids++;
00680                        }
00681                     else
00682                        esyslog("ERROR: too many DPIDs!"); // no need to set ok to 'false'
00683                     p = NULL;
00684                     }
00685               dpids[NumDpids] = 0;
00686               dtypes[NumDpids] = 0;
00687               }
00688            int NumSpids = 0;
00689            if ((p = strchr(tpidbuf, ';')) != NULL) {
00690               *p++ = 0;
00691               char *q;
00692               char *strtok_next;
00693               while ((q = strtok_r(p, ",", &strtok_next)) != NULL) {
00694                     if (NumSpids < MAXSPIDS) {
00695                        char *l = strchr(q, '=');
00696                        if (l) {
00697                           *l++ = 0;
00698                           strn0cpy(slangs[NumSpids], l, MAXLANGCODE2);
00699                           }
00700                        else
00701                           *slangs[NumSpids] = 0;
00702                        spids[NumSpids++] = strtol(q, NULL, 10);
00703                        }
00704                     else
00705                        esyslog("ERROR: too many SPIDs!"); // no need to set ok to 'false'
00706                     p = NULL;
00707                     }
00708               spids[NumSpids] = 0;
00709               }
00710            fixedTtxtSubtitlePages = 0;
00711            if ((p = strchr(tpidbuf, '+')) != NULL) {
00712               *p++ = 0;
00713               char *q;
00714               char *strtok_next;
00715               while ((q = strtok_r(p, ",", &strtok_next)) != NULL) {
00716                     if (fixedTtxtSubtitlePages < MAXTXTPAGES) {
00717                        int page;
00718                        char *l = strchr(q, '=');
00719                        if (l)
00720                           *l++ = 0;
00721                        if (sscanf(q, "%d", &page) == 1) {
00722                           teletextSubtitlePages[fixedTtxtSubtitlePages] = tTeletextSubtitlePage(page);
00723                           if (l)
00724                              strn0cpy(teletextSubtitlePages[fixedTtxtSubtitlePages].ttxtLanguage, l, MAXLANGCODE2);
00725                           fixedTtxtSubtitlePages++;
00726                           }
00727                        else
00728                           esyslog("ERROR: invalid Teletext page!"); // no need to set ok to 'false'
00729                        }
00730                     else
00731                        esyslog("ERROR: too many Teletext pages!"); // no need to set ok to 'false'
00732                     p = NULL;
00733                     }
00734               totalTtxtSubtitlePages = fixedTtxtSubtitlePages;
00735               }
00736            if (sscanf(tpidbuf, "%d", &tpid) != 1)
00737               return false;
00738            if (caidbuf) {
00739               char *p = caidbuf;
00740               char *q;
00741               int NumCaIds = 0;
00742               char *strtok_next;
00743               while ((q = strtok_r(p, ",", &strtok_next)) != NULL) {
00744                     if (NumCaIds < MAXCAIDS) {
00745                        caids[NumCaIds++] = strtol(q, NULL, 16) & 0xFFFF;
00746                        if (NumCaIds == 1 && caids[0] <= CA_USER_MAX)
00747                           break;
00748                        }
00749                     else
00750                        esyslog("ERROR: too many CA ids!"); // no need to set ok to 'false'
00751                     p = NULL;
00752                     }
00753               caids[NumCaIds] = 0;
00754               }
00755            }
00756         strreplace(namebuf, '|', ':');
00757 
00758         char *p = strchr(namebuf, ';');
00759         if (p) {
00760            *p++ = 0;
00761            provider = strcpyrealloc(provider, p);
00762            }
00763         p = strrchr(namebuf, ','); // long name might contain a ',', so search for the rightmost one
00764         if (p) {
00765            *p++ = 0;
00766            shortName = strcpyrealloc(shortName, p);
00767            }
00768         name = strcpyrealloc(name, namebuf);
00769 
00770         free(parambuf);
00771         free(sourcebuf);
00772         free(vpidbuf);
00773         free(apidbuf);
00774         free(tpidbuf);
00775         free(caidbuf);
00776         free(namebuf);
00777         if (!GetChannelID().Valid()) {
00778            esyslog("ERROR: channel data results in invalid ID!");
00779            return false;
00780            }
00781         }
00782      else
00783         return false;
00784      }
00785   return ok;
00786 }
00787 
00788 bool cChannel::Save(FILE *f)
00789 {
00790   return fprintf(f, "%s", *ToText()) > 0;
00791 }
00792 
00793 // --- cChannelSorter --------------------------------------------------------
00794 
00795 class cChannelSorter : public cListObject {
00796 public:
00797   cChannel *channel;
00798   tChannelID channelID;
00799   cChannelSorter(cChannel *Channel) {
00800     channel = Channel;
00801     channelID = channel->GetChannelID();
00802     }
00803   virtual int Compare(const cListObject &ListObject) const {
00804     cChannelSorter *cs = (cChannelSorter *)&ListObject;
00805     return memcmp(&channelID, &cs->channelID, sizeof(channelID));
00806     }
00807   };
00808 
00809 // --- cChannels -------------------------------------------------------------
00810 
00811 cChannels Channels;
00812 
00813 cChannels::cChannels(void)
00814 {
00815   maxNumber = 0;
00816   maxChannelNameLength = 0;
00817   maxShortChannelNameLength = 0;
00818   modified = CHANNELSMOD_NONE;
00819 }
00820 
00821 void cChannels::DeleteDuplicateChannels(void)
00822 {
00823   cList<cChannelSorter> ChannelSorter;
00824   for (cChannel *channel = First(); channel; channel = Next(channel)) {
00825       if (!channel->GroupSep())
00826          ChannelSorter.Add(new cChannelSorter(channel));
00827       }
00828   ChannelSorter.Sort();
00829   cChannelSorter *cs = ChannelSorter.First();
00830   while (cs) {
00831         cChannelSorter *next = ChannelSorter.Next(cs);
00832         if (next && cs->channelID == next->channelID) {
00833            dsyslog("deleting duplicate channel %s", *next->channel->ToText());
00834            Del(next->channel);
00835            }
00836         cs = next;
00837         }
00838 }
00839 
00840 bool cChannels::Load(const char *FileName, bool AllowComments, bool MustExist)
00841 {
00842   if (cConfig<cChannel>::Load(FileName, AllowComments, MustExist)) {
00843      DeleteDuplicateChannels();
00844      ReNumber();
00845      return true;
00846      }
00847   return false;
00848 }
00849 
00850 void cChannels::HashChannel(cChannel *Channel)
00851 {
00852   channelsHashSid.Add(Channel, Channel->Sid());
00853 }
00854 
00855 void cChannels::UnhashChannel(cChannel *Channel)
00856 {
00857   channelsHashSid.Del(Channel, Channel->Sid());
00858 }
00859 
00860 int cChannels::GetNextGroup(int Idx)
00861 {
00862   cChannel *channel = Get(++Idx);
00863   while (channel && !(channel->GroupSep() && *channel->Name()))
00864         channel = Get(++Idx);
00865   return channel ? Idx : -1;
00866 }
00867 
00868 int cChannels::GetPrevGroup(int Idx)
00869 {
00870   cChannel *channel = Get(--Idx);
00871   while (channel && !(channel->GroupSep() && *channel->Name()))
00872         channel = Get(--Idx);
00873   return channel ? Idx : -1;
00874 }
00875 
00876 int cChannels::GetNextNormal(int Idx)
00877 {
00878   cChannel *channel = Get(++Idx);
00879   while (channel && channel->GroupSep())
00880         channel = Get(++Idx);
00881   return channel ? Idx : -1;
00882 }
00883 
00884 int cChannels::GetPrevNormal(int Idx)
00885 {
00886   cChannel *channel = Get(--Idx);
00887   while (channel && channel->GroupSep())
00888         channel = Get(--Idx);
00889   return channel ? Idx : -1;
00890 }
00891 
00892 void cChannels::ReNumber(void)
00893 {
00894   channelsHashSid.Clear();
00895   maxNumber = 0;
00896   int Number = 1;
00897   for (cChannel *channel = First(); channel; channel = Next(channel)) {
00898       if (channel->GroupSep()) {
00899          if (channel->Number() > Number)
00900             Number = channel->Number();
00901          }
00902       else {
00903          HashChannel(channel);
00904          maxNumber = Number;
00905          channel->SetNumber(Number++);
00906          }
00907       }
00908 }
00909 
00910 cChannel *cChannels::GetByNumber(int Number, int SkipGap)
00911 {
00912   cChannel *previous = NULL;
00913   for (cChannel *channel = First(); channel; channel = Next(channel)) {
00914       if (!channel->GroupSep()) {
00915          if (channel->Number() == Number)
00916             return channel;
00917          else if (SkipGap && channel->Number() > Number)
00918             return SkipGap > 0 ? channel : previous;
00919          previous = channel;
00920          }
00921       }
00922   return NULL;
00923 }
00924 
00925 cChannel *cChannels::GetByServiceID(int Source, int Transponder, unsigned short ServiceID)
00926 {
00927   cList<cHashObject> *list = channelsHashSid.GetList(ServiceID);
00928   if (list) {
00929      for (cHashObject *hobj = list->First(); hobj; hobj = list->Next(hobj)) {
00930          cChannel *channel = (cChannel *)hobj->Object();
00931          if (channel->Sid() == ServiceID && channel->Source() == Source && ISTRANSPONDER(channel->Transponder(), Transponder))
00932             return channel;
00933          }
00934      }
00935   return NULL;
00936 }
00937 
00938 cChannel *cChannels::GetByChannelID(tChannelID ChannelID, bool TryWithoutRid, bool TryWithoutPolarization)
00939 {
00940   int sid = ChannelID.Sid();
00941   cList<cHashObject> *list = channelsHashSid.GetList(sid);
00942   if (list) {
00943      for (cHashObject *hobj = list->First(); hobj; hobj = list->Next(hobj)) {
00944          cChannel *channel = (cChannel *)hobj->Object();
00945          if (channel->Sid() == sid && channel->GetChannelID() == ChannelID)
00946             return channel;
00947          }
00948      if (TryWithoutRid) {
00949         ChannelID.ClrRid();
00950         for (cHashObject *hobj = list->First(); hobj; hobj = list->Next(hobj)) {
00951             cChannel *channel = (cChannel *)hobj->Object();
00952             if (channel->Sid() == sid && channel->GetChannelID().ClrRid() == ChannelID)
00953                return channel;
00954             }
00955         }
00956      if (TryWithoutPolarization) {
00957         ChannelID.ClrPolarization();
00958         for (cHashObject *hobj = list->First(); hobj; hobj = list->Next(hobj)) {
00959             cChannel *channel = (cChannel *)hobj->Object();
00960             if (channel->Sid() == sid && channel->GetChannelID().ClrPolarization() == ChannelID)
00961                return channel;
00962             }
00963         }
00964      }
00965   return NULL;
00966 }
00967 cChannel *cChannels::GetByTransponderID(tChannelID ChannelID)
00968 {
00969   int source = ChannelID.Source();
00970   int nid = ChannelID.Nid();
00971   int tid = ChannelID.Tid();
00972   for (cChannel *channel = First(); channel; channel = Next(channel)) {
00973       if (channel->Tid() == tid && channel->Nid() == nid && channel->Source() == source)
00974          return channel;
00975       }
00976   return NULL;
00977 }
00978 
00979 bool cChannels::HasUniqueChannelID(cChannel *NewChannel, cChannel *OldChannel)
00980 {
00981   tChannelID NewChannelID = NewChannel->GetChannelID();
00982   for (cChannel *channel = First(); channel; channel = Next(channel)) {
00983       if (!channel->GroupSep() && channel != OldChannel && channel->GetChannelID() == NewChannelID)
00984          return false;
00985       }
00986   return true;
00987 }
00988 
00989 bool cChannels::SwitchTo(int Number)
00990 {
00991   cChannel *channel = GetByNumber(Number);
00992   return channel && cDevice::PrimaryDevice()->SwitchChannel(channel, true);
00993 }
00994 
00995 int cChannels::MaxChannelNameLength(void)
00996 {
00997   if (!maxChannelNameLength) {
00998      for (cChannel *channel = First(); channel; channel = Next(channel)) {
00999          if (!channel->GroupSep())
01000             maxChannelNameLength = max(Utf8StrLen(channel->Name()), maxChannelNameLength);
01001          }
01002      }
01003   return maxChannelNameLength;
01004 }
01005 
01006 int cChannels::MaxShortChannelNameLength(void)
01007 {
01008   if (!maxShortChannelNameLength) {
01009      for (cChannel *channel = First(); channel; channel = Next(channel)) {
01010          if (!channel->GroupSep())
01011             maxShortChannelNameLength = max(Utf8StrLen(channel->ShortName()), maxShortChannelNameLength);
01012          }
01013      }
01014   return maxShortChannelNameLength;
01015 }
01016 
01017 void cChannels::SetModified(bool ByUser)
01018 {
01019   modified = ByUser ? CHANNELSMOD_USER : !modified ? CHANNELSMOD_AUTO : modified;
01020   maxChannelNameLength = maxShortChannelNameLength = 0;
01021 }
01022 
01023 int cChannels::Modified(void)
01024 {
01025   int Result = modified;
01026   modified = CHANNELSMOD_NONE;
01027   return Result;
01028 }
01029 
01030 cChannel *cChannels::NewChannel(const cChannel *Transponder, const char *Name, const char *ShortName, const char *Provider, int Nid, int Tid, int Sid, int Rid)
01031 {
01032   if (Transponder) {
01033      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);
01034      cChannel *NewChannel = new cChannel;
01035      NewChannel->CopyTransponderData(Transponder);
01036      NewChannel->SetId(Nid, Tid, Sid, Rid);
01037      NewChannel->SetName(Name, ShortName, Provider);
01038      Add(NewChannel);
01039      ReNumber();
01040      return NewChannel;
01041      }
01042   return NULL;
01043 }
01044 
01045 cString ChannelString(const cChannel *Channel, int Number)
01046 {
01047   char buffer[256];
01048   if (Channel) {
01049      if (Channel->GroupSep())
01050         snprintf(buffer, sizeof(buffer), "%s", Channel->Name());
01051      else
01052         snprintf(buffer, sizeof(buffer), "%d%s  %s", Channel->Number(), Number ? "-" : "", Channel->Name());
01053      }
01054   else if (Number)
01055      snprintf(buffer, sizeof(buffer), "%d-", Number);
01056   else
01057      snprintf(buffer, sizeof(buffer), "%s", tr("*** Invalid Channel ***"));
01058   return buffer;
01059 }