Annotation of embedaddon/smartmontools/utility.cpp, revision 1.1
1.1 ! misho 1: /*
! 2: * utility.cpp
! 3: *
! 4: * Home page of code is: http://smartmontools.sourceforge.net
! 5: *
! 6: * Copyright (C) 2002-11 Bruce Allen <smartmontools-support@lists.sourceforge.net>
! 7: * Copyright (C) 2008-11 Christian Franke <smartmontools-support@lists.sourceforge.net>
! 8: * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org>
! 9: *
! 10: * This program is free software; you can redistribute it and/or modify
! 11: * it under the terms of the GNU General Public License as published by
! 12: * the Free Software Foundation; either version 2, or (at your option)
! 13: * any later version.
! 14: *
! 15: * You should have received a copy of the GNU General Public License
! 16: * (for example COPYING); if not, write to the Free
! 17: * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
! 18: *
! 19: * This code was originally developed as a Senior Thesis by Michael Cornwell
! 20: * at the Concurrent Systems Laboratory (now part of the Storage Systems
! 21: * Research Center), Jack Baskin School of Engineering, University of
! 22: * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
! 23: *
! 24: */
! 25:
! 26: // THIS FILE IS INTENDED FOR UTILITY ROUTINES THAT ARE APPLICABLE TO
! 27: // BOTH SCSI AND ATA DEVICES, AND THAT MAY BE USED IN SMARTD,
! 28: // SMARTCTL, OR BOTH.
! 29:
! 30: #include "config.h"
! 31:
! 32: #include <stdio.h>
! 33: #include <string.h>
! 34: #include <time.h>
! 35: #include <errno.h>
! 36: #include <stdlib.h>
! 37: #include <ctype.h>
! 38: #include <stdarg.h>
! 39: #include <sys/stat.h>
! 40: #ifdef HAVE_LOCALE_H
! 41: #include <locale.h>
! 42: #endif
! 43: #ifdef _WIN32
! 44: #include <mbstring.h> // _mbsinc()
! 45: #endif
! 46:
! 47: #include <stdexcept>
! 48:
! 49: #include "svnversion.h"
! 50: #include "int64.h"
! 51: #include "utility.h"
! 52:
! 53: #include "atacmds.h"
! 54: #include "dev_interface.h"
! 55:
! 56: const char * utility_cpp_cvsid = "$Id: utility.cpp 3305 2011-03-30 21:32:05Z chrfranke $"
! 57: UTILITY_H_CVSID INT64_H_CVSID;
! 58:
! 59: const char * packet_types[] = {
! 60: "Direct-access (disk)",
! 61: "Sequential-access (tape)",
! 62: "Printer",
! 63: "Processor",
! 64: "Write-once (optical disk)",
! 65: "CD/DVD",
! 66: "Scanner",
! 67: "Optical memory (optical disk)",
! 68: "Medium changer",
! 69: "Communications",
! 70: "Graphic arts pre-press (10)",
! 71: "Graphic arts pre-press (11)",
! 72: "Array controller",
! 73: "Enclosure services",
! 74: "Reduced block command (simplified disk)",
! 75: "Optical card reader/writer"
! 76: };
! 77:
! 78: // BUILD_INFO can be provided by package maintainers
! 79: #ifndef BUILD_INFO
! 80: #define BUILD_INFO "(local build)"
! 81: #endif
! 82:
! 83: // Make version information string
! 84: std::string format_version_info(const char * prog_name, bool full /*= false*/)
! 85: {
! 86: std::string info = strprintf(
! 87: "%s "PACKAGE_VERSION" "
! 88: #ifdef SMARTMONTOOLS_SVN_REV
! 89: SMARTMONTOOLS_SVN_DATE" r"SMARTMONTOOLS_SVN_REV
! 90: #else
! 91: "(build date "__DATE__")" // checkout without expansion of Id keywords
! 92: #endif
! 93: " [%s] "BUILD_INFO"\n"
! 94: "Copyright (C) 2002-11 by Bruce Allen, http://smartmontools.sourceforge.net\n",
! 95: prog_name, smi()->get_os_version_str().c_str()
! 96: );
! 97: if (!full)
! 98: return info;
! 99:
! 100: info += strprintf(
! 101: "\n"
! 102: "%s comes with ABSOLUTELY NO WARRANTY. This is free\n"
! 103: "software, and you are welcome to redistribute it under\n"
! 104: "the terms of the GNU General Public License Version 2.\n"
! 105: "See http://www.gnu.org for further details.\n"
! 106: "\n",
! 107: prog_name
! 108: );
! 109: info += strprintf(
! 110: "smartmontools release "PACKAGE_VERSION
! 111: " dated "SMARTMONTOOLS_RELEASE_DATE" at "SMARTMONTOOLS_RELEASE_TIME"\n"
! 112: #ifdef SMARTMONTOOLS_SVN_REV
! 113: "smartmontools SVN rev "SMARTMONTOOLS_SVN_REV
! 114: " dated "SMARTMONTOOLS_SVN_DATE" at "SMARTMONTOOLS_SVN_TIME"\n"
! 115: #else
! 116: "smartmontools SVN rev is unknown\n"
! 117: #endif
! 118: "smartmontools build host: "SMARTMONTOOLS_BUILD_HOST"\n"
! 119: "smartmontools build configured: "SMARTMONTOOLS_CONFIGURE_DATE "\n"
! 120: "%s compile dated "__DATE__" at "__TIME__"\n"
! 121: "smartmontools configure arguments: ",
! 122: prog_name
! 123: );
! 124: info += (sizeof(SMARTMONTOOLS_CONFIGURE_ARGS) > 1 ?
! 125: SMARTMONTOOLS_CONFIGURE_ARGS : "[no arguments given]");
! 126: info += '\n';
! 127:
! 128: return info;
! 129: }
! 130:
! 131: // Solaris only: Get site-default timezone. This is called from
! 132: // UpdateTimezone() when TZ environment variable is unset at startup.
! 133: #if defined (__SVR4) && defined (__sun)
! 134: static const char *TIMEZONE_FILE = "/etc/TIMEZONE";
! 135:
! 136: static char *ReadSiteDefaultTimezone(){
! 137: FILE *fp;
! 138: char buf[512], *tz;
! 139: int n;
! 140:
! 141: tz = NULL;
! 142: fp = fopen(TIMEZONE_FILE, "r");
! 143: if(fp == NULL) return NULL;
! 144: while(fgets(buf, sizeof(buf), fp)) {
! 145: if (strncmp(buf, "TZ=", 3)) // searches last "TZ=" line
! 146: continue;
! 147: n = strlen(buf) - 1;
! 148: if (buf[n] == '\n') buf[n] = 0;
! 149: if (tz) free(tz);
! 150: tz = strdup(buf);
! 151: }
! 152: fclose(fp);
! 153: return tz;
! 154: }
! 155: #endif
! 156:
! 157: // Make sure that this executable is aware if the user has changed the
! 158: // time-zone since the last time we polled devices. The cannonical
! 159: // example is a user who starts smartd on a laptop, then flies across
! 160: // time-zones with a laptop, and then changes the timezone, WITHOUT
! 161: // restarting smartd. This is a work-around for a bug in
! 162: // GLIBC. Yuk. See bug number 48184 at http://bugs.debian.org and
! 163: // thanks to Ian Redfern for posting a workaround.
! 164:
! 165: // Please refer to the smartd manual page, in the section labeled LOG
! 166: // TIMESTAMP TIMEZONE.
! 167: void FixGlibcTimeZoneBug(){
! 168: #if __GLIBC__
! 169: if (!getenv("TZ")) {
! 170: putenv((char *)"TZ=GMT"); // POSIX prototype is 'int putenv(char *)'
! 171: tzset();
! 172: putenv((char *)"TZ");
! 173: tzset();
! 174: }
! 175: #elif _WIN32
! 176: if (!getenv("TZ")) {
! 177: putenv("TZ=GMT");
! 178: tzset();
! 179: putenv("TZ="); // empty value removes TZ, putenv("TZ") does nothing
! 180: tzset();
! 181: }
! 182: #elif defined (__SVR4) && defined (__sun)
! 183: // In Solaris, putenv("TZ=") sets null string and invalid timezone.
! 184: // putenv("TZ") does nothing. With invalid TZ, tzset() do as if
! 185: // TZ=GMT. With TZ unset, /etc/TIMEZONE will be read only _once_ at
! 186: // first tzset() call. Conclusion: Unlike glibc, dynamic
! 187: // configuration of timezone can be done only by changing actual
! 188: // value of TZ environment value.
! 189: enum tzstate { NOT_CALLED_YET, USER_TIMEZONE, TRACK_TIMEZONE };
! 190: static enum tzstate state = NOT_CALLED_YET;
! 191:
! 192: static struct stat prev_stat;
! 193: static char *prev_tz;
! 194: struct stat curr_stat;
! 195: char *curr_tz;
! 196:
! 197: if(state == NOT_CALLED_YET) {
! 198: if(getenv("TZ")) {
! 199: state = USER_TIMEZONE; // use supplied timezone
! 200: } else {
! 201: state = TRACK_TIMEZONE;
! 202: if(stat(TIMEZONE_FILE, &prev_stat)) {
! 203: state = USER_TIMEZONE; // no TZ, no timezone file; use GMT forever
! 204: } else {
! 205: prev_tz = ReadSiteDefaultTimezone(); // track timezone file change
! 206: if(prev_tz) putenv(prev_tz);
! 207: }
! 208: }
! 209: tzset();
! 210: } else if(state == TRACK_TIMEZONE) {
! 211: if(stat(TIMEZONE_FILE, &curr_stat) == 0
! 212: && (curr_stat.st_ctime != prev_stat.st_ctime
! 213: || curr_stat.st_mtime != prev_stat.st_mtime)) {
! 214: // timezone file changed
! 215: curr_tz = ReadSiteDefaultTimezone();
! 216: if(curr_tz) {
! 217: putenv(curr_tz);
! 218: if(prev_tz) free(prev_tz);
! 219: prev_tz = curr_tz; prev_stat = curr_stat;
! 220: }
! 221: }
! 222: tzset();
! 223: }
! 224: #endif
! 225: // OTHER OS/LIBRARY FIXES SHOULD GO HERE, IF DESIRED. PLEASE TRY TO
! 226: // KEEP THEM INDEPENDENT.
! 227: return;
! 228: }
! 229:
! 230: #ifdef _WIN32
! 231: // Fix strings in tzname[] to avoid long names with non-ascii characters.
! 232: // If TZ is not set, tzset() in the MSVC runtime sets tzname[] to the
! 233: // national language timezone names returned by GetTimezoneInformation().
! 234: static char * fixtzname(char * dest, int destsize, const char * src)
! 235: {
! 236: int i = 0, j = 0;
! 237: while (src[i] && j < destsize-1) {
! 238: int i2 = (const char *)_mbsinc((const unsigned char *)src+i) - src;
! 239: if (i2 > i+1)
! 240: i = i2; // Ignore multibyte chars
! 241: else {
! 242: if ('A' <= src[i] && src[i] <= 'Z')
! 243: dest[j++] = src[i]; // "Pacific Standard Time" => "PST"
! 244: i++;
! 245: }
! 246: }
! 247: if (j < 2)
! 248: j = 0;
! 249: dest[j] = 0;
! 250: return dest;
! 251: }
! 252: #endif // _WIN32
! 253:
! 254: // This value follows the peripheral device type value as defined in
! 255: // SCSI Primary Commands, ANSI INCITS 301:1997. It is also used in
! 256: // the ATA standard for packet devices to define the device type.
! 257: const char *packetdevicetype(int type){
! 258: if (type<0x10)
! 259: return packet_types[type];
! 260:
! 261: if (type<0x20)
! 262: return "Reserved";
! 263:
! 264: return "Unknown";
! 265: }
! 266:
! 267: // Runtime check of byte ordering, throws if different from isbigendian().
! 268: void check_endianness()
! 269: {
! 270: union {
! 271: // Force compile error if int type is not 32bit.
! 272: unsigned char c[sizeof(unsigned) == 4 ? 4 : -1];
! 273: unsigned i;
! 274: } x = {{1,2,3,4}};
! 275:
! 276: int big = -1;
! 277: switch (x.i) {
! 278: case 0x01020304: big = 1; break;
! 279: case 0x04030201: big = 0; break;
! 280: }
! 281:
! 282: if (big != (isbigendian() ? 1 : 0))
! 283: throw std::logic_error("CPU endianness does not match compile time test");
! 284: }
! 285:
! 286: // Utility function prints date and time and timezone into a character
! 287: // buffer of length>=64. All the fuss is needed to get the right
! 288: // timezone info (sigh).
! 289: void dateandtimezoneepoch(char *buffer, time_t tval){
! 290: struct tm *tmval;
! 291: const char *timezonename;
! 292: char datebuffer[DATEANDEPOCHLEN];
! 293: int lenm1;
! 294: #ifdef _WIN32
! 295: char tzfixbuf[6+1];
! 296: #endif
! 297:
! 298: FixGlibcTimeZoneBug();
! 299:
! 300: // Get the time structure. We need this to determine if we are in
! 301: // daylight savings time or not.
! 302: tmval=localtime(&tval);
! 303:
! 304: // Convert to an ASCII string, put in datebuffer
! 305: // same as: asctime_r(tmval, datebuffer);
! 306: strncpy(datebuffer, asctime(tmval), DATEANDEPOCHLEN);
! 307: datebuffer[DATEANDEPOCHLEN-1]='\0';
! 308:
! 309: // Remove newline
! 310: lenm1=strlen(datebuffer)-1;
! 311: datebuffer[lenm1>=0?lenm1:0]='\0';
! 312:
! 313: // correct timezone name
! 314: if (tmval->tm_isdst==0)
! 315: // standard time zone
! 316: timezonename=tzname[0];
! 317: else if (tmval->tm_isdst>0)
! 318: // daylight savings in effect
! 319: timezonename=tzname[1];
! 320: else
! 321: // unable to determine if daylight savings in effect
! 322: timezonename="";
! 323:
! 324: #ifdef _WIN32
! 325: // Fix long non-ascii timezone names
! 326: if (!getenv("TZ"))
! 327: timezonename=fixtzname(tzfixbuf, sizeof(tzfixbuf), timezonename);
! 328: #endif
! 329:
! 330: // Finally put the information into the buffer as needed.
! 331: snprintf(buffer, DATEANDEPOCHLEN, "%s %s", datebuffer, timezonename);
! 332:
! 333: return;
! 334: }
! 335:
! 336: // Date and timezone gets printed into string pointed to by buffer
! 337: void dateandtimezone(char *buffer){
! 338:
! 339: // Get the epoch (time in seconds since Jan 1 1970)
! 340: time_t tval=time(NULL);
! 341:
! 342: dateandtimezoneepoch(buffer, tval);
! 343: return;
! 344: }
! 345:
! 346: // A replacement for perror() that sends output to our choice of
! 347: // printing. If errno not set then just print message.
! 348: void syserror(const char *message){
! 349:
! 350: if (errno) {
! 351: // Get the correct system error message:
! 352: const char *errormessage=strerror(errno);
! 353:
! 354: // Check that caller has handed a sensible string, and provide
! 355: // appropriate output. See perrror(3) man page to understand better.
! 356: if (message && *message)
! 357: pout("%s: %s\n",message, errormessage);
! 358: else
! 359: pout("%s\n",errormessage);
! 360: }
! 361: else if (message && *message)
! 362: pout("%s\n",message);
! 363:
! 364: return;
! 365: }
! 366:
! 367: // POSIX extended regular expressions interpret unmatched ')' ordinary:
! 368: // "The close-parenthesis shall be considered special in this context
! 369: // only if matched with a preceding open-parenthesis."
! 370: //
! 371: // Actual '(...)' nesting errors remain undetected on strict POSIX
! 372: // implementations (glibc) but an error is reported on others (Cygwin).
! 373: //
! 374: // The check below is rather incomplete because it does not handle
! 375: // e.g. '\)' '[)]'.
! 376: // But it should work for the regex subset used in drive database
! 377: // and smartd '-s' directives.
! 378: static int check_regex_nesting(const char * pattern)
! 379: {
! 380: int level = 0, i;
! 381: for (i = 0; pattern[i] && level >= 0; i++) {
! 382: switch (pattern[i]) {
! 383: case '(': level++; break;
! 384: case ')': level--; break;
! 385: }
! 386: }
! 387: return level;
! 388: }
! 389:
! 390: // Wrapper class for regex(3)
! 391:
! 392: regular_expression::regular_expression()
! 393: : m_flags(0)
! 394: {
! 395: memset(&m_regex_buf, 0, sizeof(m_regex_buf));
! 396: }
! 397:
! 398: regular_expression::regular_expression(const char * pattern, int flags,
! 399: bool throw_on_error /*= true*/)
! 400: {
! 401: memset(&m_regex_buf, 0, sizeof(m_regex_buf));
! 402: if (!compile(pattern, flags) && throw_on_error)
! 403: throw std::runtime_error(strprintf(
! 404: "error in regular expression \"%s\": %s",
! 405: m_pattern.c_str(), m_errmsg.c_str()));
! 406: }
! 407:
! 408: regular_expression::~regular_expression()
! 409: {
! 410: free_buf();
! 411: }
! 412:
! 413: regular_expression::regular_expression(const regular_expression & x)
! 414: {
! 415: memset(&m_regex_buf, 0, sizeof(m_regex_buf));
! 416: copy(x);
! 417: }
! 418:
! 419: regular_expression & regular_expression::operator=(const regular_expression & x)
! 420: {
! 421: free_buf();
! 422: copy(x);
! 423: return *this;
! 424: }
! 425:
! 426: void regular_expression::free_buf()
! 427: {
! 428: if (nonempty(&m_regex_buf, sizeof(m_regex_buf))) {
! 429: regfree(&m_regex_buf);
! 430: memset(&m_regex_buf, 0, sizeof(m_regex_buf));
! 431: }
! 432: }
! 433:
! 434: void regular_expression::copy(const regular_expression & x)
! 435: {
! 436: m_pattern = x.m_pattern;
! 437: m_flags = x.m_flags;
! 438: m_errmsg = x.m_errmsg;
! 439:
! 440: if (!m_pattern.empty() && m_errmsg.empty()) {
! 441: // There is no POSIX compiled-regex-copy command.
! 442: if (!compile())
! 443: throw std::runtime_error(strprintf(
! 444: "Unable to recompile regular expression \"%s\": %s",
! 445: m_pattern.c_str(), m_errmsg.c_str()));
! 446: }
! 447: }
! 448:
! 449: bool regular_expression::compile(const char * pattern, int flags)
! 450: {
! 451: free_buf();
! 452: m_pattern = pattern;
! 453: m_flags = flags;
! 454: return compile();
! 455: }
! 456:
! 457: bool regular_expression::compile()
! 458: {
! 459: int errcode = regcomp(&m_regex_buf, m_pattern.c_str(), m_flags);
! 460: if (errcode) {
! 461: char errmsg[512];
! 462: regerror(errcode, &m_regex_buf, errmsg, sizeof(errmsg));
! 463: m_errmsg = errmsg;
! 464: free_buf();
! 465: return false;
! 466: }
! 467:
! 468: if (check_regex_nesting(m_pattern.c_str()) < 0) {
! 469: m_errmsg = "Unmatched ')'";
! 470: free_buf();
! 471: return false;
! 472: }
! 473:
! 474: m_errmsg.clear();
! 475: return true;
! 476: }
! 477:
! 478: // Splits an argument to the -r option into a name part and an (optional)
! 479: // positive integer part. s is a pointer to a string containing the
! 480: // argument. After the call, s will point to the name part and *i the
! 481: // integer part if there is one or 1 otherwise. Note that the string s may
! 482: // be changed by this function. Returns zero if successful and non-zero
! 483: // otherwise.
! 484: int split_report_arg(char *s, int *i)
! 485: {
! 486: if ((s = strchr(s, ','))) {
! 487: // Looks like there's a name part and an integer part.
! 488: char *tailptr;
! 489:
! 490: *s++ = '\0';
! 491: if (*s == '0' || !isdigit((int)*s)) // The integer part must be positive
! 492: return 1;
! 493: errno = 0;
! 494: *i = (int) strtol(s, &tailptr, 10);
! 495: if (errno || *tailptr != '\0')
! 496: return 1;
! 497: } else {
! 498: // There's no integer part.
! 499: *i = 1;
! 500: }
! 501:
! 502: return 0;
! 503: }
! 504:
! 505: // same as above but sets *i to -1 if missing , argument
! 506: int split_report_arg2(char *s, int *i){
! 507: char *tailptr;
! 508: s+=6;
! 509:
! 510: if (*s=='\0' || !isdigit((int)*s)) {
! 511: // What's left must be integer
! 512: *i=-1;
! 513: return 1;
! 514: }
! 515:
! 516: errno = 0;
! 517: *i = (int) strtol(s, &tailptr, 10);
! 518: if (errno || *tailptr != '\0') {
! 519: *i=-1;
! 520: return 1;
! 521: }
! 522:
! 523: return 0;
! 524: }
! 525:
! 526: #ifndef HAVE_STRTOULL
! 527: // Replacement for missing strtoull() (Linux with libc < 6, MSVC)
! 528: // Functionality reduced to requirements of smartd and split_selective_arg().
! 529:
! 530: uint64_t strtoull(const char * p, char * * endp, int base)
! 531: {
! 532: uint64_t result, maxres;
! 533: int i = 0;
! 534: char c = p[i++];
! 535:
! 536: if (!base) {
! 537: if (c == '0') {
! 538: if (p[i] == 'x' || p[i] == 'X') {
! 539: base = 16; i++;
! 540: }
! 541: else
! 542: base = 8;
! 543: c = p[i++];
! 544: }
! 545: else
! 546: base = 10;
! 547: }
! 548:
! 549: result = 0;
! 550: maxres = ~(uint64_t)0 / (unsigned)base;
! 551: for (;;) {
! 552: unsigned digit;
! 553: if ('0' <= c && c <= '9')
! 554: digit = c - '0';
! 555: else if ('A' <= c && c <= 'Z')
! 556: digit = c - 'A' + 10;
! 557: else if ('a' <= c && c <= 'z')
! 558: digit = c - 'a' + 10;
! 559: else
! 560: break;
! 561: if (digit >= (unsigned)base)
! 562: break;
! 563: if (!( result < maxres
! 564: || (result == maxres && digit <= ~(uint64_t)0 % (unsigned)base))) {
! 565: result = ~(uint64_t)0; errno = ERANGE; // return on overflow
! 566: break;
! 567: }
! 568: result = result * (unsigned)base + digit;
! 569: c = p[i++];
! 570: }
! 571: if (endp)
! 572: *endp = (char *)p + i - 1;
! 573: return result;
! 574: }
! 575: #endif // HAVE_STRTOLL
! 576:
! 577: // Splits an argument to the -t option that is assumed to be of the form
! 578: // "selective,%lld-%lld" (prefixes of "0" (for octal) and "0x"/"0X" (for hex)
! 579: // are allowed). The first long long int is assigned to *start and the second
! 580: // to *stop. Returns zero if successful and non-zero otherwise.
! 581: int split_selective_arg(char *s, uint64_t *start,
! 582: uint64_t *stop, int *mode)
! 583: {
! 584: char *tailptr;
! 585: if (!(s = strchr(s, ',')))
! 586: return 1;
! 587: bool add = false;
! 588: if (!isdigit((int)(*++s))) {
! 589: *start = *stop = 0;
! 590: if (!strncmp(s, "redo", 4))
! 591: *mode = SEL_REDO;
! 592: else if (!strncmp(s, "next", 4))
! 593: *mode = SEL_NEXT;
! 594: else if (!strncmp(s, "cont", 4))
! 595: *mode = SEL_CONT;
! 596: else
! 597: return 1;
! 598: s += 4;
! 599: if (!*s)
! 600: return 0;
! 601: if (*s != '+')
! 602: return 1;
! 603: }
! 604: else {
! 605: *mode = SEL_RANGE;
! 606: errno = 0;
! 607: // Last argument to strtoull (the base) is 0 meaning that decimal is assumed
! 608: // unless prefixes of "0" (for octal) or "0x"/"0X" (for hex) are used.
! 609: *start = strtoull(s, &tailptr, 0);
! 610: s = tailptr;
! 611: add = (*s == '+');
! 612: if (!(!errno && (add || *s == '-')))
! 613: return 1;
! 614: if (!strcmp(s, "-max")) {
! 615: *stop = ~(uint64_t)0; // replaced by max LBA later
! 616: return 0;
! 617: }
! 618: }
! 619: *stop = strtoull(s+1, &tailptr, 0);
! 620: if (errno || *tailptr != '\0')
! 621: return 1;
! 622: if (add) {
! 623: if (*stop > 0)
! 624: (*stop)--;
! 625: *stop += *start; // -t select,N+M => -t select,N,(N+M-1)
! 626: }
! 627: return 0;
! 628: }
! 629:
! 630: #ifdef OLD_INTERFACE
! 631:
! 632: int64_t bytes = 0;
! 633:
! 634: // Helps debugging. If the second argument is non-negative, then
! 635: // decrement bytes by that amount. Else decrement bytes by (one plus)
! 636: // length of null terminated string.
! 637: void *FreeNonZero1(void *address, int size, int line, const char* file){
! 638: if (address) {
! 639: if (size<0)
! 640: bytes-=1+strlen((char*)address);
! 641: else
! 642: bytes-=size;
! 643: return CheckFree1(address, line, file);
! 644: }
! 645: return NULL;
! 646: }
! 647:
! 648: // To help with memory checking. Use when it is known that address is
! 649: // NOT null.
! 650: void *CheckFree1(void *address, int /*whatline*/, const char* /*file*/){
! 651: if (address){
! 652: free(address);
! 653: return NULL;
! 654: }
! 655: throw std::runtime_error("Internal error in CheckFree()");
! 656: }
! 657:
! 658: // A custom version of calloc() that tracks memory use
! 659: void *Calloc(size_t nmemb, size_t size) {
! 660: void *ptr=calloc(nmemb, size);
! 661:
! 662: if (ptr)
! 663: bytes+=nmemb*size;
! 664:
! 665: return ptr;
! 666: }
! 667:
! 668: // A custom version of strdup() that keeps track of how much memory is
! 669: // being allocated. If mustexist is set, it also throws an error if we
! 670: // try to duplicate a NULL string.
! 671: char *CustomStrDup(const char *ptr, int mustexist, int /*whatline*/, const char* /*file*/){
! 672: char *tmp;
! 673:
! 674: // report error if ptr is NULL and mustexist is set
! 675: if (ptr==NULL){
! 676: if (mustexist)
! 677: throw std::runtime_error("Internal error in CustomStrDup()");
! 678: else
! 679: return NULL;
! 680: }
! 681:
! 682: // make a copy of the string...
! 683: tmp=strdup(ptr);
! 684:
! 685: if (!tmp)
! 686: throw std::bad_alloc();
! 687:
! 688: // and track memory usage
! 689: bytes+=1+strlen(ptr);
! 690:
! 691: return tmp;
! 692: }
! 693:
! 694: #endif // OLD_INTERFACE
! 695:
! 696:
! 697: // Returns true if region of memory contains non-zero entries
! 698: bool nonempty(const void * data, int size)
! 699: {
! 700: for (int i = 0; i < size; i++)
! 701: if (((const unsigned char *)data)[i])
! 702: return true;
! 703: return false;
! 704: }
! 705:
! 706:
! 707: // This routine converts an integer number of milliseconds into a test
! 708: // string of the form Xd+Yh+Zm+Ts.msec. The resulting text string is
! 709: // written to the array.
! 710: void MsecToText(unsigned int msec, char *txt){
! 711: unsigned int days, hours, min, sec;
! 712:
! 713: days = msec/86400000U;
! 714: msec -= days*86400000U;
! 715:
! 716: hours = msec/3600000U;
! 717: msec -= hours*3600000U;
! 718:
! 719: min = msec/60000U;
! 720: msec -= min*60000U;
! 721:
! 722: sec = msec/1000U;
! 723: msec -= sec*1000U;
! 724:
! 725: if (days) {
! 726: txt += sprintf(txt, "%2dd+", (int)days);
! 727: }
! 728:
! 729: sprintf(txt, "%02d:%02d:%02d.%03d", (int)hours, (int)min, (int)sec, (int)msec);
! 730: return;
! 731: }
! 732:
! 733: // Format integer with thousands separator
! 734: const char * format_with_thousands_sep(char * str, int strsize, uint64_t val,
! 735: const char * thousands_sep /* = 0 */)
! 736: {
! 737: if (!thousands_sep) {
! 738: thousands_sep = ",";
! 739: #ifdef HAVE_LOCALE_H
! 740: setlocale(LC_ALL, "");
! 741: const struct lconv * currentlocale = localeconv();
! 742: if (*(currentlocale->thousands_sep))
! 743: thousands_sep = currentlocale->thousands_sep;
! 744: #endif
! 745: }
! 746:
! 747: char num[64];
! 748: snprintf(num, sizeof(num), "%"PRIu64, val);
! 749: int numlen = strlen(num);
! 750:
! 751: int i = 0, j = 0;
! 752: do
! 753: str[j++] = num[i++];
! 754: while (i < numlen && (numlen - i) % 3 != 0 && j < strsize-1);
! 755: str[j] = 0;
! 756:
! 757: while (i < numlen && j < strsize-1) {
! 758: j += snprintf(str+j, strsize-j, "%s%.3s", thousands_sep, num+i);
! 759: i += 3;
! 760: }
! 761:
! 762: return str;
! 763: }
! 764:
! 765: // Format capacity with SI prefixes
! 766: const char * format_capacity(char * str, int strsize, uint64_t val,
! 767: const char * decimal_point /* = 0 */)
! 768: {
! 769: if (!decimal_point) {
! 770: decimal_point = ".";
! 771: #ifdef HAVE_LOCALE_H
! 772: setlocale(LC_ALL, "");
! 773: const struct lconv * currentlocale = localeconv();
! 774: if (*(currentlocale->decimal_point))
! 775: decimal_point = currentlocale->decimal_point;
! 776: #endif
! 777: }
! 778:
! 779: const unsigned factor = 1000; // 1024 for KiB,MiB,...
! 780: static const char prefixes[] = " KMGTP";
! 781:
! 782: // Find d with val in [d, d*factor)
! 783: unsigned i = 0;
! 784: uint64_t d = 1;
! 785: for (uint64_t d2 = d * factor; val >= d2; d2 *= factor) {
! 786: d = d2;
! 787: if (++i >= sizeof(prefixes)-2)
! 788: break;
! 789: }
! 790:
! 791: // Print 3 digits
! 792: uint64_t n = val / d;
! 793: if (i == 0)
! 794: snprintf(str, strsize, "%u B", (unsigned)n);
! 795: else if (n >= 100) // "123 xB"
! 796: snprintf(str, strsize, "%"PRIu64" %cB", n, prefixes[i]);
! 797: else if (n >= 10) // "12.3 xB"
! 798: snprintf(str, strsize, "%"PRIu64"%s%u %cB", n, decimal_point,
! 799: (unsigned)(((val % d) * 10) / d), prefixes[i]);
! 800: else // "1.23 xB"
! 801: snprintf(str, strsize, "%"PRIu64"%s%02u %cB", n, decimal_point,
! 802: (unsigned)(((val % d) * 100) / d), prefixes[i]);
! 803:
! 804: return str;
! 805: }
! 806:
! 807: // return (v)sprintf() formatted std::string
! 808:
! 809: std::string vstrprintf(const char * fmt, va_list ap)
! 810: {
! 811: char buf[512];
! 812: vsnprintf(buf, sizeof(buf), fmt, ap);
! 813: buf[sizeof(buf)-1] = 0;
! 814: return buf;
! 815: }
! 816:
! 817: std::string strprintf(const char * fmt, ...)
! 818: {
! 819: va_list ap; va_start(ap, fmt);
! 820: std::string str = vstrprintf(fmt, ap);
! 821: va_end(ap);
! 822: return str;
! 823: }
! 824:
! 825:
! 826: #ifndef HAVE_WORKING_SNPRINTF
! 827: // Some versions of (v)snprintf() don't append null char on overflow (MSVCRT.DLL),
! 828: // and/or return -1 on overflow (old Linux).
! 829: // Below are sane replacements substituted by #define in utility.h.
! 830:
! 831: #undef vsnprintf
! 832: #if defined(_WIN32) && defined(_MSC_VER)
! 833: #define vsnprintf _vsnprintf
! 834: #endif
! 835:
! 836: int safe_vsnprintf(char *buf, int size, const char *fmt, va_list ap)
! 837: {
! 838: int i;
! 839: if (size <= 0)
! 840: return 0;
! 841: i = vsnprintf(buf, size, fmt, ap);
! 842: if (0 <= i && i < size)
! 843: return i;
! 844: buf[size-1] = 0;
! 845: return strlen(buf); // Note: cannot detect for overflow, not necessary here.
! 846: }
! 847:
! 848: int safe_snprintf(char *buf, int size, const char *fmt, ...)
! 849: {
! 850: int i; va_list ap;
! 851: va_start(ap, fmt);
! 852: i = safe_vsnprintf(buf, size, fmt, ap);
! 853: va_end(ap);
! 854: return i;
! 855: }
! 856:
! 857: #endif
! 858:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>