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