| 1 |
ph10 |
474 |
// Copyright (c) 2010, Google Inc. |
| 2 |
nigel |
77 |
// 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 |
ph10 |
97 |
#ifdef HAVE_CONFIG_H |
| 33 |
ph10 |
236 |
#include "config.h" |
| 34 |
ph10 |
97 |
#endif |
| 35 |
|
|
|
| 36 |
nigel |
77 |
#include <stdlib.h> |
| 37 |
|
|
#include <stdio.h> |
| 38 |
|
|
#include <ctype.h> |
| 39 |
|
|
#include <limits.h> /* for SHRT_MIN, USHRT_MAX, etc */ |
| 40 |
|
|
#include <assert.h> |
| 41 |
|
|
#include <errno.h> |
| 42 |
|
|
#include <string> |
| 43 |
nigel |
81 |
#include <algorithm> |
| 44 |
ph10 |
199 |
|
| 45 |
|
|
#include "pcrecpp_internal.h" |
| 46 |
ph10 |
236 |
#include "pcre.h" |
| 47 |
nigel |
77 |
#include "pcrecpp.h" |
| 48 |
ph10 |
199 |
#include "pcre_stringpiece.h" |
| 49 |
nigel |
77 |
|
| 50 |
|
|
|
| 51 |
|
|
namespace pcrecpp { |
| 52 |
|
|
|
| 53 |
|
|
// Maximum number of args we can set |
| 54 |
|
|
static const int kMaxArgs = 16; |
| 55 |
|
|
static const int kVecSize = (1 + kMaxArgs) * 3; // results + PCRE workspace |
| 56 |
|
|
|
| 57 |
|
|
// Special object that stands-in for no argument |
| 58 |
ph10 |
308 |
Arg RE::no_arg((void*)NULL); |
| 59 |
nigel |
77 |
|
| 60 |
ph10 |
322 |
// This is for ABI compatibility with old versions of pcre (pre-7.6), |
| 61 |
|
|
// which defined a global no_arg variable instead of putting it in the |
| 62 |
ph10 |
330 |
// RE class. This works on GCC >= 3, at least. It definitely works |
| 63 |
|
|
// for ELF, but may not for other object formats (Mach-O, for |
| 64 |
|
|
// instance, does not support aliases.) We could probably have a more |
| 65 |
|
|
// inclusive test if we ever needed it. (Note that not only the |
| 66 |
|
|
// __attribute__ syntax, but also __USER_LABEL_PREFIX__, are |
| 67 |
ph10 |
328 |
// gnu-specific.) |
| 68 |
ph10 |
330 |
#if defined(__GNUC__) && __GNUC__ >= 3 && defined(__ELF__) |
| 69 |
|
|
# define ULP_AS_STRING(x) ULP_AS_STRING_INTERNAL(x) |
| 70 |
|
|
# define ULP_AS_STRING_INTERNAL(x) #x |
| 71 |
|
|
# define USER_LABEL_PREFIX_STR ULP_AS_STRING(__USER_LABEL_PREFIX__) |
| 72 |
ph10 |
328 |
extern Arg no_arg |
| 73 |
ph10 |
330 |
__attribute__((alias(USER_LABEL_PREFIX_STR "_ZN7pcrecpp2RE6no_argE"))); |
| 74 |
ph10 |
322 |
#endif |
| 75 |
|
|
|
| 76 |
nigel |
77 |
// If a regular expression has no error, its error_ field points here |
| 77 |
|
|
static const string empty_string; |
| 78 |
|
|
|
| 79 |
|
|
// If the user doesn't ask for any options, we just use this one |
| 80 |
|
|
static RE_Options default_options; |
| 81 |
|
|
|
| 82 |
nigel |
93 |
void RE::Init(const string& pat, const RE_Options* options) { |
| 83 |
nigel |
77 |
pattern_ = pat; |
| 84 |
|
|
if (options == NULL) { |
| 85 |
|
|
options_ = default_options; |
| 86 |
|
|
} else { |
| 87 |
|
|
options_ = *options; |
| 88 |
|
|
} |
| 89 |
|
|
error_ = &empty_string; |
| 90 |
|
|
re_full_ = NULL; |
| 91 |
|
|
re_partial_ = NULL; |
| 92 |
|
|
|
| 93 |
|
|
re_partial_ = Compile(UNANCHORED); |
| 94 |
|
|
if (re_partial_ != NULL) { |
| 95 |
ph10 |
179 |
re_full_ = Compile(ANCHOR_BOTH); |
| 96 |
nigel |
77 |
} |
| 97 |
|
|
} |
| 98 |
|
|
|
| 99 |
nigel |
93 |
void RE::Cleanup() { |
| 100 |
ph10 |
179 |
if (re_full_ != NULL) (*pcre_free)(re_full_); |
| 101 |
|
|
if (re_partial_ != NULL) (*pcre_free)(re_partial_); |
| 102 |
|
|
if (error_ != &empty_string) delete error_; |
| 103 |
nigel |
77 |
} |
| 104 |
|
|
|
| 105 |
nigel |
93 |
|
| 106 |
|
|
RE::~RE() { |
| 107 |
|
|
Cleanup(); |
| 108 |
|
|
} |
| 109 |
|
|
|
| 110 |
|
|
|
| 111 |
nigel |
77 |
pcre* RE::Compile(Anchor anchor) { |
| 112 |
|
|
// First, convert RE_Options into pcre options |
| 113 |
|
|
int pcre_options = 0; |
| 114 |
nigel |
81 |
pcre_options = options_.all_options(); |
| 115 |
nigel |
77 |
|
| 116 |
|
|
// Special treatment for anchoring. This is needed because at |
| 117 |
|
|
// runtime pcre only provides an option for anchoring at the |
| 118 |
|
|
// beginning of a string (unless you use offset). |
| 119 |
|
|
// |
| 120 |
|
|
// There are three types of anchoring we want: |
| 121 |
|
|
// UNANCHORED Compile the original pattern, and use |
| 122 |
|
|
// a pcre unanchored match. |
| 123 |
|
|
// ANCHOR_START Compile the original pattern, and use |
| 124 |
|
|
// a pcre anchored match. |
| 125 |
|
|
// ANCHOR_BOTH Tack a "\z" to the end of the original pattern |
| 126 |
|
|
// and use a pcre anchored match. |
| 127 |
|
|
|
| 128 |
|
|
const char* compile_error; |
| 129 |
|
|
int eoffset; |
| 130 |
|
|
pcre* re; |
| 131 |
|
|
if (anchor != ANCHOR_BOTH) { |
| 132 |
|
|
re = pcre_compile(pattern_.c_str(), pcre_options, |
| 133 |
|
|
&compile_error, &eoffset, NULL); |
| 134 |
|
|
} else { |
| 135 |
|
|
// Tack a '\z' at the end of RE. Parenthesize it first so that |
| 136 |
|
|
// the '\z' applies to all top-level alternatives in the regexp. |
| 137 |
|
|
string wrapped = "(?:"; // A non-counting grouping operator |
| 138 |
|
|
wrapped += pattern_; |
| 139 |
|
|
wrapped += ")\\z"; |
| 140 |
|
|
re = pcre_compile(wrapped.c_str(), pcre_options, |
| 141 |
|
|
&compile_error, &eoffset, NULL); |
| 142 |
|
|
} |
| 143 |
|
|
if (re == NULL) { |
| 144 |
|
|
if (error_ == &empty_string) error_ = new string(compile_error); |
| 145 |
|
|
} |
| 146 |
|
|
return re; |
| 147 |
|
|
} |
| 148 |
|
|
|
| 149 |
|
|
/***** Matching interfaces *****/ |
| 150 |
|
|
|
| 151 |
|
|
bool RE::FullMatch(const StringPiece& text, |
| 152 |
|
|
const Arg& ptr1, |
| 153 |
|
|
const Arg& ptr2, |
| 154 |
|
|
const Arg& ptr3, |
| 155 |
|
|
const Arg& ptr4, |
| 156 |
|
|
const Arg& ptr5, |
| 157 |
|
|
const Arg& ptr6, |
| 158 |
|
|
const Arg& ptr7, |
| 159 |
|
|
const Arg& ptr8, |
| 160 |
|
|
const Arg& ptr9, |
| 161 |
|
|
const Arg& ptr10, |
| 162 |
|
|
const Arg& ptr11, |
| 163 |
|
|
const Arg& ptr12, |
| 164 |
|
|
const Arg& ptr13, |
| 165 |
|
|
const Arg& ptr14, |
| 166 |
|
|
const Arg& ptr15, |
| 167 |
|
|
const Arg& ptr16) const { |
| 168 |
|
|
const Arg* args[kMaxArgs]; |
| 169 |
|
|
int n = 0; |
| 170 |
|
|
if (&ptr1 == &no_arg) goto done; args[n++] = &ptr1; |
| 171 |
|
|
if (&ptr2 == &no_arg) goto done; args[n++] = &ptr2; |
| 172 |
|
|
if (&ptr3 == &no_arg) goto done; args[n++] = &ptr3; |
| 173 |
|
|
if (&ptr4 == &no_arg) goto done; args[n++] = &ptr4; |
| 174 |
|
|
if (&ptr5 == &no_arg) goto done; args[n++] = &ptr5; |
| 175 |
|
|
if (&ptr6 == &no_arg) goto done; args[n++] = &ptr6; |
| 176 |
|
|
if (&ptr7 == &no_arg) goto done; args[n++] = &ptr7; |
| 177 |
|
|
if (&ptr8 == &no_arg) goto done; args[n++] = &ptr8; |
| 178 |
|
|
if (&ptr9 == &no_arg) goto done; args[n++] = &ptr9; |
| 179 |
|
|
if (&ptr10 == &no_arg) goto done; args[n++] = &ptr10; |
| 180 |
|
|
if (&ptr11 == &no_arg) goto done; args[n++] = &ptr11; |
| 181 |
|
|
if (&ptr12 == &no_arg) goto done; args[n++] = &ptr12; |
| 182 |
|
|
if (&ptr13 == &no_arg) goto done; args[n++] = &ptr13; |
| 183 |
|
|
if (&ptr14 == &no_arg) goto done; args[n++] = &ptr14; |
| 184 |
|
|
if (&ptr15 == &no_arg) goto done; args[n++] = &ptr15; |
| 185 |
|
|
if (&ptr16 == &no_arg) goto done; args[n++] = &ptr16; |
| 186 |
|
|
done: |
| 187 |
|
|
|
| 188 |
|
|
int consumed; |
| 189 |
|
|
int vec[kVecSize]; |
| 190 |
|
|
return DoMatchImpl(text, ANCHOR_BOTH, &consumed, args, n, vec, kVecSize); |
| 191 |
|
|
} |
| 192 |
|
|
|
| 193 |
|
|
bool RE::PartialMatch(const StringPiece& text, |
| 194 |
|
|
const Arg& ptr1, |
| 195 |
|
|
const Arg& ptr2, |
| 196 |
|
|
const Arg& ptr3, |
| 197 |
|
|
const Arg& ptr4, |
| 198 |
|
|
const Arg& ptr5, |
| 199 |
|
|
const Arg& ptr6, |
| 200 |
|
|
const Arg& ptr7, |
| 201 |
|
|
const Arg& ptr8, |
| 202 |
|
|
const Arg& ptr9, |
| 203 |
|
|
const Arg& ptr10, |
| 204 |
|
|
const Arg& ptr11, |
| 205 |
|
|
const Arg& ptr12, |
| 206 |
|
|
const Arg& ptr13, |
| 207 |
|
|
const Arg& ptr14, |
| 208 |
|
|
const Arg& ptr15, |
| 209 |
|
|
const Arg& ptr16) const { |
| 210 |
|
|
const Arg* args[kMaxArgs]; |
| 211 |
|
|
int n = 0; |
| 212 |
|
|
if (&ptr1 == &no_arg) goto done; args[n++] = &ptr1; |
| 213 |
|
|
if (&ptr2 == &no_arg) goto done; args[n++] = &ptr2; |
| 214 |
|
|
if (&ptr3 == &no_arg) goto done; args[n++] = &ptr3; |
| 215 |
|
|
if (&ptr4 == &no_arg) goto done; args[n++] = &ptr4; |
| 216 |
|
|
if (&ptr5 == &no_arg) goto done; args[n++] = &ptr5; |
| 217 |
|
|
if (&ptr6 == &no_arg) goto done; args[n++] = &ptr6; |
| 218 |
|
|
if (&ptr7 == &no_arg) goto done; args[n++] = &ptr7; |
| 219 |
|
|
if (&ptr8 == &no_arg) goto done; args[n++] = &ptr8; |
| 220 |
|
|
if (&ptr9 == &no_arg) goto done; args[n++] = &ptr9; |
| 221 |
|
|
if (&ptr10 == &no_arg) goto done; args[n++] = &ptr10; |
| 222 |
|
|
if (&ptr11 == &no_arg) goto done; args[n++] = &ptr11; |
| 223 |
|
|
if (&ptr12 == &no_arg) goto done; args[n++] = &ptr12; |
| 224 |
|
|
if (&ptr13 == &no_arg) goto done; args[n++] = &ptr13; |
| 225 |
|
|
if (&ptr14 == &no_arg) goto done; args[n++] = &ptr14; |
| 226 |
|
|
if (&ptr15 == &no_arg) goto done; args[n++] = &ptr15; |
| 227 |
|
|
if (&ptr16 == &no_arg) goto done; args[n++] = &ptr16; |
| 228 |
|
|
done: |
| 229 |
|
|
|
| 230 |
|
|
int consumed; |
| 231 |
|
|
int vec[kVecSize]; |
| 232 |
|
|
return DoMatchImpl(text, UNANCHORED, &consumed, args, n, vec, kVecSize); |
| 233 |
|
|
} |
| 234 |
|
|
|
| 235 |
|
|
bool RE::Consume(StringPiece* input, |
| 236 |
|
|
const Arg& ptr1, |
| 237 |
|
|
const Arg& ptr2, |
| 238 |
|
|
const Arg& ptr3, |
| 239 |
|
|
const Arg& ptr4, |
| 240 |
|
|
const Arg& ptr5, |
| 241 |
|
|
const Arg& ptr6, |
| 242 |
|
|
const Arg& ptr7, |
| 243 |
|
|
const Arg& ptr8, |
| 244 |
|
|
const Arg& ptr9, |
| 245 |
|
|
const Arg& ptr10, |
| 246 |
|
|
const Arg& ptr11, |
| 247 |
|
|
const Arg& ptr12, |
| 248 |
|
|
const Arg& ptr13, |
| 249 |
|
|
const Arg& ptr14, |
| 250 |
|
|
const Arg& ptr15, |
| 251 |
|
|
const Arg& ptr16) const { |
| 252 |
|
|
const Arg* args[kMaxArgs]; |
| 253 |
|
|
int n = 0; |
| 254 |
|
|
if (&ptr1 == &no_arg) goto done; args[n++] = &ptr1; |
| 255 |
|
|
if (&ptr2 == &no_arg) goto done; args[n++] = &ptr2; |
| 256 |
|
|
if (&ptr3 == &no_arg) goto done; args[n++] = &ptr3; |
| 257 |
|
|
if (&ptr4 == &no_arg) goto done; args[n++] = &ptr4; |
| 258 |
|
|
if (&ptr5 == &no_arg) goto done; args[n++] = &ptr5; |
| 259 |
|
|
if (&ptr6 == &no_arg) goto done; args[n++] = &ptr6; |
| 260 |
|
|
if (&ptr7 == &no_arg) goto done; args[n++] = &ptr7; |
| 261 |
|
|
if (&ptr8 == &no_arg) goto done; args[n++] = &ptr8; |
| 262 |
|
|
if (&ptr9 == &no_arg) goto done; args[n++] = &ptr9; |
| 263 |
|
|
if (&ptr10 == &no_arg) goto done; args[n++] = &ptr10; |
| 264 |
|
|
if (&ptr11 == &no_arg) goto done; args[n++] = &ptr11; |
| 265 |
|
|
if (&ptr12 == &no_arg) goto done; args[n++] = &ptr12; |
| 266 |
|
|
if (&ptr13 == &no_arg) goto done; args[n++] = &ptr13; |
| 267 |
|
|
if (&ptr14 == &no_arg) goto done; args[n++] = &ptr14; |
| 268 |
|
|
if (&ptr15 == &no_arg) goto done; args[n++] = &ptr15; |
| 269 |
|
|
if (&ptr16 == &no_arg) goto done; args[n++] = &ptr16; |
| 270 |
|
|
done: |
| 271 |
|
|
|
| 272 |
|
|
int consumed; |
| 273 |
|
|
int vec[kVecSize]; |
| 274 |
|
|
if (DoMatchImpl(*input, ANCHOR_START, &consumed, |
| 275 |
|
|
args, n, vec, kVecSize)) { |
| 276 |
|
|
input->remove_prefix(consumed); |
| 277 |
|
|
return true; |
| 278 |
|
|
} else { |
| 279 |
|
|
return false; |
| 280 |
|
|
} |
| 281 |
|
|
} |
| 282 |
|
|
|
| 283 |
|
|
bool RE::FindAndConsume(StringPiece* input, |
| 284 |
|
|
const Arg& ptr1, |
| 285 |
|
|
const Arg& ptr2, |
| 286 |
|
|
const Arg& ptr3, |
| 287 |
|
|
const Arg& ptr4, |
| 288 |
|
|
const Arg& ptr5, |
| 289 |
|
|
const Arg& ptr6, |
| 290 |
|
|
const Arg& ptr7, |
| 291 |
|
|
const Arg& ptr8, |
| 292 |
|
|
const Arg& ptr9, |
| 293 |
|
|
const Arg& ptr10, |
| 294 |
|
|
const Arg& ptr11, |
| 295 |
|
|
const Arg& ptr12, |
| 296 |
|
|
const Arg& ptr13, |
| 297 |
|
|
const Arg& ptr14, |
| 298 |
|
|
const Arg& ptr15, |
| 299 |
|
|
const Arg& ptr16) const { |
| 300 |
|
|
const Arg* args[kMaxArgs]; |
| 301 |
|
|
int n = 0; |
| 302 |
|
|
if (&ptr1 == &no_arg) goto done; args[n++] = &ptr1; |
| 303 |
|
|
if (&ptr2 == &no_arg) goto done; args[n++] = &ptr2; |
| 304 |
|
|
if (&ptr3 == &no_arg) goto done; args[n++] = &ptr3; |
| 305 |
|
|
if (&ptr4 == &no_arg) goto done; args[n++] = &ptr4; |
| 306 |
|
|
if (&ptr5 == &no_arg) goto done; args[n++] = &ptr5; |
| 307 |
|
|
if (&ptr6 == &no_arg) goto done; args[n++] = &ptr6; |
| 308 |
|
|
if (&ptr7 == &no_arg) goto done; args[n++] = &ptr7; |
| 309 |
|
|
if (&ptr8 == &no_arg) goto done; args[n++] = &ptr8; |
| 310 |
|
|
if (&ptr9 == &no_arg) goto done; args[n++] = &ptr9; |
| 311 |
|
|
if (&ptr10 == &no_arg) goto done; args[n++] = &ptr10; |
| 312 |
|
|
if (&ptr11 == &no_arg) goto done; args[n++] = &ptr11; |
| 313 |
|
|
if (&ptr12 == &no_arg) goto done; args[n++] = &ptr12; |
| 314 |
|
|
if (&ptr13 == &no_arg) goto done; args[n++] = &ptr13; |
| 315 |
|
|
if (&ptr14 == &no_arg) goto done; args[n++] = &ptr14; |
| 316 |
|
|
if (&ptr15 == &no_arg) goto done; args[n++] = &ptr15; |
| 317 |
|
|
if (&ptr16 == &no_arg) goto done; args[n++] = &ptr16; |
| 318 |
|
|
done: |
| 319 |
|
|
|
| 320 |
|
|
int consumed; |
| 321 |
|
|
int vec[kVecSize]; |
| 322 |
|
|
if (DoMatchImpl(*input, UNANCHORED, &consumed, |
| 323 |
|
|
args, n, vec, kVecSize)) { |
| 324 |
|
|
input->remove_prefix(consumed); |
| 325 |
|
|
return true; |
| 326 |
|
|
} else { |
| 327 |
|
|
return false; |
| 328 |
|
|
} |
| 329 |
|
|
} |
| 330 |
|
|
|
| 331 |
|
|
bool RE::Replace(const StringPiece& rewrite, |
| 332 |
|
|
string *str) const { |
| 333 |
|
|
int vec[kVecSize]; |
| 334 |
ph10 |
474 |
int matches = TryMatch(*str, 0, UNANCHORED, true, vec, kVecSize); |
| 335 |
nigel |
77 |
if (matches == 0) |
| 336 |
|
|
return false; |
| 337 |
|
|
|
| 338 |
|
|
string s; |
| 339 |
|
|
if (!Rewrite(&s, rewrite, *str, vec, matches)) |
| 340 |
|
|
return false; |
| 341 |
|
|
|
| 342 |
|
|
assert(vec[0] >= 0); |
| 343 |
|
|
assert(vec[1] >= 0); |
| 344 |
|
|
str->replace(vec[0], vec[1] - vec[0], s); |
| 345 |
|
|
return true; |
| 346 |
|
|
} |
| 347 |
|
|
|
| 348 |
nigel |
91 |
// Returns PCRE_NEWLINE_CRLF, PCRE_NEWLINE_CR, or PCRE_NEWLINE_LF. |
| 349 |
|
|
// Note that PCRE_NEWLINE_CRLF is defined to be P_N_CR | P_N_LF. |
| 350 |
ph10 |
253 |
// Modified by PH to add PCRE_NEWLINE_ANY and PCRE_NEWLINE_ANYCRLF. |
| 351 |
|
|
|
| 352 |
nigel |
91 |
static int NewlineMode(int pcre_options) { |
| 353 |
|
|
// TODO: if we can make it threadsafe, cache this var |
| 354 |
|
|
int newline_mode = 0; |
| 355 |
|
|
/* if (newline_mode) return newline_mode; */ // do this once it's cached |
| 356 |
ph10 |
253 |
if (pcre_options & (PCRE_NEWLINE_CRLF|PCRE_NEWLINE_CR|PCRE_NEWLINE_LF| |
| 357 |
|
|
PCRE_NEWLINE_ANY|PCRE_NEWLINE_ANYCRLF)) { |
| 358 |
nigel |
91 |
newline_mode = (pcre_options & |
| 359 |
ph10 |
253 |
(PCRE_NEWLINE_CRLF|PCRE_NEWLINE_CR|PCRE_NEWLINE_LF| |
| 360 |
|
|
PCRE_NEWLINE_ANY|PCRE_NEWLINE_ANYCRLF)); |
| 361 |
nigel |
91 |
} else { |
| 362 |
|
|
int newline; |
| 363 |
|
|
pcre_config(PCRE_CONFIG_NEWLINE, &newline); |
| 364 |
|
|
if (newline == 10) |
| 365 |
|
|
newline_mode = PCRE_NEWLINE_LF; |
| 366 |
|
|
else if (newline == 13) |
| 367 |
|
|
newline_mode = PCRE_NEWLINE_CR; |
| 368 |
|
|
else if (newline == 3338) |
| 369 |
|
|
newline_mode = PCRE_NEWLINE_CRLF; |
| 370 |
ph10 |
253 |
else if (newline == -1) |
| 371 |
|
|
newline_mode = PCRE_NEWLINE_ANY; |
| 372 |
|
|
else if (newline == -2) |
| 373 |
ph10 |
259 |
newline_mode = PCRE_NEWLINE_ANYCRLF; |
| 374 |
nigel |
91 |
else |
| 375 |
ph10 |
356 |
assert(NULL == "Unexpected return value from pcre_config(NEWLINE)"); |
| 376 |
nigel |
91 |
} |
| 377 |
|
|
return newline_mode; |
| 378 |
|
|
} |
| 379 |
|
|
|
| 380 |
nigel |
77 |
int RE::GlobalReplace(const StringPiece& rewrite, |
| 381 |
|
|
string *str) const { |
| 382 |
|
|
int count = 0; |
| 383 |
|
|
int vec[kVecSize]; |
| 384 |
|
|
string out; |
| 385 |
|
|
int start = 0; |
| 386 |
|
|
int lastend = -1; |
| 387 |
ph10 |
474 |
bool last_match_was_empty_string = false; |
| 388 |
nigel |
77 |
|
| 389 |
ph10 |
297 |
while (start <= static_cast<int>(str->length())) { |
| 390 |
ph10 |
474 |
// If the previous match was for the empty string, we shouldn't |
| 391 |
|
|
// just match again: we'll match in the same way and get an |
| 392 |
|
|
// infinite loop. Instead, we do the match in a special way: |
| 393 |
|
|
// anchored -- to force another try at the same position -- |
| 394 |
|
|
// and with a flag saying that this time, ignore empty matches. |
| 395 |
|
|
// If this special match returns, that means there's a non-empty |
| 396 |
|
|
// match at this position as well, and we can continue. If not, |
| 397 |
|
|
// we do what perl does, and just advance by one. |
| 398 |
|
|
// Notice that perl prints '@@@' for this; |
| 399 |
|
|
// perl -le '$_ = "aa"; s/b*|aa/@/g; print' |
| 400 |
|
|
int matches; |
| 401 |
|
|
if (last_match_was_empty_string) { |
| 402 |
|
|
matches = TryMatch(*str, start, ANCHOR_START, false, vec, kVecSize); |
| 403 |
|
|
if (matches <= 0) { |
| 404 |
|
|
int matchend = start + 1; // advance one character. |
| 405 |
|
|
// If the current char is CR and we're in CRLF mode, skip LF too. |
| 406 |
|
|
// Note it's better to call pcre_fullinfo() than to examine |
| 407 |
|
|
// all_options(), since options_ could have changed bewteen |
| 408 |
|
|
// compile-time and now, but this is simpler and safe enough. |
| 409 |
|
|
// Modified by PH to add ANY and ANYCRLF. |
| 410 |
|
|
if (matchend < static_cast<int>(str->length()) && |
| 411 |
|
|
(*str)[start] == '\r' && (*str)[matchend] == '\n' && |
| 412 |
|
|
(NewlineMode(options_.all_options()) == PCRE_NEWLINE_CRLF || |
| 413 |
|
|
NewlineMode(options_.all_options()) == PCRE_NEWLINE_ANY || |
| 414 |
|
|
NewlineMode(options_.all_options()) == PCRE_NEWLINE_ANYCRLF)) { |
| 415 |
|
|
matchend++; |
| 416 |
|
|
} |
| 417 |
|
|
// We also need to advance more than one char if we're in utf8 mode. |
| 418 |
nigel |
91 |
#ifdef SUPPORT_UTF8 |
| 419 |
ph10 |
474 |
if (options_.utf8()) { |
| 420 |
|
|
while (matchend < static_cast<int>(str->length()) && |
| 421 |
|
|
((*str)[matchend] & 0xc0) == 0x80) |
| 422 |
|
|
matchend++; |
| 423 |
|
|
} |
| 424 |
|
|
#endif |
| 425 |
|
|
if (start < static_cast<int>(str->length())) |
| 426 |
|
|
out.append(*str, start, matchend - start); |
| 427 |
|
|
start = matchend; |
| 428 |
|
|
last_match_was_empty_string = false; |
| 429 |
|
|
continue; |
| 430 |
nigel |
91 |
} |
| 431 |
nigel |
77 |
} else { |
| 432 |
ph10 |
474 |
matches = TryMatch(*str, start, UNANCHORED, true, vec, kVecSize); |
| 433 |
|
|
if (matches <= 0) |
| 434 |
|
|
break; |
| 435 |
nigel |
77 |
} |
| 436 |
ph10 |
474 |
int matchstart = vec[0], matchend = vec[1]; |
| 437 |
|
|
assert(matchstart >= start); |
| 438 |
|
|
assert(matchend >= matchstart); |
| 439 |
|
|
out.append(*str, start, matchstart - start); |
| 440 |
|
|
Rewrite(&out, rewrite, *str, vec, matches); |
| 441 |
|
|
start = matchend; |
| 442 |
|
|
lastend = matchend; |
| 443 |
|
|
count++; |
| 444 |
|
|
last_match_was_empty_string = (matchstart == matchend); |
| 445 |
nigel |
77 |
} |
| 446 |
|
|
|
| 447 |
|
|
if (count == 0) |
| 448 |
|
|
return 0; |
| 449 |
|
|
|
| 450 |
|
|
if (start < static_cast<int>(str->length())) |
| 451 |
|
|
out.append(*str, start, str->length() - start); |
| 452 |
|
|
swap(out, *str); |
| 453 |
|
|
return count; |
| 454 |
|
|
} |
| 455 |
|
|
|
| 456 |
|
|
bool RE::Extract(const StringPiece& rewrite, |
| 457 |
|
|
const StringPiece& text, |
| 458 |
|
|
string *out) const { |
| 459 |
|
|
int vec[kVecSize]; |
| 460 |
ph10 |
474 |
int matches = TryMatch(text, 0, UNANCHORED, true, vec, kVecSize); |
| 461 |
nigel |
77 |
if (matches == 0) |
| 462 |
|
|
return false; |
| 463 |
nigel |
81 |
out->erase(); |
| 464 |
nigel |
77 |
return Rewrite(out, rewrite, text, vec, matches); |
| 465 |
|
|
} |
| 466 |
|
|
|
| 467 |
nigel |
93 |
/*static*/ string RE::QuoteMeta(const StringPiece& unquoted) { |
| 468 |
|
|
string result; |
| 469 |
|
|
|
| 470 |
|
|
// Escape any ascii character not in [A-Za-z_0-9]. |
| 471 |
|
|
// |
| 472 |
|
|
// Note that it's legal to escape a character even if it has no |
| 473 |
|
|
// special meaning in a regular expression -- so this function does |
| 474 |
|
|
// that. (This also makes it identical to the perl function of the |
| 475 |
ph10 |
326 |
// same name; see `perldoc -f quotemeta`.) The one exception is |
| 476 |
|
|
// escaping NUL: rather than doing backslash + NUL, like perl does, |
| 477 |
|
|
// we do '\0', because pcre itself doesn't take embedded NUL chars. |
| 478 |
nigel |
93 |
for (int ii = 0; ii < unquoted.size(); ++ii) { |
| 479 |
|
|
// Note that using 'isalnum' here raises the benchmark time from |
| 480 |
|
|
// 32ns to 58ns: |
| 481 |
ph10 |
326 |
if (unquoted[ii] == '\0') { |
| 482 |
|
|
result += "\\0"; |
| 483 |
|
|
} else if ((unquoted[ii] < 'a' || unquoted[ii] > 'z') && |
| 484 |
|
|
(unquoted[ii] < 'A' || unquoted[ii] > 'Z') && |
| 485 |
|
|
(unquoted[ii] < '0' || unquoted[ii] > '9') && |
| 486 |
|
|
unquoted[ii] != '_' && |
| 487 |
|
|
// If this is the part of a UTF8 or Latin1 character, we need |
| 488 |
|
|
// to copy this byte without escaping. Experimentally this is |
| 489 |
|
|
// what works correctly with the regexp library. |
| 490 |
|
|
!(unquoted[ii] & 128)) { |
| 491 |
nigel |
93 |
result += '\\'; |
| 492 |
ph10 |
326 |
result += unquoted[ii]; |
| 493 |
|
|
} else { |
| 494 |
|
|
result += unquoted[ii]; |
| 495 |
nigel |
93 |
} |
| 496 |
|
|
} |
| 497 |
|
|
|
| 498 |
|
|
return result; |
| 499 |
|
|
} |
| 500 |
|
|
|
| 501 |
nigel |
77 |
/***** Actual matching and rewriting code *****/ |
| 502 |
|
|
|
| 503 |
|
|
int RE::TryMatch(const StringPiece& text, |
| 504 |
|
|
int startpos, |
| 505 |
|
|
Anchor anchor, |
| 506 |
ph10 |
474 |
bool empty_ok, |
| 507 |
nigel |
77 |
int *vec, |
| 508 |
|
|
int vecsize) const { |
| 509 |
|
|
pcre* re = (anchor == ANCHOR_BOTH) ? re_full_ : re_partial_; |
| 510 |
|
|
if (re == NULL) { |
| 511 |
|
|
//fprintf(stderr, "Matching against invalid re: %s\n", error_->c_str()); |
| 512 |
|
|
return 0; |
| 513 |
|
|
} |
| 514 |
|
|
|
| 515 |
ph10 |
199 |
pcre_extra extra = { 0, 0, 0, 0, 0, 0 }; |
| 516 |
nigel |
77 |
if (options_.match_limit() > 0) { |
| 517 |
nigel |
87 |
extra.flags |= PCRE_EXTRA_MATCH_LIMIT; |
| 518 |
nigel |
77 |
extra.match_limit = options_.match_limit(); |
| 519 |
|
|
} |
| 520 |
nigel |
87 |
if (options_.match_limit_recursion() > 0) { |
| 521 |
|
|
extra.flags |= PCRE_EXTRA_MATCH_LIMIT_RECURSION; |
| 522 |
|
|
extra.match_limit_recursion = options_.match_limit_recursion(); |
| 523 |
|
|
} |
| 524 |
ph10 |
474 |
|
| 525 |
|
|
int options = 0; |
| 526 |
|
|
if (anchor != UNANCHORED) |
| 527 |
|
|
options |= PCRE_ANCHORED; |
| 528 |
|
|
if (!empty_ok) |
| 529 |
|
|
options |= PCRE_NOTEMPTY; |
| 530 |
|
|
|
| 531 |
nigel |
77 |
int rc = pcre_exec(re, // The regular expression object |
| 532 |
|
|
&extra, |
| 533 |
nigel |
87 |
(text.data() == NULL) ? "" : text.data(), |
| 534 |
nigel |
77 |
text.size(), |
| 535 |
|
|
startpos, |
| 536 |
ph10 |
474 |
options, |
| 537 |
nigel |
77 |
vec, |
| 538 |
|
|
vecsize); |
| 539 |
|
|
|
| 540 |
|
|
// Handle errors |
| 541 |
|
|
if (rc == PCRE_ERROR_NOMATCH) { |
| 542 |
|
|
return 0; |
| 543 |
|
|
} else if (rc < 0) { |
| 544 |
|
|
//fprintf(stderr, "Unexpected return code: %d when matching '%s'\n", |
| 545 |
|
|
// re, pattern_.c_str()); |
| 546 |
|
|
return 0; |
| 547 |
|
|
} else if (rc == 0) { |
| 548 |
|
|
// pcre_exec() returns 0 as a special case when the number of |
| 549 |
|
|
// capturing subpatterns exceeds the size of the vector. |
| 550 |
|
|
// When this happens, there is a match and the output vector |
| 551 |
|
|
// is filled, but we miss out on the positions of the extra subpatterns. |
| 552 |
|
|
rc = vecsize / 2; |
| 553 |
|
|
} |
| 554 |
|
|
|
| 555 |
|
|
return rc; |
| 556 |
|
|
} |
| 557 |
|
|
|
| 558 |
|
|
bool RE::DoMatchImpl(const StringPiece& text, |
| 559 |
|
|
Anchor anchor, |
| 560 |
|
|
int* consumed, |
| 561 |
|
|
const Arg* const* args, |
| 562 |
|
|
int n, |
| 563 |
|
|
int* vec, |
| 564 |
|
|
int vecsize) const { |
| 565 |
|
|
assert((1 + n) * 3 <= vecsize); // results + PCRE workspace |
| 566 |
ph10 |
474 |
int matches = TryMatch(text, 0, anchor, true, vec, vecsize); |
| 567 |
nigel |
77 |
assert(matches >= 0); // TryMatch never returns negatives |
| 568 |
|
|
if (matches == 0) |
| 569 |
|
|
return false; |
| 570 |
|
|
|
| 571 |
|
|
*consumed = vec[1]; |
| 572 |
|
|
|
| 573 |
nigel |
87 |
if (n == 0 || args == NULL) { |
| 574 |
nigel |
77 |
// We are not interested in results |
| 575 |
|
|
return true; |
| 576 |
|
|
} |
| 577 |
|
|
|
| 578 |
nigel |
87 |
if (NumberOfCapturingGroups() < n) { |
| 579 |
|
|
// RE has fewer capturing groups than number of arg pointers passed in |
| 580 |
|
|
return false; |
| 581 |
|
|
} |
| 582 |
|
|
|
| 583 |
nigel |
77 |
// If we got here, we must have matched the whole pattern. |
| 584 |
|
|
// We do not need (can not do) any more checks on the value of 'matches' here |
| 585 |
|
|
// -- see the comment for TryMatch. |
| 586 |
|
|
for (int i = 0; i < n; i++) { |
| 587 |
|
|
const int start = vec[2*(i+1)]; |
| 588 |
|
|
const int limit = vec[2*(i+1)+1]; |
| 589 |
|
|
if (!args[i]->Parse(text.data() + start, limit-start)) { |
| 590 |
|
|
// TODO: Should we indicate what the error was? |
| 591 |
|
|
return false; |
| 592 |
|
|
} |
| 593 |
|
|
} |
| 594 |
|
|
|
| 595 |
|
|
return true; |
| 596 |
|
|
} |
| 597 |
|
|
|
| 598 |
|
|
bool RE::DoMatch(const StringPiece& text, |
| 599 |
|
|
Anchor anchor, |
| 600 |
|
|
int* consumed, |
| 601 |
|
|
const Arg* const args[], |
| 602 |
|
|
int n) const { |
| 603 |
|
|
assert(n >= 0); |
| 604 |
|
|
size_t const vecsize = (1 + n) * 3; // results + PCRE workspace |
| 605 |
|
|
// (as for kVecSize) |
| 606 |
|
|
int space[21]; // use stack allocation for small vecsize (common case) |
| 607 |
|
|
int* vec = vecsize <= 21 ? space : new int[vecsize]; |
| 608 |
ph10 |
530 |
bool retval = DoMatchImpl(text, anchor, consumed, args, n, vec, (int)vecsize); |
| 609 |
nigel |
77 |
if (vec != space) delete [] vec; |
| 610 |
|
|
return retval; |
| 611 |
|
|
} |
| 612 |
|
|
|
| 613 |
|
|
bool RE::Rewrite(string *out, const StringPiece &rewrite, |
| 614 |
|
|
const StringPiece &text, int *vec, int veclen) const { |
| 615 |
|
|
for (const char *s = rewrite.data(), *end = s + rewrite.size(); |
| 616 |
|
|
s < end; s++) { |
| 617 |
|
|
int c = *s; |
| 618 |
|
|
if (c == '\\') { |
| 619 |
|
|
c = *++s; |
| 620 |
|
|
if (isdigit(c)) { |
| 621 |
|
|
int n = (c - '0'); |
| 622 |
|
|
if (n >= veclen) { |
| 623 |
|
|
//fprintf(stderr, requested group %d in regexp %.*s\n", |
| 624 |
|
|
// n, rewrite.size(), rewrite.data()); |
| 625 |
|
|
return false; |
| 626 |
|
|
} |
| 627 |
|
|
int start = vec[2 * n]; |
| 628 |
|
|
if (start >= 0) |
| 629 |
|
|
out->append(text.data() + start, vec[2 * n + 1] - start); |
| 630 |
|
|
} else if (c == '\\') { |
| 631 |
ph10 |
331 |
*out += '\\'; |
| 632 |
nigel |
77 |
} else { |
| 633 |
|
|
//fprintf(stderr, "invalid rewrite pattern: %.*s\n", |
| 634 |
|
|
// rewrite.size(), rewrite.data()); |
| 635 |
|
|
return false; |
| 636 |
|
|
} |
| 637 |
|
|
} else { |
| 638 |
ph10 |
331 |
*out += c; |
| 639 |
nigel |
77 |
} |
| 640 |
|
|
} |
| 641 |
|
|
return true; |
| 642 |
|
|
} |
| 643 |
|
|
|
| 644 |
|
|
// Return the number of capturing subpatterns, or -1 if the |
| 645 |
|
|
// regexp wasn't valid on construction. |
| 646 |
nigel |
87 |
int RE::NumberOfCapturingGroups() const { |
| 647 |
nigel |
77 |
if (re_partial_ == NULL) return -1; |
| 648 |
|
|
|
| 649 |
|
|
int result; |
| 650 |
|
|
int pcre_retval = pcre_fullinfo(re_partial_, // The regular expression object |
| 651 |
|
|
NULL, // We did not study the pattern |
| 652 |
|
|
PCRE_INFO_CAPTURECOUNT, |
| 653 |
|
|
&result); |
| 654 |
|
|
assert(pcre_retval == 0); |
| 655 |
|
|
return result; |
| 656 |
|
|
} |
| 657 |
|
|
|
| 658 |
|
|
/***** Parsers for various types *****/ |
| 659 |
|
|
|
| 660 |
|
|
bool Arg::parse_null(const char* str, int n, void* dest) { |
| 661 |
|
|
// We fail if somebody asked us to store into a non-NULL void* pointer |
| 662 |
|
|
return (dest == NULL); |
| 663 |
|
|
} |
| 664 |
|
|
|
| 665 |
|
|
bool Arg::parse_string(const char* str, int n, void* dest) { |
| 666 |
ph10 |
263 |
if (dest == NULL) return true; |
| 667 |
nigel |
77 |
reinterpret_cast<string*>(dest)->assign(str, n); |
| 668 |
|
|
return true; |
| 669 |
|
|
} |
| 670 |
|
|
|
| 671 |
|
|
bool Arg::parse_stringpiece(const char* str, int n, void* dest) { |
| 672 |
ph10 |
263 |
if (dest == NULL) return true; |
| 673 |
nigel |
77 |
reinterpret_cast<StringPiece*>(dest)->set(str, n); |
| 674 |
|
|
return true; |
| 675 |
|
|
} |
| 676 |
|
|
|
| 677 |
|
|
bool Arg::parse_char(const char* str, int n, void* dest) { |
| 678 |
|
|
if (n != 1) return false; |
| 679 |
ph10 |
263 |
if (dest == NULL) return true; |
| 680 |
nigel |
77 |
*(reinterpret_cast<char*>(dest)) = str[0]; |
| 681 |
|
|
return true; |
| 682 |
|
|
} |
| 683 |
|
|
|
| 684 |
|
|
bool Arg::parse_uchar(const char* str, int n, void* dest) { |
| 685 |
|
|
if (n != 1) return false; |
| 686 |
ph10 |
263 |
if (dest == NULL) return true; |
| 687 |
nigel |
77 |
*(reinterpret_cast<unsigned char*>(dest)) = str[0]; |
| 688 |
|
|
return true; |
| 689 |
|
|
} |
| 690 |
|
|
|
| 691 |
|
|
// Largest number spec that we are willing to parse |
| 692 |
|
|
static const int kMaxNumberLength = 32; |
| 693 |
|
|
|
| 694 |
|
|
// REQUIRES "buf" must have length at least kMaxNumberLength+1 |
| 695 |
|
|
// REQUIRES "n > 0" |
| 696 |
|
|
// Copies "str" into "buf" and null-terminates if necessary. |
| 697 |
|
|
// Returns one of: |
| 698 |
|
|
// a. "str" if no termination is needed |
| 699 |
|
|
// b. "buf" if the string was copied and null-terminated |
| 700 |
|
|
// c. "" if the input was invalid and has no hope of being parsed |
| 701 |
|
|
static const char* TerminateNumber(char* buf, const char* str, int n) { |
| 702 |
|
|
if ((n > 0) && isspace(*str)) { |
| 703 |
|
|
// We are less forgiving than the strtoxxx() routines and do not |
| 704 |
|
|
// allow leading spaces. |
| 705 |
|
|
return ""; |
| 706 |
|
|
} |
| 707 |
|
|
|
| 708 |
|
|
// See if the character right after the input text may potentially |
| 709 |
|
|
// look like a digit. |
| 710 |
|
|
if (isdigit(str[n]) || |
| 711 |
|
|
((str[n] >= 'a') && (str[n] <= 'f')) || |
| 712 |
|
|
((str[n] >= 'A') && (str[n] <= 'F'))) { |
| 713 |
|
|
if (n > kMaxNumberLength) return ""; // Input too big to be a valid number |
| 714 |
|
|
memcpy(buf, str, n); |
| 715 |
|
|
buf[n] = '\0'; |
| 716 |
|
|
return buf; |
| 717 |
|
|
} else { |
| 718 |
|
|
// We can parse right out of the supplied string, so return it. |
| 719 |
|
|
return str; |
| 720 |
|
|
} |
| 721 |
|
|
} |
| 722 |
|
|
|
| 723 |
|
|
bool Arg::parse_long_radix(const char* str, |
| 724 |
|
|
int n, |
| 725 |
|
|
void* dest, |
| 726 |
|
|
int radix) { |
| 727 |
|
|
if (n == 0) return false; |
| 728 |
|
|
char buf[kMaxNumberLength+1]; |
| 729 |
|
|
str = TerminateNumber(buf, str, n); |
| 730 |
|
|
char* end; |
| 731 |
|
|
errno = 0; |
| 732 |
|
|
long r = strtol(str, &end, radix); |
| 733 |
|
|
if (end != str + n) return false; // Leftover junk |
| 734 |
|
|
if (errno) return false; |
| 735 |
ph10 |
263 |
if (dest == NULL) return true; |
| 736 |
nigel |
77 |
*(reinterpret_cast<long*>(dest)) = r; |
| 737 |
|
|
return true; |
| 738 |
|
|
} |
| 739 |
|
|
|
| 740 |
|
|
bool Arg::parse_ulong_radix(const char* str, |
| 741 |
|
|
int n, |
| 742 |
|
|
void* dest, |
| 743 |
|
|
int radix) { |
| 744 |
|
|
if (n == 0) return false; |
| 745 |
|
|
char buf[kMaxNumberLength+1]; |
| 746 |
|
|
str = TerminateNumber(buf, str, n); |
| 747 |
nigel |
87 |
if (str[0] == '-') return false; // strtoul() on a negative number?! |
| 748 |
nigel |
77 |
char* end; |
| 749 |
|
|
errno = 0; |
| 750 |
|
|
unsigned long r = strtoul(str, &end, radix); |
| 751 |
|
|
if (end != str + n) return false; // Leftover junk |
| 752 |
|
|
if (errno) return false; |
| 753 |
ph10 |
263 |
if (dest == NULL) return true; |
| 754 |
nigel |
77 |
*(reinterpret_cast<unsigned long*>(dest)) = r; |
| 755 |
|
|
return true; |
| 756 |
|
|
} |
| 757 |
|
|
|
| 758 |
|
|
bool Arg::parse_short_radix(const char* str, |
| 759 |
|
|
int n, |
| 760 |
|
|
void* dest, |
| 761 |
|
|
int radix) { |
| 762 |
|
|
long r; |
| 763 |
|
|
if (!parse_long_radix(str, n, &r, radix)) return false; // Could not parse |
| 764 |
|
|
if (r < SHRT_MIN || r > SHRT_MAX) return false; // Out of range |
| 765 |
ph10 |
263 |
if (dest == NULL) return true; |
| 766 |
ph10 |
256 |
*(reinterpret_cast<short*>(dest)) = static_cast<short>(r); |
| 767 |
nigel |
77 |
return true; |
| 768 |
|
|
} |
| 769 |
|
|
|
| 770 |
|
|
bool Arg::parse_ushort_radix(const char* str, |
| 771 |
|
|
int n, |
| 772 |
|
|
void* dest, |
| 773 |
|
|
int radix) { |
| 774 |
|
|
unsigned long r; |
| 775 |
|
|
if (!parse_ulong_radix(str, n, &r, radix)) return false; // Could not parse |
| 776 |
|
|
if (r > USHRT_MAX) return false; // Out of range |
| 777 |
ph10 |
263 |
if (dest == NULL) return true; |
| 778 |
ph10 |
256 |
*(reinterpret_cast<unsigned short*>(dest)) = static_cast<unsigned short>(r); |
| 779 |
nigel |
77 |
return true; |
| 780 |
|
|
} |
| 781 |
|
|
|
| 782 |
|
|
bool Arg::parse_int_radix(const char* str, |
| 783 |
|
|
int n, |
| 784 |
|
|
void* dest, |
| 785 |
|
|
int radix) { |
| 786 |
|
|
long r; |
| 787 |
|
|
if (!parse_long_radix(str, n, &r, radix)) return false; // Could not parse |
| 788 |
|
|
if (r < INT_MIN || r > INT_MAX) return false; // Out of range |
| 789 |
ph10 |
263 |
if (dest == NULL) return true; |
| 790 |
nigel |
77 |
*(reinterpret_cast<int*>(dest)) = r; |
| 791 |
|
|
return true; |
| 792 |
|
|
} |
| 793 |
|
|
|
| 794 |
|
|
bool Arg::parse_uint_radix(const char* str, |
| 795 |
|
|
int n, |
| 796 |
|
|
void* dest, |
| 797 |
|
|
int radix) { |
| 798 |
|
|
unsigned long r; |
| 799 |
|
|
if (!parse_ulong_radix(str, n, &r, radix)) return false; // Could not parse |
| 800 |
|
|
if (r > UINT_MAX) return false; // Out of range |
| 801 |
ph10 |
263 |
if (dest == NULL) return true; |
| 802 |
nigel |
77 |
*(reinterpret_cast<unsigned int*>(dest)) = r; |
| 803 |
|
|
return true; |
| 804 |
|
|
} |
| 805 |
|
|
|
| 806 |
|
|
bool Arg::parse_longlong_radix(const char* str, |
| 807 |
|
|
int n, |
| 808 |
|
|
void* dest, |
| 809 |
|
|
int radix) { |
| 810 |
|
|
#ifndef HAVE_LONG_LONG |
| 811 |
|
|
return false; |
| 812 |
|
|
#else |
| 813 |
|
|
if (n == 0) return false; |
| 814 |
|
|
char buf[kMaxNumberLength+1]; |
| 815 |
|
|
str = TerminateNumber(buf, str, n); |
| 816 |
|
|
char* end; |
| 817 |
|
|
errno = 0; |
| 818 |
|
|
#if defined HAVE_STRTOQ |
| 819 |
|
|
long long r = strtoq(str, &end, radix); |
| 820 |
|
|
#elif defined HAVE_STRTOLL |
| 821 |
|
|
long long r = strtoll(str, &end, radix); |
| 822 |
ph10 |
257 |
#elif defined HAVE__STRTOI64 |
| 823 |
|
|
long long r = _strtoi64(str, &end, radix); |
| 824 |
ph10 |
486 |
#elif defined HAVE_STRTOIMAX |
| 825 |
|
|
long long r = strtoimax(str, &end, radix); |
| 826 |
nigel |
77 |
#else |
| 827 |
|
|
#error parse_longlong_radix: cannot convert input to a long-long |
| 828 |
|
|
#endif |
| 829 |
|
|
if (end != str + n) return false; // Leftover junk |
| 830 |
|
|
if (errno) return false; |
| 831 |
ph10 |
263 |
if (dest == NULL) return true; |
| 832 |
nigel |
77 |
*(reinterpret_cast<long long*>(dest)) = r; |
| 833 |
|
|
return true; |
| 834 |
|
|
#endif /* HAVE_LONG_LONG */ |
| 835 |
|
|
} |
| 836 |
|
|
|
| 837 |
|
|
bool Arg::parse_ulonglong_radix(const char* str, |
| 838 |
|
|
int n, |
| 839 |
|
|
void* dest, |
| 840 |
|
|
int radix) { |
| 841 |
|
|
#ifndef HAVE_UNSIGNED_LONG_LONG |
| 842 |
|
|
return false; |
| 843 |
|
|
#else |
| 844 |
|
|
if (n == 0) return false; |
| 845 |
|
|
char buf[kMaxNumberLength+1]; |
| 846 |
|
|
str = TerminateNumber(buf, str, n); |
| 847 |
nigel |
87 |
if (str[0] == '-') return false; // strtoull() on a negative number?! |
| 848 |
nigel |
77 |
char* end; |
| 849 |
|
|
errno = 0; |
| 850 |
|
|
#if defined HAVE_STRTOQ |
| 851 |
|
|
unsigned long long r = strtouq(str, &end, radix); |
| 852 |
|
|
#elif defined HAVE_STRTOLL |
| 853 |
|
|
unsigned long long r = strtoull(str, &end, radix); |
| 854 |
ph10 |
257 |
#elif defined HAVE__STRTOI64 |
| 855 |
|
|
unsigned long long r = _strtoui64(str, &end, radix); |
| 856 |
ph10 |
486 |
#elif defined HAVE_STRTOIMAX |
| 857 |
|
|
unsigned long long r = strtoumax(str, &end, radix); |
| 858 |
nigel |
77 |
#else |
| 859 |
|
|
#error parse_ulonglong_radix: cannot convert input to a long-long |
| 860 |
|
|
#endif |
| 861 |
|
|
if (end != str + n) return false; // Leftover junk |
| 862 |
|
|
if (errno) return false; |
| 863 |
ph10 |
263 |
if (dest == NULL) return true; |
| 864 |
nigel |
77 |
*(reinterpret_cast<unsigned long long*>(dest)) = r; |
| 865 |
|
|
return true; |
| 866 |
|
|
#endif /* HAVE_UNSIGNED_LONG_LONG */ |
| 867 |
|
|
} |
| 868 |
|
|
|
| 869 |
|
|
bool Arg::parse_double(const char* str, int n, void* dest) { |
| 870 |
|
|
if (n == 0) return false; |
| 871 |
|
|
static const int kMaxLength = 200; |
| 872 |
|
|
char buf[kMaxLength]; |
| 873 |
|
|
if (n >= kMaxLength) return false; |
| 874 |
|
|
memcpy(buf, str, n); |
| 875 |
|
|
buf[n] = '\0'; |
| 876 |
|
|
errno = 0; |
| 877 |
|
|
char* end; |
| 878 |
|
|
double r = strtod(buf, &end); |
| 879 |
|
|
if (end != buf + n) return false; // Leftover junk |
| 880 |
|
|
if (errno) return false; |
| 881 |
ph10 |
263 |
if (dest == NULL) return true; |
| 882 |
nigel |
77 |
*(reinterpret_cast<double*>(dest)) = r; |
| 883 |
|
|
return true; |
| 884 |
|
|
} |
| 885 |
|
|
|
| 886 |
|
|
bool Arg::parse_float(const char* str, int n, void* dest) { |
| 887 |
|
|
double r; |
| 888 |
|
|
if (!parse_double(str, n, &r)) return false; |
| 889 |
ph10 |
263 |
if (dest == NULL) return true; |
| 890 |
nigel |
77 |
*(reinterpret_cast<float*>(dest)) = static_cast<float>(r); |
| 891 |
|
|
return true; |
| 892 |
|
|
} |
| 893 |
|
|
|
| 894 |
|
|
|
| 895 |
|
|
#define DEFINE_INTEGER_PARSERS(name) \ |
| 896 |
|
|
bool Arg::parse_##name(const char* str, int n, void* dest) { \ |
| 897 |
|
|
return parse_##name##_radix(str, n, dest, 10); \ |
| 898 |
|
|
} \ |
| 899 |
|
|
bool Arg::parse_##name##_hex(const char* str, int n, void* dest) { \ |
| 900 |
|
|
return parse_##name##_radix(str, n, dest, 16); \ |
| 901 |
|
|
} \ |
| 902 |
|
|
bool Arg::parse_##name##_octal(const char* str, int n, void* dest) { \ |
| 903 |
|
|
return parse_##name##_radix(str, n, dest, 8); \ |
| 904 |
|
|
} \ |
| 905 |
|
|
bool Arg::parse_##name##_cradix(const char* str, int n, void* dest) { \ |
| 906 |
|
|
return parse_##name##_radix(str, n, dest, 0); \ |
| 907 |
|
|
} |
| 908 |
|
|
|
| 909 |
nigel |
93 |
DEFINE_INTEGER_PARSERS(short) /* */ |
| 910 |
|
|
DEFINE_INTEGER_PARSERS(ushort) /* */ |
| 911 |
|
|
DEFINE_INTEGER_PARSERS(int) /* Don't use semicolons after these */ |
| 912 |
|
|
DEFINE_INTEGER_PARSERS(uint) /* statements because they can cause */ |
| 913 |
|
|
DEFINE_INTEGER_PARSERS(long) /* compiler warnings if the checking */ |
| 914 |
|
|
DEFINE_INTEGER_PARSERS(ulong) /* level is turned up high enough. */ |
| 915 |
|
|
DEFINE_INTEGER_PARSERS(longlong) /* */ |
| 916 |
|
|
DEFINE_INTEGER_PARSERS(ulonglong) /* */ |
| 917 |
nigel |
77 |
|
| 918 |
|
|
#undef DEFINE_INTEGER_PARSERS |
| 919 |
|
|
|
| 920 |
|
|
} // namespace pcrecpp |