File: | epgsearchtools.c |
Location: | line 1018, column 20 |
Description: | Potential memory leak |
1 | /* -*- c++ -*- | |||
2 | Copyright (C) 2004-2013 Christian Wieninger | |||
3 | ||||
4 | This program is free software; you can redistribute it and/or | |||
5 | modify it under the terms of the GNU General Public License | |||
6 | as published by the Free Software Foundation; either version 2 | |||
7 | of the License, or (at your option) any later version. | |||
8 | ||||
9 | This program is distributed in the hope that it will be useful, | |||
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
12 | GNU General Public License for more details. | |||
13 | ||||
14 | You should have received a copy of the GNU General Public License | |||
15 | along with this program; if not, write to the Free Software | |||
16 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |||
17 | Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html | |||
18 | ||||
19 | The author can be reached at cwieninger@gmx.de | |||
20 | ||||
21 | The project's page is at http://winni.vdr-developer.org/epgsearch | |||
22 | */ | |||
23 | ||||
24 | #include <algorithm> | |||
25 | #include <string> | |||
26 | #include <fstream> | |||
27 | #include <iostream> | |||
28 | #include <ctype.h> | |||
29 | #include <netdb.h> | |||
30 | #include <arpa/inet.h> | |||
31 | #ifdef __FreeBSD__ | |||
32 | #include <netinet/in.h> | |||
33 | #endif | |||
34 | #include "uservars.h" | |||
35 | #include "epgsearchtools.h" | |||
36 | #include "epgsearchext.h" | |||
37 | #include "epgsearchcats.h" | |||
38 | #include "epgsearchcfg.h" | |||
39 | #include "svdrpclient.h" | |||
40 | #include "distance.h" | |||
41 | #include "md5.h" | |||
42 | #include "afuzzy.h" | |||
43 | #include "timerstatus.h" | |||
44 | #include <langinfo.h> | |||
45 | ||||
46 | #ifdef HAVE_PCREPOSIX | |||
47 | #include <pcreposix.h> | |||
48 | #elif defined(HAVE_LIBTRE) | |||
49 | #include <tre/regex.h> | |||
50 | #else | |||
51 | #include <regex.h> | |||
52 | #endif | |||
53 | ||||
54 | const char AllowedChars[] = trNOOP("$ abcdefghijklmnopqrstuvwxyz0123456789-.,#~\\^$[]|()*+?{}/:%@&_")("$ abcdefghijklmnopqrstuvwxyz0123456789-.,#~\\^$[]|()*+?{}/:%@&_" ); | |||
55 | extern bool isUTF8; | |||
56 | ||||
57 | int CompareEventTime(const void *p1, const void *p2) | |||
58 | { | |||
59 | time_t time1 = (*(cSearchResult **)p1)->event->StartTime(); | |||
60 | time_t time2 = (*(cSearchResult **)p2)->event->StartTime(); | |||
61 | if (time1 == time2) | |||
62 | return (int)(ChannelNrFromEvent((*(cSearchResult **)p1)->event) - | |||
63 | ChannelNrFromEvent((*(cSearchResult **)p2)->event)); | |||
64 | else | |||
65 | return (int)(time1 - time2); | |||
66 | } | |||
67 | ||||
68 | int CompareEventChannel(const void *p1, const void *p2) | |||
69 | { | |||
70 | int ch1 = ChannelNrFromEvent((*(cSearchResult **)p1)->event); | |||
71 | int ch2 = ChannelNrFromEvent((*(cSearchResult **)p2)->event); | |||
72 | if (ch1 == ch2) | |||
73 | return (int)((*(cSearchResult **)p1)->event->StartTime() - | |||
74 | (*(cSearchResult **)p2)->event->StartTime()); | |||
75 | else | |||
76 | return ch1 - ch2; | |||
77 | } | |||
78 | ||||
79 | int CompareSearchExtPrioDescTerm(const void *p1, const void *p2) | |||
80 | { | |||
81 | int prio1 = (*(cSearchExt **)p1)->Priority; | |||
82 | int prio2 = (*(cSearchExt **)p2)->Priority; | |||
83 | if (prio2 != prio1) | |||
84 | return prio2 - prio1; | |||
85 | else | |||
86 | return strcmp((*(cSearchExt **)p1)->search, (*(cSearchExt **)p2)->search); | |||
87 | } | |||
88 | ||||
89 | cString IndentMenuItem(const char* szString, int indentions) | |||
90 | { | |||
91 | char* szIndented = NULL__null; | |||
92 | msprintf(&szIndented, "%*s", strlen(szString)+indentions*2, szString); | |||
93 | cString szIndentedStr(szIndented, true /*take pointer*/); | |||
94 | return szIndentedStr; | |||
95 | } | |||
96 | ||||
97 | bool MatchesSearchMode(const char* szTest, const char* searchText, int mode, const char* delim, int tolerance) | |||
98 | { | |||
99 | if (szTest && *szTest) | |||
100 | { | |||
101 | if (mode == 0) // substring | |||
102 | return (strstr(szTest, searchText) != NULL__null); | |||
103 | else if (mode == 1 || mode == 2) // AND or OR | |||
104 | { | |||
105 | bool bTesting = false; | |||
106 | char *pstrSearchToken, *pptr; | |||
107 | bool bFirst=true; | |||
108 | char *pstrSearch=strdup(searchText); | |||
109 | pstrSearchToken=strtok_r(pstrSearch, delim, &pptr); | |||
110 | while(pstrSearchToken) | |||
111 | { | |||
112 | if(szTest && strstr(szTest, skipspace(pstrSearchToken))) | |||
113 | { | |||
114 | if(mode==1) | |||
115 | { // means AND | |||
116 | if(bFirst) | |||
117 | { | |||
118 | bTesting=true; | |||
119 | bFirst=false; | |||
120 | } | |||
121 | else | |||
122 | bTesting&=true; | |||
123 | } | |||
124 | else | |||
125 | bTesting|=true; | |||
126 | } | |||
127 | else | |||
128 | {// not found!! | |||
129 | if(mode==1) | |||
130 | { // means AND | |||
131 | bTesting=false; | |||
132 | bFirst=false; | |||
133 | } | |||
134 | } | |||
135 | pstrSearchToken=strtok_r(NULL__null, delim, &pptr); | |||
136 | } | |||
137 | free(pstrSearch); | |||
138 | return bTesting; | |||
139 | } | |||
140 | else if (mode == 3) // match exactly | |||
141 | { | |||
142 | if (strcmp(szTest, searchText) == 0) | |||
143 | return true; | |||
144 | else | |||
145 | return false; | |||
146 | } | |||
147 | else if (mode == 4) // regexp | |||
148 | { | |||
149 | regex_t re; | |||
150 | ||||
151 | if ( 0 == regcomp(&re, searchText, REG_EXTENDED1 | REG_NOSUB(((1 << 1) << 1) << 1)) ) | |||
152 | { | |||
153 | int status = regexec( &re, szTest, 0, NULL__null, 0); | |||
154 | regfree(&re); | |||
155 | return (status == 0); | |||
156 | } | |||
157 | return false; | |||
158 | } | |||
159 | else if (mode == 5) // fuzzy | |||
160 | { | |||
161 | AFUZZY af = { NULL__null, NULL__null, NULL__null, NULL__null, NULL__null, NULL__null, { 0 }, { 0 }, 0, 0, 0, 0, 0, 0 }; | |||
162 | string query = searchText?searchText:""; | |||
163 | if (query.size() > 32) query = query.substr(0, 32); | |||
164 | afuzzy_init(query.c_str(), tolerance, 0, &af); | |||
165 | /* Checking substring */ | |||
166 | int res = afuzzy_checkSUB(szTest, &af); | |||
167 | afuzzy_free(&af); | |||
168 | return (res > 0); | |||
169 | } | |||
170 | else if (mode >= 10 && mode <= 15) | |||
171 | { | |||
172 | int testvalue = atoi(szTest); | |||
173 | int value = atoi(searchText); | |||
174 | if (value == 0) return true; | |||
175 | ||||
176 | if (mode == 10) // less | |||
177 | return testvalue < value; | |||
178 | else if (mode == 11) // less or equal | |||
179 | return testvalue <= value; | |||
180 | else if (mode == 12) // greater | |||
181 | return testvalue > value; | |||
182 | else if (mode == 13) // greater or equal | |||
183 | return testvalue >= value; | |||
184 | else if (mode == 14) // equal | |||
185 | return testvalue == value; | |||
186 | else if (mode == 15) // not equal | |||
187 | return testvalue != value; | |||
188 | } | |||
189 | } | |||
190 | return false; | |||
191 | } | |||
192 | ||||
193 | void ToLower(char* szText) | |||
194 | { | |||
195 | if (!szText) | |||
196 | return; | |||
197 | ||||
198 | if (!isUTF8) | |||
199 | { | |||
200 | for (int loop = 0; szText[loop] !=0; loop++) | |||
201 | szText[loop] = tolower(szText[loop]); | |||
202 | return; | |||
203 | } | |||
204 | else | |||
205 | { | |||
206 | int length = strlen(szText)+1; | |||
207 | uint* valueUtf8 = new uint[length]; | |||
208 | int lengthUtf8 = Utf8ToArray(szText, valueUtf8, length); | |||
209 | for(int i=0; i<lengthUtf8; i++) | |||
210 | valueUtf8[i] = Utf8to(lower, valueUtf8[i])(cCharSetConv::SystemCharacterTable() ? tolower(valueUtf8[i]) : towlower(valueUtf8[i])); | |||
211 | Utf8FromArray(valueUtf8, szText, length); | |||
212 | delete [] valueUtf8; | |||
213 | } | |||
214 | } | |||
215 | ||||
216 | char* GetExtEPGValue(const cEvent* e, cSearchExtCat* SearchExtCat) | |||
217 | { | |||
218 | if (!e || !SearchExtCat) | |||
219 | return NULL__null; | |||
220 | return GetExtEPGValue(e->Description(), SearchExtCat->name, SearchExtCat->format); | |||
221 | } | |||
222 | ||||
223 | char* GetExtEPGValue(const char* description, const char* catname, const char *format) | |||
224 | { | |||
225 | if (isempty(description)) | |||
226 | return NULL__null; | |||
227 | char* tmp1 = NULL__null; | |||
228 | char* tmp2 = NULL__null; | |||
229 | ||||
230 | // search the category, must be the first line or at the beginning of a line | |||
231 | if (msprintf(&tmp1, "\n%s: ", catname)==-1 || | |||
232 | msprintf(&tmp2, "%s: ", catname)==-1) | |||
233 | return NULL__null; | |||
234 | char* descr = strdup(description); | |||
235 | char* cat = NULL__null; | |||
236 | int valueOffset = 0; | |||
237 | if ((cat = strstr(descr, tmp1)) != NULL__null) | |||
238 | { | |||
239 | cat++; // skip linefeed | |||
240 | valueOffset = strlen(tmp1); | |||
241 | } | |||
242 | else if (strstr(descr, tmp2) == descr) // in first line | |||
243 | { | |||
244 | cat = descr; | |||
245 | valueOffset = strlen(tmp2)+1; | |||
246 | } | |||
247 | else | |||
248 | { | |||
249 | free(descr); | |||
250 | free(tmp1); | |||
251 | free(tmp2); | |||
252 | return NULL__null; | |||
253 | } | |||
254 | ||||
255 | // search the value to appear before the next line feed or end | |||
256 | char* end = strchr(cat, '\n'); | |||
257 | int endpos = strlen(cat); | |||
258 | if (end) | |||
259 | endpos = end - cat; | |||
260 | cat[endpos] = 0; | |||
261 | ||||
262 | char* value = NULL__null; | |||
263 | msprintf(&value, "%s", cat + valueOffset - 1); | |||
264 | if (format) | |||
265 | { | |||
266 | int ivalue; | |||
267 | if (sscanf(value,"%8i",&ivalue)==1) | |||
268 | { | |||
269 | free(value); | |||
270 | value = NULL__null; | |||
271 | msprintf(&value, format, ivalue); | |||
272 | } | |||
273 | } | |||
274 | free(descr); | |||
275 | free(tmp1); | |||
276 | free(tmp2); | |||
277 | ||||
278 | return value; | |||
279 | } | |||
280 | ||||
281 | char* GetAuxValue(const char* aux, const char* name) | |||
282 | { | |||
283 | if (isempty(aux)) | |||
284 | return NULL__null; | |||
285 | ||||
286 | char* descr = strdup(aux); | |||
287 | char* beginaux = strstr(descr, "<epgsearch>"); | |||
288 | char* endaux = strstr(descr, "</epgsearch>"); | |||
289 | if (!beginaux || !endaux) { | |||
290 | free(descr); | |||
291 | return NULL__null; | |||
292 | } | |||
293 | ||||
294 | beginaux += 11; // strlen("<epgsearch>"); | |||
295 | endaux[0] = 0; | |||
296 | memmove(descr, beginaux, endaux - beginaux + 1); | |||
297 | ||||
298 | if (strcmp(name, "epgsearch") == 0) | |||
299 | return descr; // full aux | |||
300 | ||||
301 | int namelen = strlen(name); | |||
302 | char catname[100] = ""; | |||
303 | catname[0] = '<'; | |||
304 | memcpy(catname + 1, name, namelen); | |||
305 | catname[1 + namelen] = '>'; | |||
306 | catname[2 + namelen] = 0; | |||
307 | ||||
308 | char* cat = strcasestr(descr, catname); | |||
309 | if (!cat) { | |||
310 | free(descr); | |||
311 | return NULL__null; | |||
312 | } | |||
313 | ||||
314 | cat += namelen + 2; | |||
315 | char* end = strstr(cat, "</"); | |||
316 | if (!end) { | |||
317 | free(descr); | |||
318 | return NULL__null; | |||
319 | } | |||
320 | end[0] = 0; | |||
321 | ||||
322 | int catlen = end - cat + 1; | |||
323 | char* value = (char *) malloc(catlen); | |||
324 | memcpy(value, cat, catlen); | |||
325 | ||||
326 | free(descr); | |||
327 | return value; | |||
328 | } | |||
329 | ||||
330 | char* GetAuxValue(const cRecording *recording, const char* name) | |||
331 | { | |||
332 | if (!recording || !recording->Info()) return NULL__null; | |||
333 | return GetAuxValue(recording->Info()->Aux(), name); | |||
334 | } | |||
335 | ||||
336 | char* GetAuxValue(const cTimer *timer, const char* name) | |||
337 | { | |||
338 | if (!timer || !timer->Aux()) return NULL__null; | |||
339 | return GetAuxValue(timer->Aux(), name); | |||
340 | } | |||
341 | ||||
342 | string UpdateAuxValue(string aux, string section, long num) | |||
343 | { | |||
344 | return UpdateAuxValue(aux, section, NumToString(num)); | |||
345 | } | |||
346 | ||||
347 | string UpdateAuxValue(string aux, string section, string value) | |||
348 | { | |||
349 | string secStart = "<" + section + ">"; | |||
350 | string secEnd = "</" + section + ">"; | |||
351 | int valueStartPos = aux.find(secStart); | |||
352 | int valueEndPos = aux.find(secEnd); | |||
353 | if (valueStartPos >= 0 && valueEndPos >= 0) | |||
354 | aux.replace(valueStartPos + secStart.size(), valueEndPos - valueStartPos - secStart.size(), value); | |||
355 | else | |||
356 | aux += secStart + value + secEnd; | |||
357 | return aux; | |||
358 | } | |||
359 | ||||
360 | // replace s1 with s2 in s ignoring the case of s1 | |||
361 | char *strreplacei(char *s, const char *s1, const char *s2) | |||
362 | { | |||
363 | char *p = strcasestr(s, s1); | |||
364 | if (p) { | |||
365 | int of = p - s; | |||
366 | int l = strlen(s); | |||
367 | int l1 = strlen(s1); | |||
368 | int l2 = 0; | |||
369 | if (s2) | |||
370 | l2 = strlen(s2); | |||
371 | if (l2 > l1) | |||
372 | s = (char *)realloc(s, l + l2 - l1 + 1); | |||
373 | if (l2 != l1) | |||
374 | memmove(s + of + l2, s + of + l1, l - of - l1 + 1); | |||
375 | memcpy(s + of, s2, l2); | |||
376 | } | |||
377 | return s; | |||
378 | } | |||
379 | ||||
380 | std::string strreplace( | |||
381 | std::string& result, | |||
382 | const std::string& replaceWhat, | |||
383 | const std::string& replaceWithWhat) | |||
384 | { | |||
385 | while(1) | |||
386 | { | |||
387 | const int pos = result.find(replaceWhat); | |||
388 | if (pos==-1) break; | |||
389 | result.replace(pos,replaceWhat.size(),replaceWithWhat); | |||
390 | } | |||
391 | return result; | |||
392 | } | |||
393 | ||||
394 | ||||
395 | void sleepMSec(long ms) | |||
396 | { | |||
397 | cCondWait::SleepMs(ms); | |||
398 | } | |||
399 | ||||
400 | void sleepSec(long s) | |||
401 | { | |||
402 | sleepMSec(s * 1000); | |||
403 | } | |||
404 | ||||
405 | bool SendViaSVDRP(cString SVDRPcmd) | |||
406 | { | |||
407 | bool bSuccess = true; | |||
408 | cString cmdbuf; | |||
409 | if (EPGSearchConfig.useExternalSVDRP) | |||
410 | { | |||
411 | cmdbuf = cString::sprintf("%s -p %d \"%s\"", | |||
412 | cSVDRPClient::SVDRPSendCmd, | |||
413 | EPGSearchConfig.SVDRPPort, | |||
414 | *SVDRPcmd); | |||
415 | ||||
416 | FILE *p = popen(cmdbuf, "r"); | |||
417 | if (p) | |||
418 | pclose(p); | |||
419 | else | |||
420 | { | |||
421 | LogFile.eSysLog("can't open pipe for command '%s'", *cmdbuf); | |||
422 | bSuccess = false; | |||
423 | } | |||
424 | } | |||
425 | else | |||
426 | { | |||
427 | cmdbuf = SVDRPcmd; | |||
428 | cSVDRPClient client; | |||
429 | if (!client.SendCmd(*cmdbuf)) | |||
430 | { | |||
431 | LogFile.eSysLog("command '%s' failed", *cmdbuf); | |||
432 | bSuccess = false; | |||
433 | } | |||
434 | } | |||
435 | ||||
436 | return bSuccess; | |||
437 | } | |||
438 | ||||
439 | int SendMsg(cString Message, bool confirm, int seconds, eMessageType messageType) | |||
440 | { | |||
441 | int Keys = Skins.QueueMessage(messageType, Message, seconds, confirm?seconds+2:0); | |||
442 | return Keys; | |||
443 | } | |||
444 | ||||
445 | ||||
446 | bool InEditMode(const char* ItemText, const char* ItemName, const char* ItemValue) | |||
447 | { | |||
448 | bool bEditMode = true; | |||
449 | // ugly solution to detect, if in edit mode | |||
450 | char* value = strdup(ItemText); | |||
451 | strreplace(value, ItemName, ""); | |||
452 | strreplace(value, ":\t", ""); | |||
453 | // for bigpatch | |||
454 | strreplace(value, "\t", ""); | |||
455 | if (strlen(value) == strlen(ItemValue)) | |||
456 | bEditMode = false; | |||
457 | free(value); | |||
458 | return bEditMode; | |||
459 | } | |||
460 | ||||
461 | // checks if the timer was triggered from a search timer and return a pointer to the search | |||
462 | cSearchExt* TriggeredFromSearchTimer(const cTimer* timer) | |||
463 | { | |||
464 | char* searchID = GetAuxValue(timer, "s-id"); | |||
465 | if (!searchID) | |||
466 | return NULL__null; | |||
467 | ||||
468 | cSearchExt* search = SearchExts.GetSearchFromID(atoi(searchID)); | |||
469 | free(searchID); | |||
470 | return search; | |||
471 | } | |||
472 | ||||
473 | int TriggeredFromSearchTimerID(const cTimer* timer) | |||
474 | { | |||
475 | cSearchExt* trigger = TriggeredFromSearchTimer(timer); | |||
476 | if (trigger) | |||
477 | return trigger->ID; | |||
478 | else | |||
479 | return -1; | |||
480 | } | |||
481 | ||||
482 | double FuzzyMatch(const char* s1, const char* s2, int maxLength) | |||
483 | { | |||
484 | Distance D; | |||
485 | int dist = D.LD (s1, s2, maxLength); | |||
486 | double fMaxLength = max(strlen(s1), strlen(s2)); | |||
487 | return (fMaxLength - dist)/fMaxLength; | |||
488 | } | |||
489 | ||||
490 | bool DescriptionMatches(const char* eDescr, const char* rDescr, int matchLimit) | |||
491 | { | |||
492 | if (eDescr == NULL__null && rDescr == NULL__null) return true; | |||
493 | if (eDescr == NULL__null && rDescr != NULL__null) return false; | |||
494 | if (eDescr != NULL__null && rDescr == NULL__null) return false; | |||
495 | int l_eDescr = strlen(eDescr); | |||
496 | int l_rDescr = strlen(rDescr); | |||
497 | if (l_eDescr == l_rDescr && strcmp(eDescr, rDescr) == 0) return true; | |||
498 | ||||
499 | // partial match: | |||
500 | // first check the length, should only be different at match limit | |||
501 | int minLength = min(l_eDescr, l_rDescr); | |||
502 | int maxLength = max(l_eDescr, l_rDescr); | |||
503 | if (100*double(minLength)/double(maxLength) < matchLimit) | |||
504 | return false; | |||
505 | ||||
506 | // last try with Levenshtein Distance, only compare the first 1000 chars | |||
507 | double fMatch = FuzzyMatch(eDescr, rDescr, 1000); | |||
508 | double tmp_matchlimit = matchLimit/100.0; | |||
509 | if(maxLength - minLength < 5 && matchLimit < 95) | |||
510 | { | |||
511 | tmp_matchlimit = 0.95; | |||
512 | LogFile.Log(2,"difference between both descriptions is < 5 setting matchlimit to: %.2f %%", tmp_matchlimit*100); | |||
513 | } | |||
514 | if (fMatch > tmp_matchlimit) | |||
515 | { | |||
516 | LogFile.Log(2,"match is: %.2f %%", fMatch*100); | |||
517 | return true; | |||
518 | } | |||
519 | return false; | |||
520 | } | |||
521 | ||||
522 | const cEvent* GetEvent(cTimer* timer) | |||
523 | { | |||
524 | const cEvent* event = NULL__null; | |||
525 | const cChannel *channel = timer->Channel(); | |||
526 | time_t Time = timer->StartTime() + (timer->StopTime() - timer->StartTime()) / 2; | |||
527 | for (int seconds = 0; seconds <= 3; seconds++) | |||
528 | { | |||
529 | { | |||
530 | cSchedulesLock SchedulesLock; | |||
531 | const cSchedules *Schedules = cSchedules::Schedules(SchedulesLock); | |||
532 | if (Schedules) { | |||
533 | const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID()); | |||
534 | if (Schedule) { | |||
535 | event = Schedule->GetEventAround(Time); | |||
536 | if (event) return event; | |||
537 | } | |||
538 | } | |||
539 | } | |||
540 | if (seconds == 0) | |||
541 | LogFile.Log(2,"waiting for EPG info..."); | |||
542 | sleepSec(1); | |||
543 | } | |||
544 | LogFile.Log(1,"no EPG info available"); | |||
545 | return NULL__null; | |||
546 | } | |||
547 | ||||
548 | // this extracts the real description from a given epg entry cutting all that looks like a category line | |||
549 | // we assume that a category has a name not longer than MAXCATNAMELENGTH and a value not longer than | |||
550 | // MAXCATVALUELENGTH (so in most cases e.g. the 'cast' category will stay part of the description). name and | |||
551 | // value are separated with ': ' | |||
552 | #define MAXCATNAMELENGTH40 40 | |||
553 | #define MAXCATVALUELENGTH60 60 | |||
554 | ||||
555 | char* GetRawDescription(const char* descr) | |||
556 | { | |||
557 | if (!descr || !*descr) return NULL__null; | |||
558 | ||||
559 | char* rawDescr = (char*) calloc(strlen(descr)+1, sizeof(char)); | |||
560 | ||||
561 | const char* tmp = descr; | |||
562 | while(tmp) | |||
563 | { | |||
564 | // extract a single line | |||
565 | const char* lf = strchr(tmp, '\n'); | |||
566 | char* line = NULL__null; | |||
567 | if (lf) | |||
568 | line = strndup(tmp, lf-tmp); | |||
569 | else | |||
570 | line = strdup(tmp); | |||
571 | // if (lf) *lf = 0; | |||
572 | // check for category | |||
573 | char* delim = strstr(line, ": "); | |||
574 | if (!delim || | |||
575 | (delim && (delim - line > MAXCATNAMELENGTH40 || strlen(line) - (delim - line) + 2 > MAXCATVALUELENGTH60))) | |||
576 | if (*line) | |||
577 | strcat(rawDescr, line); | |||
578 | ||||
579 | if (lf) tmp += strlen(line)+1; else tmp = NULL__null; | |||
580 | free(line); | |||
581 | } | |||
582 | return rawDescr; | |||
583 | } | |||
584 | ||||
585 | void PrepareTimerFile(const cEvent* event, cTimer* timer) | |||
586 | { | |||
587 | if (!event) return; | |||
588 | if (EPGSearchConfig.addSubtitleToTimer == addSubtitleNever && strlen(EPGSearchConfig.defrecdir) == 0) // nothing to do | |||
589 | return; | |||
590 | if (!isempty(event->ShortText())) // add subtitle if present | |||
591 | { | |||
592 | bool addSubtitle = (EPGSearchConfig.addSubtitleToTimer != addSubtitleNever); | |||
593 | if (EPGSearchConfig.addSubtitleToTimer == addSubtitleSmart) | |||
594 | if (event->Duration() > 80*60) | |||
595 | addSubtitle = false; | |||
596 | ||||
597 | if (addSubtitle) | |||
598 | { | |||
599 | char tmp[MaxFileName255] = ""; | |||
600 | snprintf(tmp, MaxFileName255, "%s~%s", event->Title(), event->ShortText()); | |||
601 | timer->SetFile(tmp); | |||
602 | } | |||
603 | } | |||
604 | if (strlen(EPGSearchConfig.defrecdir) > 0) | |||
605 | { | |||
606 | char directory[MaxFileName255] = ""; | |||
607 | strn0cpy(directory, EPGSearchConfig.defrecdir,sizeof(directory)); | |||
608 | cVarExpr varExprDir(directory); | |||
609 | if (!varExprDir.DependsOnVar("%title%", event)) | |||
610 | { | |||
611 | strcat(directory, "~"); | |||
612 | strcat(directory, timer->File()); | |||
613 | } | |||
614 | // parse the epxression and evaluate it | |||
615 | cVarExpr varExpr(directory); | |||
616 | strcpy(directory, varExpr.Evaluate(event).c_str()); | |||
617 | if (strchr(directory, '%') == NULL__null) // only set directory to new value if all categories could have been replaced | |||
618 | timer->SetFile(directory); | |||
619 | } | |||
620 | } | |||
621 | ||||
622 | bool EventsMatch(const cEvent* event1, const cEvent* event2, bool compareTitle, int compareSubtitle, bool compareSummary, int compareDate, unsigned long catvaluesAvoidRepeat, int matchLimit) | |||
623 | { | |||
624 | if (!event1 || !event2) return false; | |||
625 | if (event1 == event2) return true; | |||
626 | ||||
627 | // only compare the alphanumeric portions | |||
628 | string Title1 = ""; | |||
629 | string Title2 = ""; | |||
630 | if (compareTitle) | |||
631 | { | |||
632 | string s1 = event1->Title()?event1->Title():""; | |||
633 | string s2 = event2->Title()?event2->Title():""; | |||
634 | Title1 = GetAlNum(s1); | |||
635 | Title2 = GetAlNum(s2); | |||
636 | std::transform(Title1.begin(), Title1.end(), Title1.begin(), tolower); | |||
637 | std::transform(Title2.begin(), Title2.end(), Title2.begin(), tolower); | |||
638 | } | |||
639 | string Subtitle1 = ""; | |||
640 | string Subtitle2 = ""; | |||
641 | if (compareSubtitle) | |||
642 | { | |||
643 | string s1 = event1->ShortText()?event1->ShortText():""; | |||
644 | string s2 = event2->ShortText()?event2->ShortText():""; | |||
645 | Subtitle1 = GetAlNum(s1); | |||
646 | Subtitle2 = GetAlNum(s2); | |||
647 | std::transform(Subtitle1.begin(), Subtitle1.end(), Subtitle1.begin(), tolower); | |||
648 | std::transform(Subtitle2.begin(), Subtitle2.end(), Subtitle2.begin(), tolower); | |||
649 | } | |||
650 | string compareExpression = ""; | |||
651 | if (compareDate == 1) compareExpression = "%date%"; | |||
652 | if (compareDate == 2) compareExpression = "%year%-%week%"; | |||
653 | if (compareDate == 3) compareExpression = "%year%-%month%"; | |||
654 | ||||
655 | bool match = false; | |||
656 | if ((!compareTitle || Title1 == Title2) && | |||
657 | (!compareSubtitle || (Subtitle1 == Subtitle2 && Subtitle1!=""))) | |||
658 | { | |||
659 | const char* Descr1 = event1->Description(); | |||
660 | const char* Descr2 = event2->Description(); | |||
661 | if (compareSummary) | |||
662 | { | |||
663 | char* rawDescr1 = GetRawDescription(Descr1); | |||
664 | char* rawDescr2 = GetRawDescription(Descr2); | |||
665 | match = DescriptionMatches(rawDescr1, rawDescr2, matchLimit); | |||
666 | free(rawDescr1); | |||
667 | free(rawDescr2); | |||
668 | if (!match) return false; | |||
669 | } | |||
670 | if (compareExpression.size() > 0) | |||
671 | { | |||
672 | cVarExpr varExpr(compareExpression); | |||
673 | string resEvent1 = varExpr.Evaluate(event1); | |||
674 | string resEvent2 = varExpr.Evaluate(event2); | |||
675 | if (resEvent1 != resEvent2) | |||
676 | return false; | |||
677 | } | |||
678 | if (catvaluesAvoidRepeat != 0) // check categories | |||
679 | { | |||
680 | bool bCatMatch = ((Descr1 && Descr2) || (!Descr1 && !Descr2)); | |||
681 | cSearchExtCat *SearchExtCat = SearchExtCats.First(); | |||
682 | int index = 0; | |||
683 | while (catvaluesAvoidRepeat > 0 && SearchExtCat && bCatMatch) | |||
684 | { | |||
685 | if (catvaluesAvoidRepeat & (1<<index)) | |||
686 | { | |||
687 | char* CatValue1 = GetExtEPGValue(Descr1, SearchExtCat->name, SearchExtCat->format); | |||
688 | char* CatValue2 = GetExtEPGValue(Descr2, SearchExtCat->name, SearchExtCat->format); | |||
689 | if ((!CatValue1 && CatValue2) || | |||
690 | (!CatValue2 && CatValue1) || | |||
691 | (CatValue1 && CatValue2 && strcmp(CatValue1, CatValue2) != 0)) | |||
692 | bCatMatch = false; | |||
693 | free(CatValue1); | |||
694 | free(CatValue2); | |||
695 | } | |||
696 | SearchExtCat = SearchExtCats.Next(SearchExtCat); | |||
697 | index++; | |||
698 | } | |||
699 | if (bCatMatch) | |||
700 | match = true; | |||
701 | } | |||
702 | else | |||
703 | match = true; | |||
704 | } | |||
705 | return match; | |||
706 | } | |||
707 | ||||
708 | int ChannelNrFromEvent(const cEvent* pEvent) | |||
709 | { | |||
710 | if (!pEvent) | |||
711 | return -1; | |||
712 | cChannel* channel = Channels.GetByChannelID(pEvent->ChannelID(), true, true); | |||
713 | if (!channel) | |||
714 | return -1; | |||
715 | else | |||
716 | return channel->Number(); | |||
717 | } | |||
718 | ||||
719 | void DelTimer(int index) | |||
720 | { | |||
721 | cString cmdbuf = cString::sprintf("DELT %d", index); | |||
722 | LogFile.Log(2, "delete timer %d", index); | |||
723 | SendViaSVDRP(cmdbuf); | |||
724 | gl_timerStatusMonitor->SetConflictCheckAdvised(); | |||
725 | } | |||
726 | ||||
727 | char* FixSeparators(char* buffer, char sep) | |||
728 | { | |||
729 | int l = strlen(buffer); | |||
730 | char *dest = buffer; | |||
731 | for (int i = 0; i < l; i ++) { | |||
732 | char c = buffer[i]; | |||
733 | int j = i; | |||
734 | if (c == sep) { | |||
735 | for (j = i + 1; (j < l) & (buffer[j] == ' '); j++) | |||
736 | ; | |||
737 | ||||
738 | if ((j <= l) | (i + 1 < j)) { | |||
739 | switch (buffer[j]) { | |||
740 | case '\t': | |||
741 | i = j; | |||
742 | c = '\t'; | |||
743 | break; | |||
744 | case 0: | |||
745 | i = j; | |||
746 | c = 0; | |||
747 | break; | |||
748 | default: | |||
749 | break; | |||
750 | } | |||
751 | } | |||
752 | } | |||
753 | if (c == '\t') { | |||
754 | for (; (j < l) & (buffer[j] == ' '); j++) | |||
755 | ; | |||
756 | if (j < l && buffer[j] == sep) { | |||
757 | buffer[j] = '\t'; | |||
758 | i = j - 1; | |||
759 | continue; | |||
760 | } | |||
761 | } | |||
762 | *dest++ = c; | |||
763 | } | |||
764 | *dest = 0; | |||
765 | return buffer; | |||
766 | } | |||
767 | ||||
768 | cString DateTime(time_t t) | |||
769 | { | |||
770 | char buffer[32]; | |||
771 | if (t == 0) | |||
772 | time(&t); | |||
773 | struct tm tm_r; | |||
774 | tm *tm = localtime_r(&t, &tm_r); | |||
775 | snprintf(buffer, sizeof(buffer), "%02d.%02d. %02d:%02d", tm->tm_mday, tm->tm_mon + 1, tm->tm_hour, tm->tm_min); | |||
776 | return buffer; | |||
777 | } | |||
778 | ||||
779 | string NumToString(long num) | |||
780 | { | |||
781 | ostringstream os; | |||
782 | os << num; | |||
783 | return os.str(); | |||
784 | } | |||
785 | ||||
786 | int FindIgnoreCase(const string& expr, const string& query) | |||
787 | { | |||
788 | const char *p = expr.c_str(); | |||
789 | const char *r = strcasestr(p, query.c_str()); | |||
790 | ||||
791 | if (!r) | |||
792 | return -1; | |||
793 | return r - p; | |||
794 | } | |||
795 | ||||
796 | bool EqualsNoCase(const string& a, const string& b) | |||
797 | { | |||
798 | return strcasecmp(a.c_str(), b.c_str()) == 0; | |||
799 | } | |||
800 | ||||
801 | string Strip(const string& input) | |||
802 | { | |||
803 | string str = input; | |||
804 | string::size_type pos = str.find_last_not_of(' '); | |||
805 | if(pos != string::npos) { | |||
806 | str.erase(pos + 1); | |||
807 | pos = str.find_first_not_of(' '); | |||
808 | if(pos != string::npos) str.erase(0, pos); | |||
809 | } | |||
810 | else str.erase(str.begin(), str.end()); | |||
811 | return str; | |||
812 | } | |||
813 | ||||
814 | string GetAlNum(const string& s) | |||
815 | { | |||
816 | string res; | |||
817 | for(unsigned int i=0; i<s.size(); i++) | |||
818 | if (isalnum(s[i])) | |||
819 | res += s[i]; | |||
820 | return res; | |||
821 | } | |||
822 | ||||
823 | string ReplaceAll(const string& input, const string& what, const string& with) | |||
824 | { | |||
825 | string result = input; | |||
826 | int pos = 0; | |||
827 | ||||
828 | while((pos = FindIgnoreCase(result, what)) >= 0) | |||
829 | result.replace(pos, what.size(), with); | |||
830 | return result; | |||
831 | } | |||
832 | ||||
833 | string EscapeString(const string& S) | |||
834 | { | |||
835 | string tmp = S; | |||
836 | int apostrophPos = 0; | |||
837 | int apostrophTempPos = 0; | |||
838 | while((apostrophPos = tmp.find("'", apostrophTempPos)) >= apostrophTempPos) { | |||
839 | tmp.replace(apostrophPos, 1, "'\"'\"'"); | |||
840 | apostrophTempPos = apostrophPos + 5; | |||
841 | } | |||
842 | return tmp; | |||
843 | } | |||
844 | ||||
845 | string QuoteApostroph(const string& S) | |||
846 | { | |||
847 | string tmp = S; | |||
848 | int apostrophPos = 0; | |||
849 | int apostrophTempPos = 0; | |||
850 | while((apostrophPos = tmp.find("\"", apostrophTempPos)) >= apostrophTempPos) { | |||
851 | tmp.replace(apostrophPos, 1, "\\\""); | |||
852 | apostrophTempPos = apostrophPos + 2; | |||
853 | } | |||
854 | return tmp; | |||
855 | } | |||
856 | ||||
857 | string MD5(const string& input) | |||
858 | { | |||
859 | char* szInput = strdup(input.c_str()); | |||
860 | if (!szInput) return ""; | |||
861 | char* szRes = MD5String(szInput); | |||
862 | string res = szRes; | |||
863 | free(szRes); | |||
864 | return res; | |||
865 | } | |||
866 | ||||
867 | void SetAux(cTimer* timer, string aux) | |||
868 | { | |||
869 | if (!timer) return; | |||
870 | string timerText = *timer->ToText(); | |||
871 | string::size_type auxPos = string::npos; | |||
872 | for(int i=0; i<=7; i++) // get aux value | |||
873 | auxPos = timerText.find(":", auxPos == string::npos?0:auxPos+1); | |||
874 | if (auxPos == string::npos) return; | |||
875 | timerText.replace(auxPos+1, timerText.size()-auxPos+1, aux); | |||
876 | timer->Parse(timerText.c_str()); | |||
877 | } | |||
878 | ||||
879 | int msprintf(char **strp, const char *fmt, ...) | |||
880 | { | |||
881 | va_list ap; | |||
882 | va_start (ap, fmt)__builtin_va_start(ap, fmt); | |||
883 | int res=vasprintf (strp, fmt, ap); | |||
884 | va_end (ap)__builtin_va_end(ap); | |||
885 | return res; | |||
886 | } | |||
887 | ||||
888 | std::string GetCodeset() | |||
889 | { | |||
890 | // Taken from VDR's vdr.c | |||
891 | char *CodeSet = NULL__null; | |||
892 | if (setlocale(LC_CTYPE0, "")) | |||
893 | CodeSet = nl_langinfo(CODESETCODESET); | |||
894 | else { | |||
895 | char *LangEnv = getenv("LANG"); // last resort in case locale stuff isn't installed | |||
896 | if (LangEnv) { | |||
897 | CodeSet = strchr(LangEnv, '.'); | |||
898 | if (CodeSet) | |||
899 | CodeSet++; // skip the dot | |||
900 | } | |||
901 | } | |||
902 | if (CodeSet) | |||
903 | return std::string(CodeSet); | |||
904 | else | |||
905 | return "ISO-8859-15"; | |||
906 | } | |||
907 | ||||
908 | /* Read a line from a socket */ | |||
909 | ssize_t Readline(int sockd, char *vptr, size_t maxlen) { | |||
910 | size_t n, rc; | |||
911 | char c, *buffer; | |||
912 | ||||
913 | buffer = vptr; | |||
914 | ||||
915 | for ( n = 1; n < maxlen; n++ ) { | |||
916 | ||||
917 | if ( (rc = read(sockd, &c, 1)) == 1 ) { | |||
918 | if ( c == '\n' ) | |||
919 | break; | |||
920 | *buffer++ = c; | |||
921 | } | |||
922 | else if ( rc == 0 ) { | |||
923 | if ( n == 1 ) | |||
924 | return 0; | |||
925 | else | |||
926 | break; | |||
927 | } | |||
928 | else { | |||
929 | if ( errno(*__errno_location ()) == EINTR4 ) | |||
930 | continue; | |||
931 | return -1; | |||
932 | } | |||
933 | } | |||
934 | ||||
935 | *buffer = 0; | |||
936 | return n; | |||
937 | } | |||
938 | ||||
939 | /* Write a line to a socket */ | |||
940 | ssize_t Writeline(int sockd, const char *vptr, ssize_t n) { | |||
941 | ssize_t nleft; | |||
942 | ssize_t nwritten; | |||
943 | const char *buffer; | |||
944 | ||||
945 | buffer = vptr; | |||
946 | nleft = n; | |||
947 | ||||
948 | while ( nleft > 0 ) { | |||
949 | if ( (nwritten = write(sockd, buffer, nleft)) <= 0 ) { | |||
950 | if ( errno(*__errno_location ()) == EINTR4 ) | |||
951 | nwritten = 0; | |||
952 | else | |||
953 | return -1; | |||
954 | } | |||
955 | nleft -= nwritten; | |||
956 | buffer += nwritten; | |||
957 | } | |||
958 | ||||
959 | return n; | |||
960 | } | |||
961 | ||||
962 | long getAddrFromString(const char* hostnameOrIp, struct sockaddr_in* addr) | |||
963 | { | |||
964 | unsigned long ip; | |||
965 | ||||
966 | struct hostent * he; | |||
967 | ||||
968 | if(hostnameOrIp==NULL__null || addr==NULL__null) | |||
969 | return -1; | |||
970 | ||||
971 | ip=inet_addr(hostnameOrIp); | |||
972 | ||||
973 | if(ip!=INADDR_NONE((in_addr_t) 0xffffffff)) | |||
974 | { | |||
975 | addr->sin_addr.s_addr=ip; | |||
976 | return 0; | |||
977 | } | |||
978 | else | |||
979 | { | |||
980 | he=gethostbyname(hostnameOrIp); | |||
981 | if(he==NULL__null) | |||
982 | return -1; | |||
983 | else | |||
984 | memcpy(&(addr->sin_addr),he->h_addr_list[0],4); | |||
985 | return 0; | |||
986 | } | |||
987 | } | |||
988 | ||||
989 | #if VDRVERSNUM20005 >= 10712 | |||
990 | char *cCommand::result = NULL__null; | |||
991 | ||||
992 | cCommand::cCommand(void) | |||
993 | { | |||
994 | title = command = NULL__null; | |||
995 | confirm = false; | |||
996 | } | |||
997 | ||||
998 | cCommand::~cCommand() | |||
999 | { | |||
1000 | free(title); | |||
1001 | free(command); | |||
1002 | } | |||
1003 | ||||
1004 | bool cCommand::Parse(const char *s) | |||
1005 | { | |||
1006 | const char *p = strchr(s, ':'); | |||
1007 | if (p) { | |||
| ||||
1008 | int l = p - s; | |||
1009 | if (l > 0) { | |||
1010 | title = MALLOC(char, l + 1)(char *)malloc(sizeof(char) * (l + 1)); | |||
1011 | stripspace(strn0cpy(title, s, l + 1)); | |||
1012 | if (!isempty(title)) { | |||
1013 | int l = strlen(title); | |||
1014 | if (l > 1 && title[l - 1] == '?') { | |||
1015 | confirm = true; | |||
1016 | title[l - 1] = 0; | |||
1017 | } | |||
1018 | command = stripspace(strdup(skipspace(p + 1))); | |||
| ||||
1019 | return !isempty(command); | |||
1020 | } | |||
1021 | } | |||
1022 | } | |||
1023 | return false; | |||
1024 | } | |||
1025 | ||||
1026 | const char *cCommand::Execute(const char *Parameters) | |||
1027 | { | |||
1028 | free(result); | |||
1029 | result = NULL__null; | |||
1030 | cString cmdbuf; | |||
1031 | if (Parameters) | |||
1032 | cmdbuf = cString::sprintf("%s %s", command, Parameters); | |||
1033 | const char *cmd = *cmdbuf ? *cmdbuf : command; | |||
1034 | dsyslog("executing command '%s'", cmd)void( (SysLogLevel > 2) ? syslog_with_tid(3, "executing command '%s'" , cmd) : void() ); | |||
1035 | cPipe p; | |||
1036 | if (p.Open(cmd, "r")) { | |||
1037 | int l = 0; | |||
1038 | int c; | |||
1039 | while ((c = fgetc(p)) != EOF(-1)) { | |||
1040 | if (l % 20 == 0) | |||
1041 | result = (char *)realloc(result, l + 21); | |||
1042 | result[l++] = char(c); | |||
1043 | } | |||
1044 | if (result) | |||
1045 | result[l] = 0; | |||
1046 | p.Close(); | |||
1047 | } | |||
1048 | else | |||
1049 | esyslog("ERROR: can't open pipe for command '%s'", cmd)void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: can't open pipe for command '%s'" , cmd) : void() ); | |||
1050 | return result; | |||
1051 | } | |||
1052 | #endif |