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>