|
MythTV
0.25-pre
|
00001 00002 #include "util.h" 00003 00004 // C++ headers 00005 #include <iostream> 00006 00007 using namespace std; 00008 00009 // C headers 00010 #include <cerrno> 00011 #include <stdlib.h> 00012 #include <time.h> 00013 00014 // POSIX 00015 #include <unistd.h> 00016 #include <fcntl.h> 00017 #include <sched.h> 00018 00019 // System specific C headers 00020 #include "compat.h" 00021 00022 #ifdef linux 00023 #include <sys/vfs.h> 00024 #include <sys/sysinfo.h> 00025 #endif 00026 00027 #if CONFIG_DARWIN 00028 #include <mach/mach.h> 00029 #endif 00030 00031 #ifdef BSD 00032 #include <sys/mount.h> // for struct statfs 00033 #include <sys/sysctl.h> 00034 #endif 00035 00036 // Qt headers 00037 #include <QReadWriteLock> 00038 #include <QNetworkProxy> 00039 #include <QFileInfo> 00040 #include <QFile> 00041 #include <QDir> 00042 #include <QUrl> 00043 00044 // Myth headers 00045 #include "mythcorecontext.h" 00046 #include "exitcodes.h" 00047 #include "mythlogging.h" 00048 #include "msocketdevice.h" 00049 #include "mythsocket.h" 00050 #include "mythcoreutil.h" 00051 #include "mythsystem.h" 00052 00053 #include "mythconfig.h" // for CONFIG_DARWIN 00054 00058 QDateTime mythCurrentDateTime() 00059 { 00060 QDateTime rettime = QDateTime::currentDateTime(); 00061 QTime orig = rettime.time(); 00062 rettime.setTime(orig.addMSecs(-orig.msec())); 00063 return rettime; 00064 } 00065 00066 QDateTime myth_dt_from_string(const QString &dtstr) 00067 { 00068 if (dtstr.isEmpty()) 00069 return QDateTime(); 00070 00071 if (!dtstr.contains("-") && dtstr.length() == 14) 00072 { 00073 // must be in yyyyMMddhhmmss format 00074 return QDateTime::fromString(dtstr, "yyyyMMddhhmmss"); 00075 } 00076 00077 return QDateTime::fromString(dtstr, Qt::ISODate); 00078 } 00079 00086 QString MythDateTimeToString(const QDateTime& datetime, uint format) 00087 { 00088 QString result; 00089 00090 if (format & (kDateFull | kDateShort)) 00091 result += MythDateToString(datetime.date(), format); 00092 00093 if (format & kTime) 00094 { 00095 if (!result.isEmpty()) 00096 result.append(", "); 00097 00098 result += MythTimeToString(datetime.time(), format); 00099 } 00100 00101 return result; 00102 } 00103 00110 QString MythDateToString(const QDate& date, uint format) 00111 { 00112 QString result; 00113 00114 if (format & (kDateFull | kDateShort)) 00115 { 00116 QDate now = QDate::currentDate(); 00117 00118 QString stringformat; 00119 if (format & kDateShort) 00120 stringformat = gCoreContext->GetSetting("ShortDateFormat", "ddd d"); 00121 else 00122 stringformat = gCoreContext->GetSetting("DateFormat", "ddd d MMMM"); 00123 00124 if (format & kAddYear) 00125 { 00126 if (!stringformat.contains("yy")) // Matches both 2 or 4 digit year 00127 stringformat.append(" yyyy"); 00128 } 00129 00130 if (format & ~kDateShort) 00131 { 00132 if ((format & kSimplify) && (now == date)) 00133 result = QObject::tr("Today"); 00134 else if ((format & kSimplify) && (now.addDays(-1) == date)) 00135 result = QObject::tr("Yesterday"); 00136 else if ((format & kSimplify) && (now.addDays(1) == date)) 00137 result = QObject::tr("Tomorrow"); 00138 } 00139 00140 if (result.isEmpty()) 00141 result = date.toString(stringformat); 00142 } 00143 00144 return result; 00145 } 00146 00153 QString MythTimeToString(const QTime& time, uint format) 00154 { 00155 QString result; 00156 00157 if (format & kTime) 00158 { 00159 QString timeformat = gCoreContext->GetSetting("TimeFormat", "h:mm AP"); 00160 result = time.toString(timeformat); 00161 } 00162 00163 return result; 00164 } 00165 00166 int calc_utc_offset(void) 00167 { 00168 QDateTime loc = QDateTime::currentDateTime(); 00169 QDateTime utc = QDateTime::currentDateTime().toUTC(); 00170 00171 int utc_offset = MythSecsTo(utc, loc); 00172 00173 // clamp to nearest minute if within 10 seconds 00174 int off = utc_offset % 60; 00175 if (abs(off) < 10) 00176 utc_offset -= off; 00177 if (off < -50 && off > -60) 00178 utc_offset -= 60 + off; 00179 if (off > +50 && off < +60) 00180 utc_offset += 60 - off; 00181 00182 return utc_offset; 00183 } 00184 00185 static bool compare_zone_files(QFileInfo first_file_info, 00186 QFileInfo second_file_info) 00187 { 00188 if (!first_file_info.isFile() || !second_file_info.isFile() || 00189 !first_file_info.isReadable() || !second_file_info.isReadable()) 00190 return false; 00191 00192 qint64 first_file_size = first_file_info.size(); 00193 // sanity check - zoneinfo files should typically be less than 00194 // about 4kB, but leave room for growth 00195 if ((first_file_size > 200 * 1024) || 00196 (second_file_info.size() != first_file_size)) 00197 return false; 00198 00199 QFile first_file(first_file_info.absoluteFilePath()); 00200 QByteArray first_file_data; 00201 first_file_data.resize(first_file_size); 00202 QFile second_file(second_file_info.absoluteFilePath()); 00203 QByteArray second_file_data; 00204 second_file_data.resize(first_file_size); 00205 if (first_file.open(QIODevice::ReadOnly)) 00206 { 00207 QDataStream in(&first_file); 00208 if (in.readRawData(first_file_data.data(), 00209 first_file_size) != first_file_size) 00210 { 00211 first_file.close(); 00212 return false; 00213 } 00214 first_file.close(); 00215 } 00216 if (second_file.open(QIODevice::ReadOnly)) 00217 { 00218 QDataStream in(&second_file); 00219 if (in.readRawData(second_file_data.data(), 00220 first_file_size) != first_file_size) 00221 { 00222 second_file.close(); 00223 return false; 00224 } 00225 second_file.close(); 00226 } 00227 if (first_file_data == second_file_data) 00228 return true; 00229 00230 return false; 00231 } 00232 00233 #ifndef USING_MINGW 00234 /* Helper function for getSystemTimeZoneID() that compares the 00235 zoneinfo_file_path (regular) file with files in the zoneinfo_dir_path until 00236 it finds a match. The matching file's name is used to determine the time 00237 zone ID. */ 00238 static QString findZoneinfoFile(QString zoneinfo_file_path, 00239 QString zoneinfo_dir_path) 00240 { 00241 QString zone_id("UNDEF"); 00242 QDir zoneinfo_dir(zoneinfo_dir_path); 00243 QFileInfoList dirlist = zoneinfo_dir.entryInfoList(); 00244 QFileInfo info; 00245 QString basename; 00246 QFileInfo zoneinfo_file_info(zoneinfo_file_path); 00247 00248 for (QFileInfoList::const_iterator it = dirlist.begin(); 00249 it != dirlist.end(); ++it) 00250 { 00251 info = *it; 00252 // Skip '.' and '..' and other files starting with "." and 00253 // skip localtime (which is often a link to zoneinfo_file_path) 00254 basename = info.baseName(); 00255 if (basename.isEmpty() || (basename == "localtime")) { 00256 continue; 00257 } 00258 if (info.isDir()) 00259 { 00260 zone_id = findZoneinfoFile(zoneinfo_file_path, 00261 info.absoluteFilePath()); 00262 if (zone_id != "UNDEF") 00263 return zone_id; 00264 } 00265 else if (compare_zone_files(zoneinfo_file_info, info)) 00266 { 00267 zone_id = info.absoluteFilePath(); 00268 break; 00269 } 00270 } 00271 return zone_id; 00272 } 00273 #endif 00274 00275 /* helper fuction to find the zone ID in a configuration string 00276 allows NIS-format /etc/timezone , which contains extra information: 00277 <time zone ID> <host or NIS domain name> # optional comments 00278 */ 00279 static bool parse_zone_id_config_string(QString& zone_id) 00280 { 00281 bool found = false; 00282 QString zoneinfo_dir_path("/usr/share/zoneinfo/"); 00283 QRegExp sep("\\s+"); 00284 QFileInfo file_info; 00285 00286 while (!found) 00287 { 00288 QString temp_zone_id = zone_id; 00289 temp_zone_id.replace(' ', '_'); 00290 file_info.setFile(zoneinfo_dir_path + temp_zone_id); 00291 if (file_info.exists()) 00292 { 00293 found = true; 00294 } 00295 else 00296 { 00297 zone_id = zone_id.section(sep, 0, -2); 00298 if (zone_id.isEmpty()) 00299 break; 00300 } 00301 } 00302 return found; 00303 } 00304 00305 /* helper fuction to read time zone id from a file 00306 Debian's /etc/timezone or Red Hat's /etc/sysconfig/clock */ 00307 static bool read_time_zone_id(QString filename, QString& zone_id) 00308 { 00309 bool found = false; 00310 QFile file(filename); 00311 QFileInfo info(file); 00312 if (info.exists() && info.isFile() && info.isReadable()) 00313 { 00314 if (file.open(QIODevice::ReadOnly | QIODevice::Text)) 00315 { 00316 QString line; 00317 QTextStream in(&file); 00318 // Handle whitespace and quotes 00319 QRegExp re("^(?:ZONE\\s*=)?\\s*(['\"]?)([\\w\\s/-\\+]+)\\1\\s*(?:#.*)?$"); 00320 re.setPatternSyntax(QRegExp::RegExp2); 00321 while (!in.atEnd()) 00322 { 00323 line = in.readLine(); 00324 if (re.indexIn(line) != -1) 00325 { 00326 zone_id = re.cap(2); 00327 if (parse_zone_id_config_string(zone_id)) 00328 found = true; 00329 break; 00330 } 00331 } 00332 file.close(); 00333 } 00334 } 00335 return found; 00336 } 00337 00338 /* Helper function for getTimeZoneID() that provides an unprocessed time zone 00339 id obtained using system-dependent means of identifying the system's time 00340 zone. */ 00341 static QString getSystemTimeZoneID(void) 00342 { 00343 QString zone_id("UNDEF"); 00344 #ifdef USING_MINGW 00345 // typedef struct _TIME_ZONE_INFORMATION { ... 00346 // GetTimeZoneInformation(); 00347 // ... 00348 // Sadly, Windows zone names are different to the (probably Unix) 00349 // backend's names - "AUS Eastern Standard Time" vs "Australia/Sydney". 00350 // Translation is not worthwhile. Leave it as UNDEF to check the offset. 00351 #else 00352 // Try to determine the time zone information by inspecting the system 00353 // configuration 00354 QString time_zone_file_path("/etc/timezone"); 00355 QString clock_file_path("/etc/sysconfig/clock"); 00356 QString zoneinfo_file_path("/etc/localtime"); 00357 QString zoneinfo_dir_path("/usr/share/zoneinfo"); 00358 00359 // First, check time_zone_file_path (used by Debian-based systems) 00360 if (read_time_zone_id(time_zone_file_path, zone_id)) 00361 return zone_id; 00362 00363 // Next, look for the ZONE entry in clock_file_path (used by Red Hat-based 00364 // systems) 00365 if (read_time_zone_id(clock_file_path, zone_id)) 00366 return zone_id; 00367 00368 // Next check zoneinfo_file_path 00369 QFile zoneinfo_file(zoneinfo_file_path); 00370 QFileInfo info(zoneinfo_file); 00371 00372 if (info.exists() && info.isFile()) 00373 { 00374 QString tz; 00375 if (info.isSymLink()) 00376 { 00377 // The symlink refers to a file whose name contains the zone ID 00378 tz = info.symLinkTarget(); 00379 } 00380 else 00381 { 00382 // The zoneinfo_file is a copy of the file in the 00383 // zoneinfo_dir_path, so search for the same file in 00384 // zoneinfo_dir_path 00385 tz = findZoneinfoFile(zoneinfo_file_path, zoneinfo_dir_path); 00386 } 00387 if (tz != "UNDEF") 00388 { 00389 int pos = 0; 00390 // Get the zone ID from the filename 00391 // Look for the basename of zoneinfo_dir_path in case it's a 00392 // relative link 00393 QString zoneinfo_dirname = zoneinfo_dir_path.section('/', -1); 00394 if ((pos = tz.indexOf(zoneinfo_dirname)) != -1) 00395 { 00396 zone_id = tz.right(tz.size() - (pos + 1) - 00397 zoneinfo_dirname.size()); 00398 } 00399 } 00400 else 00401 { 00402 // If we still haven't found a time zone, try localtime_r() to at 00403 // least get the zone name/abbreviation (as opposed to the 00404 // identifier for the set of rules governing the zone) 00405 char name[64]; 00406 time_t t; 00407 struct tm *result = (struct tm *)malloc(sizeof(*result)); 00408 00409 if (result != NULL) 00410 { 00411 t = time(NULL); 00412 localtime_r(&t, result); 00413 if (result != NULL) 00414 { 00415 if (strftime(name, sizeof(name), "%Z", result) > 0) 00416 zone_id = name; 00417 free(result); 00418 } 00419 } 00420 } 00421 } 00422 00423 #endif 00424 return zone_id; 00425 } 00426 00431 QString getTimeZoneID(void) 00432 { 00433 QString zone_id("UNDEF"); 00434 #ifndef USING_MINGW 00435 // First, try the TZ environment variable to check for environment-specific 00436 // overrides 00437 QString tz = getenv("TZ"); 00438 if (tz.isEmpty()) 00439 { 00440 // No TZ, so attempt to determine the system-configured time zone ID 00441 tz = getSystemTimeZoneID(); 00442 } 00443 00444 if (!tz.isEmpty()) 00445 { 00446 zone_id = tz; 00447 if (zone_id.startsWith("\"") || zone_id.startsWith("'")) 00448 zone_id.remove(0, 1); 00449 if (zone_id.endsWith("\"") || zone_id.endsWith("'")) 00450 zone_id.chop(1); 00451 if (zone_id.startsWith(":")) 00452 zone_id.remove(0, 1); 00453 // the "posix/" subdirectory typically contains the same files as the 00454 // "zoneinfo/" parent directory, but are not typically what are in use 00455 if (zone_id.startsWith("posix/")) 00456 zone_id.remove(0, 6); 00457 } 00458 00459 #endif 00460 return zone_id; 00461 } 00462 00463 /* Helper function for checkTimeZone() that compares zone ID's. 00464 In the event that the zone ID's differ, checks to see if the local 00465 zoneinfo database has both zone ID's and if they're equivalent. */ 00466 static bool compare_zone_IDs(QString firstZoneID, QString secondZoneID) 00467 { 00468 // Some distros use spaces rather than underscores in the zone ID, so 00469 // allow matches where the only difference is space vs. underscore. 00470 firstZoneID.replace(' ', '_'); 00471 secondZoneID.replace(' ', '_'); 00472 if (firstZoneID == secondZoneID) 00473 return true; 00474 00475 // Although the zone ID names don't match, they may refer to equivalent 00476 // rules, so compare the files 00477 QString zoneinfo_dir_path("/usr/share/zoneinfo"); 00478 QFileInfo firstInfo(zoneinfo_dir_path + "/" + firstZoneID); 00479 QFileInfo secondInfo(zoneinfo_dir_path + "/" + secondZoneID); 00480 if (compare_zone_files(firstInfo, secondInfo)) 00481 return true; 00482 00483 return false; 00484 } 00485 00486 static void print_timezone_info(QString master_zone_id, QString local_zone_id, 00487 int master_utc_offset, int local_utc_offset, 00488 QString master_time, QString local_time) 00489 { 00490 LOG(VB_GENERAL, LOG_NOTICE, 00491 QString("Detected time zone settings:\n" 00492 " Master: Zone ID: '%1', UTC Offset: '%2', Current Time: '%3'\n" 00493 " Local: Zone ID: '%4', UTC Offset: '%5', Current Time: '%6'\n") 00494 .arg(master_zone_id).arg(master_utc_offset).arg(master_time) 00495 .arg(local_zone_id).arg(local_utc_offset).arg(local_time)); 00496 } 00497 00502 bool checkTimeZone(void) 00503 { 00504 if (gCoreContext->IsMasterBackend()) 00505 return true; 00506 00507 QStringList master_settings(QString("QUERY_TIME_ZONE")); 00508 if (!gCoreContext->SendReceiveStringList(master_settings)) 00509 { 00510 LOG(VB_GENERAL, LOG_CRIT, 00511 "Unable to determine master backend time zone " 00512 "settings. If those settings differ from local " 00513 "settings, some functionality will fail."); 00514 return true; 00515 } 00516 00517 return checkTimeZone(master_settings); 00518 } 00519 00522 bool checkTimeZone(const QStringList &master_settings) 00523 { 00524 QDateTime local_time = mythCurrentDateTime(); 00525 QString local_time_string = local_time.toString(Qt::ISODate); 00526 00527 bool have_zone_IDs = true; 00528 00529 QString master_time_zone_ID = master_settings[0]; 00530 int master_utc_offset = master_settings[1].toInt(); 00531 QString master_time_string = master_settings[2]; 00532 QString local_time_zone_ID = getTimeZoneID(); 00533 int local_utc_offset = calc_utc_offset(); 00534 00535 if (master_time_zone_ID == "UNDEF") 00536 { 00537 LOG(VB_GENERAL, LOG_CRIT, 00538 "Unable to determine master backend time zone " 00539 "settings. If local time zone settings differ " 00540 "from master backend settings, some functionality will fail."); 00541 have_zone_IDs = false; 00542 } 00543 if (local_time_zone_ID == "UNDEF") 00544 { 00545 LOG(VB_GENERAL, LOG_CRIT, 00546 "Unable to determine local time zone settings. " 00547 "If local time zone settings differ from " 00548 "master backend settings, some functionality will fail."); 00549 have_zone_IDs = false; 00550 } 00551 00552 if (have_zone_IDs && 00553 !compare_zone_IDs(master_time_zone_ID, local_time_zone_ID)) 00554 { 00555 LOG(VB_GENERAL, LOG_CRIT, "Time zone settings on the master backend " 00556 "differ from those on this system."); 00557 00558 print_timezone_info(master_time_zone_ID, local_time_zone_ID, 00559 master_utc_offset, local_utc_offset, 00560 master_time_string, local_time_string); 00561 return false; 00562 } 00563 00564 // Verify offset 00565 if (master_utc_offset != local_utc_offset) 00566 { 00567 LOG(VB_GENERAL, LOG_CRIT, "UTC offset on the master backend differs " 00568 "from offset on this system."); 00569 00570 print_timezone_info(master_time_zone_ID, local_time_zone_ID, 00571 master_utc_offset, local_utc_offset, 00572 master_time_string, local_time_string); 00573 return false; 00574 } 00575 00576 // Verify current time 00577 if (master_time_string == "UNDEF") 00578 { 00579 LOG(VB_GENERAL, LOG_CRIT, 00580 "Unable to determine current time on the master " 00581 "backend . If local time or time zone settings " 00582 "differ from those on the master backend, some " 00583 "functionality will fail."); 00584 } 00585 else 00586 { 00587 QDateTime master_time = QDateTime::fromString(master_time_string, 00588 Qt::ISODate); 00589 uint timediff = abs(master_time.secsTo(local_time)); 00590 if (timediff > 300) 00591 { 00592 LOG(VB_GENERAL, LOG_CRIT, "Current time on the master backend " 00593 "differs from time on this system."); 00594 print_timezone_info(master_time_zone_ID, local_time_zone_ID, 00595 master_utc_offset, local_utc_offset, 00596 master_time_string, local_time_string); 00597 return false; 00598 } 00599 else if (timediff > 20) 00600 { 00601 LOG(VB_GENERAL, LOG_CRIT, 00602 QString("Warning! Time difference between the master " 00603 "backend and this system is %1 seconds.") 00604 .arg(timediff)); 00605 } 00606 } 00607 00608 return true; 00609 } 00610 00614 int MythSecsTo(const QDateTime &from, const QDateTime &to) 00615 { 00616 return (from.time().secsTo(to.time()) + 00617 from.date().daysTo(to.date()) * 60 * 60 * 24); 00618 } 00619 00623 QDateTime MythUTCToLocal(const QDateTime &utc) 00624 { 00625 QDateTime local = QDateTime(QDate(1970, 1, 1)); 00626 00627 int timesecs = MythSecsTo(local, utc); 00628 QDateTime localdt; 00629 localdt.setTime_t(timesecs); 00630 00631 return localdt; 00632 } 00633 00638 bool getUptime(time_t &uptime) 00639 { 00640 #ifdef __linux__ 00641 struct sysinfo sinfo; 00642 if (sysinfo(&sinfo) == -1) 00643 { 00644 LOG(VB_GENERAL, LOG_ERR, "sysinfo() error"); 00645 return false; 00646 } 00647 else 00648 uptime = sinfo.uptime; 00649 00650 #elif defined(__FreeBSD__) || CONFIG_DARWIN 00651 00652 int mib[2]; 00653 struct timeval bootTime; 00654 size_t len; 00655 00656 // Uptime is calculated. Get this machine's boot time 00657 // and subtract it from the current machine time 00658 len = sizeof(bootTime); 00659 mib[0] = CTL_KERN; 00660 mib[1] = KERN_BOOTTIME; 00661 if (sysctl(mib, 2, &bootTime, &len, NULL, 0) == -1) 00662 { 00663 LOG(VB_GENERAL, LOG_ERR, "sysctl() error"); 00664 return false; 00665 } 00666 else 00667 uptime = time(NULL) - bootTime.tv_sec; 00668 #elif defined(USING_MINGW) 00669 uptime = ::GetTickCount() / 1000; 00670 #else 00671 // Hmmm. Not Linux, not FreeBSD or Darwin. What else is there :-) 00672 LOG(VB_GENERAL, LOG_NOTICE, "Unknown platform. How do I get the uptime?"); 00673 return false; 00674 #endif 00675 00676 return true; 00677 } 00678 00685 bool getMemStats(int &totalMB, int &freeMB, int &totalVM, int &freeVM) 00686 { 00687 #ifdef __linux__ 00688 size_t MB = (1024*1024); 00689 struct sysinfo sinfo; 00690 if (sysinfo(&sinfo) == -1) 00691 { 00692 LOG(VB_GENERAL, LOG_ERR, 00693 "getMemStats(): Error, sysinfo() call failed."); 00694 return false; 00695 } 00696 else 00697 totalMB = (int)((sinfo.totalram * sinfo.mem_unit)/MB), 00698 freeMB = (int)((sinfo.freeram * sinfo.mem_unit)/MB), 00699 totalVM = (int)((sinfo.totalswap * sinfo.mem_unit)/MB), 00700 freeVM = (int)((sinfo.freeswap * sinfo.mem_unit)/MB); 00701 00702 #elif CONFIG_DARWIN 00703 mach_port_t mp; 00704 mach_msg_type_number_t count; 00705 vm_size_t pageSize; 00706 vm_statistics_data_t s; 00707 00708 mp = mach_host_self(); 00709 00710 // VM page size 00711 if (host_page_size(mp, &pageSize) != KERN_SUCCESS) 00712 pageSize = 4096; // If we can't look it up, 4K is a good guess 00713 00714 count = HOST_VM_INFO_COUNT; 00715 if (host_statistics(mp, HOST_VM_INFO, 00716 (host_info_t)&s, &count) != KERN_SUCCESS) 00717 { 00718 LOG(VB_GENERAL, LOG_ERR, "getMemStats(): Error, " 00719 "failed to get virtual memory statistics."); 00720 return false; 00721 } 00722 00723 pageSize >>= 10; // This gives usages in KB 00724 totalMB = (s.active_count + s.inactive_count + 00725 s.wire_count + s.free_count) * pageSize / 1024; 00726 freeMB = s.free_count * pageSize / 1024; 00727 00728 00729 // This is a real hack. I have not found a way to ask the kernel how much 00730 // swap it is using, and the dynamic_pager daemon doesn't even seem to be 00731 // able to report what filesystem it is using for the swapfiles. So, we do: 00732 int64_t total, used, free; 00733 free = getDiskSpace("/private/var/vm", total, used); 00734 totalVM = (int)(total >> 10); 00735 freeVM = (int)(free >> 10); 00736 00737 #else 00738 LOG(VB_GENERAL, LOG_NOTICE, "getMemStats(): Unknown platform. " 00739 "How do I get the memory stats?"); 00740 return false; 00741 #endif 00742 00743 return true; 00744 } 00745 00753 bool hasUtf8(const char *str) 00754 { 00755 const uchar *c = (uchar *) str; 00756 00757 while (*c++) 00758 { 00759 // ASCII is < 0x80. 00760 // 0xC2..0xF4 is probably UTF-8. 00761 // Anything else probably ISO-8859-1 (Latin-1, Unicode) 00762 00763 if (*c > 0xC1 && *c < 0xF5) 00764 { 00765 int bytesToCheck = 2; // Assume 0xC2-0xDF (2 byte sequence) 00766 00767 if (*c > 0xDF) // Maybe 0xE0-0xEF (3 byte sequence) 00768 ++bytesToCheck; 00769 if (*c > 0xEF) // Matches 0xF0-0xF4 (4 byte sequence) 00770 ++bytesToCheck; 00771 00772 while (bytesToCheck--) 00773 { 00774 ++c; 00775 00776 if (! *c) // String ended in middle 00777 return false; // Not valid UTF-8 00778 00779 if (*c < 0x80 || *c > 0xBF) // Bad UTF-8 sequence 00780 break; // Keep checking in outer loop 00781 } 00782 00783 if (!bytesToCheck) // Have checked all the bytes in the sequence 00784 return true; // Hooray! We found valid UTF-8! 00785 } 00786 } 00787 00788 return false; 00789 } 00790 00798 bool ping(const QString &host, int timeout) 00799 { 00800 #ifdef USING_MINGW 00801 QString cmd = QString("%systemroot%\\system32\\ping.exe -i %1 -n 1 %2>NUL") 00802 .arg(timeout).arg(host); 00803 00804 if (myth_system(cmd, kMSDontBlockInputDevs | kMSDontDisableDrawing | 00805 kMSProcessEvents) != GENERIC_EXIT_OK) 00806 return false; 00807 #else 00808 QString cmd = QString("ping -t %1 -c 1 %2 >/dev/null 2>&1") 00809 .arg(timeout).arg(host); 00810 00811 if (myth_system(cmd, kMSDontBlockInputDevs | kMSDontDisableDrawing | 00812 kMSProcessEvents) != GENERIC_EXIT_OK) 00813 { 00814 // ping command may not like -t argument, or the host might not 00815 // be listening. Try to narrow down with a quick ping to localhost: 00816 00817 cmd = "ping -t 1 -c 1 localhost >/dev/null 2>&1"; 00818 00819 if (myth_system(cmd, kMSDontBlockInputDevs | kMSDontDisableDrawing | 00820 kMSProcessEvents) != GENERIC_EXIT_OK) 00821 { 00822 // Assume -t is bad - do a ping that might cause a timeout: 00823 cmd = QString("ping -c 1 %1 >/dev/null 2>&1").arg(host); 00824 00825 if (myth_system(cmd, kMSDontBlockInputDevs | kMSDontDisableDrawing | 00826 kMSProcessEvents) != GENERIC_EXIT_OK) 00827 return false; // it failed with or without the -t 00828 00829 return true; 00830 } 00831 else // Pinging localhost worked, so targeted host wasn't listening 00832 return false; 00833 } 00834 #endif 00835 00836 return true; 00837 } 00838 00842 bool telnet(const QString &host, int port) 00843 { 00844 MythSocket *s = new MythSocket(); 00845 00846 bool connected = s->connect(host, port); 00847 s->DownRef(); 00848 00849 return connected; 00850 } 00851 00873 long long copy(QFile &dst, QFile &src, uint block_size) 00874 { 00875 uint buflen = (block_size < 1024) ? (16 * 1024) : block_size; 00876 char *buf = new char[buflen]; 00877 bool odst = false, osrc = false; 00878 00879 if (!buf) 00880 return -1LL; 00881 00882 if (!dst.isWritable() && !dst.isOpen()) 00883 odst = dst.open(QIODevice::Unbuffered | 00884 QIODevice::WriteOnly | 00885 QIODevice::Truncate); 00886 00887 if (!src.isReadable() && !src.isOpen()) 00888 osrc = src.open(QIODevice::Unbuffered|QIODevice::ReadOnly); 00889 00890 bool ok = dst.isWritable() && src.isReadable(); 00891 long long total_bytes = 0LL; 00892 while (ok) 00893 { 00894 long long rlen, wlen, off = 0; 00895 rlen = src.read(buf, buflen); 00896 if (rlen<0) 00897 { 00898 LOG(VB_GENERAL, LOG_ERR, "read error"); 00899 ok = false; 00900 break; 00901 } 00902 if (rlen==0) 00903 break; 00904 00905 total_bytes += (long long) rlen; 00906 00907 while ((rlen-off>0) && ok) 00908 { 00909 wlen = dst.write(buf + off, rlen - off); 00910 if (wlen>=0) 00911 off+= wlen; 00912 if (wlen<0) 00913 { 00914 LOG(VB_GENERAL, LOG_ERR, "write error"); 00915 ok = false; 00916 } 00917 } 00918 } 00919 delete[] buf; 00920 00921 if (odst) 00922 dst.close(); 00923 00924 if (osrc) 00925 src.close(); 00926 00927 return (ok) ? total_bytes : -1LL; 00928 } 00929 00930 QString createTempFile(QString name_template, bool dir) 00931 { 00932 int ret = -1; 00933 00934 #ifdef USING_MINGW 00935 char temppath[MAX_PATH] = "."; 00936 char tempfilename[MAX_PATH] = ""; 00937 // if GetTempPath fails, use current dir 00938 GetTempPathA(MAX_PATH, temppath); 00939 if (GetTempFileNameA(temppath, "mth", 0, tempfilename)) 00940 { 00941 if (dir) 00942 { 00943 // GetTempFileNameA creates the file, so delete it before mkdir 00944 unlink(tempfilename); 00945 ret = mkdir(tempfilename); 00946 } 00947 else 00948 ret = open(tempfilename, O_CREAT | O_RDWR, S_IREAD | S_IWRITE); 00949 } 00950 QString tmpFileName(tempfilename); 00951 #else 00952 QByteArray safe_name_template = name_template.toAscii(); 00953 const char *tmp = safe_name_template.constData(); 00954 char *ctemplate = strdup(tmp); 00955 00956 if (dir) 00957 { 00958 ret = (mkdtemp(ctemplate)) ? 0 : -1; 00959 } 00960 else 00961 { 00962 mode_t cur_umask = umask(S_IRWXO | S_IRWXG); 00963 ret = mkstemp(ctemplate); 00964 umask(cur_umask); 00965 } 00966 00967 QString tmpFileName(ctemplate); 00968 free(ctemplate); 00969 #endif 00970 00971 if (ret == -1) 00972 { 00973 LOG(VB_GENERAL, LOG_ERR, QString("createTempFile(%1), Error ") 00974 .arg(name_template) + ENO); 00975 return name_template; 00976 } 00977 00978 if (!dir && (ret >= 0)) 00979 close(ret); 00980 00981 return tmpFileName; 00982 } 00983 01002 void makeFileAccessible(QString filename) 01003 { 01004 QByteArray fname = filename.toAscii(); 01005 chmod(fname.constData(), 0666); 01006 } 01007 01011 QString getResponse(const QString &query, const QString &def) 01012 { 01013 QByteArray tmp = query.toLocal8Bit(); 01014 cout << tmp.constData(); 01015 01016 tmp = def.toLocal8Bit(); 01017 if (def.size()) 01018 cout << " [" << tmp.constData() << "] "; 01019 else 01020 cout << " "; 01021 01022 if (!isatty(fileno(stdin)) || !isatty(fileno(stdout))) 01023 { 01024 cout << endl << "[console is not interactive, using default '" 01025 << tmp.constData() << "']" << endl; 01026 return def; 01027 } 01028 01029 char response[80]; 01030 cin.clear(); 01031 cin.getline(response, 80); 01032 if (!cin.good()) 01033 { 01034 cout << endl; 01035 LOG(VB_GENERAL, LOG_ERR, "Read from stdin failed"); 01036 return NULL; 01037 } 01038 01039 QString qresponse = response; 01040 01041 if (qresponse.isEmpty()) 01042 qresponse = def; 01043 01044 return qresponse; 01045 } 01046 01050 int intResponse(const QString &query, int def) 01051 { 01052 QString str_resp = getResponse(query, QString("%1").arg(def)); 01053 if (str_resp.isEmpty()) 01054 return def; 01055 bool ok; 01056 int resp = str_resp.toInt(&ok); 01057 return (ok ? resp : def); 01058 } 01059 01060 01061 QString getSymlinkTarget(const QString &start_file, 01062 QStringList *intermediaries, 01063 unsigned maxLinks) 01064 { 01065 #if 0 01066 LOG(VB_GENERAL, LOG_DEBUG, 01067 QString("getSymlinkTarget('%1', 0x%2, %3)") 01068 .arg(start_file).arg((uint64_t)intermediaries,0,16) 01069 .arg(maxLinks)); 01070 #endif 01071 01072 QString link = QString::null; 01073 QString cur_file = start_file; cur_file.detach(); 01074 QFileInfo fi(cur_file); 01075 01076 if (intermediaries) 01077 { 01078 intermediaries->clear(); 01079 intermediaries->push_back(start_file); 01080 } 01081 01082 for (uint i = 0; (i <= maxLinks) && fi.isSymLink() && 01083 !(link = fi.readLink()).isEmpty(); i++) 01084 { 01085 cur_file = (link[0] == '/') ? 01086 link : // absolute link 01087 fi.absoluteDir().absolutePath() + "/" + link; // relative link 01088 01089 if (intermediaries && !intermediaries->contains(cur_file)) 01090 intermediaries->push_back(cur_file); 01091 01092 fi = QFileInfo(cur_file); 01093 } 01094 01095 if (intermediaries) 01096 intermediaries->detach(); 01097 01098 #if 0 01099 if (intermediaries) 01100 { 01101 for (uint i = 0; i < intermediaries->size(); i++) 01102 { 01103 LOG(VB_GENERAL, LOG_DEBUG, QString(" inter%1: %2") 01104 .arg(i).arg((*intermediaries)[i])); 01105 } 01106 } 01107 01108 LOG(VB_GENERAL, LOG_DEBUG, 01109 QString("getSymlinkTarget() -> '%1'") 01110 .arg((!fi.isSymLink()) ? cur_file : QString::null)); 01111 #endif 01112 01113 return (!fi.isSymLink()) ? cur_file : QString::null; 01114 } 01115 01116 void sendPlaybackStart(void) 01117 { 01118 MythEvent me(QString("PLAYBACK_START %1").arg(gCoreContext->GetHostName())); 01119 gCoreContext->dispatchNow(me); 01120 } 01121 01122 void sendPlaybackEnd(void) 01123 { 01124 MythEvent me(QString("PLAYBACK_END %1").arg(gCoreContext->GetHostName())); 01125 gCoreContext->dispatchNow(me); 01126 } 01127 01128 bool IsMACAddress(QString MAC) 01129 { 01130 QStringList tokens = MAC.split(':'); 01131 if (tokens.size() != 6) 01132 { 01133 LOG(VB_NETWORK, LOG_ERR, 01134 QString("IsMACAddress(%1) = false, doesn't have 6 parts").arg(MAC)); 01135 return false; 01136 } 01137 01138 int y; 01139 bool ok; 01140 int value; 01141 for (y = 0; y < 6; y++) 01142 { 01143 if (tokens[y].isEmpty()) 01144 { 01145 LOG(VB_NETWORK, LOG_ERR, 01146 QString("IsMACAddress(%1) = false, part #%2 is empty.") 01147 .arg(MAC).arg(y)); 01148 return false; 01149 } 01150 01151 value = tokens[y].toInt(&ok, 16); 01152 if (!ok) 01153 { 01154 LOG(VB_NETWORK, LOG_ERR, 01155 QString("IsMACAddress(%1) = false, unable to " 01156 "convert part '%2' to integer.") 01157 .arg(MAC).arg(tokens[y])); 01158 return false; 01159 } 01160 01161 if (value > 255) 01162 { 01163 LOG(VB_NETWORK, LOG_ERR, 01164 QString("IsMACAddress(%1) = false, part #%2 " 01165 "evaluates to %3 which is higher than 255.") 01166 .arg(MAC).arg(y).arg(value)); 01167 return false; 01168 } 01169 } 01170 01171 LOG(VB_NETWORK, LOG_DEBUG, QString("IsMACAddress(%1) = true").arg(MAC)); 01172 return true; 01173 } 01174 01175 QString FileHash(QString filename) 01176 { 01177 QFile file(filename); 01178 QFileInfo fileinfo(file); 01179 qint64 initialsize = fileinfo.size(); 01180 quint64 hash = 0; 01181 01182 if (initialsize == 0) 01183 return QString("NULL"); 01184 01185 if (file.open(QIODevice::ReadOnly)) 01186 hash = initialsize; 01187 else 01188 { 01189 LOG(VB_GENERAL, LOG_ERR, 01190 "Error: Unable to open selected file, missing read permissions?"); 01191 return QString("NULL"); 01192 } 01193 01194 file.seek(0); 01195 QDataStream stream(&file); 01196 stream.setByteOrder(QDataStream::LittleEndian); 01197 for (quint64 tmp = 0, i = 0; i < 65536/sizeof(tmp); i++) 01198 { 01199 stream >> tmp; 01200 hash += tmp; 01201 } 01202 01203 file.seek(initialsize - 65536); 01204 for (quint64 tmp = 0, i = 0; i < 65536/sizeof(tmp); i++) 01205 { 01206 stream >> tmp; 01207 hash += tmp; 01208 } 01209 01210 file.close(); 01211 01212 QString output = QString("%1").arg(hash, 0, 16); 01213 return output; 01214 } 01215 01216 bool WakeOnLAN(QString MAC) 01217 { 01218 char msg[1024] = "\xFF\xFF\xFF\xFF\xFF\xFF"; 01219 int msglen = 6; 01220 int x, y; 01221 QStringList tokens = MAC.split(':'); 01222 int macaddr[6]; 01223 bool ok; 01224 01225 if (tokens.size() != 6) 01226 { 01227 LOG(VB_GENERAL, LOG_ERR, 01228 QString( "WakeOnLan(%1): Incorrect MAC length").arg(MAC)); 01229 return false; 01230 } 01231 01232 for (y = 0; y < 6; y++) 01233 { 01234 macaddr[y] = tokens[y].toInt(&ok, 16); 01235 01236 if (!ok) 01237 { 01238 LOG(VB_GENERAL, LOG_ERR, 01239 QString( "WakeOnLan(%1): Invalid MAC address").arg(MAC)); 01240 return false; 01241 } 01242 } 01243 01244 for (x = 0; x < 16; x++) 01245 for (y = 0; y < 6; y++) 01246 msg[msglen++] = macaddr[y]; 01247 01248 LOG(VB_NETWORK, LOG_INFO, 01249 QString("WakeOnLan(): Sending WOL packet to %1").arg(MAC)); 01250 01251 MSocketDevice socket(MSocketDevice::Datagram); 01252 socket.setBroadcast(true); 01253 socket.writeBlock(msg, msglen, QHostAddress("255.255.255.255"), 32767); 01254 01255 return true; 01256 } 01257 01258 bool IsPulseAudioRunning(void) 01259 { 01260 #ifdef USING_MINGW 01261 return false; 01262 #else 01263 01264 #if CONFIG_DARWIN || (__FreeBSD__) || defined(__OpenBSD__) 01265 const char *command = "ps -ax | grep -i pulseaudio | grep -v grep > /dev/null"; 01266 #else 01267 const char *command = "ps ch -C pulseaudio -o pid > /dev/null"; 01268 #endif 01269 // Do NOT use kMSProcessEvents here, it will cause deadlock 01270 uint res = myth_system(command, kMSDontBlockInputDevs | 01271 kMSDontDisableDrawing); 01272 return (res == GENERIC_EXIT_OK); 01273 #endif // USING_MINGW 01274 } 01275 01276 bool myth_nice(int val) 01277 { 01278 errno = 0; 01279 int ret = nice(val); 01280 01281 if ((-1 == ret) && (0 != errno) && (val >= 0)) 01282 { 01283 LOG(VB_GENERAL, LOG_ERR, "Failed to nice process" + ENO); 01284 return false; 01285 } 01286 01287 return true; 01288 } 01289 01290 void myth_yield(void) 01291 { 01292 #ifdef _POSIX_PRIORITY_SCHEDULING 01293 if (sched_yield()<0) 01294 usleep(5000); 01295 #else 01296 usleep(5000); 01297 #endif 01298 } 01299 01317 #if defined(__linux__) && ( defined(__i386__) || defined(__ppc__) || \ 01318 defined(__x86_64__) || defined(__ia64__) ) 01319 01320 #include <stdio.h> 01321 #include <stdlib.h> 01322 #include <errno.h> 01323 #include <getopt.h> 01324 #include <unistd.h> 01325 #include <sys/ptrace.h> 01326 #include <asm/unistd.h> 01327 01328 #if defined(__i386__) 01329 # define __NR_ioprio_set 289 01330 # define __NR_ioprio_get 290 01331 #elif defined(__ppc__) 01332 # define __NR_ioprio_set 273 01333 # define __NR_ioprio_get 274 01334 #elif defined(__x86_64__) 01335 # define __NR_ioprio_set 251 01336 # define __NR_ioprio_get 252 01337 #elif defined(__ia64__) 01338 # define __NR_ioprio_set 1274 01339 # define __NR_ioprio_get 1275 01340 #endif 01341 01342 #define IOPRIO_BITS (16) 01343 #define IOPRIO_CLASS_SHIFT (13) 01344 #define IOPRIO_PRIO_MASK ((1UL << IOPRIO_CLASS_SHIFT) - 1) 01345 #define IOPRIO_PRIO_CLASS(mask) ((mask) >> IOPRIO_CLASS_SHIFT) 01346 #define IOPRIO_PRIO_DATA(mask) ((mask) & IOPRIO_PRIO_MASK) 01347 #define IOPRIO_PRIO_VALUE(class, data) (((class) << IOPRIO_CLASS_SHIFT) | data) 01348 01349 enum { IOPRIO_CLASS_NONE,IOPRIO_CLASS_RT,IOPRIO_CLASS_BE,IOPRIO_CLASS_IDLE, }; 01350 enum { IOPRIO_WHO_PROCESS = 1, IOPRIO_WHO_PGRP, IOPRIO_WHO_USER, }; 01351 01352 bool myth_ioprio(int val) 01353 { 01354 int new_ioclass = (val < 0) ? IOPRIO_CLASS_RT : 01355 (val > 7) ? IOPRIO_CLASS_IDLE : IOPRIO_CLASS_BE; 01356 int new_iodata = (new_ioclass == IOPRIO_CLASS_BE) ? val : 0; 01357 int new_ioprio = IOPRIO_PRIO_VALUE(new_ioclass, new_iodata); 01358 01359 int pid = getpid(); 01360 int old_ioprio = syscall(__NR_ioprio_get, IOPRIO_WHO_PROCESS, pid); 01361 if (old_ioprio == new_ioprio) 01362 return true; 01363 01364 int ret = syscall(__NR_ioprio_set, IOPRIO_WHO_PROCESS, pid, new_ioprio); 01365 01366 if (-1 == ret && EPERM == errno && IOPRIO_CLASS_BE != new_ioclass) 01367 { 01368 new_iodata = (new_ioclass == IOPRIO_CLASS_RT) ? 0 : 7; 01369 new_ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, new_iodata); 01370 ret = syscall(__NR_ioprio_set, IOPRIO_WHO_PROCESS, pid, new_ioprio); 01371 } 01372 01373 return 0 == ret; 01374 } 01375 01376 #else 01377 01378 bool myth_ioprio(int) { return true; } 01379 01380 #endif 01381 01382 bool MythRemoveDirectory(QDir &aDir) 01383 { 01384 if (!aDir.exists())//QDir::NoDotAndDotDot 01385 return false; 01386 01387 QFileInfoList entries = aDir.entryInfoList(QDir::NoDotAndDotDot | 01388 QDir::Dirs | QDir::Files); 01389 int count = entries.size(); 01390 bool has_err = false; 01391 01392 for (int idx = 0; idx < count && !has_err; idx++) 01393 { 01394 QFileInfo entryInfo = entries[idx]; 01395 QString path = entryInfo.absoluteFilePath(); 01396 if (entryInfo.isDir()) 01397 { 01398 QDir dir(path); 01399 has_err = MythRemoveDirectory(dir); 01400 } 01401 else 01402 { 01403 QFile file(path); 01404 if (!file.remove()) 01405 has_err = true; 01406 } 01407 } 01408 01409 if (!has_err && !aDir.rmdir(aDir.absolutePath())) 01410 has_err = true; 01411 01412 return(has_err); 01413 } 01414 01415 QString &ShellEscape(QString &string) 01416 { 01417 if (string.contains("\"")) 01418 string = string.replace("\"", "\\\""); 01419 01420 if (string.contains("\'")) 01421 string = string.replace("\'", "\\\'"); 01422 01423 if (string.contains(" ")) 01424 { 01425 string.prepend("\""); 01426 string.append("\""); 01427 } 01428 01429 return string; 01430 } 01431 01443 void setHttpProxy(void) 01444 { 01445 QString LOC = "setHttpProxy() - "; 01446 QNetworkProxy p; 01447 01448 01449 // Set http proxy for the application if specified in environment variable 01450 QString var(getenv("http_proxy")); 01451 if (var.isEmpty()) 01452 var = getenv("HTTP_PROXY"); // Sadly, some OS envs are case sensitive 01453 if (var.length()) 01454 { 01455 if (!var.startsWith("http://")) // i.e. just a host name 01456 var.prepend("http://"); 01457 01458 QUrl url = QUrl(var, QUrl::TolerantMode); 01459 QString host = url.host(); 01460 int port = url.port(); 01461 01462 if (port == -1) // Parsing error 01463 { 01464 port = 0; // The default when creating a QNetworkProxy 01465 01466 if (telnet(host, 1080)) // Socks? 01467 port = 1080; 01468 if (telnet(host, 3128)) // Squid 01469 port = 3128; 01470 if (telnet(host, 8080)) // MS ISA 01471 port = 8080; 01472 01473 LOG(VB_NETWORK, LOG_INFO, LOC + 01474 QString("assuming port %1 on host %2") .arg(port).arg(host)); 01475 url.setPort(port); 01476 } 01477 else if (!ping(host, 1)) 01478 LOG(VB_GENERAL, LOG_ERR, LOC + 01479 QString("cannot locate host %1").arg(host) + 01480 "\n\t\t\tPlease check HTTP_PROXY environment variable!"); 01481 else if (!telnet(host,port)) 01482 LOG(VB_GENERAL, LOG_ERR, LOC + 01483 QString("%1:%2 - cannot connect!").arg(host).arg(port) + 01484 "\n\t\t\tPlease check HTTP_PROXY environment variable!"); 01485 01486 #if 0 01487 LOG(VB_NETWORK, LOG_DEBUG, LOC + QString("using http://%1:%2@%3:%4") 01488 .arg(url.userName()).arg(url.password()) 01489 .arg(host).arg(port)); 01490 #endif 01491 p = QNetworkProxy(QNetworkProxy::HttpCachingProxy, 01492 host, port, url.userName(), url.password()); 01493 QNetworkProxy::setApplicationProxy(p); 01494 return; 01495 } 01496 01497 LOG(VB_NETWORK, LOG_DEBUG, LOC + "no HTTP_PROXY environment var."); 01498 01499 // Use Qt to look for user proxy settings stored by the OS or browser: 01500 01501 QList<QNetworkProxy> proxies; 01502 QNetworkProxyQuery query(QUrl("http://www.mythtv.org")); 01503 01504 proxies = QNetworkProxyFactory::systemProxyForQuery(query); 01505 01506 Q_FOREACH (p, proxies) 01507 { 01508 QString host = p.hostName(); 01509 int port = p.port(); 01510 01511 if (p.type() == QNetworkProxy::NoProxy) 01512 continue; 01513 01514 if (!telnet(host, port)) 01515 { 01516 LOG(VB_NETWORK, LOG_ERR, LOC + 01517 "failed to contact proxy host " + host); 01518 continue; 01519 } 01520 01521 LOG(VB_NETWORK, LOG_INFO, LOC + QString("using proxy host %1:%2") 01522 .arg(host).arg(port)); 01523 QNetworkProxy::setApplicationProxy(p); 01524 01525 // Allow sub-commands to use this proxy 01526 // via myth_system(command), by setting HTTP_PROXY 01527 QString url; 01528 01529 if (p.user().length()) 01530 url = "http://%1:%2@%3:%4", 01531 url = url.arg(p.user()).arg(p.password()); 01532 else 01533 url = "http://%1:%2"; 01534 01535 url = url.arg(p.hostName()).arg(p.port()); 01536 setenv("HTTP_PROXY", url.toAscii(), 1); 01537 setenv("http_proxy", url.toAscii(), 0); 01538 01539 return; 01540 } 01541 01542 LOG(VB_NETWORK, LOG_ERR, LOC + "failed to find a network proxy"); 01543 } 01544 01545 void wrapList(QStringList &list, int width) 01546 { 01547 int i; 01548 01549 for(i = 0; i < list.size(); i++) 01550 { 01551 QString string = list.at(i); 01552 01553 if( string.size() <= width ) 01554 continue; 01555 01556 QString left = string.left(width); 01557 bool inserted = false; 01558 01559 while( !inserted && left.right(1) != " " ) 01560 { 01561 if( string.mid(left.size(), 1) == " " ) 01562 { 01563 list.replace(i, left); 01564 list.insert(i+1, string.mid(left.size()).trimmed()); 01565 inserted = true; 01566 } 01567 else 01568 { 01569 left.chop(1); 01570 if( !left.contains(" ") ) 01571 { 01572 // Line is too long, just hyphenate it 01573 list.replace(i, left + "-"); 01574 list.insert(i+1, string.mid(left.size())); 01575 inserted = true; 01576 } 01577 } 01578 } 01579 01580 if( !inserted ) 01581 { 01582 left.chop(1); 01583 list.replace(i, left); 01584 list.insert(i+1, string.mid(left.size()).trimmed()); 01585 } 01586 } 01587 } 01588 01589 QString xml_indent(uint level) 01590 { 01591 static QReadWriteLock rw_lock; 01592 static QMap<uint,QString> cache; 01593 01594 rw_lock.lockForRead(); 01595 QMap<uint,QString>::const_iterator it = cache.find(level); 01596 if (it != cache.end()) 01597 { 01598 QString tmp = *it; 01599 rw_lock.unlock(); 01600 return tmp; 01601 } 01602 rw_lock.unlock(); 01603 01604 QString ret = ""; 01605 for (uint i = 0; i < level; i++) 01606 ret += " "; 01607 01608 rw_lock.lockForWrite(); 01609 cache[level] = ret; 01610 rw_lock.unlock(); 01611 01612 return ret; 01613 } 01614 01615 /* vim: set expandtab tabstop=4 shiftwidth=4: */
1.7.6.1