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