root/trunk/sm5/utility.cpp @ 2832

Revision 2832, 21.2 KB (checked in by chrfranke, 5 years ago)

Set eol-style native for all source files.

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