/* * tools.c: Tools for handling configuration files and strings * * See the README file for copyright information and how to reach the author. * */ #include #include #include #include "tools.h" // // HTML conversion code taken from RSS Reader plugin for VDR // http://www.saunalahti.fi/~rahrenbe/vdr/rssreader/ // by Rolf Ahrenberg // // --- Static ----------------------------------------------------------- #define ELEMENTS(x) (sizeof(x) / sizeof(x[0])) struct conv_table { const char *from; const char *to; }; static struct conv_table pre_conv_table[] = { // 'to' field must be smaller than 'from' {"
", "\n"} }; // Conversion page: http://www.ltg.ed.ac.uk/~richard/utf-8.cgi static struct conv_table post_conv_table[] = { // 'to' field must be smaller than 'from' {""", "\x22"}, {""", "\x22"}, {"&", "\x26"}, {"&", "\x26"}, {"&", "\x26"}, {"'", "\x27"}, {"(", "\x28"}, {")", "\x29"}, {":", "\x3a"}, {"<", "\x3c"}, {"<", "\x3c"}, {">", "\x3e"}, {">", "\x3e"}, {"[", "\x5b"}, {"]", "\x5d"}, {" ", "\xc2\xa0"}, {" ", "\xc2\xa0"}, {"°", "\xc2\xb0"}, {"°", "\xc2\xb0"}, {"´", "\xc2\xb4"}, {"´", "\xc2\xb4"}, {"Ä", "\xc3\x84"}, {"Ä", "\xc3\x84"}, {"Å", "\xc3\x85"}, {"Å", "\xc3\x85"}, {"Ö", "\xc3\x96"}, {"Ö", "\xc3\x96"}, {"Ü", "\xc3\x9c"}, {"Ü", "\xc3\x9c"}, {"ß", "\xc3\x9f"}, {"ß", "\xc3\x9f"}, {"â", "\xc3\xa2"}, {"â", "\xc3\xa2"}, {"ä", "\xc3\xa4"}, {"ä", "\xc3\xa4"}, {"å", "\xc3\xa5"}, {"å", "\xc3\xa5"}, {"ç", "\xc3\xa7"}, {"ç", "\xc3\xa7"}, {"é", "\xc3\xa9"}, {"é", "\xc3\xa9"}, {"ê", "\xc3\xaa"}, {"ê", "\xc3\xaa"}, {"ö", "\xc3\xb6"}, {"ö", "\xc3\xb6"}, {"ü", "\xc3\xbc"}, {"ü", "\xc3\xbc"}, {"–", "\xe2\x80\x93"}, {"–", "\xe2\x80\x93"}, {"—", "\xe2\x80\x94"}, {"—", "\xe2\x80\x94"}, {"‘", "\xe2\x80\x98"}, {"‘", "\xe2\x80\x98"}, {"’", "\xe2\x80\x99"}, {"’", "\xe2\x80\x99"}, {"‚", "\xe2\x80\x9a"}, {"‚", "\xe2\x80\x9a"}, {"“", "\xe2\x80\x9c"}, {"“", "\xe2\x80\x9c"}, {"”", "\xe2\x80\x9d"}, {"”", "\xe2\x80\x9d"}, {"„", "\xe2\x80\x9e"}, {"„", "\xe2\x80\x9e"}, {"′", "\xe2\x80\xb3"}, {"″", "\xe2\x80\xb3"}, {"€", "\xe2\x82\xac"}, {"€", "\xe2\x82\xac"}, {"\n\n", "\n"}, // let's also strip multiple linefeeds }; static char *htmlcharconv(char *str, struct conv_table *conv, unsigned int elem) { if (str && conv) { for (unsigned int i = 0; i < elem; ++i) { char *ptr = strstr(str, conv[i].from); while (ptr) { long of = ptr - str; size_t l = strlen(str); size_t l1 = strlen(conv[i].from); size_t l2 = strlen(conv[i].to); if (l2 > l1) { error("htmlcharconv(): cannot reallocate string"); return str; } if (l2 != l1) memmove(str + of + l2, str + of + l1, l - of - l1 + 1); strncpy(str + of, conv[i].to, l2); ptr = strstr(str, conv[i].from); } } return str; } return NULL; } // --- General functions ------------------------------------------------ char *striphtml(char *str) { if (str) { char *c, t = 0, *r; str = htmlcharconv(str, pre_conv_table, ELEMENTS(pre_conv_table)); c = str; r = str; while (*str != '\0') { if (*str == '<') t++; else if (*str == '>') t--; else if (t < 1) *(c++) = *str; str++; } *c = '\0'; return htmlcharconv(r, post_conv_table, ELEMENTS(post_conv_table)); } return NULL; } // --- cAddEventThread -------------------------------------------------- class cAddEventListItem : public cListObject { protected: cEvent *event; tChannelID channelID; public: cAddEventListItem(cEvent *Event, tChannelID ChannelID) { event = Event; channelID = ChannelID; } tChannelID GetChannelID() { return channelID; } cEvent *GetEvent() { return event; } ~cAddEventListItem() { } }; class cAddEventThread : public cThread { private: cTimeMs LastHandleEvent; cList *list; enum { INSERT_TIMEOUT_IN_MS = 10000 }; protected: virtual void Action(void); public: cAddEventThread(void); ~cAddEventThread(void); void AddEvent(cEvent *Event, tChannelID ChannelID); }; cAddEventThread::cAddEventThread(void) :cThread("cAddEventThread"), LastHandleEvent() { list = new cList; } cAddEventThread::~cAddEventThread(void) { LOCK_THREAD; list->cList::Clear(); Cancel(3); } void cAddEventThread::Action(void) { SetPriority(19); while (Running() && !LastHandleEvent.TimedOut()) { cAddEventListItem *e = NULL; cSchedulesLock SchedulesLock(true, 10); cSchedules *schedules = (cSchedules *)cSchedules::Schedules(SchedulesLock); Lock(); while (schedules && (e = list->First()) != NULL) { cSchedule *schedule = (cSchedule *)schedules->GetSchedule(Channels.GetByChannelID(e->GetChannelID()), true); schedule->AddEvent(e->GetEvent()); EpgHandlers.SortSchedule(schedule); EpgHandlers.DropOutdated(schedule, e->GetEvent()->StartTime(), e->GetEvent()->EndTime(), e->GetEvent()->TableID(), e->GetEvent()->Version()); list->Del(e); } Unlock(); cCondWait::SleepMs(10); } } void cAddEventThread::AddEvent(cEvent *Event, tChannelID ChannelID) { LOCK_THREAD; list->Add(new cAddEventListItem(Event, ChannelID)); LastHandleEvent.Set(INSERT_TIMEOUT_IN_MS); } static cAddEventThread AddEventThread; // --- Add event to schedule -------------------------------------------- void AddEvent(cEvent *Event, tChannelID ChannelID) { AddEventThread.AddEvent(Event, ChannelID); if (!AddEventThread.Active()) AddEventThread.Start(); } // --- Listitem --------------------------------------------------------- cListItem::cListItem() { enabled = false; string = NULL; numchannels = 0; channels_num = NULL; channels_id = NULL; } cListItem::~cListItem(void) { Free(); } void cListItem::Free(void) { FREE(channels_num); FREE(channels_id); FREE(string); numchannels = 0; enabled = false; } tChannelID *cListItem::GetChannelID(int index) { if (channels_id && index >= 0 && index < numchannels) return &channels_id[index]; else return NULL; } int cListItem::GetChannelNum(int index) { if (channels_num && index >= 0 && index < numchannels) return channels_num[index]; else return 0; } bool cListItem::IsActive(tChannelID ChannelID) { bool active = false; if (numchannels > 0) { int i = 0; int channel_number = Channels.GetByChannelID(ChannelID)->Number(); while (i < numchannels) { if ((channel_number == GetChannelNum(i)) || (GetChannelID(i) && (ChannelID == *GetChannelID(i)))) { active = true; break; } ++i; } } else active = true; return active; } int cListItem::LoadChannelsFromString(const char *string) { numchannels = 0; bool numbers = false; if (string != NULL) { if (atoi(string)) numbers = true; char *tmpstring = strdup(string); char *c = strtok(tmpstring, ","); while (c) { ++numchannels; char *d = 0; if (numbers && (d = strchr(c, '-')))// only true if numbers are used numchannels = numchannels + atoi(d + 1) - atoi(c); c = strtok(NULL, ","); } free(tmpstring); } if (numchannels > 0) { char *tmpstring = strdup(string); // Use channel numbers if (numbers) channels_num = (int *)malloc(sizeof(int)*numchannels); else// use channel IDs channels_id = (tChannelID *)malloc(sizeof(tChannelID)*numchannels); int i = 0; char *c = strtok(tmpstring, ","); while (i < numchannels) { // Use channel numbers if (numbers) { channels_num[i] = atoi(c); if (char *d = strchr(c, '-')) { int count = atoi(d + 1) - channels_num[i] + 1; int j = 1; while (j < count) { channels_num[i + j] = channels_num[i] + j; ++j; } i = i + count; } } else // use channel IDs channels_id[i] = tChannelID::FromString(c); c = strtok(NULL, ","); ++i; } free(tmpstring); } return numchannels; } void cListItem::SetFromString(char *s, bool Enabled) { enabled = Enabled; if (s[0] == '!') string = strdup(s + 1); else string = strdup(s); // disable comments and inactive lines if (s[0] == '!' || s[0] == '#') enabled = false; } void cListItem::ToggleEnabled(void) { enabled = !enabled; } void cListItem::PrintConfigLineToFile(FILE *f) { if (f) fprintf(f, "%s%s\n", (!enabled && string && *string != '#') ? "!" : "", string); }