File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / smartmontools / utility.cpp
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Oct 9 09:36:45 2012 UTC (11 years, 8 months ago) by misho
Branches: smartmontools, elwix, MAIN
CVS tags: v5_43, HEAD
smartmontools

    1: /*
    2:  * utility.cpp
    3:  *
    4:  * Home page of code is: http://smartmontools.sourceforge.net
    5:  *
    6:  * Copyright (C) 2002-12 Bruce Allen <smartmontools-support@lists.sourceforge.net>
    7:  * Copyright (C) 2008-12 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,v 1.1.1.2 2012/10/09 09:36:45 misho Exp $"
   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-12 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; either\n"
  105:     "version 2, or (at your option) any later version.\n"
  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>