]> git.proxmox.com Git - mirror_smartmontools-debian.git/blob - utility.cpp
Imported Upstream version 6.1+svn3812
[mirror_smartmontools-debian.git] / utility.cpp
1 /*
2 * utility.cpp
3 *
4 * Home page of code is: http://smartmontools.sourceforge.net
5 *
6 * Copyright (C) 2002-12 Bruce Allen <smartmontools-support@lists.sourceforge.net>
7 * Copyright (C) 2008-13 Christian Franke <smartmontools-support@lists.sourceforge.net>
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, see <http://www.gnu.org/licenses/>.
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
29 #include "config.h"
30
31 #include <stdio.h>
32 #include <string.h>
33 #include <time.h>
34 #include <errno.h>
35 #include <stdlib.h>
36 #include <ctype.h>
37 #include <stdarg.h>
38 #include <sys/stat.h>
39 #ifdef HAVE_LOCALE_H
40 #include <locale.h>
41 #endif
42 #ifdef _WIN32
43 #include <mbstring.h> // _mbsinc()
44 #endif
45
46 #include <stdexcept>
47
48 #include "svnversion.h"
49 #include "int64.h"
50 #include "utility.h"
51
52 #include "atacmds.h"
53 #include "dev_interface.h"
54
55 const char * utility_cpp_cvsid = "$Id: utility.cpp 3739 2013-01-01 16:32:48Z chrfranke $"
56 UTILITY_H_CVSID INT64_H_CVSID;
57
58 const 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
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
83 std::string format_version_info(const char * prog_name, bool full /*= false*/)
84 {
85 std::string info = strprintf(
86 "%s "PACKAGE_VERSION" "
87 #ifdef SMARTMONTOOLS_SVN_REV
88 SMARTMONTOOLS_SVN_DATE" r"SMARTMONTOOLS_SVN_REV
89 #else
90 "(build date "__DATE__")" // checkout without expansion of Id keywords
91 #endif
92 " [%s] "BUILD_INFO"\n"
93 "Copyright (C) 2002-13, Bruce Allen, Christian Franke, www.smartmontools.org\n",
94 prog_name, smi()->get_os_version_str().c_str()
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"
103 "the terms of the GNU General Public License; either\n"
104 "version 2, or (at your option) any later version.\n"
105 "See http://www.gnu.org for further details.\n"
106 "\n",
107 prog_name
108 );
109 info += strprintf(
110 "smartmontools release "PACKAGE_VERSION
111 " dated "SMARTMONTOOLS_RELEASE_DATE" at "SMARTMONTOOLS_RELEASE_TIME"\n"
112 #ifdef SMARTMONTOOLS_SVN_REV
113 "smartmontools SVN rev "SMARTMONTOOLS_SVN_REV
114 " dated "SMARTMONTOOLS_SVN_DATE" at "SMARTMONTOOLS_SVN_TIME"\n"
115 #else
116 "smartmontools SVN rev is unknown\n"
117 #endif
118 "smartmontools build host: "SMARTMONTOOLS_BUILD_HOST"\n"
119 "smartmontools build configured: "SMARTMONTOOLS_CONFIGURE_DATE "\n"
120 "%s compile dated "__DATE__" at "__TIME__"\n"
121 "smartmontools configure arguments: ",
122 prog_name
123 );
124 info += (sizeof(SMARTMONTOOLS_CONFIGURE_ARGS) > 1 ?
125 SMARTMONTOOLS_CONFIGURE_ARGS : "[no arguments given]");
126 info += '\n';
127
128 return info;
129 }
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)
134 static const char *TIMEZONE_FILE = "/etc/TIMEZONE";
135
136 static 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.
167 void FixGlibcTimeZoneBug(){
168 #if __GLIBC__
169 if (!getenv("TZ")) {
170 putenv((char *)"TZ=GMT"); // POSIX prototype is 'int putenv(char *)'
171 tzset();
172 putenv((char *)"TZ");
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().
234 static 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.
257 const 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
267 // Runtime check of byte ordering, throws if different from isbigendian().
268 void check_endianness()
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 }
281
282 if (big != (isbigendian() ? 1 : 0))
283 throw std::logic_error("CPU endianness does not match compile time test");
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).
289 void dateandtimezoneepoch(char *buffer, time_t tval){
290 struct tm *tmval;
291 const char *timezonename;
292 char datebuffer[DATEANDEPOCHLEN];
293 int lenm1;
294 #ifdef _WIN32
295 char tzfixbuf[6+1];
296 #endif
297
298 FixGlibcTimeZoneBug();
299
300 // Get the time structure. We need this to determine if we are in
301 // daylight savings time or not.
302 tmval=localtime(&tval);
303
304 // Convert to an ASCII string, put in datebuffer
305 // same as: asctime_r(tmval, datebuffer);
306 strncpy(datebuffer, asctime(tmval), DATEANDEPOCHLEN);
307 datebuffer[DATEANDEPOCHLEN-1]='\0';
308
309 // Remove newline
310 lenm1=strlen(datebuffer)-1;
311 datebuffer[lenm1>=0?lenm1:0]='\0';
312
313 // correct timezone name
314 if (tmval->tm_isdst==0)
315 // standard time zone
316 timezonename=tzname[0];
317 else if (tmval->tm_isdst>0)
318 // daylight savings in effect
319 timezonename=tzname[1];
320 else
321 // unable to determine if daylight savings in effect
322 timezonename="";
323
324 #ifdef _WIN32
325 // Fix long non-ascii timezone names
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 // Date and timezone gets printed into string pointed to by buffer
337 void dateandtimezone(char *buffer){
338
339 // Get the epoch (time in seconds since Jan 1 1970)
340 time_t tval=time(NULL);
341
342 dateandtimezoneepoch(buffer, tval);
343 return;
344 }
345
346 // A replacement for perror() that sends output to our choice of
347 // printing. If errno not set then just print message.
348 void syserror(const char *message){
349
350 if (errno) {
351 // Get the correct system error message:
352 const char *errormessage=strerror(errno);
353
354 // Check that caller has handed a sensible string, and provide
355 // appropriate output. See perrror(3) man page to understand better.
356 if (message && *message)
357 pout("%s: %s\n",message, errormessage);
358 else
359 pout("%s\n",errormessage);
360 }
361 else if (message && *message)
362 pout("%s\n",message);
363
364 return;
365 }
366
367 // POSIX extended regular expressions interpret unmatched ')' ordinary:
368 // "The close-parenthesis shall be considered special in this context
369 // only if matched with a preceding open-parenthesis."
370 //
371 // Actual '(...)' nesting errors remain undetected on strict POSIX
372 // implementations (glibc) but an error is reported on others (Cygwin).
373 //
374 // The check below is rather incomplete because it does not handle
375 // e.g. '\)' '[)]'.
376 // But it should work for the regex subset used in drive database
377 // and smartd '-s' directives.
378 static int check_regex_nesting(const char * pattern)
379 {
380 int level = 0, i;
381 for (i = 0; pattern[i] && level >= 0; i++) {
382 switch (pattern[i]) {
383 case '(': level++; break;
384 case ')': level--; break;
385 }
386 }
387 return level;
388 }
389
390 // Wrapper class for regex(3)
391
392 regular_expression::regular_expression()
393 : m_flags(0)
394 {
395 memset(&m_regex_buf, 0, sizeof(m_regex_buf));
396 }
397
398 regular_expression::regular_expression(const char * pattern, int flags,
399 bool throw_on_error /*= true*/)
400 {
401 memset(&m_regex_buf, 0, sizeof(m_regex_buf));
402 if (!compile(pattern, flags) && throw_on_error)
403 throw std::runtime_error(strprintf(
404 "error in regular expression \"%s\": %s",
405 m_pattern.c_str(), m_errmsg.c_str()));
406 }
407
408 regular_expression::~regular_expression()
409 {
410 free_buf();
411 }
412
413 regular_expression::regular_expression(const regular_expression & x)
414 {
415 memset(&m_regex_buf, 0, sizeof(m_regex_buf));
416 copy(x);
417 }
418
419 regular_expression & regular_expression::operator=(const regular_expression & x)
420 {
421 free_buf();
422 copy(x);
423 return *this;
424 }
425
426 void regular_expression::free_buf()
427 {
428 if (nonempty(&m_regex_buf, sizeof(m_regex_buf))) {
429 regfree(&m_regex_buf);
430 memset(&m_regex_buf, 0, sizeof(m_regex_buf));
431 }
432 }
433
434 void regular_expression::copy(const regular_expression & x)
435 {
436 m_pattern = x.m_pattern;
437 m_flags = x.m_flags;
438 m_errmsg = x.m_errmsg;
439
440 if (!m_pattern.empty() && m_errmsg.empty()) {
441 // There is no POSIX compiled-regex-copy command.
442 if (!compile())
443 throw std::runtime_error(strprintf(
444 "Unable to recompile regular expression \"%s\": %s",
445 m_pattern.c_str(), m_errmsg.c_str()));
446 }
447 }
448
449 bool regular_expression::compile(const char * pattern, int flags)
450 {
451 free_buf();
452 m_pattern = pattern;
453 m_flags = flags;
454 return compile();
455 }
456
457 bool regular_expression::compile()
458 {
459 int errcode = regcomp(&m_regex_buf, m_pattern.c_str(), m_flags);
460 if (errcode) {
461 char errmsg[512];
462 regerror(errcode, &m_regex_buf, errmsg, sizeof(errmsg));
463 m_errmsg = errmsg;
464 free_buf();
465 return false;
466 }
467
468 if (check_regex_nesting(m_pattern.c_str()) < 0) {
469 m_errmsg = "Unmatched ')'";
470 free_buf();
471 return false;
472 }
473
474 m_errmsg.clear();
475 return true;
476 }
477
478 // Splits an argument to the -r option into a name part and an (optional)
479 // positive integer part. s is a pointer to a string containing the
480 // argument. After the call, s will point to the name part and *i the
481 // integer part if there is one or 1 otherwise. Note that the string s may
482 // be changed by this function. Returns zero if successful and non-zero
483 // otherwise.
484 int split_report_arg(char *s, int *i)
485 {
486 if ((s = strchr(s, ','))) {
487 // Looks like there's a name part and an integer part.
488 char *tailptr;
489
490 *s++ = '\0';
491 if (*s == '0' || !isdigit((int)*s)) // The integer part must be positive
492 return 1;
493 errno = 0;
494 *i = (int) strtol(s, &tailptr, 10);
495 if (errno || *tailptr != '\0')
496 return 1;
497 } else {
498 // There's no integer part.
499 *i = 1;
500 }
501
502 return 0;
503 }
504
505 #ifndef HAVE_STRTOULL
506 // Replacement for missing strtoull() (Linux with libc < 6, MSVC)
507 // Functionality reduced to requirements of smartd and split_selective_arg().
508
509 uint64_t strtoull(const char * p, char * * endp, int base)
510 {
511 uint64_t result, maxres;
512 int i = 0;
513 char c = p[i++];
514
515 if (!base) {
516 if (c == '0') {
517 if (p[i] == 'x' || p[i] == 'X') {
518 base = 16; i++;
519 }
520 else
521 base = 8;
522 c = p[i++];
523 }
524 else
525 base = 10;
526 }
527
528 result = 0;
529 maxres = ~(uint64_t)0 / (unsigned)base;
530 for (;;) {
531 unsigned digit;
532 if ('0' <= c && c <= '9')
533 digit = c - '0';
534 else if ('A' <= c && c <= 'Z')
535 digit = c - 'A' + 10;
536 else if ('a' <= c && c <= 'z')
537 digit = c - 'a' + 10;
538 else
539 break;
540 if (digit >= (unsigned)base)
541 break;
542 if (!( result < maxres
543 || (result == maxres && digit <= ~(uint64_t)0 % (unsigned)base))) {
544 result = ~(uint64_t)0; errno = ERANGE; // return on overflow
545 break;
546 }
547 result = result * (unsigned)base + digit;
548 c = p[i++];
549 }
550 if (endp)
551 *endp = (char *)p + i - 1;
552 return result;
553 }
554 #endif // HAVE_STRTOLL
555
556 // Splits an argument to the -t option that is assumed to be of the form
557 // "selective,%lld-%lld" (prefixes of "0" (for octal) and "0x"/"0X" (for hex)
558 // are allowed). The first long long int is assigned to *start and the second
559 // to *stop. Returns zero if successful and non-zero otherwise.
560 int split_selective_arg(char *s, uint64_t *start,
561 uint64_t *stop, int *mode)
562 {
563 char *tailptr;
564 if (!(s = strchr(s, ',')))
565 return 1;
566 bool add = false;
567 if (!isdigit((int)(*++s))) {
568 *start = *stop = 0;
569 if (!strncmp(s, "redo", 4))
570 *mode = SEL_REDO;
571 else if (!strncmp(s, "next", 4))
572 *mode = SEL_NEXT;
573 else if (!strncmp(s, "cont", 4))
574 *mode = SEL_CONT;
575 else
576 return 1;
577 s += 4;
578 if (!*s)
579 return 0;
580 if (*s != '+')
581 return 1;
582 }
583 else {
584 *mode = SEL_RANGE;
585 errno = 0;
586 // Last argument to strtoull (the base) is 0 meaning that decimal is assumed
587 // unless prefixes of "0" (for octal) or "0x"/"0X" (for hex) are used.
588 *start = strtoull(s, &tailptr, 0);
589 s = tailptr;
590 add = (*s == '+');
591 if (!(!errno && (add || *s == '-')))
592 return 1;
593 if (!strcmp(s, "-max")) {
594 *stop = ~(uint64_t)0; // replaced by max LBA later
595 return 0;
596 }
597 }
598
599 errno = 0;
600 *stop = strtoull(s+1, &tailptr, 0);
601 if (errno || *tailptr != '\0')
602 return 1;
603 if (add) {
604 if (*stop > 0)
605 (*stop)--;
606 *stop += *start; // -t select,N+M => -t select,N,(N+M-1)
607 }
608 return 0;
609 }
610
611 #ifdef OLD_INTERFACE
612
613 int64_t bytes = 0;
614
615 // Helps debugging. If the second argument is non-negative, then
616 // decrement bytes by that amount. Else decrement bytes by (one plus)
617 // length of null terminated string.
618 void *FreeNonZero1(void *address, int size, int line, const char* file){
619 if (address) {
620 if (size<0)
621 bytes-=1+strlen((char*)address);
622 else
623 bytes-=size;
624 return CheckFree1(address, line, file);
625 }
626 return NULL;
627 }
628
629 // To help with memory checking. Use when it is known that address is
630 // NOT null.
631 void *CheckFree1(void *address, int /*whatline*/, const char* /*file*/){
632 if (address){
633 free(address);
634 return NULL;
635 }
636 throw std::runtime_error("Internal error in CheckFree()");
637 }
638
639 // A custom version of calloc() that tracks memory use
640 void *Calloc(size_t nmemb, size_t size) {
641 void *ptr=calloc(nmemb, size);
642
643 if (ptr)
644 bytes+=nmemb*size;
645
646 return ptr;
647 }
648
649 // A custom version of strdup() that keeps track of how much memory is
650 // being allocated. If mustexist is set, it also throws an error if we
651 // try to duplicate a NULL string.
652 char *CustomStrDup(const char *ptr, int mustexist, int /*whatline*/, const char* /*file*/){
653 char *tmp;
654
655 // report error if ptr is NULL and mustexist is set
656 if (ptr==NULL){
657 if (mustexist)
658 throw std::runtime_error("Internal error in CustomStrDup()");
659 else
660 return NULL;
661 }
662
663 // make a copy of the string...
664 tmp=strdup(ptr);
665
666 if (!tmp)
667 throw std::bad_alloc();
668
669 // and track memory usage
670 bytes+=1+strlen(ptr);
671
672 return tmp;
673 }
674
675 #endif // OLD_INTERFACE
676
677
678 // Returns true if region of memory contains non-zero entries
679 bool nonempty(const void * data, int size)
680 {
681 for (int i = 0; i < size; i++)
682 if (((const unsigned char *)data)[i])
683 return true;
684 return false;
685 }
686
687 // Format integer with thousands separator
688 const char * format_with_thousands_sep(char * str, int strsize, uint64_t val,
689 const char * thousands_sep /* = 0 */)
690 {
691 if (!thousands_sep) {
692 thousands_sep = ",";
693 #ifdef HAVE_LOCALE_H
694 setlocale(LC_ALL, "");
695 const struct lconv * currentlocale = localeconv();
696 if (*(currentlocale->thousands_sep))
697 thousands_sep = currentlocale->thousands_sep;
698 #endif
699 }
700
701 char num[64];
702 snprintf(num, sizeof(num), "%"PRIu64, val);
703 int numlen = strlen(num);
704
705 int i = 0, j = 0;
706 do
707 str[j++] = num[i++];
708 while (i < numlen && (numlen - i) % 3 != 0 && j < strsize-1);
709 str[j] = 0;
710
711 while (i < numlen && j < strsize-1) {
712 j += snprintf(str+j, strsize-j, "%s%.3s", thousands_sep, num+i);
713 i += 3;
714 }
715
716 return str;
717 }
718
719 // Format capacity with SI prefixes
720 const char * format_capacity(char * str, int strsize, uint64_t val,
721 const char * decimal_point /* = 0 */)
722 {
723 if (!decimal_point) {
724 decimal_point = ".";
725 #ifdef HAVE_LOCALE_H
726 setlocale(LC_ALL, "");
727 const struct lconv * currentlocale = localeconv();
728 if (*(currentlocale->decimal_point))
729 decimal_point = currentlocale->decimal_point;
730 #endif
731 }
732
733 const unsigned factor = 1000; // 1024 for KiB,MiB,...
734 static const char prefixes[] = " KMGTP";
735
736 // Find d with val in [d, d*factor)
737 unsigned i = 0;
738 uint64_t d = 1;
739 for (uint64_t d2 = d * factor; val >= d2; d2 *= factor) {
740 d = d2;
741 if (++i >= sizeof(prefixes)-2)
742 break;
743 }
744
745 // Print 3 digits
746 uint64_t n = val / d;
747 if (i == 0)
748 snprintf(str, strsize, "%u B", (unsigned)n);
749 else if (n >= 100) // "123 xB"
750 snprintf(str, strsize, "%"PRIu64" %cB", n, prefixes[i]);
751 else if (n >= 10) // "12.3 xB"
752 snprintf(str, strsize, "%"PRIu64"%s%u %cB", n, decimal_point,
753 (unsigned)(((val % d) * 10) / d), prefixes[i]);
754 else // "1.23 xB"
755 snprintf(str, strsize, "%"PRIu64"%s%02u %cB", n, decimal_point,
756 (unsigned)(((val % d) * 100) / d), prefixes[i]);
757
758 return str;
759 }
760
761 // return (v)sprintf() formatted std::string
762
763 std::string vstrprintf(const char * fmt, va_list ap)
764 {
765 char buf[512];
766 vsnprintf(buf, sizeof(buf), fmt, ap);
767 buf[sizeof(buf)-1] = 0;
768 return buf;
769 }
770
771 std::string strprintf(const char * fmt, ...)
772 {
773 va_list ap; va_start(ap, fmt);
774 std::string str = vstrprintf(fmt, ap);
775 va_end(ap);
776 return str;
777 }
778
779
780 #ifndef HAVE_WORKING_SNPRINTF
781 // Some versions of (v)snprintf() don't append null char on overflow (MSVCRT.DLL),
782 // and/or return -1 on overflow (old Linux).
783 // Below are sane replacements substituted by #define in utility.h.
784
785 #undef vsnprintf
786 #if defined(_WIN32) && defined(_MSC_VER)
787 #define vsnprintf _vsnprintf
788 #endif
789
790 int safe_vsnprintf(char *buf, int size, const char *fmt, va_list ap)
791 {
792 int i;
793 if (size <= 0)
794 return 0;
795 i = vsnprintf(buf, size, fmt, ap);
796 if (0 <= i && i < size)
797 return i;
798 buf[size-1] = 0;
799 return strlen(buf); // Note: cannot detect for overflow, not necessary here.
800 }
801
802 int safe_snprintf(char *buf, int size, const char *fmt, ...)
803 {
804 int i; va_list ap;
805 va_start(ap, fmt);
806 i = safe_vsnprintf(buf, size, fmt, ap);
807 va_end(ap);
808 return i;
809 }
810
811 #endif
812