]> git.proxmox.com Git - mirror_smartmontools-debian.git/blame - utility.cpp
New upstream version 6.6
[mirror_smartmontools-debian.git] / utility.cpp
CommitLineData
832b75ed 1/*
4d59bff9 2 * utility.cpp
832b75ed 3 *
a86ec89e 4 * Home page of code is: http://www.smartmontools.org
832b75ed 5 *
a86ec89e 6 * Copyright (C) 2002-12 Bruce Allen
f9e10201 7 * Copyright (C) 2008-17 Christian Franke
832b75ed
GG
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
ee38a438 16 * (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
832b75ed
GG
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
a7e8ffec
GI
29#include "config.h"
30
832b75ed
GG
31#include <stdio.h>
32#include <string.h>
33#include <time.h>
34#include <errno.h>
35#include <stdlib.h>
36#include <ctype.h>
832b75ed
GG
37#include <stdarg.h>
38#include <sys/stat.h>
a7e8ffec
GI
39#ifdef HAVE_LOCALE_H
40#include <locale.h>
41#endif
832b75ed
GG
42#ifdef _WIN32
43#include <mbstring.h> // _mbsinc()
44#endif
45
2127e193
GI
46#include <stdexcept>
47
2127e193 48#include "svnversion.h"
832b75ed
GG
49#include "int64.h"
50#include "utility.h"
51
2127e193
GI
52#include "atacmds.h"
53#include "dev_interface.h"
54
f9e10201 55const char * utility_cpp_cvsid = "$Id: utility.cpp 4583 2017-11-03 21:10:09Z chrfranke $"
2127e193 56 UTILITY_H_CVSID INT64_H_CVSID;
832b75ed
GG
57
58const 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
2127e193
GI
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
83std::string format_version_info(const char * prog_name, bool full /*= false*/)
84{
85 std::string info = strprintf(
d2e702cf 86 "%s " PACKAGE_VERSION " "
cfbba5b9 87#ifdef SMARTMONTOOLS_SVN_REV
d2e702cf 88 SMARTMONTOOLS_SVN_DATE " r" SMARTMONTOOLS_SVN_REV
cfbba5b9 89#else
d2e702cf 90 "(build date " __DATE__ ")" // checkout without expansion of Id keywords
cfbba5b9 91#endif
d2e702cf 92 " [%s] " BUILD_INFO "\n"
f9e10201 93 "Copyright (C) 2002-17, Bruce Allen, Christian Franke, www.smartmontools.org\n",
54965743 94 prog_name, smi()->get_os_version_str().c_str()
2127e193
GI
95 );
96 if (!full)
97 return info;
98
a86ec89e
GI
99 info += "\n";
100 info += prog_name;
101 info += " comes with ABSOLUTELY NO WARRANTY. This is free\n"
2127e193 102 "software, and you are welcome to redistribute it under\n"
d008864d
GI
103 "the terms of the GNU General Public License; either\n"
104 "version 2, or (at your option) any later version.\n"
2127e193 105 "See http://www.gnu.org for further details.\n"
a86ec89e 106 "\n"
d2e702cf
GI
107 "smartmontools release " PACKAGE_VERSION
108 " dated " SMARTMONTOOLS_RELEASE_DATE " at " SMARTMONTOOLS_RELEASE_TIME "\n"
cfbba5b9 109#ifdef SMARTMONTOOLS_SVN_REV
d2e702cf
GI
110 "smartmontools SVN rev " SMARTMONTOOLS_SVN_REV
111 " dated " SMARTMONTOOLS_SVN_DATE " at " SMARTMONTOOLS_SVN_TIME "\n"
cfbba5b9
GI
112#else
113 "smartmontools SVN rev is unknown\n"
114#endif
d2e702cf 115 "smartmontools build host: " SMARTMONTOOLS_BUILD_HOST "\n"
a86ec89e
GI
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
d2e702cf 126#if defined(__GNUC__) && defined(__VERSION__) // works also with CLang
a86ec89e 127 ", GCC " __VERSION__
d2e702cf 128#endif
a86ec89e
GI
129 "\n"
130 "smartmontools configure arguments:"
d2e702cf 131 ;
bed94269 132 info += (sizeof(SMARTMONTOOLS_CONFIGURE_ARGS) > 1 ?
a86ec89e 133 SMARTMONTOOLS_CONFIGURE_ARGS : " [no arguments given]");
bed94269 134 info += '\n';
2127e193
GI
135
136 return info;
137}
832b75ed
GG
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)
142static const char *TIMEZONE_FILE = "/etc/TIMEZONE";
143
144static 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.
175void FixGlibcTimeZoneBug(){
176#if __GLIBC__
177 if (!getenv("TZ")) {
2127e193 178 putenv((char *)"TZ=GMT"); // POSIX prototype is 'int putenv(char *)'
832b75ed 179 tzset();
2127e193 180 putenv((char *)"TZ");
832b75ed
GG
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().
242static 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.
265const 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
e9583e0c 275// Runtime check of byte ordering, throws if different from isbigendian().
d2e702cf 276static void check_endianness()
e9583e0c
GI
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 }
832b75ed 289
e9583e0c
GI
290 if (big != (isbigendian() ? 1 : 0))
291 throw std::logic_error("CPU endianness does not match compile time test");
832b75ed
GG
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).
297void dateandtimezoneepoch(char *buffer, time_t tval){
298 struct tm *tmval;
2127e193 299 const char *timezonename;
832b75ed
GG
300 char datebuffer[DATEANDEPOCHLEN];
301 int lenm1;
832b75ed
GG
302
303 FixGlibcTimeZoneBug();
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';
f9e10201
JD
317
318#if defined(_WIN32) && defined(_MSC_VER)
319 // tzname is missing in MSVC14
320 #define tzname _tzname
321#endif
322
832b75ed
GG
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
a86ec89e
GI
336 // cppcheck-suppress variableScope
337 char tzfixbuf[6+1] = "";
832b75ed
GG
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
349void dateandtimezone(char *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
832b75ed
GG
358// A replacement for perror() that sends output to our choice of
359// printing. If errno not set then just print message.
360void 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
3d17a85c
GI
379// Check regular expression for non-portable features.
380//
832b75ed
GG
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//
3d17a85c
GI
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//
394static const char * check_regex(const char * pattern)
832b75ed 395{
3d17a85c
GI
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;
832b75ed 405 }
3d17a85c
GI
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";
832b75ed 432 }
3d17a85c
GI
433
434 return (const char *)0;
832b75ed
GG
435}
436
2127e193 437// Wrapper class for regex(3)
832b75ed 438
2127e193
GI
439regular_expression::regular_expression()
440: m_flags(0)
441{
442 memset(&m_regex_buf, 0, sizeof(m_regex_buf));
443}
444
cfbba5b9
GI
445regular_expression::regular_expression(const char * pattern, int flags,
446 bool throw_on_error /*= true*/)
2127e193
GI
447{
448 memset(&m_regex_buf, 0, sizeof(m_regex_buf));
cfbba5b9
GI
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()));
2127e193
GI
453}
454
455regular_expression::~regular_expression()
456{
457 free_buf();
458}
459
460regular_expression::regular_expression(const regular_expression & x)
461{
462 memset(&m_regex_buf, 0, sizeof(m_regex_buf));
463 copy(x);
464}
465
466regular_expression & regular_expression::operator=(const regular_expression & x)
467{
468 free_buf();
469 copy(x);
470 return *this;
471}
472
473void regular_expression::free_buf()
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));
832b75ed 478 }
2127e193
GI
479}
480
481void regular_expression::copy(const regular_expression & x)
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
496bool 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
504bool regular_expression::compile()
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
3d17a85c
GI
515 const char * errmsg = check_regex(m_pattern.c_str());
516 if (errmsg) {
517 m_errmsg = errmsg;
2127e193
GI
518 free_buf();
519 return false;
520 }
521
522 m_errmsg.clear();
523 return true;
832b75ed
GG
524}
525
832b75ed 526#ifndef HAVE_STRTOULL
2127e193
GI
527// Replacement for missing strtoull() (Linux with libc < 6, MSVC)
528// Functionality reduced to requirements of smartd and split_selective_arg().
832b75ed 529
2127e193 530uint64_t strtoull(const char * p, char * * endp, int base)
832b75ed
GG
531{
532 uint64_t result, maxres;
533 int i = 0;
534 char c = p[i++];
2127e193
GI
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++];
832b75ed
GG
544 }
545 else
2127e193 546 base = 10;
832b75ed 547 }
832b75ed
GG
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 }
2127e193
GI
571 if (endp)
572 *endp = (char *)p + i - 1;
832b75ed
GG
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.
581int split_selective_arg(char *s, uint64_t *start,
a37e7145 582 uint64_t *stop, int *mode)
832b75ed
GG
583{
584 char *tailptr;
832b75ed
GG
585 if (!(s = strchr(s, ',')))
586 return 1;
a37e7145
GG
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 }
ee38a438
GI
619
620 errno = 0;
a37e7145 621 *stop = strtoull(s+1, &tailptr, 0);
832b75ed
GG
622 if (errno || *tailptr != '\0')
623 return 1;
a37e7145
GG
624 if (add) {
625 if (*stop > 0)
626 (*stop)--;
627 *stop += *start; // -t select,N+M => -t select,N,(N+M-1)
628 }
832b75ed
GG
629 return 0;
630}
631
2127e193
GI
632// Returns true if region of memory contains non-zero entries
633bool 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;
832b75ed
GG
639}
640
a86ec89e
GI
641// Copy not null terminated char array to null terminated string.
642// Replace non-ascii characters. Remove leading and trailing blanks.
643const 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
a7e8ffec
GI
666// Format integer with thousands separator
667const 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];
d2e702cf 681 snprintf(num, sizeof(num), "%" PRIu64, val);
a7e8ffec
GI
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
699const 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"
d2e702cf 729 snprintf(str, strsize, "%" PRIu64 " %cB", n, prefixes[i]);
a7e8ffec 730 else if (n >= 10) // "12.3 xB"
d2e702cf 731 snprintf(str, strsize, "%" PRIu64 "%s%u %cB", n, decimal_point,
a7e8ffec
GI
732 (unsigned)(((val % d) * 10) / d), prefixes[i]);
733 else // "1.23 xB"
d2e702cf 734 snprintf(str, strsize, "%" PRIu64 "%s%02u %cB", n, decimal_point,
a7e8ffec
GI
735 (unsigned)(((val % d) * 100) / d), prefixes[i]);
736
737 return str;
738}
739
2127e193 740// return (v)sprintf() formatted std::string
f9e10201 741__attribute_format_printf(1, 0)
2127e193
GI
742std::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
750std::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
832b75ed
GG
758
759#ifndef HAVE_WORKING_SNPRINTF
d2e702cf
GI
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).
832b75ed
GG
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
769int 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
781int 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
f9e10201
JD
790static void check_snprintf() {}
791
792#elif defined(__GNUC__) && (__GNUC__ >= 7)
793
794// G++ 7+: Assume sane implementation and avoid -Wformat-truncation warning
795static void check_snprintf() {}
796
797#else
d2e702cf
GI
798
799static void check_snprintf()
800{
801 char buf[] = "ABCDEFGHI";
802 int n1 = snprintf(buf, 8, "123456789");
803 int n2 = snprintf(buf, 0, "X");
804 if (!(!strcmp(buf, "1234567") && n1 == 9 && n2 == 1))
f9e10201 805 throw std::logic_error("Function snprintf() does not conform to C99");
d2e702cf
GI
806}
807
808#endif // HAVE_WORKING_SNPRINTF
a23d5117 809
d2e702cf
GI
810// Runtime check of ./configure result, throws on error.
811void check_config()
812{
813 check_endianness();
d2e702cf 814 check_snprintf();
d2e702cf 815}