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