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