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

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>
1.1.1.3 ! misho       7:  * Copyright (C) 2008-13 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
1.1.1.3 ! misho      16:  * (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
1.1       misho      17:  *
                     18:  * This code was originally developed as a Senior Thesis by Michael Cornwell
                     19:  * at the Concurrent Systems Laboratory (now part of the Storage Systems
                     20:  * Research Center), Jack Baskin School of Engineering, University of
                     21:  * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
                     22:  *
                     23:  */
                     24: 
                     25: // THIS FILE IS INTENDED FOR UTILITY ROUTINES THAT ARE APPLICABLE TO
                     26: // BOTH SCSI AND ATA DEVICES, AND THAT MAY BE USED IN SMARTD,
                     27: // SMARTCTL, OR BOTH.
                     28: 
                     29: #include "config.h"
                     30: 
                     31: #include <stdio.h>
                     32: #include <string.h>
                     33: #include <time.h>
                     34: #include <errno.h>
                     35: #include <stdlib.h>
                     36: #include <ctype.h>
                     37: #include <stdarg.h>
                     38: #include <sys/stat.h>
                     39: #ifdef HAVE_LOCALE_H
                     40: #include <locale.h>
                     41: #endif
                     42: #ifdef _WIN32
                     43: #include <mbstring.h> // _mbsinc()
                     44: #endif
                     45: 
                     46: #include <stdexcept>
                     47: 
                     48: #include "svnversion.h"
                     49: #include "int64.h"
                     50: #include "utility.h"
                     51: 
                     52: #include "atacmds.h"
                     53: #include "dev_interface.h"
                     54: 
1.1.1.3 ! misho      55: const char * utility_cpp_cvsid = "$Id: utility.cpp 3739 2013-01-01 16:32:48Z chrfranke $"
1.1       misho      56:                                  UTILITY_H_CVSID INT64_H_CVSID;
                     57: 
                     58: const char * packet_types[] = {
                     59:         "Direct-access (disk)",
                     60:         "Sequential-access (tape)",
                     61:         "Printer",
                     62:         "Processor",
                     63:         "Write-once (optical disk)",
                     64:         "CD/DVD",
                     65:         "Scanner",
                     66:         "Optical memory (optical disk)",
                     67:         "Medium changer",
                     68:         "Communications",
                     69:         "Graphic arts pre-press (10)",
                     70:         "Graphic arts pre-press (11)",
                     71:         "Array controller",
                     72:         "Enclosure services",
                     73:         "Reduced block command (simplified disk)",
                     74:         "Optical card reader/writer"
                     75: };
                     76: 
                     77: // BUILD_INFO can be provided by package maintainers
                     78: #ifndef BUILD_INFO
                     79: #define BUILD_INFO "(local build)"
                     80: #endif
                     81: 
                     82: // Make version information string
                     83: std::string format_version_info(const char * prog_name, bool full /*= false*/)
                     84: {
                     85:   std::string info = strprintf(
                     86:     "%s "PACKAGE_VERSION" "
                     87: #ifdef SMARTMONTOOLS_SVN_REV
                     88:       SMARTMONTOOLS_SVN_DATE" r"SMARTMONTOOLS_SVN_REV
                     89: #else
                     90:       "(build date "__DATE__")" // checkout without expansion of Id keywords
                     91: #endif
                     92:       " [%s] "BUILD_INFO"\n"
1.1.1.3 ! misho      93:     "Copyright (C) 2002-13, Bruce Allen, Christian Franke, www.smartmontools.org\n",
1.1       misho      94:     prog_name, smi()->get_os_version_str().c_str()
                     95:   );
                     96:   if (!full)
                     97:     return info;
                     98: 
                     99:   info += strprintf(
                    100:     "\n"
                    101:     "%s comes with ABSOLUTELY NO WARRANTY. This is free\n"
                    102:     "software, and you are welcome to redistribute it under\n"
1.1.1.2   misho     103:     "the terms of the GNU General Public License; either\n"
                    104:     "version 2, or (at your option) any later version.\n"
1.1       misho     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: #ifndef HAVE_STRTOULL
                    506: // Replacement for missing strtoull() (Linux with libc < 6, MSVC)
                    507: // Functionality reduced to requirements of smartd and split_selective_arg().
                    508: 
                    509: uint64_t strtoull(const char * p, char * * endp, int base)
                    510: {
                    511:   uint64_t result, maxres;
                    512:   int i = 0;
                    513:   char c = p[i++];
                    514: 
                    515:   if (!base) {
                    516:     if (c == '0') {
                    517:       if (p[i] == 'x' || p[i] == 'X') {
                    518:         base = 16; i++;
                    519:       }
                    520:       else
                    521:         base = 8;
                    522:       c = p[i++];
                    523:     }
                    524:     else
                    525:       base = 10;
                    526:   }
                    527: 
                    528:   result = 0;
                    529:   maxres = ~(uint64_t)0 / (unsigned)base;
                    530:   for (;;) {
                    531:     unsigned digit;
                    532:     if ('0' <= c && c <= '9')
                    533:       digit = c - '0';
                    534:     else if ('A' <= c && c <= 'Z')
                    535:       digit = c - 'A' + 10;
                    536:     else if ('a' <= c && c <= 'z')
                    537:       digit = c - 'a' + 10;
                    538:     else
                    539:       break;
                    540:     if (digit >= (unsigned)base)
                    541:       break;
                    542:     if (!(   result < maxres
                    543:           || (result == maxres && digit <= ~(uint64_t)0 % (unsigned)base))) {
                    544:       result = ~(uint64_t)0; errno = ERANGE; // return on overflow
                    545:       break;
                    546:     }
                    547:     result = result * (unsigned)base + digit;
                    548:     c = p[i++];
                    549:   }
                    550:   if (endp)
                    551:     *endp = (char *)p + i - 1;
                    552:   return result;
                    553: }
                    554: #endif // HAVE_STRTOLL
                    555: 
                    556: // Splits an argument to the -t option that is assumed to be of the form
                    557: // "selective,%lld-%lld" (prefixes of "0" (for octal) and "0x"/"0X" (for hex)
                    558: // are allowed).  The first long long int is assigned to *start and the second
                    559: // to *stop.  Returns zero if successful and non-zero otherwise.
                    560: int split_selective_arg(char *s, uint64_t *start,
                    561:                         uint64_t *stop, int *mode)
                    562: {
                    563:   char *tailptr;
                    564:   if (!(s = strchr(s, ',')))
                    565:     return 1;
                    566:   bool add = false;
                    567:   if (!isdigit((int)(*++s))) {
                    568:     *start = *stop = 0;
                    569:     if (!strncmp(s, "redo", 4))
                    570:       *mode = SEL_REDO;
                    571:     else if (!strncmp(s, "next", 4))
                    572:       *mode = SEL_NEXT;
                    573:     else if (!strncmp(s, "cont", 4))
                    574:       *mode = SEL_CONT;
                    575:     else
                    576:       return 1;
                    577:     s += 4;
                    578:     if (!*s)
                    579:       return 0;
                    580:     if (*s != '+')
                    581:       return 1;
                    582:   }
                    583:   else {
                    584:     *mode = SEL_RANGE;
                    585:     errno = 0;
                    586:     // Last argument to strtoull (the base) is 0 meaning that decimal is assumed
                    587:     // unless prefixes of "0" (for octal) or "0x"/"0X" (for hex) are used.
                    588:     *start = strtoull(s, &tailptr, 0);
                    589:     s = tailptr;
                    590:     add = (*s == '+');
                    591:     if (!(!errno && (add || *s == '-')))
                    592:       return 1;
                    593:     if (!strcmp(s, "-max")) {
                    594:       *stop = ~(uint64_t)0; // replaced by max LBA later
                    595:       return 0;
                    596:     }
                    597:   }
1.1.1.3 ! misho     598: 
        !           599:   errno = 0;
1.1       misho     600:   *stop = strtoull(s+1, &tailptr, 0);
                    601:   if (errno || *tailptr != '\0')
                    602:     return 1;
                    603:   if (add) {
                    604:     if (*stop > 0)
                    605:       (*stop)--;
                    606:     *stop += *start; // -t select,N+M => -t select,N,(N+M-1)
                    607:   }
                    608:   return 0;
                    609: }
                    610: 
                    611: #ifdef OLD_INTERFACE
                    612: 
                    613: int64_t bytes = 0;
                    614: 
                    615: // Helps debugging.  If the second argument is non-negative, then
                    616: // decrement bytes by that amount.  Else decrement bytes by (one plus)
                    617: // length of null terminated string.
                    618: void *FreeNonZero1(void *address, int size, int line, const char* file){
                    619:   if (address) {
                    620:     if (size<0)
                    621:       bytes-=1+strlen((char*)address);
                    622:     else
                    623:       bytes-=size;
                    624:     return CheckFree1(address, line, file);
                    625:   }
                    626:   return NULL;
                    627: }
                    628: 
                    629: // To help with memory checking.  Use when it is known that address is
                    630: // NOT null.
                    631: void *CheckFree1(void *address, int /*whatline*/, const char* /*file*/){
                    632:   if (address){
                    633:     free(address);
                    634:     return NULL;
                    635:   }
                    636:   throw std::runtime_error("Internal error in CheckFree()");
                    637: }
                    638: 
                    639: // A custom version of calloc() that tracks memory use
                    640: void *Calloc(size_t nmemb, size_t size) { 
                    641:   void *ptr=calloc(nmemb, size);
                    642:   
                    643:   if (ptr)
                    644:     bytes+=nmemb*size;
                    645: 
                    646:   return ptr;
                    647: }
                    648: 
                    649: // A custom version of strdup() that keeps track of how much memory is
                    650: // being allocated. If mustexist is set, it also throws an error if we
                    651: // try to duplicate a NULL string.
                    652: char *CustomStrDup(const char *ptr, int mustexist, int /*whatline*/, const char* /*file*/){
                    653:   char *tmp;
                    654: 
                    655:   // report error if ptr is NULL and mustexist is set
                    656:   if (ptr==NULL){
                    657:     if (mustexist)
                    658:       throw std::runtime_error("Internal error in CustomStrDup()");
                    659:     else
                    660:       return NULL;
                    661:   }
                    662: 
                    663:   // make a copy of the string...
                    664:   tmp=strdup(ptr);
                    665:   
                    666:   if (!tmp)
                    667:     throw std::bad_alloc();
                    668:   
                    669:   // and track memory usage
                    670:   bytes+=1+strlen(ptr);
                    671:   
                    672:   return tmp;
                    673: }
                    674: 
                    675: #endif // OLD_INTERFACE
                    676: 
                    677: 
                    678: // Returns true if region of memory contains non-zero entries
                    679: bool nonempty(const void * data, int size)
                    680: {
                    681:   for (int i = 0; i < size; i++)
                    682:     if (((const unsigned char *)data)[i])
                    683:       return true;
                    684:   return false;
                    685: }
                    686: 
                    687: // Format integer with thousands separator
                    688: const char * format_with_thousands_sep(char * str, int strsize, uint64_t val,
                    689:                                        const char * thousands_sep /* = 0 */)
                    690: {
                    691:   if (!thousands_sep) {
                    692:     thousands_sep = ",";
                    693: #ifdef HAVE_LOCALE_H
                    694:     setlocale(LC_ALL, "");
                    695:     const struct lconv * currentlocale = localeconv();
                    696:     if (*(currentlocale->thousands_sep))
                    697:       thousands_sep = currentlocale->thousands_sep;
                    698: #endif
                    699:   }
                    700: 
                    701:   char num[64];
                    702:   snprintf(num, sizeof(num), "%"PRIu64, val);
                    703:   int numlen = strlen(num);
                    704: 
                    705:   int i = 0, j = 0;
                    706:   do
                    707:     str[j++] = num[i++];
                    708:   while (i < numlen && (numlen - i) % 3 != 0 && j < strsize-1);
                    709:   str[j] = 0;
                    710: 
                    711:   while (i < numlen && j < strsize-1) {
                    712:     j += snprintf(str+j, strsize-j, "%s%.3s", thousands_sep, num+i);
                    713:     i += 3;
                    714:   }
                    715: 
                    716:   return str;
                    717: }
                    718: 
                    719: // Format capacity with SI prefixes
                    720: const char * format_capacity(char * str, int strsize, uint64_t val,
                    721:                              const char * decimal_point /* = 0 */)
                    722: {
                    723:   if (!decimal_point) {
                    724:     decimal_point = ".";
                    725: #ifdef HAVE_LOCALE_H
                    726:     setlocale(LC_ALL, "");
                    727:     const struct lconv * currentlocale = localeconv();
                    728:     if (*(currentlocale->decimal_point))
                    729:       decimal_point = currentlocale->decimal_point;
                    730: #endif
                    731:   }
                    732: 
                    733:   const unsigned factor = 1000; // 1024 for KiB,MiB,...
                    734:   static const char prefixes[] = " KMGTP";
                    735: 
                    736:   // Find d with val in [d, d*factor)
                    737:   unsigned i = 0;
                    738:   uint64_t d = 1;
                    739:   for (uint64_t d2 = d * factor; val >= d2; d2 *= factor) {
                    740:     d = d2;
                    741:     if (++i >= sizeof(prefixes)-2)
                    742:       break;
                    743:   }
                    744: 
                    745:   // Print 3 digits
                    746:   uint64_t n = val / d;
                    747:   if (i == 0)
                    748:     snprintf(str, strsize, "%u B", (unsigned)n);
                    749:   else if (n >= 100) // "123 xB"
                    750:     snprintf(str, strsize, "%"PRIu64" %cB", n, prefixes[i]);
                    751:   else if (n >= 10)  // "12.3 xB"
                    752:     snprintf(str, strsize, "%"PRIu64"%s%u %cB", n, decimal_point,
                    753:         (unsigned)(((val % d) * 10) / d), prefixes[i]);
                    754:   else               // "1.23 xB"
                    755:     snprintf(str, strsize, "%"PRIu64"%s%02u %cB", n, decimal_point,
                    756:         (unsigned)(((val % d) * 100) / d), prefixes[i]);
                    757: 
                    758:   return str;
                    759: }
                    760: 
                    761: // return (v)sprintf() formatted std::string
                    762: 
                    763: std::string vstrprintf(const char * fmt, va_list ap)
                    764: {
                    765:   char buf[512];
                    766:   vsnprintf(buf, sizeof(buf), fmt, ap);
                    767:   buf[sizeof(buf)-1] = 0;
                    768:   return buf;
                    769: }
                    770: 
                    771: std::string strprintf(const char * fmt, ...)
                    772: {
                    773:   va_list ap; va_start(ap, fmt);
                    774:   std::string str = vstrprintf(fmt, ap);
                    775:   va_end(ap);
                    776:   return str;
                    777: }
                    778: 
                    779: 
                    780: #ifndef HAVE_WORKING_SNPRINTF
                    781: // Some versions of (v)snprintf() don't append null char on overflow (MSVCRT.DLL),
                    782: // and/or return -1 on overflow (old Linux).
                    783: // Below are sane replacements substituted by #define in utility.h.
                    784: 
                    785: #undef vsnprintf
                    786: #if defined(_WIN32) && defined(_MSC_VER)
                    787: #define vsnprintf _vsnprintf
                    788: #endif
                    789: 
                    790: int safe_vsnprintf(char *buf, int size, const char *fmt, va_list ap)
                    791: {
                    792:   int i;
                    793:   if (size <= 0)
                    794:     return 0;
                    795:   i = vsnprintf(buf, size, fmt, ap);
                    796:   if (0 <= i && i < size)
                    797:     return i;
                    798:   buf[size-1] = 0;
                    799:   return strlen(buf); // Note: cannot detect for overflow, not necessary here.
                    800: }
                    801: 
                    802: int safe_snprintf(char *buf, int size, const char *fmt, ...)
                    803: {
                    804:   int i; va_list ap;
                    805:   va_start(ap, fmt);
                    806:   i = safe_vsnprintf(buf, size, fmt, ap);
                    807:   va_end(ap);
                    808:   return i;
                    809: }
                    810: 
                    811: #endif
                    812: 

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