Annotation of embedaddon/smartmontools/utility.cpp, revision 1.1.1.4
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>
1.1.1.3 misho 7: * Copyright (C) 2008-13 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
1.1.1.3 misho 16: * (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
1.1 misho 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:
1.1.1.4 ! misho 55: const char * utility_cpp_cvsid = "$Id: utility.cpp 3838 2013-07-21 16:32:27Z chrfranke $"
1.1 misho 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"
1.1.1.3 misho 93: "Copyright (C) 2002-13, Bruce Allen, Christian Franke, www.smartmontools.org\n",
1.1 misho 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"
1.1.1.2 misho 103: "the terms of the GNU General Public License; either\n"
104: "version 2, or (at your option) any later version.\n"
1.1 misho 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:
1.1.1.4 ! misho 367: // Check regular expression for non-portable features.
! 368: //
1.1 misho 369: // POSIX extended regular expressions interpret unmatched ')' ordinary:
370: // "The close-parenthesis shall be considered special in this context
371: // only if matched with a preceding open-parenthesis."
372: //
1.1.1.4 ! misho 373: // GNU libc and BSD libc support unmatched ')', Cygwin reports an error.
! 374: //
! 375: // POSIX extended regular expressions do not define empty subexpressions:
! 376: // "A vertical-line appearing first or last in an ERE, or immediately following
! 377: // a vertical-line or a left-parenthesis, or immediately preceding a
! 378: // right-parenthesis, produces undefined results."
! 379: //
! 380: // GNU libc and Cygwin support empty subexpressions, BSD libc reports an error.
! 381: //
! 382: static const char * check_regex(const char * pattern)
! 383: {
! 384: int level = 0;
! 385: char c;
! 386:
! 387: for (int i = 0; (c = pattern[i]); i++) {
! 388: // Skip "\x"
! 389: if (c == '\\') {
! 390: if (!pattern[++i])
! 391: break;
! 392: continue;
1.1 misho 393: }
1.1.1.4 ! misho 394:
! 395: // Skip "[...]"
! 396: if (c == '[') {
! 397: if (pattern[++i] == '^')
! 398: i++;
! 399: if (!pattern[i++])
! 400: break;
! 401: while ((c = pattern[i]) && c != ']')
! 402: i++;
! 403: if (!c)
! 404: break;
! 405: continue;
! 406: }
! 407:
! 408: // Check "(...)" nesting
! 409: if (c == '(')
! 410: level++;
! 411: else if (c == ')' && --level < 0)
! 412: return "Unmatched ')'";
! 413:
! 414: // Check for leading/trailing '|' or "||", "|)", "|$", "(|", "^|"
! 415: char c1;
! 416: if ( (c == '|' && ( i == 0 || !(c1 = pattern[i+1])
! 417: || c1 == '|' || c1 == ')' || c1 == '$'))
! 418: || ((c == '(' || c == '^') && pattern[i+1] == '|') )
! 419: return "Empty '|' subexpression";
1.1 misho 420: }
1.1.1.4 ! misho 421:
! 422: return (const char *)0;
1.1 misho 423: }
424:
425: // Wrapper class for regex(3)
426:
427: regular_expression::regular_expression()
428: : m_flags(0)
429: {
430: memset(&m_regex_buf, 0, sizeof(m_regex_buf));
431: }
432:
433: regular_expression::regular_expression(const char * pattern, int flags,
434: bool throw_on_error /*= true*/)
435: {
436: memset(&m_regex_buf, 0, sizeof(m_regex_buf));
437: if (!compile(pattern, flags) && throw_on_error)
438: throw std::runtime_error(strprintf(
439: "error in regular expression \"%s\": %s",
440: m_pattern.c_str(), m_errmsg.c_str()));
441: }
442:
443: regular_expression::~regular_expression()
444: {
445: free_buf();
446: }
447:
448: regular_expression::regular_expression(const regular_expression & x)
449: {
450: memset(&m_regex_buf, 0, sizeof(m_regex_buf));
451: copy(x);
452: }
453:
454: regular_expression & regular_expression::operator=(const regular_expression & x)
455: {
456: free_buf();
457: copy(x);
458: return *this;
459: }
460:
461: void regular_expression::free_buf()
462: {
463: if (nonempty(&m_regex_buf, sizeof(m_regex_buf))) {
464: regfree(&m_regex_buf);
465: memset(&m_regex_buf, 0, sizeof(m_regex_buf));
466: }
467: }
468:
469: void regular_expression::copy(const regular_expression & x)
470: {
471: m_pattern = x.m_pattern;
472: m_flags = x.m_flags;
473: m_errmsg = x.m_errmsg;
474:
475: if (!m_pattern.empty() && m_errmsg.empty()) {
476: // There is no POSIX compiled-regex-copy command.
477: if (!compile())
478: throw std::runtime_error(strprintf(
479: "Unable to recompile regular expression \"%s\": %s",
480: m_pattern.c_str(), m_errmsg.c_str()));
481: }
482: }
483:
484: bool regular_expression::compile(const char * pattern, int flags)
485: {
486: free_buf();
487: m_pattern = pattern;
488: m_flags = flags;
489: return compile();
490: }
491:
492: bool regular_expression::compile()
493: {
494: int errcode = regcomp(&m_regex_buf, m_pattern.c_str(), m_flags);
495: if (errcode) {
496: char errmsg[512];
497: regerror(errcode, &m_regex_buf, errmsg, sizeof(errmsg));
498: m_errmsg = errmsg;
499: free_buf();
500: return false;
501: }
502:
1.1.1.4 ! misho 503: const char * errmsg = check_regex(m_pattern.c_str());
! 504: if (errmsg) {
! 505: m_errmsg = errmsg;
1.1 misho 506: free_buf();
507: return false;
508: }
509:
510: m_errmsg.clear();
511: return true;
512: }
513:
514: // Splits an argument to the -r option into a name part and an (optional)
515: // positive integer part. s is a pointer to a string containing the
516: // argument. After the call, s will point to the name part and *i the
517: // integer part if there is one or 1 otherwise. Note that the string s may
518: // be changed by this function. Returns zero if successful and non-zero
519: // otherwise.
520: int split_report_arg(char *s, int *i)
521: {
522: if ((s = strchr(s, ','))) {
523: // Looks like there's a name part and an integer part.
524: char *tailptr;
525:
526: *s++ = '\0';
527: if (*s == '0' || !isdigit((int)*s)) // The integer part must be positive
528: return 1;
529: errno = 0;
530: *i = (int) strtol(s, &tailptr, 10);
531: if (errno || *tailptr != '\0')
532: return 1;
533: } else {
534: // There's no integer part.
535: *i = 1;
536: }
537:
538: return 0;
539: }
540:
541: #ifndef HAVE_STRTOULL
542: // Replacement for missing strtoull() (Linux with libc < 6, MSVC)
543: // Functionality reduced to requirements of smartd and split_selective_arg().
544:
545: uint64_t strtoull(const char * p, char * * endp, int base)
546: {
547: uint64_t result, maxres;
548: int i = 0;
549: char c = p[i++];
550:
551: if (!base) {
552: if (c == '0') {
553: if (p[i] == 'x' || p[i] == 'X') {
554: base = 16; i++;
555: }
556: else
557: base = 8;
558: c = p[i++];
559: }
560: else
561: base = 10;
562: }
563:
564: result = 0;
565: maxres = ~(uint64_t)0 / (unsigned)base;
566: for (;;) {
567: unsigned digit;
568: if ('0' <= c && c <= '9')
569: digit = c - '0';
570: else if ('A' <= c && c <= 'Z')
571: digit = c - 'A' + 10;
572: else if ('a' <= c && c <= 'z')
573: digit = c - 'a' + 10;
574: else
575: break;
576: if (digit >= (unsigned)base)
577: break;
578: if (!( result < maxres
579: || (result == maxres && digit <= ~(uint64_t)0 % (unsigned)base))) {
580: result = ~(uint64_t)0; errno = ERANGE; // return on overflow
581: break;
582: }
583: result = result * (unsigned)base + digit;
584: c = p[i++];
585: }
586: if (endp)
587: *endp = (char *)p + i - 1;
588: return result;
589: }
590: #endif // HAVE_STRTOLL
591:
592: // Splits an argument to the -t option that is assumed to be of the form
593: // "selective,%lld-%lld" (prefixes of "0" (for octal) and "0x"/"0X" (for hex)
594: // are allowed). The first long long int is assigned to *start and the second
595: // to *stop. Returns zero if successful and non-zero otherwise.
596: int split_selective_arg(char *s, uint64_t *start,
597: uint64_t *stop, int *mode)
598: {
599: char *tailptr;
600: if (!(s = strchr(s, ',')))
601: return 1;
602: bool add = false;
603: if (!isdigit((int)(*++s))) {
604: *start = *stop = 0;
605: if (!strncmp(s, "redo", 4))
606: *mode = SEL_REDO;
607: else if (!strncmp(s, "next", 4))
608: *mode = SEL_NEXT;
609: else if (!strncmp(s, "cont", 4))
610: *mode = SEL_CONT;
611: else
612: return 1;
613: s += 4;
614: if (!*s)
615: return 0;
616: if (*s != '+')
617: return 1;
618: }
619: else {
620: *mode = SEL_RANGE;
621: errno = 0;
622: // Last argument to strtoull (the base) is 0 meaning that decimal is assumed
623: // unless prefixes of "0" (for octal) or "0x"/"0X" (for hex) are used.
624: *start = strtoull(s, &tailptr, 0);
625: s = tailptr;
626: add = (*s == '+');
627: if (!(!errno && (add || *s == '-')))
628: return 1;
629: if (!strcmp(s, "-max")) {
630: *stop = ~(uint64_t)0; // replaced by max LBA later
631: return 0;
632: }
633: }
1.1.1.3 misho 634:
635: errno = 0;
1.1 misho 636: *stop = strtoull(s+1, &tailptr, 0);
637: if (errno || *tailptr != '\0')
638: return 1;
639: if (add) {
640: if (*stop > 0)
641: (*stop)--;
642: *stop += *start; // -t select,N+M => -t select,N,(N+M-1)
643: }
644: return 0;
645: }
646:
647: #ifdef OLD_INTERFACE
648:
649: int64_t bytes = 0;
650:
651: // Helps debugging. If the second argument is non-negative, then
652: // decrement bytes by that amount. Else decrement bytes by (one plus)
653: // length of null terminated string.
654: void *FreeNonZero1(void *address, int size, int line, const char* file){
655: if (address) {
656: if (size<0)
657: bytes-=1+strlen((char*)address);
658: else
659: bytes-=size;
660: return CheckFree1(address, line, file);
661: }
662: return NULL;
663: }
664:
665: // To help with memory checking. Use when it is known that address is
666: // NOT null.
667: void *CheckFree1(void *address, int /*whatline*/, const char* /*file*/){
668: if (address){
669: free(address);
670: return NULL;
671: }
672: throw std::runtime_error("Internal error in CheckFree()");
673: }
674:
675: // A custom version of calloc() that tracks memory use
676: void *Calloc(size_t nmemb, size_t size) {
677: void *ptr=calloc(nmemb, size);
678:
679: if (ptr)
680: bytes+=nmemb*size;
681:
682: return ptr;
683: }
684:
685: // A custom version of strdup() that keeps track of how much memory is
686: // being allocated. If mustexist is set, it also throws an error if we
687: // try to duplicate a NULL string.
688: char *CustomStrDup(const char *ptr, int mustexist, int /*whatline*/, const char* /*file*/){
689: char *tmp;
690:
691: // report error if ptr is NULL and mustexist is set
692: if (ptr==NULL){
693: if (mustexist)
694: throw std::runtime_error("Internal error in CustomStrDup()");
695: else
696: return NULL;
697: }
698:
699: // make a copy of the string...
700: tmp=strdup(ptr);
701:
702: if (!tmp)
703: throw std::bad_alloc();
704:
705: // and track memory usage
706: bytes+=1+strlen(ptr);
707:
708: return tmp;
709: }
710:
711: #endif // OLD_INTERFACE
712:
713:
714: // Returns true if region of memory contains non-zero entries
715: bool nonempty(const void * data, int size)
716: {
717: for (int i = 0; i < size; i++)
718: if (((const unsigned char *)data)[i])
719: return true;
720: return false;
721: }
722:
723: // Format integer with thousands separator
724: const char * format_with_thousands_sep(char * str, int strsize, uint64_t val,
725: const char * thousands_sep /* = 0 */)
726: {
727: if (!thousands_sep) {
728: thousands_sep = ",";
729: #ifdef HAVE_LOCALE_H
730: setlocale(LC_ALL, "");
731: const struct lconv * currentlocale = localeconv();
732: if (*(currentlocale->thousands_sep))
733: thousands_sep = currentlocale->thousands_sep;
734: #endif
735: }
736:
737: char num[64];
738: snprintf(num, sizeof(num), "%"PRIu64, val);
739: int numlen = strlen(num);
740:
741: int i = 0, j = 0;
742: do
743: str[j++] = num[i++];
744: while (i < numlen && (numlen - i) % 3 != 0 && j < strsize-1);
745: str[j] = 0;
746:
747: while (i < numlen && j < strsize-1) {
748: j += snprintf(str+j, strsize-j, "%s%.3s", thousands_sep, num+i);
749: i += 3;
750: }
751:
752: return str;
753: }
754:
755: // Format capacity with SI prefixes
756: const char * format_capacity(char * str, int strsize, uint64_t val,
757: const char * decimal_point /* = 0 */)
758: {
759: if (!decimal_point) {
760: decimal_point = ".";
761: #ifdef HAVE_LOCALE_H
762: setlocale(LC_ALL, "");
763: const struct lconv * currentlocale = localeconv();
764: if (*(currentlocale->decimal_point))
765: decimal_point = currentlocale->decimal_point;
766: #endif
767: }
768:
769: const unsigned factor = 1000; // 1024 for KiB,MiB,...
770: static const char prefixes[] = " KMGTP";
771:
772: // Find d with val in [d, d*factor)
773: unsigned i = 0;
774: uint64_t d = 1;
775: for (uint64_t d2 = d * factor; val >= d2; d2 *= factor) {
776: d = d2;
777: if (++i >= sizeof(prefixes)-2)
778: break;
779: }
780:
781: // Print 3 digits
782: uint64_t n = val / d;
783: if (i == 0)
784: snprintf(str, strsize, "%u B", (unsigned)n);
785: else if (n >= 100) // "123 xB"
786: snprintf(str, strsize, "%"PRIu64" %cB", n, prefixes[i]);
787: else if (n >= 10) // "12.3 xB"
788: snprintf(str, strsize, "%"PRIu64"%s%u %cB", n, decimal_point,
789: (unsigned)(((val % d) * 10) / d), prefixes[i]);
790: else // "1.23 xB"
791: snprintf(str, strsize, "%"PRIu64"%s%02u %cB", n, decimal_point,
792: (unsigned)(((val % d) * 100) / d), prefixes[i]);
793:
794: return str;
795: }
796:
797: // return (v)sprintf() formatted std::string
798:
799: std::string vstrprintf(const char * fmt, va_list ap)
800: {
801: char buf[512];
802: vsnprintf(buf, sizeof(buf), fmt, ap);
803: buf[sizeof(buf)-1] = 0;
804: return buf;
805: }
806:
807: std::string strprintf(const char * fmt, ...)
808: {
809: va_list ap; va_start(ap, fmt);
810: std::string str = vstrprintf(fmt, ap);
811: va_end(ap);
812: return str;
813: }
814:
815:
816: #ifndef HAVE_WORKING_SNPRINTF
817: // Some versions of (v)snprintf() don't append null char on overflow (MSVCRT.DLL),
818: // and/or return -1 on overflow (old Linux).
819: // Below are sane replacements substituted by #define in utility.h.
820:
821: #undef vsnprintf
822: #if defined(_WIN32) && defined(_MSC_VER)
823: #define vsnprintf _vsnprintf
824: #endif
825:
826: int safe_vsnprintf(char *buf, int size, const char *fmt, va_list ap)
827: {
828: int i;
829: if (size <= 0)
830: return 0;
831: i = vsnprintf(buf, size, fmt, ap);
832: if (0 <= i && i < size)
833: return i;
834: buf[size-1] = 0;
835: return strlen(buf); // Note: cannot detect for overflow, not necessary here.
836: }
837:
838: int safe_snprintf(char *buf, int size, const char *fmt, ...)
839: {
840: int i; va_list ap;
841: va_start(ap, fmt);
842: i = safe_vsnprintf(buf, size, fmt, ap);
843: va_end(ap);
844: return i;
845: }
846:
847: #endif
848:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>