|
smartmontools SVN Rev 3317
Utility to control and monitor storage systems with "S.M.A.R.T."
|
00001 /* 00002 * utility.cpp 00003 * 00004 * Home page of code is: http://smartmontools.sourceforge.net 00005 * 00006 * Copyright (C) 2002-12 Bruce Allen <smartmontools-support@lists.sourceforge.net> 00007 * Copyright (C) 2008-13 Christian Franke <smartmontools-support@lists.sourceforge.net> 00008 * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org> 00009 * 00010 * This program is free software; you can redistribute it and/or modify 00011 * it under the terms of the GNU General Public License as published by 00012 * the Free Software Foundation; either version 2, or (at your option) 00013 * any later version. 00014 * 00015 * You should have received a copy of the GNU General Public License 00016 * (for example COPYING); If not, see <http://www.gnu.org/licenses/>. 00017 * 00018 * This code was originally developed as a Senior Thesis by Michael Cornwell 00019 * at the Concurrent Systems Laboratory (now part of the Storage Systems 00020 * Research Center), Jack Baskin School of Engineering, University of 00021 * California, Santa Cruz. http://ssrc.soe.ucsc.edu/ 00022 * 00023 */ 00024 00025 // THIS FILE IS INTENDED FOR UTILITY ROUTINES THAT ARE APPLICABLE TO 00026 // BOTH SCSI AND ATA DEVICES, AND THAT MAY BE USED IN SMARTD, 00027 // SMARTCTL, OR BOTH. 00028 00029 #include "config.h" 00030 00031 #include <stdio.h> 00032 #include <string.h> 00033 #include <time.h> 00034 #include <errno.h> 00035 #include <stdlib.h> 00036 #include <ctype.h> 00037 #include <stdarg.h> 00038 #include <sys/stat.h> 00039 #ifdef HAVE_LOCALE_H 00040 #include <locale.h> 00041 #endif 00042 #ifdef _WIN32 00043 #include <mbstring.h> // _mbsinc() 00044 #endif 00045 00046 #include <stdexcept> 00047 00048 #include "svnversion.h" 00049 #include "int64.h" 00050 #include "utility.h" 00051 00052 #include "atacmds.h" 00053 #include "dev_interface.h" 00054 00055 const char * utility_cpp_cvsid = "$Id: utility.cpp 3739 2013-01-01 16:32:48Z chrfranke $" 00056 UTILITY_H_CVSID INT64_H_CVSID; 00057 00058 const char * packet_types[] = { 00059 "Direct-access (disk)", 00060 "Sequential-access (tape)", 00061 "Printer", 00062 "Processor", 00063 "Write-once (optical disk)", 00064 "CD/DVD", 00065 "Scanner", 00066 "Optical memory (optical disk)", 00067 "Medium changer", 00068 "Communications", 00069 "Graphic arts pre-press (10)", 00070 "Graphic arts pre-press (11)", 00071 "Array controller", 00072 "Enclosure services", 00073 "Reduced block command (simplified disk)", 00074 "Optical card reader/writer" 00075 }; 00076 00077 // BUILD_INFO can be provided by package maintainers 00078 #ifndef BUILD_INFO 00079 #define BUILD_INFO "(local build)" 00080 #endif 00081 00082 // Make version information string 00083 std::string format_version_info(const char * prog_name, bool full /*= false*/) 00084 { 00085 std::string info = strprintf( 00086 "%s "PACKAGE_VERSION" " 00087 #ifdef SMARTMONTOOLS_SVN_REV 00088 SMARTMONTOOLS_SVN_DATE" r"SMARTMONTOOLS_SVN_REV 00089 #else 00090 "(build date "__DATE__")" // checkout without expansion of Id keywords 00091 #endif 00092 " [%s] "BUILD_INFO"\n" 00093 "Copyright (C) 2002-13, Bruce Allen, Christian Franke, www.smartmontools.org\n", 00094 prog_name, smi()->get_os_version_str().c_str() 00095 ); 00096 if (!full) 00097 return info; 00098 00099 info += strprintf( 00100 "\n" 00101 "%s comes with ABSOLUTELY NO WARRANTY. This is free\n" 00102 "software, and you are welcome to redistribute it under\n" 00103 "the terms of the GNU General Public License; either\n" 00104 "version 2, or (at your option) any later version.\n" 00105 "See http://www.gnu.org for further details.\n" 00106 "\n", 00107 prog_name 00108 ); 00109 info += strprintf( 00110 "smartmontools release "PACKAGE_VERSION 00111 " dated "SMARTMONTOOLS_RELEASE_DATE" at "SMARTMONTOOLS_RELEASE_TIME"\n" 00112 #ifdef SMARTMONTOOLS_SVN_REV 00113 "smartmontools SVN rev "SMARTMONTOOLS_SVN_REV 00114 " dated "SMARTMONTOOLS_SVN_DATE" at "SMARTMONTOOLS_SVN_TIME"\n" 00115 #else 00116 "smartmontools SVN rev is unknown\n" 00117 #endif 00118 "smartmontools build host: "SMARTMONTOOLS_BUILD_HOST"\n" 00119 "smartmontools build configured: "SMARTMONTOOLS_CONFIGURE_DATE "\n" 00120 "%s compile dated "__DATE__" at "__TIME__"\n" 00121 "smartmontools configure arguments: ", 00122 prog_name 00123 ); 00124 info += (sizeof(SMARTMONTOOLS_CONFIGURE_ARGS) > 1 ? 00125 SMARTMONTOOLS_CONFIGURE_ARGS : "[no arguments given]"); 00126 info += '\n'; 00127 00128 return info; 00129 } 00130 00131 // Solaris only: Get site-default timezone. This is called from 00132 // UpdateTimezone() when TZ environment variable is unset at startup. 00133 #if defined (__SVR4) && defined (__sun) 00134 static const char *TIMEZONE_FILE = "/etc/TIMEZONE"; 00135 00136 static char *ReadSiteDefaultTimezone(){ 00137 FILE *fp; 00138 char buf[512], *tz; 00139 int n; 00140 00141 tz = NULL; 00142 fp = fopen(TIMEZONE_FILE, "r"); 00143 if(fp == NULL) return NULL; 00144 while(fgets(buf, sizeof(buf), fp)) { 00145 if (strncmp(buf, "TZ=", 3)) // searches last "TZ=" line 00146 continue; 00147 n = strlen(buf) - 1; 00148 if (buf[n] == '\n') buf[n] = 0; 00149 if (tz) free(tz); 00150 tz = strdup(buf); 00151 } 00152 fclose(fp); 00153 return tz; 00154 } 00155 #endif 00156 00157 // Make sure that this executable is aware if the user has changed the 00158 // time-zone since the last time we polled devices. The cannonical 00159 // example is a user who starts smartd on a laptop, then flies across 00160 // time-zones with a laptop, and then changes the timezone, WITHOUT 00161 // restarting smartd. This is a work-around for a bug in 00162 // GLIBC. Yuk. See bug number 48184 at http://bugs.debian.org and 00163 // thanks to Ian Redfern for posting a workaround. 00164 00165 // Please refer to the smartd manual page, in the section labeled LOG 00166 // TIMESTAMP TIMEZONE. 00167 void FixGlibcTimeZoneBug(){ 00168 #if __GLIBC__ 00169 if (!getenv("TZ")) { 00170 putenv((char *)"TZ=GMT"); // POSIX prototype is 'int putenv(char *)' 00171 tzset(); 00172 putenv((char *)"TZ"); 00173 tzset(); 00174 } 00175 #elif _WIN32 00176 if (!getenv("TZ")) { 00177 putenv("TZ=GMT"); 00178 tzset(); 00179 putenv("TZ="); // empty value removes TZ, putenv("TZ") does nothing 00180 tzset(); 00181 } 00182 #elif defined (__SVR4) && defined (__sun) 00183 // In Solaris, putenv("TZ=") sets null string and invalid timezone. 00184 // putenv("TZ") does nothing. With invalid TZ, tzset() do as if 00185 // TZ=GMT. With TZ unset, /etc/TIMEZONE will be read only _once_ at 00186 // first tzset() call. Conclusion: Unlike glibc, dynamic 00187 // configuration of timezone can be done only by changing actual 00188 // value of TZ environment value. 00189 enum tzstate { NOT_CALLED_YET, USER_TIMEZONE, TRACK_TIMEZONE }; 00190 static enum tzstate state = NOT_CALLED_YET; 00191 00192 static struct stat prev_stat; 00193 static char *prev_tz; 00194 struct stat curr_stat; 00195 char *curr_tz; 00196 00197 if(state == NOT_CALLED_YET) { 00198 if(getenv("TZ")) { 00199 state = USER_TIMEZONE; // use supplied timezone 00200 } else { 00201 state = TRACK_TIMEZONE; 00202 if(stat(TIMEZONE_FILE, &prev_stat)) { 00203 state = USER_TIMEZONE; // no TZ, no timezone file; use GMT forever 00204 } else { 00205 prev_tz = ReadSiteDefaultTimezone(); // track timezone file change 00206 if(prev_tz) putenv(prev_tz); 00207 } 00208 } 00209 tzset(); 00210 } else if(state == TRACK_TIMEZONE) { 00211 if(stat(TIMEZONE_FILE, &curr_stat) == 0 00212 && (curr_stat.st_ctime != prev_stat.st_ctime 00213 || curr_stat.st_mtime != prev_stat.st_mtime)) { 00214 // timezone file changed 00215 curr_tz = ReadSiteDefaultTimezone(); 00216 if(curr_tz) { 00217 putenv(curr_tz); 00218 if(prev_tz) free(prev_tz); 00219 prev_tz = curr_tz; prev_stat = curr_stat; 00220 } 00221 } 00222 tzset(); 00223 } 00224 #endif 00225 // OTHER OS/LIBRARY FIXES SHOULD GO HERE, IF DESIRED. PLEASE TRY TO 00226 // KEEP THEM INDEPENDENT. 00227 return; 00228 } 00229 00230 #ifdef _WIN32 00231 // Fix strings in tzname[] to avoid long names with non-ascii characters. 00232 // If TZ is not set, tzset() in the MSVC runtime sets tzname[] to the 00233 // national language timezone names returned by GetTimezoneInformation(). 00234 static char * fixtzname(char * dest, int destsize, const char * src) 00235 { 00236 int i = 0, j = 0; 00237 while (src[i] && j < destsize-1) { 00238 int i2 = (const char *)_mbsinc((const unsigned char *)src+i) - src; 00239 if (i2 > i+1) 00240 i = i2; // Ignore multibyte chars 00241 else { 00242 if ('A' <= src[i] && src[i] <= 'Z') 00243 dest[j++] = src[i]; // "Pacific Standard Time" => "PST" 00244 i++; 00245 } 00246 } 00247 if (j < 2) 00248 j = 0; 00249 dest[j] = 0; 00250 return dest; 00251 } 00252 #endif // _WIN32 00253 00254 // This value follows the peripheral device type value as defined in 00255 // SCSI Primary Commands, ANSI INCITS 301:1997. It is also used in 00256 // the ATA standard for packet devices to define the device type. 00257 const char *packetdevicetype(int type){ 00258 if (type<0x10) 00259 return packet_types[type]; 00260 00261 if (type<0x20) 00262 return "Reserved"; 00263 00264 return "Unknown"; 00265 } 00266 00267 // Runtime check of byte ordering, throws if different from isbigendian(). 00268 void check_endianness() 00269 { 00270 union { 00271 // Force compile error if int type is not 32bit. 00272 unsigned char c[sizeof(unsigned) == 4 ? 4 : -1]; 00273 unsigned i; 00274 } x = {{1,2,3,4}}; 00275 00276 int big = -1; 00277 switch (x.i) { 00278 case 0x01020304: big = 1; break; 00279 case 0x04030201: big = 0; break; 00280 } 00281 00282 if (big != (isbigendian() ? 1 : 0)) 00283 throw std::logic_error("CPU endianness does not match compile time test"); 00284 } 00285 00286 // Utility function prints date and time and timezone into a character 00287 // buffer of length>=64. All the fuss is needed to get the right 00288 // timezone info (sigh). 00289 void dateandtimezoneepoch(char *buffer, time_t tval){ 00290 struct tm *tmval; 00291 const char *timezonename; 00292 char datebuffer[DATEANDEPOCHLEN]; 00293 int lenm1; 00294 #ifdef _WIN32 00295 char tzfixbuf[6+1]; 00296 #endif 00297 00298 FixGlibcTimeZoneBug(); 00299 00300 // Get the time structure. We need this to determine if we are in 00301 // daylight savings time or not. 00302 tmval=localtime(&tval); 00303 00304 // Convert to an ASCII string, put in datebuffer 00305 // same as: asctime_r(tmval, datebuffer); 00306 strncpy(datebuffer, asctime(tmval), DATEANDEPOCHLEN); 00307 datebuffer[DATEANDEPOCHLEN-1]='\0'; 00308 00309 // Remove newline 00310 lenm1=strlen(datebuffer)-1; 00311 datebuffer[lenm1>=0?lenm1:0]='\0'; 00312 00313 // correct timezone name 00314 if (tmval->tm_isdst==0) 00315 // standard time zone 00316 timezonename=tzname[0]; 00317 else if (tmval->tm_isdst>0) 00318 // daylight savings in effect 00319 timezonename=tzname[1]; 00320 else 00321 // unable to determine if daylight savings in effect 00322 timezonename=""; 00323 00324 #ifdef _WIN32 00325 // Fix long non-ascii timezone names 00326 if (!getenv("TZ")) 00327 timezonename=fixtzname(tzfixbuf, sizeof(tzfixbuf), timezonename); 00328 #endif 00329 00330 // Finally put the information into the buffer as needed. 00331 snprintf(buffer, DATEANDEPOCHLEN, "%s %s", datebuffer, timezonename); 00332 00333 return; 00334 } 00335 00336 // Date and timezone gets printed into string pointed to by buffer 00337 void dateandtimezone(char *buffer){ 00338 00339 // Get the epoch (time in seconds since Jan 1 1970) 00340 time_t tval=time(NULL); 00341 00342 dateandtimezoneepoch(buffer, tval); 00343 return; 00344 } 00345 00346 // A replacement for perror() that sends output to our choice of 00347 // printing. If errno not set then just print message. 00348 void syserror(const char *message){ 00349 00350 if (errno) { 00351 // Get the correct system error message: 00352 const char *errormessage=strerror(errno); 00353 00354 // Check that caller has handed a sensible string, and provide 00355 // appropriate output. See perrror(3) man page to understand better. 00356 if (message && *message) 00357 pout("%s: %s\n",message, errormessage); 00358 else 00359 pout("%s\n",errormessage); 00360 } 00361 else if (message && *message) 00362 pout("%s\n",message); 00363 00364 return; 00365 } 00366 00367 // POSIX extended regular expressions interpret unmatched ')' ordinary: 00368 // "The close-parenthesis shall be considered special in this context 00369 // only if matched with a preceding open-parenthesis." 00370 // 00371 // Actual '(...)' nesting errors remain undetected on strict POSIX 00372 // implementations (glibc) but an error is reported on others (Cygwin). 00373 // 00374 // The check below is rather incomplete because it does not handle 00375 // e.g. '\)' '[)]'. 00376 // But it should work for the regex subset used in drive database 00377 // and smartd '-s' directives. 00378 static int check_regex_nesting(const char * pattern) 00379 { 00380 int level = 0, i; 00381 for (i = 0; pattern[i] && level >= 0; i++) { 00382 switch (pattern[i]) { 00383 case '(': level++; break; 00384 case ')': level--; break; 00385 } 00386 } 00387 return level; 00388 } 00389 00390 // Wrapper class for regex(3) 00391 00392 regular_expression::regular_expression() 00393 : m_flags(0) 00394 { 00395 memset(&m_regex_buf, 0, sizeof(m_regex_buf)); 00396 } 00397 00398 regular_expression::regular_expression(const char * pattern, int flags, 00399 bool throw_on_error /*= true*/) 00400 { 00401 memset(&m_regex_buf, 0, sizeof(m_regex_buf)); 00402 if (!compile(pattern, flags) && throw_on_error) 00403 throw std::runtime_error(strprintf( 00404 "error in regular expression \"%s\": %s", 00405 m_pattern.c_str(), m_errmsg.c_str())); 00406 } 00407 00408 regular_expression::~regular_expression() 00409 { 00410 free_buf(); 00411 } 00412 00413 regular_expression::regular_expression(const regular_expression & x) 00414 { 00415 memset(&m_regex_buf, 0, sizeof(m_regex_buf)); 00416 copy(x); 00417 } 00418 00419 regular_expression & regular_expression::operator=(const regular_expression & x) 00420 { 00421 free_buf(); 00422 copy(x); 00423 return *this; 00424 } 00425 00426 void regular_expression::free_buf() 00427 { 00428 if (nonempty(&m_regex_buf, sizeof(m_regex_buf))) { 00429 regfree(&m_regex_buf); 00430 memset(&m_regex_buf, 0, sizeof(m_regex_buf)); 00431 } 00432 } 00433 00434 void regular_expression::copy(const regular_expression & x) 00435 { 00436 m_pattern = x.m_pattern; 00437 m_flags = x.m_flags; 00438 m_errmsg = x.m_errmsg; 00439 00440 if (!m_pattern.empty() && m_errmsg.empty()) { 00441 // There is no POSIX compiled-regex-copy command. 00442 if (!compile()) 00443 throw std::runtime_error(strprintf( 00444 "Unable to recompile regular expression \"%s\": %s", 00445 m_pattern.c_str(), m_errmsg.c_str())); 00446 } 00447 } 00448 00449 bool regular_expression::compile(const char * pattern, int flags) 00450 { 00451 free_buf(); 00452 m_pattern = pattern; 00453 m_flags = flags; 00454 return compile(); 00455 } 00456 00457 bool regular_expression::compile() 00458 { 00459 int errcode = regcomp(&m_regex_buf, m_pattern.c_str(), m_flags); 00460 if (errcode) { 00461 char errmsg[512]; 00462 regerror(errcode, &m_regex_buf, errmsg, sizeof(errmsg)); 00463 m_errmsg = errmsg; 00464 free_buf(); 00465 return false; 00466 } 00467 00468 if (check_regex_nesting(m_pattern.c_str()) < 0) { 00469 m_errmsg = "Unmatched ')'"; 00470 free_buf(); 00471 return false; 00472 } 00473 00474 m_errmsg.clear(); 00475 return true; 00476 } 00477 00478 // Splits an argument to the -r option into a name part and an (optional) 00479 // positive integer part. s is a pointer to a string containing the 00480 // argument. After the call, s will point to the name part and *i the 00481 // integer part if there is one or 1 otherwise. Note that the string s may 00482 // be changed by this function. Returns zero if successful and non-zero 00483 // otherwise. 00484 int split_report_arg(char *s, int *i) 00485 { 00486 if ((s = strchr(s, ','))) { 00487 // Looks like there's a name part and an integer part. 00488 char *tailptr; 00489 00490 *s++ = '\0'; 00491 if (*s == '0' || !isdigit((int)*s)) // The integer part must be positive 00492 return 1; 00493 errno = 0; 00494 *i = (int) strtol(s, &tailptr, 10); 00495 if (errno || *tailptr != '\0') 00496 return 1; 00497 } else { 00498 // There's no integer part. 00499 *i = 1; 00500 } 00501 00502 return 0; 00503 } 00504 00505 #ifndef HAVE_STRTOULL 00506 // Replacement for missing strtoull() (Linux with libc < 6, MSVC) 00507 // Functionality reduced to requirements of smartd and split_selective_arg(). 00508 00509 uint64_t strtoull(const char * p, char * * endp, int base) 00510 { 00511 uint64_t result, maxres; 00512 int i = 0; 00513 char c = p[i++]; 00514 00515 if (!base) { 00516 if (c == '0') { 00517 if (p[i] == 'x' || p[i] == 'X') { 00518 base = 16; i++; 00519 } 00520 else 00521 base = 8; 00522 c = p[i++]; 00523 } 00524 else 00525 base = 10; 00526 } 00527 00528 result = 0; 00529 maxres = ~(uint64_t)0 / (unsigned)base; 00530 for (;;) { 00531 unsigned digit; 00532 if ('0' <= c && c <= '9') 00533 digit = c - '0'; 00534 else if ('A' <= c && c <= 'Z') 00535 digit = c - 'A' + 10; 00536 else if ('a' <= c && c <= 'z') 00537 digit = c - 'a' + 10; 00538 else 00539 break; 00540 if (digit >= (unsigned)base) 00541 break; 00542 if (!( result < maxres 00543 || (result == maxres && digit <= ~(uint64_t)0 % (unsigned)base))) { 00544 result = ~(uint64_t)0; errno = ERANGE; // return on overflow 00545 break; 00546 } 00547 result = result * (unsigned)base + digit; 00548 c = p[i++]; 00549 } 00550 if (endp) 00551 *endp = (char *)p + i - 1; 00552 return result; 00553 } 00554 #endif // HAVE_STRTOLL 00555 00556 // Splits an argument to the -t option that is assumed to be of the form 00557 // "selective,%lld-%lld" (prefixes of "0" (for octal) and "0x"/"0X" (for hex) 00558 // are allowed). The first long long int is assigned to *start and the second 00559 // to *stop. Returns zero if successful and non-zero otherwise. 00560 int split_selective_arg(char *s, uint64_t *start, 00561 uint64_t *stop, int *mode) 00562 { 00563 char *tailptr; 00564 if (!(s = strchr(s, ','))) 00565 return 1; 00566 bool add = false; 00567 if (!isdigit((int)(*++s))) { 00568 *start = *stop = 0; 00569 if (!strncmp(s, "redo", 4)) 00570 *mode = SEL_REDO; 00571 else if (!strncmp(s, "next", 4)) 00572 *mode = SEL_NEXT; 00573 else if (!strncmp(s, "cont", 4)) 00574 *mode = SEL_CONT; 00575 else 00576 return 1; 00577 s += 4; 00578 if (!*s) 00579 return 0; 00580 if (*s != '+') 00581 return 1; 00582 } 00583 else { 00584 *mode = SEL_RANGE; 00585 errno = 0; 00586 // Last argument to strtoull (the base) is 0 meaning that decimal is assumed 00587 // unless prefixes of "0" (for octal) or "0x"/"0X" (for hex) are used. 00588 *start = strtoull(s, &tailptr, 0); 00589 s = tailptr; 00590 add = (*s == '+'); 00591 if (!(!errno && (add || *s == '-'))) 00592 return 1; 00593 if (!strcmp(s, "-max")) { 00594 *stop = ~(uint64_t)0; // replaced by max LBA later 00595 return 0; 00596 } 00597 } 00598 00599 errno = 0; 00600 *stop = strtoull(s+1, &tailptr, 0); 00601 if (errno || *tailptr != '\0') 00602 return 1; 00603 if (add) { 00604 if (*stop > 0) 00605 (*stop)--; 00606 *stop += *start; // -t select,N+M => -t select,N,(N+M-1) 00607 } 00608 return 0; 00609 } 00610 00611 #ifdef OLD_INTERFACE 00612 00613 int64_t bytes = 0; 00614 00615 // Helps debugging. If the second argument is non-negative, then 00616 // decrement bytes by that amount. Else decrement bytes by (one plus) 00617 // length of null terminated string. 00618 void *FreeNonZero1(void *address, int size, int line, const char* file){ 00619 if (address) { 00620 if (size<0) 00621 bytes-=1+strlen((char*)address); 00622 else 00623 bytes-=size; 00624 return CheckFree1(address, line, file); 00625 } 00626 return NULL; 00627 } 00628 00629 // To help with memory checking. Use when it is known that address is 00630 // NOT null. 00631 void *CheckFree1(void *address, int /*whatline*/, const char* /*file*/){ 00632 if (address){ 00633 free(address); 00634 return NULL; 00635 } 00636 throw std::runtime_error("Internal error in CheckFree()"); 00637 } 00638 00639 // A custom version of calloc() that tracks memory use 00640 void *Calloc(size_t nmemb, size_t size) { 00641 void *ptr=calloc(nmemb, size); 00642 00643 if (ptr) 00644 bytes+=nmemb*size; 00645 00646 return ptr; 00647 } 00648 00649 // A custom version of strdup() that keeps track of how much memory is 00650 // being allocated. If mustexist is set, it also throws an error if we 00651 // try to duplicate a NULL string. 00652 char *CustomStrDup(const char *ptr, int mustexist, int /*whatline*/, const char* /*file*/){ 00653 char *tmp; 00654 00655 // report error if ptr is NULL and mustexist is set 00656 if (ptr==NULL){ 00657 if (mustexist) 00658 throw std::runtime_error("Internal error in CustomStrDup()"); 00659 else 00660 return NULL; 00661 } 00662 00663 // make a copy of the string... 00664 tmp=strdup(ptr); 00665 00666 if (!tmp) 00667 throw std::bad_alloc(); 00668 00669 // and track memory usage 00670 bytes+=1+strlen(ptr); 00671 00672 return tmp; 00673 } 00674 00675 #endif // OLD_INTERFACE 00676 00677 00678 // Returns true if region of memory contains non-zero entries 00679 bool nonempty(const void * data, int size) 00680 { 00681 for (int i = 0; i < size; i++) 00682 if (((const unsigned char *)data)[i]) 00683 return true; 00684 return false; 00685 } 00686 00687 // Format integer with thousands separator 00688 const char * format_with_thousands_sep(char * str, int strsize, uint64_t val, 00689 const char * thousands_sep /* = 0 */) 00690 { 00691 if (!thousands_sep) { 00692 thousands_sep = ","; 00693 #ifdef HAVE_LOCALE_H 00694 setlocale(LC_ALL, ""); 00695 const struct lconv * currentlocale = localeconv(); 00696 if (*(currentlocale->thousands_sep)) 00697 thousands_sep = currentlocale->thousands_sep; 00698 #endif 00699 } 00700 00701 char num[64]; 00702 snprintf(num, sizeof(num), "%"PRIu64, val); 00703 int numlen = strlen(num); 00704 00705 int i = 0, j = 0; 00706 do 00707 str[j++] = num[i++]; 00708 while (i < numlen && (numlen - i) % 3 != 0 && j < strsize-1); 00709 str[j] = 0; 00710 00711 while (i < numlen && j < strsize-1) { 00712 j += snprintf(str+j, strsize-j, "%s%.3s", thousands_sep, num+i); 00713 i += 3; 00714 } 00715 00716 return str; 00717 } 00718 00719 // Format capacity with SI prefixes 00720 const char * format_capacity(char * str, int strsize, uint64_t val, 00721 const char * decimal_point /* = 0 */) 00722 { 00723 if (!decimal_point) { 00724 decimal_point = "."; 00725 #ifdef HAVE_LOCALE_H 00726 setlocale(LC_ALL, ""); 00727 const struct lconv * currentlocale = localeconv(); 00728 if (*(currentlocale->decimal_point)) 00729 decimal_point = currentlocale->decimal_point; 00730 #endif 00731 } 00732 00733 const unsigned factor = 1000; // 1024 for KiB,MiB,... 00734 static const char prefixes[] = " KMGTP"; 00735 00736 // Find d with val in [d, d*factor) 00737 unsigned i = 0; 00738 uint64_t d = 1; 00739 for (uint64_t d2 = d * factor; val >= d2; d2 *= factor) { 00740 d = d2; 00741 if (++i >= sizeof(prefixes)-2) 00742 break; 00743 } 00744 00745 // Print 3 digits 00746 uint64_t n = val / d; 00747 if (i == 0) 00748 snprintf(str, strsize, "%u B", (unsigned)n); 00749 else if (n >= 100) // "123 xB" 00750 snprintf(str, strsize, "%"PRIu64" %cB", n, prefixes[i]); 00751 else if (n >= 10) // "12.3 xB" 00752 snprintf(str, strsize, "%"PRIu64"%s%u %cB", n, decimal_point, 00753 (unsigned)(((val % d) * 10) / d), prefixes[i]); 00754 else // "1.23 xB" 00755 snprintf(str, strsize, "%"PRIu64"%s%02u %cB", n, decimal_point, 00756 (unsigned)(((val % d) * 100) / d), prefixes[i]); 00757 00758 return str; 00759 } 00760 00761 // return (v)sprintf() formatted std::string 00762 00763 std::string vstrprintf(const char * fmt, va_list ap) 00764 { 00765 char buf[512]; 00766 vsnprintf(buf, sizeof(buf), fmt, ap); 00767 buf[sizeof(buf)-1] = 0; 00768 return buf; 00769 } 00770 00771 std::string strprintf(const char * fmt, ...) 00772 { 00773 va_list ap; va_start(ap, fmt); 00774 std::string str = vstrprintf(fmt, ap); 00775 va_end(ap); 00776 return str; 00777 } 00778 00779 00780 #ifndef HAVE_WORKING_SNPRINTF 00781 // Some versions of (v)snprintf() don't append null char on overflow (MSVCRT.DLL), 00782 // and/or return -1 on overflow (old Linux). 00783 // Below are sane replacements substituted by #define in utility.h. 00784 00785 #undef vsnprintf 00786 #if defined(_WIN32) && defined(_MSC_VER) 00787 #define vsnprintf _vsnprintf 00788 #endif 00789 00790 int safe_vsnprintf(char *buf, int size, const char *fmt, va_list ap) 00791 { 00792 int i; 00793 if (size <= 0) 00794 return 0; 00795 i = vsnprintf(buf, size, fmt, ap); 00796 if (0 <= i && i < size) 00797 return i; 00798 buf[size-1] = 0; 00799 return strlen(buf); // Note: cannot detect for overflow, not necessary here. 00800 } 00801 00802 int safe_snprintf(char *buf, int size, const char *fmt, ...) 00803 { 00804 int i; va_list ap; 00805 va_start(ap, fmt); 00806 i = safe_vsnprintf(buf, size, fmt, ap); 00807 va_end(ap); 00808 return i; 00809 } 00810 00811 #endif 00812
1.7.4