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