| 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 |