Annotation of embedaddon/smartmontools/utility.cpp, revision 1.1.1.2

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>