Annotation of embedaddon/smartmontools/utility.cpp, revision 1.1.1.2
1.1 misho 1: /*
2: * utility.cpp
3: *
4: * Home page of code is: http://smartmontools.sourceforge.net
5: *
1.1.1.2 ! misho 6: * Copyright (C) 2002-12 Bruce Allen <smartmontools-support@lists.sourceforge.net>
! 7: * Copyright (C) 2008-12 Christian Franke <smartmontools-support@lists.sourceforge.net>
1.1 misho 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:
1.1.1.2 ! misho 56: const char * utility_cpp_cvsid = "$Id: utility.cpp 3500 2012-01-01 18:03:36Z chrfranke $"
1.1 misho 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"
1.1.1.2 ! misho 94: "Copyright (C) 2002-12 by Bruce Allen, http://smartmontools.sourceforge.net\n",
1.1 misho 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"
1.1.1.2 ! misho 104: "the terms of the GNU General Public License; either\n"
! 105: "version 2, or (at your option) any later version.\n"
1.1 misho 106: "See http://www.gnu.org for further details.\n"
107: "\n",
108: prog_name
109: );
110: info += strprintf(
111: "smartmontools release "PACKAGE_VERSION
112: " dated "SMARTMONTOOLS_RELEASE_DATE" at "SMARTMONTOOLS_RELEASE_TIME"\n"
113: #ifdef SMARTMONTOOLS_SVN_REV
114: "smartmontools SVN rev "SMARTMONTOOLS_SVN_REV
115: " dated "SMARTMONTOOLS_SVN_DATE" at "SMARTMONTOOLS_SVN_TIME"\n"
116: #else
117: "smartmontools SVN rev is unknown\n"
118: #endif
119: "smartmontools build host: "SMARTMONTOOLS_BUILD_HOST"\n"
120: "smartmontools build configured: "SMARTMONTOOLS_CONFIGURE_DATE "\n"
121: "%s compile dated "__DATE__" at "__TIME__"\n"
122: "smartmontools configure arguments: ",
123: prog_name
124: );
125: info += (sizeof(SMARTMONTOOLS_CONFIGURE_ARGS) > 1 ?
126: SMARTMONTOOLS_CONFIGURE_ARGS : "[no arguments given]");
127: info += '\n';
128:
129: return info;
130: }
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)
135: static const char *TIMEZONE_FILE = "/etc/TIMEZONE";
136:
137: static 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.
168: void FixGlibcTimeZoneBug(){
169: #if __GLIBC__
170: if (!getenv("TZ")) {
171: putenv((char *)"TZ=GMT"); // POSIX prototype is 'int putenv(char *)'
172: tzset();
173: putenv((char *)"TZ");
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().
235: static 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.
258: const 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:
268: // Runtime check of byte ordering, throws if different from isbigendian().
269: void 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: }
282:
283: if (big != (isbigendian() ? 1 : 0))
284: throw std::logic_error("CPU endianness does not match compile time test");
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).
290: void dateandtimezoneepoch(char *buffer, time_t tval){
291: struct tm *tmval;
292: const char *timezonename;
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
338: void 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:
347: // A replacement for perror() that sends output to our choice of
348: // printing. If errno not set then just print message.
349: void 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:
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. '\)' '[)]'.
377: // But it should work for the regex subset used in drive database
378: // and smartd '-s' directives.
379: static 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:
391: // Wrapper class for regex(3)
392:
393: regular_expression::regular_expression()
394: : m_flags(0)
395: {
396: memset(&m_regex_buf, 0, sizeof(m_regex_buf));
397: }
398:
399: regular_expression::regular_expression(const char * pattern, int flags,
400: bool throw_on_error /*= true*/)
401: {
402: memset(&m_regex_buf, 0, sizeof(m_regex_buf));
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()));
407: }
408:
409: regular_expression::~regular_expression()
410: {
411: free_buf();
412: }
413:
414: regular_expression::regular_expression(const regular_expression & x)
415: {
416: memset(&m_regex_buf, 0, sizeof(m_regex_buf));
417: copy(x);
418: }
419:
420: regular_expression & regular_expression::operator=(const regular_expression & x)
421: {
422: free_buf();
423: copy(x);
424: return *this;
425: }
426:
427: void 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));
432: }
433: }
434:
435: void 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:
450: bool 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:
458: bool 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;
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.
485: int 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
507: int 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
528: // Replacement for missing strtoull() (Linux with libc < 6, MSVC)
529: // Functionality reduced to requirements of smartd and split_selective_arg().
530:
531: uint64_t strtoull(const char * p, char * * endp, int base)
532: {
533: uint64_t result, maxres;
534: int i = 0;
535: char c = p[i++];
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++];
545: }
546: else
547: base = 10;
548: }
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: }
572: if (endp)
573: *endp = (char *)p + i - 1;
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.
582: int split_selective_arg(char *s, uint64_t *start,
583: uint64_t *stop, int *mode)
584: {
585: char *tailptr;
586: if (!(s = strchr(s, ',')))
587: return 1;
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);
621: if (errno || *tailptr != '\0')
622: return 1;
623: if (add) {
624: if (*stop > 0)
625: (*stop)--;
626: *stop += *start; // -t select,N+M => -t select,N,(N+M-1)
627: }
628: return 0;
629: }
630:
631: #ifdef OLD_INTERFACE
632:
633: int64_t bytes = 0;
634:
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.
638: void *FreeNonZero1(void *address, int size, int line, const char* file){
639: if (address) {
640: if (size<0)
641: bytes-=1+strlen((char*)address);
642: else
643: bytes-=size;
644: return CheckFree1(address, line, file);
645: }
646: return NULL;
647: }
648:
649: // To help with memory checking. Use when it is known that address is
650: // NOT null.
651: void *CheckFree1(void *address, int /*whatline*/, const char* /*file*/){
652: if (address){
653: free(address);
654: return NULL;
655: }
656: throw std::runtime_error("Internal error in CheckFree()");
657: }
658:
659: // A custom version of calloc() that tracks memory use
660: void *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.
672: char *CustomStrDup(const char *ptr, int mustexist, int /*whatline*/, const char* /*file*/){
673: char *tmp;
674:
675: // report error if ptr is NULL and mustexist is set
676: if (ptr==NULL){
677: if (mustexist)
678: throw std::runtime_error("Internal error in CustomStrDup()");
679: else
680: return NULL;
681: }
682:
683: // make a copy of the string...
684: tmp=strdup(ptr);
685:
686: if (!tmp)
687: throw std::bad_alloc();
688:
689: // and track memory usage
690: bytes+=1+strlen(ptr);
691:
692: return tmp;
693: }
694:
695: #endif // OLD_INTERFACE
696:
697:
698: // Returns true if region of memory contains non-zero entries
699: bool 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;
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.
711: void MsecToText(unsigned int msec, char *txt){
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);
728: }
729:
730: sprintf(txt, "%02d:%02d:%02d.%03d", (int)hours, (int)min, (int)sec, (int)msec);
731: return;
732: }
733:
734: // Format integer with thousands separator
735: const 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
767: const 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:
808: // return (v)sprintf() formatted std::string
809:
810: std::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:
818: std::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:
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:
837: int 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:
849: int 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
859:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>