blob: 2c37c44ed0214e95ba00bcf2046b912b17e35d9c [file] [log] [blame]
Nick Kralevichf73ff172014-09-27 12:41:49 -07001// 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
Nick Kralevichf73ff172014-09-27 12:41:49 -070032#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"
Janis Danisevskis1fd6dd52016-04-01 12:25:25 +010043#include "pcre2.h"
Nick Kralevichf73ff172014-09-27 12:41:49 -070044#include "pcrecpp.h"
45#include "pcre_stringpiece.h"
46
47
48namespace pcrecpp {
49
Nick Kralevichf73ff172014-09-27 12:41:49 -070050// If the user doesn't ask for any options, we just use this one
51static RE_Options default_options;
52
53void 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 }
Janis Danisevskis1fd6dd52016-04-01 12:25:25 +010060 error_ = "";
Nick Kralevichf73ff172014-09-27 12:41:49 -070061 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
70void RE::Cleanup() {
Janis Danisevskis1fd6dd52016-04-01 12:25:25 +010071 if (re_full_ != NULL) pcre2_code_free(re_full_);
72 if (re_partial_ != NULL) pcre2_code_free(re_partial_);
73 error_ = "";
Nick Kralevichf73ff172014-09-27 12:41:49 -070074}
75
76
77RE::~RE() {
78 Cleanup();
79}
80
Janis Danisevskis1fd6dd52016-04-01 12:25:25 +010081static 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}
Nick Kralevichf73ff172014-09-27 12:41:49 -070089
Janis Danisevskis1fd6dd52016-04-01 12:25:25 +010090pcre2_code* RE::Compile(Anchor anchor) {
Nick Kralevichf73ff172014-09-27 12:41:49 -070091 // First, convert RE_Options into pcre options
92 int pcre_options = 0;
93 pcre_options = options_.all_options();
Janis Danisevskis1fd6dd52016-04-01 12:25:25 +010094 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 }
Nick Kralevichf73ff172014-09-27 12:41:49 -0700113
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
Janis Danisevskis1fd6dd52016-04-01 12:25:25 +0100126 int compile_error;
127 PCRE2_SIZE eoffset;
128 pcre2_code* re;
Nick Kralevichf73ff172014-09-27 12:41:49 -0700129 if (anchor != ANCHOR_BOTH) {
Janis Danisevskis1fd6dd52016-04-01 12:25:25 +0100130 re = pcre2_compile(reinterpret_cast<PCRE2_SPTR>(pattern_.c_str()),
131 pattern_.length(), pcre_options, &compile_error,
132 &eoffset, compile_context.get());
Nick Kralevichf73ff172014-09-27 12:41:49 -0700133 } 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";
Janis Danisevskis1fd6dd52016-04-01 12:25:25 +0100139 re = pcre2_compile(reinterpret_cast<PCRE2_SPTR>(wrapped.c_str()),
140 wrapped.length(), pcre_options, &compile_error, &eoffset,
141 compile_context.get());
Nick Kralevichf73ff172014-09-27 12:41:49 -0700142 }
143 if (re == NULL) {
Janis Danisevskis1fd6dd52016-04-01 12:25:25 +0100144 format_pcre_error(compile_error, error_);
Nick Kralevichf73ff172014-09-27 12:41:49 -0700145 }
146 return re;
147}
148
149/***** Matching interfaces *****/
150
Nick Kralevichf73ff172014-09-27 12:41:49 -0700151bool RE::Replace(const StringPiece& rewrite,
152 string *str) const {
Janis Danisevskis1fd6dd52016-04-01 12:25:25 +0100153 pcre2_match_data_ptr match_data;
154 int matches = TryMatch(*str, 0, UNANCHORED, true, match_data);
Nick Kralevichf73ff172014-09-27 12:41:49 -0700155 if (matches == 0)
156 return false;
157
158 string s;
Janis Danisevskis1fd6dd52016-04-01 12:25:25 +0100159 if (!Rewrite(&s, rewrite, *str, match_data))
Nick Kralevichf73ff172014-09-27 12:41:49 -0700160 return false;
161
Janis Danisevskis1fd6dd52016-04-01 12:25:25 +0100162 auto vec = pcre2_get_ovector_pointer(match_data.get());
163
Nick Kralevichf73ff172014-09-27 12:41:49 -0700164 assert(vec[0] >= 0);
165 assert(vec[1] >= 0);
166 str->replace(vec[0], vec[1] - vec[0], s);
167 return true;
168}
169
Janis Danisevskis1fd6dd52016-04-01 12:25:25 +0100170static 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;
Nick Kralevichf73ff172014-09-27 12:41:49 -0700181 }
Nick Kralevichf73ff172014-09-27 12:41:49 -0700182}
183
184int RE::GlobalReplace(const StringPiece& rewrite,
185 string *str) const {
186 int count = 0;
Nick Kralevichf73ff172014-09-27 12:41:49 -0700187 string out;
188 int start = 0;
189 bool last_match_was_empty_string = false;
Janis Danisevskis1fd6dd52016-04-01 12:25:25 +0100190 pcre2_match_data_ptr match_data;
Nick Kralevichf73ff172014-09-27 12:41:49 -0700191
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) {
Janis Danisevskis1fd6dd52016-04-01 12:25:25 +0100205 matches = TryMatch(*str, start, ANCHOR_START, false, match_data);
Nick Kralevichf73ff172014-09-27 12:41:49 -0700206 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.
Janis Danisevskis1fd6dd52016-04-01 12:25:25 +0100209 // Note it's better to call pcre2_pattern_info() than to examine
210 // all_options(), since options_ could have changed between
Nick Kralevichf73ff172014-09-27 12:41:49 -0700211 // 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' &&
Janis Danisevskis1fd6dd52016-04-01 12:25:25 +0100215 is_multi_char_newline_mode(options_.newline_mode())) {
Nick Kralevichf73ff172014-09-27 12:41:49 -0700216 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 {
Janis Danisevskis1fd6dd52016-04-01 12:25:25 +0100233 matches = TryMatch(*str, start, UNANCHORED, true, match_data);
Nick Kralevichf73ff172014-09-27 12:41:49 -0700234 if (matches <= 0)
235 break;
236 }
Janis Danisevskis1fd6dd52016-04-01 12:25:25 +0100237 auto vec = pcre2_get_ovector_pointer(match_data.get());
Nick Kralevichf73ff172014-09-27 12:41:49 -0700238 int matchstart = vec[0], matchend = vec[1];
239 assert(matchstart >= start);
240 assert(matchend >= matchstart);
241 out.append(*str, start, matchstart - start);
Janis Danisevskis1fd6dd52016-04-01 12:25:25 +0100242 Rewrite(&out, rewrite, *str, match_data);
Nick Kralevichf73ff172014-09-27 12:41:49 -0700243 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
257bool RE::Extract(const StringPiece& rewrite,
258 const StringPiece& text,
259 string *out) const {
Janis Danisevskis1fd6dd52016-04-01 12:25:25 +0100260 pcre2_match_data_ptr match_data;
261 int matches = TryMatch(text, 0, UNANCHORED, true, match_data);
Nick Kralevichf73ff172014-09-27 12:41:49 -0700262 if (matches == 0)
263 return false;
264 out->erase();
Janis Danisevskis1fd6dd52016-04-01 12:25:25 +0100265 return Rewrite(out, rewrite, text, match_data);
Nick Kralevichf73ff172014-09-27 12:41:49 -0700266}
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 *****/
Nick Kralevichf73ff172014-09-27 12:41:49 -0700303int RE::TryMatch(const StringPiece& text,
304 int startpos,
305 Anchor anchor,
306 bool empty_ok,
Janis Danisevskis1fd6dd52016-04-01 12:25:25 +0100307 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_;
Nick Kralevichf73ff172014-09-27 12:41:49 -0700312 if (re == NULL) {
313 //fprintf(stderr, "Matching against invalid re: %s\n", error_->c_str());
314 return 0;
315 }
Janis Danisevskis1fd6dd52016-04-01 12:25:25 +0100316 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;
Nick Kralevichf73ff172014-09-27 12:41:49 -0700321
Nick Kralevichf73ff172014-09-27 12:41:49 -0700322 if (options_.match_limit() > 0) {
Janis Danisevskis1fd6dd52016-04-01 12:25:25 +0100323 pcre2_set_match_limit(match_context.get(), options_.match_limit());
Nick Kralevichf73ff172014-09-27 12:41:49 -0700324 }
325 if (options_.match_limit_recursion() > 0) {
Janis Danisevskis1fd6dd52016-04-01 12:25:25 +0100326 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;
Nick Kralevichf73ff172014-09-27 12:41:49 -0700335 }
336
337 // int options = 0;
338 // Changed by PH as a result of bugzilla #1288
Janis Danisevskis1fd6dd52016-04-01 12:25:25 +0100339 int options = (options_.all_options() & PCRE2_NO_UTF_CHECK);
Nick Kralevichf73ff172014-09-27 12:41:49 -0700340
341 if (anchor != UNANCHORED)
Janis Danisevskis1fd6dd52016-04-01 12:25:25 +0100342 options |= PCRE2_ANCHORED;
Nick Kralevichf73ff172014-09-27 12:41:49 -0700343 if (!empty_ok)
Janis Danisevskis1fd6dd52016-04-01 12:25:25 +0100344 options |= PCRE2_NOTEMPTY;
Nick Kralevichf73ff172014-09-27 12:41:49 -0700345
Janis Danisevskis1fd6dd52016-04-01 12:25:25 +0100346 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());
Nick Kralevichf73ff172014-09-27 12:41:49 -0700349
350 // Handle errors
Janis Danisevskis1fd6dd52016-04-01 12:25:25 +0100351 if (rc == PCRE2_ERROR_NOMATCH) {
352 return 0;
353 }
354 if (rc == PCRE2_ERROR_PARTIAL) {
355 // not sure what to do with partial yet
Nick Kralevichf73ff172014-09-27 12:41:49 -0700356 return 0;
357 } else if (rc < 0) {
Janis Danisevskis1fd6dd52016-04-01 12:25:25 +0100358 // For any other error condition also return 0.
Nick Kralevichf73ff172014-09-27 12:41:49 -0700359 return 0;
Nick Kralevichf73ff172014-09-27 12:41:49 -0700360 }
361
Janis Danisevskis1fd6dd52016-04-01 12:25:25 +0100362 return rc; // return number of matches found
Nick Kralevichf73ff172014-09-27 12:41:49 -0700363}
364
365bool RE::DoMatchImpl(const StringPiece& text,
366 Anchor anchor,
367 int* consumed,
Janis Danisevskis1fd6dd52016-04-01 12:25:25 +0100368 const Arg* args,
369 int n) const {
370 pcre2_match_data_ptr match_data;
371 int matches = TryMatch(text, 0, anchor, true, match_data);
Nick Kralevichf73ff172014-09-27 12:41:49 -0700372 assert(matches >= 0); // TryMatch never returns negatives
373 if (matches == 0)
374 return false;
375
Janis Danisevskis1fd6dd52016-04-01 12:25:25 +0100376 auto vec = pcre2_get_ovector_pointer(match_data.get());
377
378 // allow for NULL
379 if (consumed != NULL)
380 *consumed = vec[1];
Nick Kralevichf73ff172014-09-27 12:41:49 -0700381
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];
Janis Danisevskis1fd6dd52016-04-01 12:25:25 +0100398 if (!args[i].Parse(text.data() + start, limit - start)) {
Nick Kralevichf73ff172014-09-27 12:41:49 -0700399 // TODO: Should we indicate what the error was?
400 return false;
401 }
402 }
403
404 return true;
405}
406
407bool RE::DoMatch(const StringPiece& text,
408 Anchor anchor,
409 int* consumed,
Janis Danisevskis1fd6dd52016-04-01 12:25:25 +0100410 Arg const args[],
Nick Kralevichf73ff172014-09-27 12:41:49 -0700411 int n) const {
412 assert(n >= 0);
Janis Danisevskis1fd6dd52016-04-01 12:25:25 +0100413 bool retval = DoMatchImpl(text, anchor, consumed, args, n);
Nick Kralevichf73ff172014-09-27 12:41:49 -0700414 return retval;
415}
416
417bool RE::Rewrite(string *out, const StringPiece &rewrite,
Janis Danisevskis1fd6dd52016-04-01 12:25:25 +0100418 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());
Nick Kralevichf73ff172014-09-27 12:41:49 -0700422 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)) {
Janis Danisevskis1fd6dd52016-04-01 12:25:25 +0100428 decltype(veclen) n = (c - '0');
Nick Kralevichf73ff172014-09-27 12:41:49 -0700429 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.
453int RE::NumberOfCapturingGroups() const {
454 if (re_partial_ == NULL) return -1;
455
456 int result;
Janis Danisevskis1fd6dd52016-04-01 12:25:25 +0100457 int pcre_retval = pcre2_pattern_info(re_partial_, PCRE2_INFO_CAPTURECOUNT,
458 &result);
Nick Kralevichf73ff172014-09-27 12:41:49 -0700459 assert(pcre_retval == 0);
460 return result;
461}
462
463/***** Parsers for various types *****/
464
465bool 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
472bool 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
478bool 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
484bool 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
491bool 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
499static 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
508static 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
530bool 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
547bool 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
565bool 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
577bool 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
589bool 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
601bool 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
613bool 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
644bool Arg::parse_ulonglong_radix(const char* str,
645 int n,
646 void* dest,
647 int radix) {
648#ifndef HAVE_UNSIGNED_LONG_LONG
Nick Kralevichf73ff172014-09-27 12:41:49 -0700649 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
676bool 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
693bool 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
716DEFINE_INTEGER_PARSERS(short) /* */
717DEFINE_INTEGER_PARSERS(ushort) /* */
718DEFINE_INTEGER_PARSERS(int) /* Don't use semicolons after these */
719DEFINE_INTEGER_PARSERS(uint) /* statements because they can cause */
720DEFINE_INTEGER_PARSERS(long) /* compiler warnings if the checking */
721DEFINE_INTEGER_PARSERS(ulong) /* level is turned up high enough. */
722DEFINE_INTEGER_PARSERS(longlong) /* */
723DEFINE_INTEGER_PARSERS(ulonglong) /* */
724
725#undef DEFINE_INTEGER_PARSERS
726
727} // namespace pcrecpp