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