]>
Commit | Line | Data |
---|---|---|
832b75ed | 1 | /* |
4d59bff9 | 2 | * utility.cpp |
832b75ed GG |
3 | * |
4 | * Home page of code is: http://smartmontools.sourceforge.net | |
5 | * | |
d008864d | 6 | * Copyright (C) 2002-12 Bruce Allen <smartmontools-support@lists.sourceforge.net> |
d2e702cf | 7 | * Copyright (C) 2008-14 Christian Franke <smartmontools-support@lists.sourceforge.net> |
832b75ed GG |
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 | |
ee38a438 | 16 | * (for example COPYING); If not, see <http://www.gnu.org/licenses/>. |
832b75ed GG |
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 | ||
a7e8ffec GI |
29 | #include "config.h" |
30 | ||
832b75ed GG |
31 | #include <stdio.h> |
32 | #include <string.h> | |
33 | #include <time.h> | |
34 | #include <errno.h> | |
35 | #include <stdlib.h> | |
36 | #include <ctype.h> | |
832b75ed GG |
37 | #include <stdarg.h> |
38 | #include <sys/stat.h> | |
a7e8ffec GI |
39 | #ifdef HAVE_LOCALE_H |
40 | #include <locale.h> | |
41 | #endif | |
832b75ed GG |
42 | #ifdef _WIN32 |
43 | #include <mbstring.h> // _mbsinc() | |
44 | #endif | |
45 | ||
2127e193 GI |
46 | #include <stdexcept> |
47 | ||
2127e193 | 48 | #include "svnversion.h" |
832b75ed GG |
49 | #include "int64.h" |
50 | #include "utility.h" | |
51 | ||
2127e193 GI |
52 | #include "atacmds.h" |
53 | #include "dev_interface.h" | |
54 | ||
d2e702cf | 55 | const char * utility_cpp_cvsid = "$Id: utility.cpp 3937 2014-07-05 17:51:21Z chrfranke $" |
2127e193 | 56 | UTILITY_H_CVSID INT64_H_CVSID; |
832b75ed GG |
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 | ||
2127e193 GI |
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( | |
d2e702cf | 86 | "%s " PACKAGE_VERSION " " |
cfbba5b9 | 87 | #ifdef SMARTMONTOOLS_SVN_REV |
d2e702cf | 88 | SMARTMONTOOLS_SVN_DATE " r" SMARTMONTOOLS_SVN_REV |
cfbba5b9 | 89 | #else |
d2e702cf | 90 | "(build date " __DATE__ ")" // checkout without expansion of Id keywords |
cfbba5b9 | 91 | #endif |
d2e702cf GI |
92 | " [%s] " BUILD_INFO "\n" |
93 | "Copyright (C) 2002-14, Bruce Allen, Christian Franke, www.smartmontools.org\n", | |
54965743 | 94 | prog_name, smi()->get_os_version_str().c_str() |
2127e193 GI |
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" | |
d008864d GI |
103 | "the terms of the GNU General Public License; either\n" |
104 | "version 2, or (at your option) any later version.\n" | |
2127e193 | 105 | "See http://www.gnu.org for further details.\n" |
bed94269 GI |
106 | "\n", |
107 | prog_name | |
108 | ); | |
d2e702cf GI |
109 | info += |
110 | "smartmontools release " PACKAGE_VERSION | |
111 | " dated " SMARTMONTOOLS_RELEASE_DATE " at " SMARTMONTOOLS_RELEASE_TIME "\n" | |
cfbba5b9 | 112 | #ifdef SMARTMONTOOLS_SVN_REV |
d2e702cf GI |
113 | "smartmontools SVN rev " SMARTMONTOOLS_SVN_REV |
114 | " dated " SMARTMONTOOLS_SVN_DATE " at " SMARTMONTOOLS_SVN_TIME "\n" | |
cfbba5b9 GI |
115 | #else |
116 | "smartmontools SVN rev is unknown\n" | |
117 | #endif | |
d2e702cf GI |
118 | "smartmontools build host: " SMARTMONTOOLS_BUILD_HOST "\n" |
119 | #if defined(__GNUC__) && defined(__VERSION__) // works also with CLang | |
120 | "smartmontools build with: GCC " __VERSION__ "\n" | |
121 | #endif | |
122 | "smartmontools configure arguments: " | |
123 | ; | |
bed94269 GI |
124 | info += (sizeof(SMARTMONTOOLS_CONFIGURE_ARGS) > 1 ? |
125 | SMARTMONTOOLS_CONFIGURE_ARGS : "[no arguments given]"); | |
126 | info += '\n'; | |
2127e193 GI |
127 | |
128 | return info; | |
129 | } | |
832b75ed GG |
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")) { | |
2127e193 | 170 | putenv((char *)"TZ=GMT"); // POSIX prototype is 'int putenv(char *)' |
832b75ed | 171 | tzset(); |
2127e193 | 172 | putenv((char *)"TZ"); |
832b75ed GG |
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 | ||
e9583e0c | 267 | // Runtime check of byte ordering, throws if different from isbigendian(). |
d2e702cf | 268 | static void check_endianness() |
e9583e0c GI |
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 | } | |
832b75ed | 281 | |
e9583e0c GI |
282 | if (big != (isbigendian() ? 1 : 0)) |
283 | throw std::logic_error("CPU endianness does not match compile time test"); | |
832b75ed GG |
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; | |
2127e193 | 291 | const char *timezonename; |
832b75ed GG |
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 | ||
832b75ed GG |
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 | ||
3d17a85c GI |
367 | // Check regular expression for non-portable features. |
368 | // | |
832b75ed GG |
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 | // | |
3d17a85c GI |
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) | |
832b75ed | 383 | { |
3d17a85c GI |
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; | |
832b75ed | 393 | } |
3d17a85c GI |
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"; | |
832b75ed | 420 | } |
3d17a85c GI |
421 | |
422 | return (const char *)0; | |
832b75ed GG |
423 | } |
424 | ||
2127e193 | 425 | // Wrapper class for regex(3) |
832b75ed | 426 | |
2127e193 GI |
427 | regular_expression::regular_expression() |
428 | : m_flags(0) | |
429 | { | |
430 | memset(&m_regex_buf, 0, sizeof(m_regex_buf)); | |
431 | } | |
432 | ||
cfbba5b9 GI |
433 | regular_expression::regular_expression(const char * pattern, int flags, |
434 | bool throw_on_error /*= true*/) | |
2127e193 GI |
435 | { |
436 | memset(&m_regex_buf, 0, sizeof(m_regex_buf)); | |
cfbba5b9 GI |
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())); | |
2127e193 GI |
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)); | |
832b75ed | 466 | } |
2127e193 GI |
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 | ||
3d17a85c GI |
503 | const char * errmsg = check_regex(m_pattern.c_str()); |
504 | if (errmsg) { | |
505 | m_errmsg = errmsg; | |
2127e193 GI |
506 | free_buf(); |
507 | return false; | |
508 | } | |
509 | ||
510 | m_errmsg.clear(); | |
511 | return true; | |
832b75ed GG |
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 | ||
832b75ed | 541 | #ifndef HAVE_STRTOULL |
2127e193 GI |
542 | // Replacement for missing strtoull() (Linux with libc < 6, MSVC) |
543 | // Functionality reduced to requirements of smartd and split_selective_arg(). | |
832b75ed | 544 | |
2127e193 | 545 | uint64_t strtoull(const char * p, char * * endp, int base) |
832b75ed GG |
546 | { |
547 | uint64_t result, maxres; | |
548 | int i = 0; | |
549 | char c = p[i++]; | |
2127e193 GI |
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++]; | |
832b75ed GG |
559 | } |
560 | else | |
2127e193 | 561 | base = 10; |
832b75ed | 562 | } |
832b75ed GG |
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 | } | |
2127e193 GI |
586 | if (endp) |
587 | *endp = (char *)p + i - 1; | |
832b75ed GG |
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, | |
a37e7145 | 597 | uint64_t *stop, int *mode) |
832b75ed GG |
598 | { |
599 | char *tailptr; | |
832b75ed GG |
600 | if (!(s = strchr(s, ','))) |
601 | return 1; | |
a37e7145 GG |
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 | } | |
ee38a438 GI |
634 | |
635 | errno = 0; | |
a37e7145 | 636 | *stop = strtoull(s+1, &tailptr, 0); |
832b75ed GG |
637 | if (errno || *tailptr != '\0') |
638 | return 1; | |
a37e7145 GG |
639 | if (add) { |
640 | if (*stop > 0) | |
641 | (*stop)--; | |
642 | *stop += *start; // -t select,N+M => -t select,N,(N+M-1) | |
643 | } | |
832b75ed GG |
644 | return 0; |
645 | } | |
646 | ||
2127e193 GI |
647 | #ifdef OLD_INTERFACE |
648 | ||
832b75ed | 649 | int64_t bytes = 0; |
2127e193 | 650 | |
832b75ed GG |
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. | |
4d59bff9 | 654 | void *FreeNonZero1(void *address, int size, int line, const char* file){ |
832b75ed GG |
655 | if (address) { |
656 | if (size<0) | |
4d59bff9 | 657 | bytes-=1+strlen((char*)address); |
832b75ed GG |
658 | else |
659 | bytes-=size; | |
4d59bff9 | 660 | return CheckFree1(address, line, file); |
832b75ed GG |
661 | } |
662 | return NULL; | |
663 | } | |
664 | ||
665 | // To help with memory checking. Use when it is known that address is | |
666 | // NOT null. | |
cfbba5b9 | 667 | void *CheckFree1(void *address, int /*whatline*/, const char* /*file*/){ |
832b75ed GG |
668 | if (address){ |
669 | free(address); | |
670 | return NULL; | |
671 | } | |
cfbba5b9 | 672 | throw std::runtime_error("Internal error in CheckFree()"); |
832b75ed GG |
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. | |
cfbba5b9 | 688 | char *CustomStrDup(const char *ptr, int mustexist, int /*whatline*/, const char* /*file*/){ |
832b75ed GG |
689 | char *tmp; |
690 | ||
691 | // report error if ptr is NULL and mustexist is set | |
692 | if (ptr==NULL){ | |
cfbba5b9 GI |
693 | if (mustexist) |
694 | throw std::runtime_error("Internal error in CustomStrDup()"); | |
832b75ed GG |
695 | else |
696 | return NULL; | |
697 | } | |
698 | ||
699 | // make a copy of the string... | |
700 | tmp=strdup(ptr); | |
701 | ||
cfbba5b9 GI |
702 | if (!tmp) |
703 | throw std::bad_alloc(); | |
832b75ed GG |
704 | |
705 | // and track memory usage | |
706 | bytes+=1+strlen(ptr); | |
707 | ||
708 | return tmp; | |
709 | } | |
710 | ||
2127e193 GI |
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; | |
832b75ed GG |
721 | } |
722 | ||
a7e8ffec GI |
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]; | |
d2e702cf | 738 | snprintf(num, sizeof(num), "%" PRIu64, val); |
a7e8ffec GI |
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" | |
d2e702cf | 786 | snprintf(str, strsize, "%" PRIu64 " %cB", n, prefixes[i]); |
a7e8ffec | 787 | else if (n >= 10) // "12.3 xB" |
d2e702cf | 788 | snprintf(str, strsize, "%" PRIu64 "%s%u %cB", n, decimal_point, |
a7e8ffec GI |
789 | (unsigned)(((val % d) * 10) / d), prefixes[i]); |
790 | else // "1.23 xB" | |
d2e702cf | 791 | snprintf(str, strsize, "%" PRIu64 "%s%02u %cB", n, decimal_point, |
a7e8ffec GI |
792 | (unsigned)(((val % d) * 100) / d), prefixes[i]); |
793 | ||
794 | return str; | |
795 | } | |
796 | ||
2127e193 GI |
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 | ||
832b75ed GG |
815 | |
816 | #ifndef HAVE_WORKING_SNPRINTF | |
d2e702cf GI |
817 | // Some versions of (v)snprintf() don't append null char (MSVCRT.DLL), |
818 | // and/or return -1 on output truncation (glibc <= 2.0.6). | |
832b75ed GG |
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 | ||
d2e702cf GI |
847 | #else // HAVE_WORKING_SNPRINTF |
848 | ||
849 | static void check_snprintf() | |
850 | { | |
851 | char buf[] = "ABCDEFGHI"; | |
852 | int n1 = snprintf(buf, 8, "123456789"); | |
853 | int n2 = snprintf(buf, 0, "X"); | |
854 | if (!(!strcmp(buf, "1234567") && n1 == 9 && n2 == 1)) | |
855 | throw std::logic_error("Function snprintf() does not conform to C99,\n" | |
856 | "please contact " PACKAGE_BUGREPORT); | |
857 | } | |
858 | ||
859 | #endif // HAVE_WORKING_SNPRINTF | |
a23d5117 | 860 | |
d2e702cf GI |
861 | // Runtime check of ./configure result, throws on error. |
862 | void check_config() | |
863 | { | |
864 | check_endianness(); | |
865 | #ifdef HAVE_WORKING_SNPRINTF | |
866 | check_snprintf(); | |
867 | #endif | |
868 | } |