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