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