]>
Commit | Line | Data |
---|---|---|
832b75ed | 1 | /* |
4d59bff9 | 2 | * utility.cpp |
832b75ed | 3 | * |
a86ec89e | 4 | * Home page of code is: http://www.smartmontools.org |
832b75ed | 5 | * |
a86ec89e | 6 | * Copyright (C) 2002-12 Bruce Allen |
ff28b140 | 7 | * Copyright (C) 2008-18 Christian Franke |
832b75ed GG |
8 | * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org> |
9 | * | |
ff28b140 | 10 | * SPDX-License-Identifier: GPL-2.0-or-later |
832b75ed GG |
11 | */ |
12 | ||
13 | // THIS FILE IS INTENDED FOR UTILITY ROUTINES THAT ARE APPLICABLE TO | |
14 | // BOTH SCSI AND ATA DEVICES, AND THAT MAY BE USED IN SMARTD, | |
15 | // SMARTCTL, OR BOTH. | |
16 | ||
a7e8ffec | 17 | #include "config.h" |
ff28b140 | 18 | #define __STDC_FORMAT_MACROS 1 // enable PRI* for C++ |
a7e8ffec | 19 | |
ff28b140 | 20 | #include <inttypes.h> |
832b75ed GG |
21 | #include <stdio.h> |
22 | #include <string.h> | |
23 | #include <time.h> | |
24 | #include <errno.h> | |
25 | #include <stdlib.h> | |
26 | #include <ctype.h> | |
832b75ed GG |
27 | #include <stdarg.h> |
28 | #include <sys/stat.h> | |
a7e8ffec GI |
29 | #ifdef HAVE_LOCALE_H |
30 | #include <locale.h> | |
31 | #endif | |
832b75ed GG |
32 | #ifdef _WIN32 |
33 | #include <mbstring.h> // _mbsinc() | |
34 | #endif | |
35 | ||
2127e193 GI |
36 | #include <stdexcept> |
37 | ||
2127e193 | 38 | #include "svnversion.h" |
832b75ed GG |
39 | #include "utility.h" |
40 | ||
2127e193 GI |
41 | #include "atacmds.h" |
42 | #include "dev_interface.h" | |
ff28b140 | 43 | #include "sg_unaligned.h" |
2127e193 | 44 | |
ff28b140 TL |
45 | const char * utility_cpp_cvsid = "$Id: utility.cpp 4842 2018-12-02 16:07:26Z chrfranke $" |
46 | UTILITY_H_CVSID; | |
832b75ed GG |
47 | |
48 | const char * packet_types[] = { | |
49 | "Direct-access (disk)", | |
50 | "Sequential-access (tape)", | |
51 | "Printer", | |
52 | "Processor", | |
53 | "Write-once (optical disk)", | |
54 | "CD/DVD", | |
55 | "Scanner", | |
56 | "Optical memory (optical disk)", | |
57 | "Medium changer", | |
58 | "Communications", | |
59 | "Graphic arts pre-press (10)", | |
60 | "Graphic arts pre-press (11)", | |
61 | "Array controller", | |
62 | "Enclosure services", | |
63 | "Reduced block command (simplified disk)", | |
64 | "Optical card reader/writer" | |
65 | }; | |
66 | ||
2127e193 GI |
67 | // BUILD_INFO can be provided by package maintainers |
68 | #ifndef BUILD_INFO | |
69 | #define BUILD_INFO "(local build)" | |
70 | #endif | |
71 | ||
72 | // Make version information string | |
73 | std::string format_version_info(const char * prog_name, bool full /*= false*/) | |
74 | { | |
75 | std::string info = strprintf( | |
d2e702cf | 76 | "%s " PACKAGE_VERSION " " |
cfbba5b9 | 77 | #ifdef SMARTMONTOOLS_SVN_REV |
d2e702cf | 78 | SMARTMONTOOLS_SVN_DATE " r" SMARTMONTOOLS_SVN_REV |
cfbba5b9 | 79 | #else |
d2e702cf | 80 | "(build date " __DATE__ ")" // checkout without expansion of Id keywords |
cfbba5b9 | 81 | #endif |
d2e702cf | 82 | " [%s] " BUILD_INFO "\n" |
ff28b140 | 83 | "Copyright (C) 2002-18, Bruce Allen, Christian Franke, www.smartmontools.org\n", |
54965743 | 84 | prog_name, smi()->get_os_version_str().c_str() |
2127e193 GI |
85 | ); |
86 | if (!full) | |
87 | return info; | |
88 | ||
a86ec89e GI |
89 | info += "\n"; |
90 | info += prog_name; | |
91 | info += " comes with ABSOLUTELY NO WARRANTY. This is free\n" | |
2127e193 | 92 | "software, and you are welcome to redistribute it under\n" |
d008864d GI |
93 | "the terms of the GNU General Public License; either\n" |
94 | "version 2, or (at your option) any later version.\n" | |
2127e193 | 95 | "See http://www.gnu.org for further details.\n" |
a86ec89e | 96 | "\n" |
d2e702cf GI |
97 | "smartmontools release " PACKAGE_VERSION |
98 | " dated " SMARTMONTOOLS_RELEASE_DATE " at " SMARTMONTOOLS_RELEASE_TIME "\n" | |
cfbba5b9 | 99 | #ifdef SMARTMONTOOLS_SVN_REV |
d2e702cf GI |
100 | "smartmontools SVN rev " SMARTMONTOOLS_SVN_REV |
101 | " dated " SMARTMONTOOLS_SVN_DATE " at " SMARTMONTOOLS_SVN_TIME "\n" | |
cfbba5b9 GI |
102 | #else |
103 | "smartmontools SVN rev is unknown\n" | |
104 | #endif | |
d2e702cf | 105 | "smartmontools build host: " SMARTMONTOOLS_BUILD_HOST "\n" |
a86ec89e | 106 | "smartmontools build with: " |
ff28b140 TL |
107 | |
108 | #define N2S_(s) #s | |
109 | #define N2S(s) "(" N2S_(s) ")" | |
110 | #if __cplusplus > 201703 | |
111 | "C++2x" N2S(__cplusplus) | |
112 | #elif __cplusplus == 201703 | |
a86ec89e | 113 | "C++17" |
ff28b140 TL |
114 | #elif __cplusplus > 201402 |
115 | "C++14" N2S(__cplusplus) | |
116 | #elif __cplusplus == 201402 | |
a86ec89e | 117 | "C++14" |
ff28b140 TL |
118 | #elif __cplusplus > 201103 |
119 | "C++11" N2S(__cplusplus) | |
120 | #elif __cplusplus == 201103 | |
a86ec89e | 121 | "C++11" |
ff28b140 TL |
122 | #elif __cplusplus > 199711 |
123 | "C++98" N2S(__cplusplus) | |
124 | #elif __cplusplus == 199711 | |
a86ec89e | 125 | "C++98" |
ff28b140 TL |
126 | #else |
127 | "C++" N2S(__cplusplus) | |
a86ec89e | 128 | #endif |
ff28b140 TL |
129 | #undef N2S |
130 | #undef N2S_ | |
131 | ||
d2e702cf | 132 | #if defined(__GNUC__) && defined(__VERSION__) // works also with CLang |
a86ec89e | 133 | ", GCC " __VERSION__ |
d2e702cf | 134 | #endif |
a86ec89e GI |
135 | "\n" |
136 | "smartmontools configure arguments:" | |
d2e702cf | 137 | ; |
bed94269 | 138 | info += (sizeof(SMARTMONTOOLS_CONFIGURE_ARGS) > 1 ? |
a86ec89e | 139 | SMARTMONTOOLS_CONFIGURE_ARGS : " [no arguments given]"); |
bed94269 | 140 | info += '\n'; |
2127e193 GI |
141 | |
142 | return info; | |
143 | } | |
832b75ed GG |
144 | |
145 | // Solaris only: Get site-default timezone. This is called from | |
146 | // UpdateTimezone() when TZ environment variable is unset at startup. | |
147 | #if defined (__SVR4) && defined (__sun) | |
148 | static const char *TIMEZONE_FILE = "/etc/TIMEZONE"; | |
149 | ||
150 | static char *ReadSiteDefaultTimezone(){ | |
151 | FILE *fp; | |
152 | char buf[512], *tz; | |
153 | int n; | |
154 | ||
155 | tz = NULL; | |
156 | fp = fopen(TIMEZONE_FILE, "r"); | |
157 | if(fp == NULL) return NULL; | |
158 | while(fgets(buf, sizeof(buf), fp)) { | |
159 | if (strncmp(buf, "TZ=", 3)) // searches last "TZ=" line | |
160 | continue; | |
161 | n = strlen(buf) - 1; | |
162 | if (buf[n] == '\n') buf[n] = 0; | |
163 | if (tz) free(tz); | |
164 | tz = strdup(buf); | |
165 | } | |
166 | fclose(fp); | |
167 | return tz; | |
168 | } | |
169 | #endif | |
170 | ||
171 | // Make sure that this executable is aware if the user has changed the | |
ff28b140 | 172 | // time-zone since the last time we polled devices. The canonical |
832b75ed GG |
173 | // example is a user who starts smartd on a laptop, then flies across |
174 | // time-zones with a laptop, and then changes the timezone, WITHOUT | |
175 | // restarting smartd. This is a work-around for a bug in | |
176 | // GLIBC. Yuk. See bug number 48184 at http://bugs.debian.org and | |
177 | // thanks to Ian Redfern for posting a workaround. | |
178 | ||
179 | // Please refer to the smartd manual page, in the section labeled LOG | |
180 | // TIMESTAMP TIMEZONE. | |
181 | void FixGlibcTimeZoneBug(){ | |
182 | #if __GLIBC__ | |
183 | if (!getenv("TZ")) { | |
2127e193 | 184 | putenv((char *)"TZ=GMT"); // POSIX prototype is 'int putenv(char *)' |
832b75ed | 185 | tzset(); |
2127e193 | 186 | putenv((char *)"TZ"); |
832b75ed GG |
187 | tzset(); |
188 | } | |
189 | #elif _WIN32 | |
190 | if (!getenv("TZ")) { | |
191 | putenv("TZ=GMT"); | |
192 | tzset(); | |
193 | putenv("TZ="); // empty value removes TZ, putenv("TZ") does nothing | |
194 | tzset(); | |
195 | } | |
196 | #elif defined (__SVR4) && defined (__sun) | |
197 | // In Solaris, putenv("TZ=") sets null string and invalid timezone. | |
198 | // putenv("TZ") does nothing. With invalid TZ, tzset() do as if | |
199 | // TZ=GMT. With TZ unset, /etc/TIMEZONE will be read only _once_ at | |
200 | // first tzset() call. Conclusion: Unlike glibc, dynamic | |
201 | // configuration of timezone can be done only by changing actual | |
202 | // value of TZ environment value. | |
203 | enum tzstate { NOT_CALLED_YET, USER_TIMEZONE, TRACK_TIMEZONE }; | |
204 | static enum tzstate state = NOT_CALLED_YET; | |
205 | ||
206 | static struct stat prev_stat; | |
207 | static char *prev_tz; | |
208 | struct stat curr_stat; | |
209 | char *curr_tz; | |
210 | ||
211 | if(state == NOT_CALLED_YET) { | |
212 | if(getenv("TZ")) { | |
213 | state = USER_TIMEZONE; // use supplied timezone | |
214 | } else { | |
215 | state = TRACK_TIMEZONE; | |
216 | if(stat(TIMEZONE_FILE, &prev_stat)) { | |
217 | state = USER_TIMEZONE; // no TZ, no timezone file; use GMT forever | |
218 | } else { | |
219 | prev_tz = ReadSiteDefaultTimezone(); // track timezone file change | |
220 | if(prev_tz) putenv(prev_tz); | |
221 | } | |
222 | } | |
223 | tzset(); | |
224 | } else if(state == TRACK_TIMEZONE) { | |
225 | if(stat(TIMEZONE_FILE, &curr_stat) == 0 | |
226 | && (curr_stat.st_ctime != prev_stat.st_ctime | |
227 | || curr_stat.st_mtime != prev_stat.st_mtime)) { | |
228 | // timezone file changed | |
229 | curr_tz = ReadSiteDefaultTimezone(); | |
230 | if(curr_tz) { | |
231 | putenv(curr_tz); | |
232 | if(prev_tz) free(prev_tz); | |
233 | prev_tz = curr_tz; prev_stat = curr_stat; | |
234 | } | |
235 | } | |
236 | tzset(); | |
237 | } | |
238 | #endif | |
239 | // OTHER OS/LIBRARY FIXES SHOULD GO HERE, IF DESIRED. PLEASE TRY TO | |
240 | // KEEP THEM INDEPENDENT. | |
241 | return; | |
242 | } | |
243 | ||
244 | #ifdef _WIN32 | |
245 | // Fix strings in tzname[] to avoid long names with non-ascii characters. | |
246 | // If TZ is not set, tzset() in the MSVC runtime sets tzname[] to the | |
247 | // national language timezone names returned by GetTimezoneInformation(). | |
248 | static char * fixtzname(char * dest, int destsize, const char * src) | |
249 | { | |
250 | int i = 0, j = 0; | |
251 | while (src[i] && j < destsize-1) { | |
252 | int i2 = (const char *)_mbsinc((const unsigned char *)src+i) - src; | |
253 | if (i2 > i+1) | |
254 | i = i2; // Ignore multibyte chars | |
255 | else { | |
256 | if ('A' <= src[i] && src[i] <= 'Z') | |
257 | dest[j++] = src[i]; // "Pacific Standard Time" => "PST" | |
258 | i++; | |
259 | } | |
260 | } | |
261 | if (j < 2) | |
262 | j = 0; | |
263 | dest[j] = 0; | |
264 | return dest; | |
265 | } | |
266 | #endif // _WIN32 | |
267 | ||
268 | // This value follows the peripheral device type value as defined in | |
269 | // SCSI Primary Commands, ANSI INCITS 301:1997. It is also used in | |
270 | // the ATA standard for packet devices to define the device type. | |
271 | const char *packetdevicetype(int type){ | |
272 | if (type<0x10) | |
273 | return packet_types[type]; | |
274 | ||
275 | if (type<0x20) | |
276 | return "Reserved"; | |
277 | ||
278 | return "Unknown"; | |
279 | } | |
280 | ||
832b75ed | 281 | // Utility function prints date and time and timezone into a character |
ff28b140 | 282 | // buffer of length 64. All the fuss is needed to get the right |
832b75ed | 283 | // timezone info (sigh). |
ff28b140 TL |
284 | void dateandtimezoneepoch(char (& buffer)[DATEANDEPOCHLEN], time_t tval) |
285 | { | |
832b75ed | 286 | struct tm *tmval; |
2127e193 | 287 | const char *timezonename; |
832b75ed GG |
288 | char datebuffer[DATEANDEPOCHLEN]; |
289 | int lenm1; | |
832b75ed GG |
290 | |
291 | FixGlibcTimeZoneBug(); | |
292 | ||
293 | // Get the time structure. We need this to determine if we are in | |
294 | // daylight savings time or not. | |
295 | tmval=localtime(&tval); | |
296 | ||
297 | // Convert to an ASCII string, put in datebuffer | |
298 | // same as: asctime_r(tmval, datebuffer); | |
299 | strncpy(datebuffer, asctime(tmval), DATEANDEPOCHLEN); | |
300 | datebuffer[DATEANDEPOCHLEN-1]='\0'; | |
301 | ||
302 | // Remove newline | |
303 | lenm1=strlen(datebuffer)-1; | |
304 | datebuffer[lenm1>=0?lenm1:0]='\0'; | |
f9e10201 JD |
305 | |
306 | #if defined(_WIN32) && defined(_MSC_VER) | |
307 | // tzname is missing in MSVC14 | |
308 | #define tzname _tzname | |
309 | #endif | |
310 | ||
832b75ed GG |
311 | // correct timezone name |
312 | if (tmval->tm_isdst==0) | |
313 | // standard time zone | |
314 | timezonename=tzname[0]; | |
315 | else if (tmval->tm_isdst>0) | |
316 | // daylight savings in effect | |
317 | timezonename=tzname[1]; | |
318 | else | |
319 | // unable to determine if daylight savings in effect | |
320 | timezonename=""; | |
321 | ||
322 | #ifdef _WIN32 | |
323 | // Fix long non-ascii timezone names | |
a86ec89e GI |
324 | // cppcheck-suppress variableScope |
325 | char tzfixbuf[6+1] = ""; | |
832b75ed GG |
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 | ||
832b75ed GG |
336 | // A replacement for perror() that sends output to our choice of |
337 | // printing. If errno not set then just print message. | |
338 | void syserror(const char *message){ | |
339 | ||
340 | if (errno) { | |
341 | // Get the correct system error message: | |
342 | const char *errormessage=strerror(errno); | |
343 | ||
344 | // Check that caller has handed a sensible string, and provide | |
345 | // appropriate output. See perrror(3) man page to understand better. | |
346 | if (message && *message) | |
347 | pout("%s: %s\n",message, errormessage); | |
348 | else | |
349 | pout("%s\n",errormessage); | |
350 | } | |
351 | else if (message && *message) | |
352 | pout("%s\n",message); | |
353 | ||
354 | return; | |
355 | } | |
356 | ||
3d17a85c GI |
357 | // Check regular expression for non-portable features. |
358 | // | |
832b75ed GG |
359 | // POSIX extended regular expressions interpret unmatched ')' ordinary: |
360 | // "The close-parenthesis shall be considered special in this context | |
361 | // only if matched with a preceding open-parenthesis." | |
362 | // | |
3d17a85c GI |
363 | // GNU libc and BSD libc support unmatched ')', Cygwin reports an error. |
364 | // | |
365 | // POSIX extended regular expressions do not define empty subexpressions: | |
366 | // "A vertical-line appearing first or last in an ERE, or immediately following | |
367 | // a vertical-line or a left-parenthesis, or immediately preceding a | |
368 | // right-parenthesis, produces undefined results." | |
369 | // | |
370 | // GNU libc and Cygwin support empty subexpressions, BSD libc reports an error. | |
371 | // | |
372 | static const char * check_regex(const char * pattern) | |
832b75ed | 373 | { |
3d17a85c GI |
374 | int level = 0; |
375 | char c; | |
376 | ||
377 | for (int i = 0; (c = pattern[i]); i++) { | |
378 | // Skip "\x" | |
379 | if (c == '\\') { | |
380 | if (!pattern[++i]) | |
381 | break; | |
382 | continue; | |
832b75ed | 383 | } |
3d17a85c GI |
384 | |
385 | // Skip "[...]" | |
386 | if (c == '[') { | |
387 | if (pattern[++i] == '^') | |
388 | i++; | |
389 | if (!pattern[i++]) | |
390 | break; | |
391 | while ((c = pattern[i]) && c != ']') | |
392 | i++; | |
393 | if (!c) | |
394 | break; | |
395 | continue; | |
396 | } | |
397 | ||
398 | // Check "(...)" nesting | |
399 | if (c == '(') | |
400 | level++; | |
401 | else if (c == ')' && --level < 0) | |
402 | return "Unmatched ')'"; | |
403 | ||
404 | // Check for leading/trailing '|' or "||", "|)", "|$", "(|", "^|" | |
405 | char c1; | |
406 | if ( (c == '|' && ( i == 0 || !(c1 = pattern[i+1]) | |
407 | || c1 == '|' || c1 == ')' || c1 == '$')) | |
408 | || ((c == '(' || c == '^') && pattern[i+1] == '|') ) | |
409 | return "Empty '|' subexpression"; | |
832b75ed | 410 | } |
3d17a85c GI |
411 | |
412 | return (const char *)0; | |
832b75ed GG |
413 | } |
414 | ||
ff28b140 | 415 | // Wrapper class for POSIX regex(3) or std::regex |
832b75ed | 416 | |
ff28b140 | 417 | #ifndef WITH_CXX11_REGEX |
2127e193 | 418 | |
ff28b140 | 419 | regular_expression::regular_expression() |
2127e193 GI |
420 | { |
421 | memset(&m_regex_buf, 0, sizeof(m_regex_buf)); | |
2127e193 GI |
422 | } |
423 | ||
424 | regular_expression::~regular_expression() | |
425 | { | |
426 | free_buf(); | |
427 | } | |
428 | ||
429 | regular_expression::regular_expression(const regular_expression & x) | |
ff28b140 TL |
430 | : m_pattern(x.m_pattern), |
431 | m_errmsg(x.m_errmsg) | |
2127e193 GI |
432 | { |
433 | memset(&m_regex_buf, 0, sizeof(m_regex_buf)); | |
ff28b140 | 434 | copy_buf(x); |
2127e193 GI |
435 | } |
436 | ||
437 | regular_expression & regular_expression::operator=(const regular_expression & x) | |
438 | { | |
ff28b140 TL |
439 | m_pattern = x.m_pattern; |
440 | m_errmsg = x.m_errmsg; | |
2127e193 | 441 | free_buf(); |
ff28b140 | 442 | copy_buf(x); |
2127e193 GI |
443 | return *this; |
444 | } | |
445 | ||
446 | void regular_expression::free_buf() | |
447 | { | |
448 | if (nonempty(&m_regex_buf, sizeof(m_regex_buf))) { | |
449 | regfree(&m_regex_buf); | |
450 | memset(&m_regex_buf, 0, sizeof(m_regex_buf)); | |
832b75ed | 451 | } |
2127e193 GI |
452 | } |
453 | ||
ff28b140 | 454 | void regular_expression::copy_buf(const regular_expression & x) |
2127e193 | 455 | { |
ff28b140 | 456 | if (nonempty(&x.m_regex_buf, sizeof(x.m_regex_buf))) { |
2127e193 GI |
457 | // There is no POSIX compiled-regex-copy command. |
458 | if (!compile()) | |
459 | throw std::runtime_error(strprintf( | |
460 | "Unable to recompile regular expression \"%s\": %s", | |
461 | m_pattern.c_str(), m_errmsg.c_str())); | |
462 | } | |
463 | } | |
464 | ||
ff28b140 TL |
465 | #endif // !WITH_CXX11_REGEX |
466 | ||
467 | regular_expression::regular_expression(const char * pattern) | |
468 | : m_pattern(pattern) | |
2127e193 | 469 | { |
ff28b140 TL |
470 | if (!compile()) |
471 | throw std::runtime_error(strprintf( | |
472 | "error in regular expression \"%s\": %s", | |
473 | m_pattern.c_str(), m_errmsg.c_str())); | |
474 | } | |
475 | ||
476 | bool regular_expression::compile(const char * pattern) | |
477 | { | |
478 | #ifndef WITH_CXX11_REGEX | |
2127e193 | 479 | free_buf(); |
ff28b140 | 480 | #endif |
2127e193 | 481 | m_pattern = pattern; |
2127e193 GI |
482 | return compile(); |
483 | } | |
484 | ||
485 | bool regular_expression::compile() | |
486 | { | |
ff28b140 TL |
487 | #ifdef WITH_CXX11_REGEX |
488 | try { | |
489 | m_regex.assign(m_pattern, std::regex_constants::extended); | |
490 | } | |
491 | catch (std::regex_error & ex) { | |
492 | m_errmsg = ex.what(); | |
493 | return false; | |
494 | } | |
495 | ||
496 | #else | |
497 | int errcode = regcomp(&m_regex_buf, m_pattern.c_str(), REG_EXTENDED); | |
2127e193 GI |
498 | if (errcode) { |
499 | char errmsg[512]; | |
500 | regerror(errcode, &m_regex_buf, errmsg, sizeof(errmsg)); | |
501 | m_errmsg = errmsg; | |
502 | free_buf(); | |
503 | return false; | |
504 | } | |
ff28b140 | 505 | #endif |
2127e193 | 506 | |
3d17a85c GI |
507 | const char * errmsg = check_regex(m_pattern.c_str()); |
508 | if (errmsg) { | |
509 | m_errmsg = errmsg; | |
ff28b140 TL |
510 | #ifdef WITH_CXX11_REGEX |
511 | m_regex = std::regex(); | |
512 | #else | |
2127e193 | 513 | free_buf(); |
ff28b140 | 514 | #endif |
2127e193 GI |
515 | return false; |
516 | } | |
517 | ||
518 | m_errmsg.clear(); | |
519 | return true; | |
832b75ed GG |
520 | } |
521 | ||
ff28b140 TL |
522 | bool regular_expression::full_match(const char * str) const |
523 | { | |
524 | #ifdef WITH_CXX11_REGEX | |
525 | return std::regex_match(str, m_regex); | |
526 | #else | |
527 | match_range range; | |
528 | return ( !regexec(&m_regex_buf, str, 1, &range, 0) | |
529 | && range.rm_so == 0 && range.rm_eo == (int)strlen(str)); | |
530 | #endif | |
531 | } | |
832b75ed | 532 | |
ff28b140 | 533 | bool regular_expression::execute(const char * str, unsigned nmatch, match_range * pmatch) const |
832b75ed | 534 | { |
ff28b140 TL |
535 | #ifdef WITH_CXX11_REGEX |
536 | std::cmatch m; | |
537 | if (!std::regex_search(str, m, m_regex)) | |
538 | return false; | |
539 | unsigned sz = m.size(); | |
540 | for (unsigned i = 0; i < nmatch; i++) { | |
541 | if (i < sz && *m[i].first) { | |
542 | pmatch[i].rm_so = m[i].first - str; | |
543 | pmatch[i].rm_eo = m[i].second - str; | |
832b75ed GG |
544 | } |
545 | else | |
ff28b140 | 546 | pmatch[i].rm_so = pmatch[i].rm_eo = -1; |
832b75ed | 547 | } |
ff28b140 | 548 | return true; |
832b75ed | 549 | |
ff28b140 TL |
550 | #else |
551 | return !regexec(&m_regex_buf, str, nmatch, pmatch, 0); | |
552 | #endif | |
832b75ed | 553 | } |
832b75ed GG |
554 | |
555 | // Splits an argument to the -t option that is assumed to be of the form | |
556 | // "selective,%lld-%lld" (prefixes of "0" (for octal) and "0x"/"0X" (for hex) | |
557 | // are allowed). The first long long int is assigned to *start and the second | |
558 | // to *stop. Returns zero if successful and non-zero otherwise. | |
559 | int split_selective_arg(char *s, uint64_t *start, | |
a37e7145 | 560 | uint64_t *stop, int *mode) |
832b75ed GG |
561 | { |
562 | char *tailptr; | |
832b75ed GG |
563 | if (!(s = strchr(s, ','))) |
564 | return 1; | |
a37e7145 GG |
565 | bool add = false; |
566 | if (!isdigit((int)(*++s))) { | |
567 | *start = *stop = 0; | |
568 | if (!strncmp(s, "redo", 4)) | |
569 | *mode = SEL_REDO; | |
570 | else if (!strncmp(s, "next", 4)) | |
571 | *mode = SEL_NEXT; | |
572 | else if (!strncmp(s, "cont", 4)) | |
573 | *mode = SEL_CONT; | |
574 | else | |
575 | return 1; | |
576 | s += 4; | |
577 | if (!*s) | |
578 | return 0; | |
579 | if (*s != '+') | |
580 | return 1; | |
581 | } | |
582 | else { | |
583 | *mode = SEL_RANGE; | |
584 | errno = 0; | |
585 | // Last argument to strtoull (the base) is 0 meaning that decimal is assumed | |
586 | // unless prefixes of "0" (for octal) or "0x"/"0X" (for hex) are used. | |
587 | *start = strtoull(s, &tailptr, 0); | |
588 | s = tailptr; | |
589 | add = (*s == '+'); | |
590 | if (!(!errno && (add || *s == '-'))) | |
591 | return 1; | |
592 | if (!strcmp(s, "-max")) { | |
593 | *stop = ~(uint64_t)0; // replaced by max LBA later | |
594 | return 0; | |
595 | } | |
596 | } | |
ee38a438 GI |
597 | |
598 | errno = 0; | |
a37e7145 | 599 | *stop = strtoull(s+1, &tailptr, 0); |
832b75ed GG |
600 | if (errno || *tailptr != '\0') |
601 | return 1; | |
a37e7145 GG |
602 | if (add) { |
603 | if (*stop > 0) | |
604 | (*stop)--; | |
605 | *stop += *start; // -t select,N+M => -t select,N,(N+M-1) | |
606 | } | |
832b75ed GG |
607 | return 0; |
608 | } | |
609 | ||
2127e193 GI |
610 | // Returns true if region of memory contains non-zero entries |
611 | bool nonempty(const void * data, int size) | |
612 | { | |
613 | for (int i = 0; i < size; i++) | |
614 | if (((const unsigned char *)data)[i]) | |
615 | return true; | |
616 | return false; | |
832b75ed GG |
617 | } |
618 | ||
a86ec89e GI |
619 | // Copy not null terminated char array to null terminated string. |
620 | // Replace non-ascii characters. Remove leading and trailing blanks. | |
621 | const char * format_char_array(char * str, int strsize, const char * chr, int chrsize) | |
622 | { | |
623 | int b = 0; | |
624 | while (b < chrsize && chr[b] == ' ') | |
625 | b++; | |
626 | int n = 0; | |
627 | while (b+n < chrsize && chr[b+n]) | |
628 | n++; | |
629 | while (n > 0 && chr[b+n-1] == ' ') | |
630 | n--; | |
631 | ||
632 | if (n >= strsize) | |
633 | n = strsize-1; | |
634 | ||
635 | for (int i = 0; i < n; i++) { | |
636 | char c = chr[b+i]; | |
637 | str[i] = (' ' <= c && c <= '~' ? c : '?'); | |
638 | } | |
639 | ||
640 | str[n] = 0; | |
641 | return str; | |
642 | } | |
643 | ||
a7e8ffec GI |
644 | // Format integer with thousands separator |
645 | const char * format_with_thousands_sep(char * str, int strsize, uint64_t val, | |
646 | const char * thousands_sep /* = 0 */) | |
647 | { | |
648 | if (!thousands_sep) { | |
649 | thousands_sep = ","; | |
650 | #ifdef HAVE_LOCALE_H | |
651 | setlocale(LC_ALL, ""); | |
652 | const struct lconv * currentlocale = localeconv(); | |
653 | if (*(currentlocale->thousands_sep)) | |
654 | thousands_sep = currentlocale->thousands_sep; | |
655 | #endif | |
656 | } | |
657 | ||
658 | char num[64]; | |
d2e702cf | 659 | snprintf(num, sizeof(num), "%" PRIu64, val); |
a7e8ffec GI |
660 | int numlen = strlen(num); |
661 | ||
662 | int i = 0, j = 0; | |
663 | do | |
664 | str[j++] = num[i++]; | |
665 | while (i < numlen && (numlen - i) % 3 != 0 && j < strsize-1); | |
666 | str[j] = 0; | |
667 | ||
668 | while (i < numlen && j < strsize-1) { | |
669 | j += snprintf(str+j, strsize-j, "%s%.3s", thousands_sep, num+i); | |
670 | i += 3; | |
671 | } | |
672 | ||
673 | return str; | |
674 | } | |
675 | ||
676 | // Format capacity with SI prefixes | |
677 | const char * format_capacity(char * str, int strsize, uint64_t val, | |
678 | const char * decimal_point /* = 0 */) | |
679 | { | |
680 | if (!decimal_point) { | |
681 | decimal_point = "."; | |
682 | #ifdef HAVE_LOCALE_H | |
683 | setlocale(LC_ALL, ""); | |
684 | const struct lconv * currentlocale = localeconv(); | |
685 | if (*(currentlocale->decimal_point)) | |
686 | decimal_point = currentlocale->decimal_point; | |
687 | #endif | |
688 | } | |
689 | ||
690 | const unsigned factor = 1000; // 1024 for KiB,MiB,... | |
691 | static const char prefixes[] = " KMGTP"; | |
692 | ||
693 | // Find d with val in [d, d*factor) | |
694 | unsigned i = 0; | |
695 | uint64_t d = 1; | |
696 | for (uint64_t d2 = d * factor; val >= d2; d2 *= factor) { | |
697 | d = d2; | |
698 | if (++i >= sizeof(prefixes)-2) | |
699 | break; | |
700 | } | |
701 | ||
702 | // Print 3 digits | |
703 | uint64_t n = val / d; | |
704 | if (i == 0) | |
705 | snprintf(str, strsize, "%u B", (unsigned)n); | |
706 | else if (n >= 100) // "123 xB" | |
d2e702cf | 707 | snprintf(str, strsize, "%" PRIu64 " %cB", n, prefixes[i]); |
a7e8ffec | 708 | else if (n >= 10) // "12.3 xB" |
d2e702cf | 709 | snprintf(str, strsize, "%" PRIu64 "%s%u %cB", n, decimal_point, |
a7e8ffec GI |
710 | (unsigned)(((val % d) * 10) / d), prefixes[i]); |
711 | else // "1.23 xB" | |
d2e702cf | 712 | snprintf(str, strsize, "%" PRIu64 "%s%02u %cB", n, decimal_point, |
a7e8ffec GI |
713 | (unsigned)(((val % d) * 100) / d), prefixes[i]); |
714 | ||
715 | return str; | |
716 | } | |
717 | ||
2127e193 | 718 | // return (v)sprintf() formatted std::string |
f9e10201 | 719 | __attribute_format_printf(1, 0) |
2127e193 GI |
720 | std::string vstrprintf(const char * fmt, va_list ap) |
721 | { | |
722 | char buf[512]; | |
723 | vsnprintf(buf, sizeof(buf), fmt, ap); | |
724 | buf[sizeof(buf)-1] = 0; | |
725 | return buf; | |
726 | } | |
727 | ||
728 | std::string strprintf(const char * fmt, ...) | |
729 | { | |
730 | va_list ap; va_start(ap, fmt); | |
731 | std::string str = vstrprintf(fmt, ap); | |
732 | va_end(ap); | |
733 | return str; | |
734 | } | |
735 | ||
ff28b140 TL |
736 | #if defined(HAVE___INT128) |
737 | // Compiler supports '__int128'. | |
738 | ||
739 | // Recursive 128-bit to string conversion function | |
740 | static int snprint_uint128(char * str, int strsize, unsigned __int128 value) | |
741 | { | |
742 | if (strsize <= 0) | |
743 | return -1; | |
744 | ||
745 | if (value <= 0xffffffffffffffffULL) { | |
746 | // Print leading digits as 64-bit value | |
747 | return snprintf(str, (size_t)strsize, "%" PRIu64, (uint64_t)value); | |
748 | } | |
749 | else { | |
750 | // Recurse to print leading digits | |
751 | const uint64_t e19 = 10000000000000000000ULL; // 2^63 < 10^19 < 2^64 | |
752 | int len1 = snprint_uint128(str, strsize, value / e19); | |
753 | if (len1 < 0) | |
754 | return -1; | |
755 | ||
756 | // Print 19 digits remainder as 64-bit value | |
757 | int len2 = snprintf(str + (len1 < strsize ? len1 : strsize - 1), | |
758 | (size_t)(len1 < strsize ? strsize - len1 : 1), | |
759 | "%019" PRIu64, (uint64_t)(value % e19) ); | |
760 | if (len2 < 0) | |
761 | return -1; | |
762 | return len1 + len2; | |
763 | } | |
764 | } | |
765 | ||
766 | // Convert 128-bit unsigned integer provided as two 64-bit halves to a string. | |
767 | const char * uint128_hilo_to_str(char * str, int strsize, uint64_t value_hi, uint64_t value_lo) | |
768 | { | |
769 | snprint_uint128(str, strsize, ((unsigned __int128)value_hi << 64) | value_lo); | |
770 | return str; | |
771 | } | |
772 | ||
773 | #elif defined(HAVE_LONG_DOUBLE_WIDER_PRINTF) | |
774 | // Compiler and *printf() support 'long double' which is wider than 'double'. | |
775 | ||
776 | const char * uint128_hilo_to_str(char * str, int strsize, uint64_t value_hi, uint64_t value_lo) | |
777 | { | |
778 | snprintf(str, strsize, "%.0Lf", value_hi * (0xffffffffffffffffULL + 1.0L) + value_lo); | |
779 | return str; | |
780 | } | |
781 | ||
782 | #else // !HAVE_LONG_DOUBLE_WIDER_PRINTF | |
783 | // No '__int128' or 'long double' support, use 'double'. | |
784 | ||
785 | const char * uint128_hilo_to_str(char * str, int strsize, uint64_t value_hi, uint64_t value_lo) | |
786 | { | |
787 | snprintf(str, strsize, "%.0f", value_hi * (0xffffffffffffffffULL + 1.0) + value_lo); | |
788 | return str; | |
789 | } | |
790 | ||
791 | #endif // HAVE___INT128 | |
792 | ||
793 | // Runtime check of byte ordering, throws on error. | |
794 | static void check_endianness() | |
795 | { | |
796 | const union { | |
797 | // Force compile error if int type is not 32bit. | |
798 | unsigned char c[sizeof(int) == 4 ? 8 : -1]; | |
799 | uint64_t i; | |
800 | } x = {{1, 2, 3, 4, 5, 6, 7, 8}}; | |
801 | const uint64_t le = 0x0807060504030201ULL; | |
802 | const uint64_t be = 0x0102030405060708ULL; | |
803 | ||
804 | if (!( x.i == (isbigendian() ? be : le) | |
805 | && sg_get_unaligned_le16(x.c) == (uint16_t)le | |
806 | && sg_get_unaligned_be16(x.c+6) == (uint16_t)be | |
807 | && sg_get_unaligned_le32(x.c) == (uint32_t)le | |
808 | && sg_get_unaligned_be32(x.c+4) == (uint32_t)be | |
809 | && sg_get_unaligned_le64(x.c) == le | |
810 | && sg_get_unaligned_be64(x.c) == be )) | |
811 | throw std::logic_error("CPU endianness does not match compile time test"); | |
812 | } | |
832b75ed GG |
813 | |
814 | #ifndef HAVE_WORKING_SNPRINTF | |
d2e702cf GI |
815 | // Some versions of (v)snprintf() don't append null char (MSVCRT.DLL), |
816 | // and/or return -1 on output truncation (glibc <= 2.0.6). | |
832b75ed GG |
817 | // Below are sane replacements substituted by #define in utility.h. |
818 | ||
819 | #undef vsnprintf | |
820 | #if defined(_WIN32) && defined(_MSC_VER) | |
821 | #define vsnprintf _vsnprintf | |
822 | #endif | |
823 | ||
824 | int safe_vsnprintf(char *buf, int size, const char *fmt, va_list ap) | |
825 | { | |
826 | int i; | |
827 | if (size <= 0) | |
828 | return 0; | |
829 | i = vsnprintf(buf, size, fmt, ap); | |
830 | if (0 <= i && i < size) | |
831 | return i; | |
832 | buf[size-1] = 0; | |
833 | return strlen(buf); // Note: cannot detect for overflow, not necessary here. | |
834 | } | |
835 | ||
836 | int safe_snprintf(char *buf, int size, const char *fmt, ...) | |
837 | { | |
838 | int i; va_list ap; | |
839 | va_start(ap, fmt); | |
840 | i = safe_vsnprintf(buf, size, fmt, ap); | |
841 | va_end(ap); | |
842 | return i; | |
843 | } | |
844 | ||
f9e10201 JD |
845 | static void check_snprintf() {} |
846 | ||
847 | #elif defined(__GNUC__) && (__GNUC__ >= 7) | |
848 | ||
849 | // G++ 7+: Assume sane implementation and avoid -Wformat-truncation warning | |
850 | static void check_snprintf() {} | |
851 | ||
852 | #else | |
d2e702cf GI |
853 | |
854 | static void check_snprintf() | |
855 | { | |
856 | char buf[] = "ABCDEFGHI"; | |
857 | int n1 = snprintf(buf, 8, "123456789"); | |
858 | int n2 = snprintf(buf, 0, "X"); | |
859 | if (!(!strcmp(buf, "1234567") && n1 == 9 && n2 == 1)) | |
f9e10201 | 860 | throw std::logic_error("Function snprintf() does not conform to C99"); |
d2e702cf GI |
861 | } |
862 | ||
863 | #endif // HAVE_WORKING_SNPRINTF | |
a23d5117 | 864 | |
d2e702cf GI |
865 | // Runtime check of ./configure result, throws on error. |
866 | void check_config() | |
867 | { | |
868 | check_endianness(); | |
d2e702cf | 869 | check_snprintf(); |
d2e702cf | 870 | } |