]> git.proxmox.com Git - mirror_smartmontools-debian.git/blob - utility.cpp
Updated changelog
[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-11 Bruce Allen <smartmontools-support@lists.sourceforge.net>
7 * Copyright (C) 2008-11 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, 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
30 #include "config.h"
31
32 #include <stdio.h>
33 #include <string.h>
34 #include <time.h>
35 #include <errno.h>
36 #include <stdlib.h>
37 #include <ctype.h>
38 #include <stdarg.h>
39 #include <sys/stat.h>
40 #ifdef HAVE_LOCALE_H
41 #include <locale.h>
42 #endif
43 #ifdef _WIN32
44 #include <mbstring.h> // _mbsinc()
45 #endif
46
47 #include <stdexcept>
48
49 #include "svnversion.h"
50 #include "int64.h"
51 #include "utility.h"
52
53 #include "atacmds.h"
54 #include "dev_interface.h"
55
56 const char * utility_cpp_cvsid = "$Id: utility.cpp 3305 2011-03-30 21:32:05Z chrfranke $"
57 UTILITY_H_CVSID INT64_H_CVSID;
58
59 const 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
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
84 std::string format_version_info(const char * prog_name, bool full /*= false*/)
85 {
86 std::string info = strprintf(
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
93 " [%s] "BUILD_INFO"\n"
94 "Copyright (C) 2002-11 by Bruce Allen, http://smartmontools.sourceforge.net\n",
95 prog_name, smi()->get_os_version_str().c_str()
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"
104 "the terms of the GNU General Public License Version 2.\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 // same as above but sets *i to -1 if missing , argument
506 int split_report_arg2(char *s, int *i){
507 char *tailptr;
508 s+=6;
509
510 if (*s=='\0' || !isdigit((int)*s)) {
511 // What's left must be integer
512 *i=-1;
513 return 1;
514 }
515
516 errno = 0;
517 *i = (int) strtol(s, &tailptr, 10);
518 if (errno || *tailptr != '\0') {
519 *i=-1;
520 return 1;
521 }
522
523 return 0;
524 }
525
526 #ifndef HAVE_STRTOULL
527 // Replacement for missing strtoull() (Linux with libc < 6, MSVC)
528 // Functionality reduced to requirements of smartd and split_selective_arg().
529
530 uint64_t strtoull(const char * p, char * * endp, int base)
531 {
532 uint64_t result, maxres;
533 int i = 0;
534 char c = p[i++];
535
536 if (!base) {
537 if (c == '0') {
538 if (p[i] == 'x' || p[i] == 'X') {
539 base = 16; i++;
540 }
541 else
542 base = 8;
543 c = p[i++];
544 }
545 else
546 base = 10;
547 }
548
549 result = 0;
550 maxres = ~(uint64_t)0 / (unsigned)base;
551 for (;;) {
552 unsigned digit;
553 if ('0' <= c && c <= '9')
554 digit = c - '0';
555 else if ('A' <= c && c <= 'Z')
556 digit = c - 'A' + 10;
557 else if ('a' <= c && c <= 'z')
558 digit = c - 'a' + 10;
559 else
560 break;
561 if (digit >= (unsigned)base)
562 break;
563 if (!( result < maxres
564 || (result == maxres && digit <= ~(uint64_t)0 % (unsigned)base))) {
565 result = ~(uint64_t)0; errno = ERANGE; // return on overflow
566 break;
567 }
568 result = result * (unsigned)base + digit;
569 c = p[i++];
570 }
571 if (endp)
572 *endp = (char *)p + i - 1;
573 return result;
574 }
575 #endif // HAVE_STRTOLL
576
577 // Splits an argument to the -t option that is assumed to be of the form
578 // "selective,%lld-%lld" (prefixes of "0" (for octal) and "0x"/"0X" (for hex)
579 // are allowed). The first long long int is assigned to *start and the second
580 // to *stop. Returns zero if successful and non-zero otherwise.
581 int split_selective_arg(char *s, uint64_t *start,
582 uint64_t *stop, int *mode)
583 {
584 char *tailptr;
585 if (!(s = strchr(s, ',')))
586 return 1;
587 bool add = false;
588 if (!isdigit((int)(*++s))) {
589 *start = *stop = 0;
590 if (!strncmp(s, "redo", 4))
591 *mode = SEL_REDO;
592 else if (!strncmp(s, "next", 4))
593 *mode = SEL_NEXT;
594 else if (!strncmp(s, "cont", 4))
595 *mode = SEL_CONT;
596 else
597 return 1;
598 s += 4;
599 if (!*s)
600 return 0;
601 if (*s != '+')
602 return 1;
603 }
604 else {
605 *mode = SEL_RANGE;
606 errno = 0;
607 // Last argument to strtoull (the base) is 0 meaning that decimal is assumed
608 // unless prefixes of "0" (for octal) or "0x"/"0X" (for hex) are used.
609 *start = strtoull(s, &tailptr, 0);
610 s = tailptr;
611 add = (*s == '+');
612 if (!(!errno && (add || *s == '-')))
613 return 1;
614 if (!strcmp(s, "-max")) {
615 *stop = ~(uint64_t)0; // replaced by max LBA later
616 return 0;
617 }
618 }
619 *stop = strtoull(s+1, &tailptr, 0);
620 if (errno || *tailptr != '\0')
621 return 1;
622 if (add) {
623 if (*stop > 0)
624 (*stop)--;
625 *stop += *start; // -t select,N+M => -t select,N,(N+M-1)
626 }
627 return 0;
628 }
629
630 #ifdef OLD_INTERFACE
631
632 int64_t bytes = 0;
633
634 // Helps debugging. If the second argument is non-negative, then
635 // decrement bytes by that amount. Else decrement bytes by (one plus)
636 // length of null terminated string.
637 void *FreeNonZero1(void *address, int size, int line, const char* file){
638 if (address) {
639 if (size<0)
640 bytes-=1+strlen((char*)address);
641 else
642 bytes-=size;
643 return CheckFree1(address, line, file);
644 }
645 return NULL;
646 }
647
648 // To help with memory checking. Use when it is known that address is
649 // NOT null.
650 void *CheckFree1(void *address, int /*whatline*/, const char* /*file*/){
651 if (address){
652 free(address);
653 return NULL;
654 }
655 throw std::runtime_error("Internal error in CheckFree()");
656 }
657
658 // A custom version of calloc() that tracks memory use
659 void *Calloc(size_t nmemb, size_t size) {
660 void *ptr=calloc(nmemb, size);
661
662 if (ptr)
663 bytes+=nmemb*size;
664
665 return ptr;
666 }
667
668 // A custom version of strdup() that keeps track of how much memory is
669 // being allocated. If mustexist is set, it also throws an error if we
670 // try to duplicate a NULL string.
671 char *CustomStrDup(const char *ptr, int mustexist, int /*whatline*/, const char* /*file*/){
672 char *tmp;
673
674 // report error if ptr is NULL and mustexist is set
675 if (ptr==NULL){
676 if (mustexist)
677 throw std::runtime_error("Internal error in CustomStrDup()");
678 else
679 return NULL;
680 }
681
682 // make a copy of the string...
683 tmp=strdup(ptr);
684
685 if (!tmp)
686 throw std::bad_alloc();
687
688 // and track memory usage
689 bytes+=1+strlen(ptr);
690
691 return tmp;
692 }
693
694 #endif // OLD_INTERFACE
695
696
697 // Returns true if region of memory contains non-zero entries
698 bool nonempty(const void * data, int size)
699 {
700 for (int i = 0; i < size; i++)
701 if (((const unsigned char *)data)[i])
702 return true;
703 return false;
704 }
705
706
707 // This routine converts an integer number of milliseconds into a test
708 // string of the form Xd+Yh+Zm+Ts.msec. The resulting text string is
709 // written to the array.
710 void MsecToText(unsigned int msec, char *txt){
711 unsigned int days, hours, min, sec;
712
713 days = msec/86400000U;
714 msec -= days*86400000U;
715
716 hours = msec/3600000U;
717 msec -= hours*3600000U;
718
719 min = msec/60000U;
720 msec -= min*60000U;
721
722 sec = msec/1000U;
723 msec -= sec*1000U;
724
725 if (days) {
726 txt += sprintf(txt, "%2dd+", (int)days);
727 }
728
729 sprintf(txt, "%02d:%02d:%02d.%03d", (int)hours, (int)min, (int)sec, (int)msec);
730 return;
731 }
732
733 // Format integer with thousands separator
734 const char * format_with_thousands_sep(char * str, int strsize, uint64_t val,
735 const char * thousands_sep /* = 0 */)
736 {
737 if (!thousands_sep) {
738 thousands_sep = ",";
739 #ifdef HAVE_LOCALE_H
740 setlocale(LC_ALL, "");
741 const struct lconv * currentlocale = localeconv();
742 if (*(currentlocale->thousands_sep))
743 thousands_sep = currentlocale->thousands_sep;
744 #endif
745 }
746
747 char num[64];
748 snprintf(num, sizeof(num), "%"PRIu64, val);
749 int numlen = strlen(num);
750
751 int i = 0, j = 0;
752 do
753 str[j++] = num[i++];
754 while (i < numlen && (numlen - i) % 3 != 0 && j < strsize-1);
755 str[j] = 0;
756
757 while (i < numlen && j < strsize-1) {
758 j += snprintf(str+j, strsize-j, "%s%.3s", thousands_sep, num+i);
759 i += 3;
760 }
761
762 return str;
763 }
764
765 // Format capacity with SI prefixes
766 const char * format_capacity(char * str, int strsize, uint64_t val,
767 const char * decimal_point /* = 0 */)
768 {
769 if (!decimal_point) {
770 decimal_point = ".";
771 #ifdef HAVE_LOCALE_H
772 setlocale(LC_ALL, "");
773 const struct lconv * currentlocale = localeconv();
774 if (*(currentlocale->decimal_point))
775 decimal_point = currentlocale->decimal_point;
776 #endif
777 }
778
779 const unsigned factor = 1000; // 1024 for KiB,MiB,...
780 static const char prefixes[] = " KMGTP";
781
782 // Find d with val in [d, d*factor)
783 unsigned i = 0;
784 uint64_t d = 1;
785 for (uint64_t d2 = d * factor; val >= d2; d2 *= factor) {
786 d = d2;
787 if (++i >= sizeof(prefixes)-2)
788 break;
789 }
790
791 // Print 3 digits
792 uint64_t n = val / d;
793 if (i == 0)
794 snprintf(str, strsize, "%u B", (unsigned)n);
795 else if (n >= 100) // "123 xB"
796 snprintf(str, strsize, "%"PRIu64" %cB", n, prefixes[i]);
797 else if (n >= 10) // "12.3 xB"
798 snprintf(str, strsize, "%"PRIu64"%s%u %cB", n, decimal_point,
799 (unsigned)(((val % d) * 10) / d), prefixes[i]);
800 else // "1.23 xB"
801 snprintf(str, strsize, "%"PRIu64"%s%02u %cB", n, decimal_point,
802 (unsigned)(((val % d) * 100) / d), prefixes[i]);
803
804 return str;
805 }
806
807 // return (v)sprintf() formatted std::string
808
809 std::string vstrprintf(const char * fmt, va_list ap)
810 {
811 char buf[512];
812 vsnprintf(buf, sizeof(buf), fmt, ap);
813 buf[sizeof(buf)-1] = 0;
814 return buf;
815 }
816
817 std::string strprintf(const char * fmt, ...)
818 {
819 va_list ap; va_start(ap, fmt);
820 std::string str = vstrprintf(fmt, ap);
821 va_end(ap);
822 return str;
823 }
824
825
826 #ifndef HAVE_WORKING_SNPRINTF
827 // Some versions of (v)snprintf() don't append null char on overflow (MSVCRT.DLL),
828 // and/or return -1 on overflow (old Linux).
829 // Below are sane replacements substituted by #define in utility.h.
830
831 #undef vsnprintf
832 #if defined(_WIN32) && defined(_MSC_VER)
833 #define vsnprintf _vsnprintf
834 #endif
835
836 int safe_vsnprintf(char *buf, int size, const char *fmt, va_list ap)
837 {
838 int i;
839 if (size <= 0)
840 return 0;
841 i = vsnprintf(buf, size, fmt, ap);
842 if (0 <= i && i < size)
843 return i;
844 buf[size-1] = 0;
845 return strlen(buf); // Note: cannot detect for overflow, not necessary here.
846 }
847
848 int safe_snprintf(char *buf, int size, const char *fmt, ...)
849 {
850 int i; va_list ap;
851 va_start(ap, fmt);
852 i = safe_vsnprintf(buf, size, fmt, ap);
853 va_end(ap);
854 return i;
855 }
856
857 #endif
858