1 // Copyright (c) 2010, Google Inc. 2 // All rights reserved. 3 // 4 // Redistribution and use in source and binary forms, with or without 5 // modification, are permitted provided that the following conditions are 6 // met: 7 // 8 // * Redistributions of source code must retain the above copyright 9 // notice, this list of conditions and the following disclaimer. 10 // * Redistributions in binary form must reproduce the above 11 // copyright notice, this list of conditions and the following disclaimer 12 // in the documentation and/or other materials provided with the 13 // distribution. 14 // * Neither the name of Google Inc. nor the names of its 15 // contributors may be used to endorse or promote products derived from 16 // this software without specific prior written permission. 17 // 18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 // 30 // Author: Sanjay Ghemawat 31 32 #include <stdlib.h> 33 #include <stdio.h> 34 #include <ctype.h> 35 #include <limits.h> /* for SHRT_MIN, USHRT_MAX, etc */ 36 #include <string.h> /* for memcpy */ 37 #include <assert.h> 38 #include <errno.h> 39 #include <string> 40 #include <algorithm> 41 42 #include "pcrecpp_internal.h" 43 #include "pcre2.h" 44 #include "pcrecpp.h" 45 #include "pcre_stringpiece.h" 46 47 48 namespace pcrecpp { 49 50 // If the user doesn't ask for any options, we just use this one 51 static RE_Options default_options; 52 53 void RE::Init(const string& pat, const RE_Options* options) { 54 pattern_ = pat; 55 if (options == NULL) { 56 options_ = default_options; 57 } else { 58 options_ = *options; 59 } 60 error_ = ""; 61 re_full_ = NULL; 62 re_partial_ = NULL; 63 64 re_partial_ = Compile(UNANCHORED); 65 if (re_partial_ != NULL) { 66 re_full_ = Compile(ANCHOR_BOTH); 67 } 68 } 69 70 void RE::Cleanup() { 71 if (re_full_ != NULL) pcre2_code_free(re_full_); 72 if (re_partial_ != NULL) pcre2_code_free(re_partial_); 73 error_ = ""; 74 } 75 76 77 RE::~RE() { 78 Cleanup(); 79 } 80 81 static void format_pcre_error(int error, string & str) { 82 PCRE2_UCHAR8 buffer[256]; 83 auto rc = pcre2_get_error_message(error, buffer, 256); 84 str.assign(reinterpret_cast<string::value_type*>(buffer)); 85 if (rc == PCRE2_ERROR_NOMEMORY) { 86 str.append("..."); 87 } 88 } 89 90 pcre2_code* RE::Compile(Anchor anchor) { 91 // First, convert RE_Options into pcre options 92 int pcre_options = 0; 93 pcre_options = options_.all_options(); 94 typedef std::unique_ptr<pcre2_compile_context, 95 decltype(pcre2_compile_context_free)*> compile_context_ptr; 96 compile_context_ptr compile_context(NULL, pcre2_compile_context_free); 97 98 // As of pcre2 the newline mode must be passed through the compile context. 99 // So we only need one if the newline mode is actually set. 100 if (options_.newline_mode()) { 101 compile_context = compile_context_ptr(pcre2_compile_context_create(NULL), 102 pcre2_compile_context_free); 103 if (!compile_context) { 104 error_ = "Unable to allocate memory for pcre2_compile_congext"; 105 return NULL; 106 } 107 if (pcre2_set_newline(compile_context.get(), 108 options_.newline_mode()) == PCRE2_ERROR_BADDATA) { 109 error_ = "REOptions: bad newline mode given"; 110 return NULL; 111 } 112 } 113 114 // Special treatment for anchoring. This is needed because at 115 // runtime pcre only provides an option for anchoring at the 116 // beginning of a string (unless you use offset). 117 // 118 // There are three types of anchoring we want: 119 // UNANCHORED Compile the original pattern, and use 120 // a pcre unanchored match. 121 // ANCHOR_START Compile the original pattern, and use 122 // a pcre anchored match. 123 // ANCHOR_BOTH Tack a "\z" to the end of the original pattern 124 // and use a pcre anchored match. 125 126 int compile_error; 127 PCRE2_SIZE eoffset; 128 pcre2_code* re; 129 if (anchor != ANCHOR_BOTH) { 130 re = pcre2_compile(reinterpret_cast<PCRE2_SPTR>(pattern_.c_str()), 131 pattern_.length(), pcre_options, &compile_error, 132 &eoffset, compile_context.get()); 133 } else { 134 // Tack a '\z' at the end of RE. Parenthesize it first so that 135 // the '\z' applies to all top-level alternatives in the regexp. 136 string wrapped = "(?:"; // A non-counting grouping operator 137 wrapped += pattern_; 138 wrapped += ")\\z"; 139 re = pcre2_compile(reinterpret_cast<PCRE2_SPTR>(wrapped.c_str()), 140 wrapped.length(), pcre_options, &compile_error, &eoffset, 141 compile_context.get()); 142 } 143 if (re == NULL) { 144 format_pcre_error(compile_error, error_); 145 } 146 return re; 147 } 148 149 /***** Matching interfaces *****/ 150 151 bool RE::Replace(const StringPiece& rewrite, 152 string *str) const { 153 pcre2_match_data_ptr match_data; 154 int matches = TryMatch(*str, 0, UNANCHORED, true, match_data); 155 if (matches == 0) 156 return false; 157 158 string s; 159 if (!Rewrite(&s, rewrite, *str, match_data)) 160 return false; 161 162 auto vec = pcre2_get_ovector_pointer(match_data.get()); 163 164 assert(vec[0] >= 0); 165 assert(vec[1] >= 0); 166 str->replace(vec[0], vec[1] - vec[0], s); 167 return true; 168 } 169 170 static bool is_multi_char_newline_mode(int value) { 171 switch (value) { 172 case PCRE2_NEWLINE_CR: 173 case PCRE2_NEWLINE_LF: 174 return false; 175 case PCRE2_NEWLINE_CRLF: 176 case PCRE2_NEWLINE_ANY: 177 case PCRE2_NEWLINE_ANYCRLF: 178 return true; 179 default: 180 return false; 181 } 182 } 183 184 int RE::GlobalReplace(const StringPiece& rewrite, 185 string *str) const { 186 int count = 0; 187 string out; 188 int start = 0; 189 bool last_match_was_empty_string = false; 190 pcre2_match_data_ptr match_data; 191 192 while (start <= static_cast<int>(str->length())) { 193 // If the previous match was for the empty string, we shouldn't 194 // just match again: we'll match in the same way and get an 195 // infinite loop. Instead, we do the match in a special way: 196 // anchored -- to force another try at the same position -- 197 // and with a flag saying that this time, ignore empty matches. 198 // If this special match returns, that means there's a non-empty 199 // match at this position as well, and we can continue. If not, 200 // we do what perl does, and just advance by one. 201 // Notice that perl prints '@@@' for this; 202 // perl -le '$_ = "aa"; s/b*|aa/@/g; print' 203 int matches; 204 if (last_match_was_empty_string) { 205 matches = TryMatch(*str, start, ANCHOR_START, false, match_data); 206 if (matches <= 0) { 207 int matchend = start + 1; // advance one character. 208 // If the current char is CR and we're in CRLF mode, skip LF too. 209 // Note it's better to call pcre2_pattern_info() than to examine 210 // all_options(), since options_ could have changed between 211 // compile-time and now, but this is simpler and safe enough. 212 // Modified by PH to add ANY and ANYCRLF. 213 if (matchend < static_cast<int>(str->length()) && 214 (*str)[start] == '\r' && (*str)[matchend] == '\n' && 215 is_multi_char_newline_mode(options_.newline_mode())) { 216 matchend++; 217 } 218 // We also need to advance more than one char if we're in utf8 mode. 219 #ifdef SUPPORT_UTF8 220 if (options_.utf8()) { 221 while (matchend < static_cast<int>(str->length()) && 222 ((*str)[matchend] & 0xc0) == 0x80) 223 matchend++; 224 } 225 #endif 226 if (start < static_cast<int>(str->length())) 227 out.append(*str, start, matchend - start); 228 start = matchend; 229 last_match_was_empty_string = false; 230 continue; 231 } 232 } else { 233 matches = TryMatch(*str, start, UNANCHORED, true, match_data); 234 if (matches <= 0) 235 break; 236 } 237 auto vec = pcre2_get_ovector_pointer(match_data.get()); 238 int matchstart = vec[0], matchend = vec[1]; 239 assert(matchstart >= start); 240 assert(matchend >= matchstart); 241 out.append(*str, start, matchstart - start); 242 Rewrite(&out, rewrite, *str, match_data); 243 start = matchend; 244 count++; 245 last_match_was_empty_string = (matchstart == matchend); 246 } 247 248 if (count == 0) 249 return 0; 250 251 if (start < static_cast<int>(str->length())) 252 out.append(*str, start, str->length() - start); 253 swap(out, *str); 254 return count; 255 } 256 257 bool RE::Extract(const StringPiece& rewrite, 258 const StringPiece& text, 259 string *out) const { 260 pcre2_match_data_ptr match_data; 261 int matches = TryMatch(text, 0, UNANCHORED, true, match_data); 262 if (matches == 0) 263 return false; 264 out->erase(); 265 return Rewrite(out, rewrite, text, match_data); 266 } 267 268 /*static*/ string RE::QuoteMeta(const StringPiece& unquoted) { 269 string result; 270 271 // Escape any ascii character not in [A-Za-z_0-9]. 272 // 273 // Note that it's legal to escape a character even if it has no 274 // special meaning in a regular expression -- so this function does 275 // that. (This also makes it identical to the perl function of the 276 // same name; see `perldoc -f quotemeta`.) The one exception is 277 // escaping NUL: rather than doing backslash + NUL, like perl does, 278 // we do '\0', because pcre itself doesn't take embedded NUL chars. 279 for (int ii = 0; ii < unquoted.size(); ++ii) { 280 // Note that using 'isalnum' here raises the benchmark time from 281 // 32ns to 58ns: 282 if (unquoted[ii] == '\0') { 283 result += "\\0"; 284 } else if ((unquoted[ii] < 'a' || unquoted[ii] > 'z') && 285 (unquoted[ii] < 'A' || unquoted[ii] > 'Z') && 286 (unquoted[ii] < '0' || unquoted[ii] > '9') && 287 unquoted[ii] != '_' && 288 // If this is the part of a UTF8 or Latin1 character, we need 289 // to copy this byte without escaping. Experimentally this is 290 // what works correctly with the regexp library. 291 !(unquoted[ii] & 128)) { 292 result += '\\'; 293 result += unquoted[ii]; 294 } else { 295 result += unquoted[ii]; 296 } 297 } 298 299 return result; 300 } 301 302 /***** Actual matching and rewriting code *****/ 303 int RE::TryMatch(const StringPiece& text, 304 int startpos, 305 Anchor anchor, 306 bool empty_ok, 307 pcre2_match_data_ptr & match_data) const { 308 typedef std::unique_ptr<pcre2_match_context, 309 decltype(pcre2_match_context_free)*> match_context_ptr; 310 311 pcre2_code* re = (anchor == ANCHOR_BOTH) ? re_full_ : re_partial_; 312 if (re == NULL) { 313 //fprintf(stderr, "Matching against invalid re: %s\n", error_->c_str()); 314 return 0; 315 } 316 match_context_ptr match_context = match_context_ptr( 317 pcre2_match_context_create(NULL), 318 pcre2_match_context_free); 319 if (!match_context) 320 return 0; 321 322 if (options_.match_limit() > 0) { 323 pcre2_set_match_limit(match_context.get(), options_.match_limit()); 324 } 325 if (options_.match_limit_recursion() > 0) { 326 pcre2_set_recursion_limit(match_context.get(), 327 options_.match_limit_recursion()); 328 } 329 330 match_data = pcre2_match_data_ptr( 331 pcre2_match_data_create_from_pattern(re, NULL), 332 pcre2_match_data_free); 333 if (!match_data) { 334 return 0; 335 } 336 337 // int options = 0; 338 // Changed by PH as a result of bugzilla #1288 339 int options = (options_.all_options() & PCRE2_NO_UTF_CHECK); 340 341 if (anchor != UNANCHORED) 342 options |= PCRE2_ANCHORED; 343 if (!empty_ok) 344 options |= PCRE2_NOTEMPTY; 345 346 int rc = pcre2_match( 347 re, reinterpret_cast<PCRE2_SPTR>((text.empty()) ? "" : text.data()), 348 text.size(), startpos, options, match_data.get(), match_context.get()); 349 350 // Handle errors 351 if (rc == PCRE2_ERROR_NOMATCH) { 352 return 0; 353 } 354 if (rc == PCRE2_ERROR_PARTIAL) { 355 // not sure what to do with partial yet 356 return 0; 357 } else if (rc < 0) { 358 // For any other error condition also return 0. 359 return 0; 360 } 361 362 return rc; // return number of matches found 363 } 364 365 bool RE::DoMatchImpl(const StringPiece& text, 366 Anchor anchor, 367 int* consumed, 368 const Arg* args, 369 int n) const { 370 pcre2_match_data_ptr match_data; 371 int matches = TryMatch(text, 0, anchor, true, match_data); 372 assert(matches >= 0); // TryMatch never returns negatives 373 if (matches == 0) 374 return false; 375 376 auto vec = pcre2_get_ovector_pointer(match_data.get()); 377 378 // allow for NULL 379 if (consumed != NULL) 380 *consumed = vec[1]; 381 382 if (n == 0 || args == NULL) { 383 // We are not interested in results 384 return true; 385 } 386 387 if (NumberOfCapturingGroups() < n) { 388 // RE has fewer capturing groups than number of arg pointers passed in 389 return false; 390 } 391 392 // If we got here, we must have matched the whole pattern. 393 // We do not need (can not do) any more checks on the value of 'matches' here 394 // -- see the comment for TryMatch. 395 for (int i = 0; i < n; i++) { 396 const int start = vec[2*(i+1)]; 397 const int limit = vec[2*(i+1)+1]; 398 if (!args[i].Parse(text.data() + start, limit - start)) { 399 // TODO: Should we indicate what the error was? 400 return false; 401 } 402 } 403 404 return true; 405 } 406 407 bool RE::DoMatch(const StringPiece& text, 408 Anchor anchor, 409 int* consumed, 410 Arg const args[], 411 int n) const { 412 assert(n >= 0); 413 bool retval = DoMatchImpl(text, anchor, consumed, args, n); 414 return retval; 415 } 416 417 bool RE::Rewrite(string *out, const StringPiece &rewrite, 418 const StringPiece &text, 419 pcre2_match_data_ptr const & match_data) const { 420 auto veclen = pcre2_get_ovector_count(match_data.get()); 421 auto vec = pcre2_get_ovector_pointer(match_data.get()); 422 for (const char *s = rewrite.data(), *end = s + rewrite.size(); 423 s < end; s++) { 424 int c = *s; 425 if (c == '\\') { 426 c = *++s; 427 if (isdigit(c)) { 428 decltype(veclen) n = (c - '0'); 429 if (n >= veclen) { 430 //fprintf(stderr, requested group %d in regexp %.*s\n", 431 // n, rewrite.size(), rewrite.data()); 432 return false; 433 } 434 int start = vec[2 * n]; 435 if (start >= 0) 436 out->append(text.data() + start, vec[2 * n + 1] - start); 437 } else if (c == '\\') { 438 *out += '\\'; 439 } else { 440 //fprintf(stderr, "invalid rewrite pattern: %.*s\n", 441 // rewrite.size(), rewrite.data()); 442 return false; 443 } 444 } else { 445 *out += c; 446 } 447 } 448 return true; 449 } 450 451 // Return the number of capturing subpatterns, or -1 if the 452 // regexp wasn't valid on construction. 453 int RE::NumberOfCapturingGroups() const { 454 if (re_partial_ == NULL) return -1; 455 456 int result; 457 int pcre_retval = pcre2_pattern_info(re_partial_, PCRE2_INFO_CAPTURECOUNT, 458 &result); 459 assert(pcre_retval == 0); 460 return result; 461 } 462 463 /***** Parsers for various types *****/ 464 465 bool Arg::parse_null(const char* str, int n, void* dest) { 466 (void)str; 467 (void)n; 468 // We fail if somebody asked us to store into a non-NULL void* pointer 469 return (dest == NULL); 470 } 471 472 bool Arg::parse_string(const char* str, int n, void* dest) { 473 if (dest == NULL) return true; 474 reinterpret_cast<string*>(dest)->assign(str, n); 475 return true; 476 } 477 478 bool Arg::parse_stringpiece(const char* str, int n, void* dest) { 479 if (dest == NULL) return true; 480 reinterpret_cast<StringPiece*>(dest)->set(str, n); 481 return true; 482 } 483 484 bool Arg::parse_char(const char* str, int n, void* dest) { 485 if (n != 1) return false; 486 if (dest == NULL) return true; 487 *(reinterpret_cast<char*>(dest)) = str[0]; 488 return true; 489 } 490 491 bool Arg::parse_uchar(const char* str, int n, void* dest) { 492 if (n != 1) return false; 493 if (dest == NULL) return true; 494 *(reinterpret_cast<unsigned char*>(dest)) = str[0]; 495 return true; 496 } 497 498 // Largest number spec that we are willing to parse 499 static const int kMaxNumberLength = 32; 500 501 // REQUIRES "buf" must have length at least kMaxNumberLength+1 502 // REQUIRES "n > 0" 503 // Copies "str" into "buf" and null-terminates if necessary. 504 // Returns one of: 505 // a. "str" if no termination is needed 506 // b. "buf" if the string was copied and null-terminated 507 // c. "" if the input was invalid and has no hope of being parsed 508 static const char* TerminateNumber(char* buf, const char* str, int n) { 509 if ((n > 0) && isspace(*str)) { 510 // We are less forgiving than the strtoxxx() routines and do not 511 // allow leading spaces. 512 return ""; 513 } 514 515 // See if the character right after the input text may potentially 516 // look like a digit. 517 if (isdigit(str[n]) || 518 ((str[n] >= 'a') && (str[n] <= 'f')) || 519 ((str[n] >= 'A') && (str[n] <= 'F'))) { 520 if (n > kMaxNumberLength) return ""; // Input too big to be a valid number 521 memcpy(buf, str, n); 522 buf[n] = '\0'; 523 return buf; 524 } else { 525 // We can parse right out of the supplied string, so return it. 526 return str; 527 } 528 } 529 530 bool Arg::parse_long_radix(const char* str, 531 int n, 532 void* dest, 533 int radix) { 534 if (n == 0) return false; 535 char buf[kMaxNumberLength+1]; 536 str = TerminateNumber(buf, str, n); 537 char* end; 538 errno = 0; 539 long r = strtol(str, &end, radix); 540 if (end != str + n) return false; // Leftover junk 541 if (errno) return false; 542 if (dest == NULL) return true; 543 *(reinterpret_cast<long*>(dest)) = r; 544 return true; 545 } 546 547 bool Arg::parse_ulong_radix(const char* str, 548 int n, 549 void* dest, 550 int radix) { 551 if (n == 0) return false; 552 char buf[kMaxNumberLength+1]; 553 str = TerminateNumber(buf, str, n); 554 if (str[0] == '-') return false; // strtoul() on a negative number?! 555 char* end; 556 errno = 0; 557 unsigned long r = strtoul(str, &end, radix); 558 if (end != str + n) return false; // Leftover junk 559 if (errno) return false; 560 if (dest == NULL) return true; 561 *(reinterpret_cast<unsigned long*>(dest)) = r; 562 return true; 563 } 564 565 bool Arg::parse_short_radix(const char* str, 566 int n, 567 void* dest, 568 int radix) { 569 long r; 570 if (!parse_long_radix(str, n, &r, radix)) return false; // Could not parse 571 if (r < SHRT_MIN || r > SHRT_MAX) return false; // Out of range 572 if (dest == NULL) return true; 573 *(reinterpret_cast<short*>(dest)) = static_cast<short>(r); 574 return true; 575 } 576 577 bool Arg::parse_ushort_radix(const char* str, 578 int n, 579 void* dest, 580 int radix) { 581 unsigned long r; 582 if (!parse_ulong_radix(str, n, &r, radix)) return false; // Could not parse 583 if (r > USHRT_MAX) return false; // Out of range 584 if (dest == NULL) return true; 585 *(reinterpret_cast<unsigned short*>(dest)) = static_cast<unsigned short>(r); 586 return true; 587 } 588 589 bool Arg::parse_int_radix(const char* str, 590 int n, 591 void* dest, 592 int radix) { 593 long r; 594 if (!parse_long_radix(str, n, &r, radix)) return false; // Could not parse 595 if (r < INT_MIN || r > INT_MAX) return false; // Out of range 596 if (dest == NULL) return true; 597 *(reinterpret_cast<int*>(dest)) = r; 598 return true; 599 } 600 601 bool Arg::parse_uint_radix(const char* str, 602 int n, 603 void* dest, 604 int radix) { 605 unsigned long r; 606 if (!parse_ulong_radix(str, n, &r, radix)) return false; // Could not parse 607 if (r > UINT_MAX) return false; // Out of range 608 if (dest == NULL) return true; 609 *(reinterpret_cast<unsigned int*>(dest)) = r; 610 return true; 611 } 612 613 bool Arg::parse_longlong_radix(const char* str, 614 int n, 615 void* dest, 616 int radix) { 617 #ifndef HAVE_LONG_LONG 618 return false; 619 #else 620 if (n == 0) return false; 621 char buf[kMaxNumberLength+1]; 622 str = TerminateNumber(buf, str, n); 623 char* end; 624 errno = 0; 625 #if defined HAVE_STRTOQ 626 long long r = strtoq(str, &end, radix); 627 #elif defined HAVE_STRTOLL 628 long long r = strtoll(str, &end, radix); 629 #elif defined HAVE__STRTOI64 630 long long r = _strtoi64(str, &end, radix); 631 #elif defined HAVE_STRTOIMAX 632 long long r = strtoimax(str, &end, radix); 633 #else 634 #error parse_longlong_radix: cannot convert input to a long-long 635 #endif 636 if (end != str + n) return false; // Leftover junk 637 if (errno) return false; 638 if (dest == NULL) return true; 639 *(reinterpret_cast<long long*>(dest)) = r; 640 return true; 641 #endif /* HAVE_LONG_LONG */ 642 } 643 644 bool Arg::parse_ulonglong_radix(const char* str, 645 int n, 646 void* dest, 647 int radix) { 648 #ifndef HAVE_UNSIGNED_LONG_LONG 649 return false; 650 #else 651 if (n == 0) return false; 652 char buf[kMaxNumberLength+1]; 653 str = TerminateNumber(buf, str, n); 654 if (str[0] == '-') return false; // strtoull() on a negative number?! 655 char* end; 656 errno = 0; 657 #if defined HAVE_STRTOQ 658 unsigned long long r = strtouq(str, &end, radix); 659 #elif defined HAVE_STRTOLL 660 unsigned long long r = strtoull(str, &end, radix); 661 #elif defined HAVE__STRTOI64 662 unsigned long long r = _strtoui64(str, &end, radix); 663 #elif defined HAVE_STRTOIMAX 664 unsigned long long r = strtoumax(str, &end, radix); 665 #else 666 #error parse_ulonglong_radix: cannot convert input to a long-long 667 #endif 668 if (end != str + n) return false; // Leftover junk 669 if (errno) return false; 670 if (dest == NULL) return true; 671 *(reinterpret_cast<unsigned long long*>(dest)) = r; 672 return true; 673 #endif /* HAVE_UNSIGNED_LONG_LONG */ 674 } 675 676 bool Arg::parse_double(const char* str, int n, void* dest) { 677 if (n == 0) return false; 678 static const int kMaxLength = 200; 679 char buf[kMaxLength]; 680 if (n >= kMaxLength) return false; 681 memcpy(buf, str, n); 682 buf[n] = '\0'; 683 errno = 0; 684 char* end; 685 double r = strtod(buf, &end); 686 if (end != buf + n) return false; // Leftover junk 687 if (errno) return false; 688 if (dest == NULL) return true; 689 *(reinterpret_cast<double*>(dest)) = r; 690 return true; 691 } 692 693 bool Arg::parse_float(const char* str, int n, void* dest) { 694 double r; 695 if (!parse_double(str, n, &r)) return false; 696 if (dest == NULL) return true; 697 *(reinterpret_cast<float*>(dest)) = static_cast<float>(r); 698 return true; 699 } 700 701 702 #define DEFINE_INTEGER_PARSERS(name) \ 703 bool Arg::parse_##name(const char* str, int n, void* dest) { \ 704 return parse_##name##_radix(str, n, dest, 10); \ 705 } \ 706 bool Arg::parse_##name##_hex(const char* str, int n, void* dest) { \ 707 return parse_##name##_radix(str, n, dest, 16); \ 708 } \ 709 bool Arg::parse_##name##_octal(const char* str, int n, void* dest) { \ 710 return parse_##name##_radix(str, n, dest, 8); \ 711 } \ 712 bool Arg::parse_##name##_cradix(const char* str, int n, void* dest) { \ 713 return parse_##name##_radix(str, n, dest, 0); \ 714 } 715 716 DEFINE_INTEGER_PARSERS(short) /* */ 717 DEFINE_INTEGER_PARSERS(ushort) /* */ 718 DEFINE_INTEGER_PARSERS(int) /* Don't use semicolons after these */ 719 DEFINE_INTEGER_PARSERS(uint) /* statements because they can cause */ 720 DEFINE_INTEGER_PARSERS(long) /* compiler warnings if the checking */ 721 DEFINE_INTEGER_PARSERS(ulong) /* level is turned up high enough. */ 722 DEFINE_INTEGER_PARSERS(longlong) /* */ 723 DEFINE_INTEGER_PARSERS(ulonglong) /* */ 724 725 #undef DEFINE_INTEGER_PARSERS 726 727 } // namespace pcrecpp 728