From 7dd909584e7c279a7f5c696170a534f150142b62 Mon Sep 17 00:00:00 2001 From: ph10 Date: Sat, 8 Nov 2014 16:09:24 +0000 Subject: [PATCH] Preliminary substitute function (not yet tested or documented). --- Makefile.am | 1 + doc/pcre2_match_data_create_from_pattern.3 | 2 +- doc/pcre2api.3 | 30 +-- src/pcre2.h.in | 23 +- src/pcre2_match_data.c | 2 +- src/pcre2_substitute.c | 269 +++++++++++++++++++++ src/pcre2_substring.c | 3 +- 7 files changed, 310 insertions(+), 20 deletions(-) create mode 100644 src/pcre2_substitute.c diff --git a/Makefile.am b/Makefile.am index 4a2fa2e..8e558d1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -314,6 +314,7 @@ COMMON_SOURCES = \ src/pcre2_pattern_info.c \ src/pcre2_string_utils.c \ src/pcre2_study.c \ + src/pcre2_substitute.c \ src/pcre2_substring.c \ src/pcre2_tables.c \ src/pcre2_ucd.c \ diff --git a/doc/pcre2_match_data_create_from_pattern.3 b/doc/pcre2_match_data_create_from_pattern.3 index aef93af..8f64b66 100644 --- a/doc/pcre2_match_data_create_from_pattern.3 +++ b/doc/pcre2_match_data_create_from_pattern.3 @@ -7,7 +7,7 @@ PCRE2 - Perl-compatible regular expressions (revised API) .B #include .PP .nf -.B pcre2_match_data_create_from_pattern(pcre2_code *\fIcode\fP, +.B pcre2_match_data_create_from_pattern(const pcre2_code *\fIcode\fP, .B " pcre2_general_context *\fIgcontext\fP);" .fi . diff --git a/doc/pcre2api.3 b/doc/pcre2api.3 index e05a60f..8b016b9 100644 --- a/doc/pcre2api.3 +++ b/doc/pcre2api.3 @@ -1,4 +1,4 @@ -.TH PCRE2API 3 "03 November 2014" "PCRE2 10.00" +.TH PCRE2API 3 "08 November 2014" "PCRE2 10.00" .SH NAME PCRE2 - Perl-compatible regular expressions (revised API) .sp @@ -25,7 +25,7 @@ document for an overview of all the PCRE2 documentation. .B pcre2_match_data_create(uint32_t \fIovecsize\fP, .B " pcre2_general_context *\fIgcontext\fP);" .sp -.B pcre2_match_data_create_from_pattern(pcre2_code *\fIcode\fP, +.B pcre2_match_data_create_from_pattern(const pcre2_code *\fIcode\fP, .B " pcre2_general_context *\fIgcontext\fP);" .sp .B int pcre2_match(const pcre2_code *\fIcode\fP, PCRE2_SPTR \fIsubject\fP, @@ -178,14 +178,14 @@ document for an overview of all the PCRE2 documentation. .B int pcre2_jit_match(const pcre2_code *\fIcode\fP, PCRE2_SPTR \fIsubject\fP, .B " PCRE2_SIZE \fIlength\fP, PCRE2_SIZE \fIstartoffset\fP," .B " uint32_t \fIoptions\fP, pcre2_match_data *\fImatch_data\fP," -.B " pcre2_match_context *\fImcontext\fP, pcre2_jit_stack *\fIjit_stack\fP);" +.B " pcre2_match_context *\fImcontext\fP);" .sp .B void pcre2_jit_free_unused_memory(pcre2_general_context *\fIgcontext\fP); .sp .B pcre2_jit_stack *pcre2_jit_stack_create(pcre2_general_context *\fIgcontext\fP, .B " PCRE2_SIZE \fIstartsize\fP, PCRE2_SIZE \fImaxsize\fP);" .sp -.B void pcre2_jit_stack_assign(const pcre2_code *\fIcode\fP, +.B void pcre2_jit_stack_assign(pcre2_match_context *\fImcontext\fP, .B " pcre2_jit_callback \fIcallback_function\fP, void *\fIcallback_data\fP);" .sp .B void pcre2_jit_stack_free(pcre2_jit_stack *\fIjit_stack\fP); @@ -783,8 +783,9 @@ units long. (The exact length needed can be found by calling \fBpcre2_config()\fP with \fBwhere\fP set to NULL.) The buffer is filled with a string that contains the name of the architecture for which the JIT compiler is configured, for example "x86 32bit (little endian + unaligned)". If JIT support -is not available, PCRE2_ERROR_BADOPTION is returned, otherwise the length of -the string, in code units, is returned. +is not available, PCRE2_ERROR_BADOPTION is returned, otherwise the number of +code units used is returned. This is the length of the string, plus one unit +for the terminating zero. .sp PCRE2_CONFIG_LINKSIZE .sp @@ -851,8 +852,8 @@ units long. (The exact length needed can be found by calling \fBpcre2_config()\fP with \fBwhere\fP set to NULL.) If PCRE2 has been compiled without Unicode support, the buffer is filled with the text "Unicode not supported". Otherwise, the Unicode version string (for example, "7.0.0") is -inserted. The string is zero-terminated. The function returns the length of the -string in code units. +inserted. The number of code units used is returned. This is the length of the +string plus one unit for the terminating zero. .sp PCRE2_CONFIG_UNICODE .sp @@ -864,8 +865,9 @@ otherwise it is set to zero. Unicode support implies UTF support. The \fIwhere\fP argument should point to a buffer that is at least 12 code units long. (The exact length needed can be found by calling \fBpcre2_config()\fP with \fBwhere\fP set to NULL.) The buffer is filled with -the PCRE2 version string, zero-terminated. The length of the string (in code -units) is returned. +the PCRE2 version string, zero-terminated. The number of code units used is +returned. This is the length of the string plus one unit for the terminating +zero. . . .\" HTML @@ -1261,14 +1263,14 @@ textual error message from any error code. .B int pcre2_jit_match(const pcre2_code *\fIcode\fP, PCRE2_SPTR \fIsubject\fP, .B " PCRE2_SIZE \fIlength\fP, PCRE2_SIZE \fIstartoffset\fP," .B " uint32_t \fIoptions\fP, pcre2_match_data *\fImatch_data\fP," -.B " pcre2_match_context *\fImcontext\fP, pcre2_jit_stack *\fIjit_stack\fP);" +.B " pcre2_match_context *\fImcontext\fP);" .sp .B void pcre2_jit_free_unused_memory(pcre2_general_context *\fIgcontext\fP); .sp .B pcre2_jit_stack *pcre2_jit_stack_create(pcre2_general_context *\fIgcontext\fP, .B " PCRE2_SIZE \fIstartsize\fP, PCRE2_SIZE \fImaxsize\fP);" .sp -.B void pcre2_jit_stack_assign(const pcre2_code *\fIcode\fP, +.B void pcre2_jit_stack_assign(pcre2_match_context *\fImcontext\fP, .B " pcre2_jit_callback \fIcallback_function\fP, void *\fIcallback_data\fP);" .sp .B void pcre2_jit_stack_free(pcre2_jit_stack *\fIjit_stack\fP); @@ -1642,7 +1644,7 @@ pattern with the JIT compiler does not alter the value returned by this option. .B pcre2_match_data_create(uint32_t \fIovecsize\fP, .B " pcre2_general_context *\fIgcontext\fP);" .sp -.B pcre2_match_data_create_from_pattern(pcre2_code *\fIcode\fP, +.B pcre2_match_data_create_from_pattern(const pcre2_code *\fIcode\fP, .B " pcre2_general_context *\fIgcontext\fP);" .sp .B void pcre2_match_data_free(pcre2_match_data *\fImatch_data\fP); @@ -2631,6 +2633,6 @@ Cambridge CB2 3QH, England. .rs .sp .nf -Last updated: 03 November 2014 +Last updated: 08 November 2014 Copyright (c) 1997-2014 University of Cambridge. .fi diff --git a/src/pcre2.h.in b/src/pcre2.h.in index 6e93a76..0ea211c 100644 --- a/src/pcre2.h.in +++ b/src/pcre2.h.in @@ -125,8 +125,8 @@ D is inspected during pcre2_dfa_match() execution #define PCRE2_JIT_PARTIAL_HARD 0x00000004u /* These are for pcre2_match() and pcre2_dfa_match(). Note that PCRE2_ANCHORED, -PCRE2_NO_START_OPTIMIZE, and PCRE2_NO_UTF_CHECK can also be passed to these -functions, so take care not to define synonyms by mistake. */ +and PCRE2_NO_UTF_CHECK can also be passed to these functions, so take care not +to define synonyms by mistake. */ #define PCRE2_NOTBOL 0x00000001u #define PCRE2_NOTEOL 0x00000002u @@ -140,6 +140,10 @@ functions, so take care not to define synonyms by mistake. */ #define PCRE2_DFA_RESTART 0x00000040u #define PCRE2_DFA_SHORTEST 0x00000080u +/* This is an additional option for pcre2_substitute(). */ + +#define PCRE2_SUBSTITUTE_GLOBAL 0x00000100u + /* Newline and \R settings, for use in compile contexts. The newline values must be kept in step with values set in config.h and both sets must all be greater than zero. */ @@ -406,7 +410,8 @@ PCRE2_EXP_DECL \ pcre2_match_data *pcre2_match_data_create(uint32_t, \ pcre2_general_context *); \ PCRE2_EXP_DECL \ - pcre2_match_data *pcre2_match_data_create_from_pattern(pcre2_code *, \ + pcre2_match_data *pcre2_match_data_create_from_pattern(\ + const pcre2_code *, \ pcre2_general_context *); \ PCRE2_EXP_DECL int pcre2_dfa_match(const pcre2_code *, PCRE2_SPTR, \ PCRE2_SIZE, PCRE2_SIZE, uint32_t, \ @@ -447,6 +452,16 @@ PCRE2_EXP_DECL int pcre2_substring_list_get(pcre2_match_data *, \ PCRE2_UCHAR ***, PCRE2_SIZE **); +/* Convenience function for match + substitute. */ + +#define PCRE2_SUBSTITUTE_FUNCTION \ +PCRE2_EXP_DECL int pcre2_substitute(const pcre2_code *, \ + PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE, uint32_t, \ + pcre2_match_data *, pcre2_match_context *, \ + PCRE2_SPTR, PCRE2_SIZE, PCRE2_UCHAR *, \ + PCRE2_SIZE *); + + /* Functions for JIT processing */ #define PCRE2_JIT_FUNCTIONS \ @@ -550,6 +565,7 @@ pcre2_compile are called by application code. */ #define pcre2_set_parens_nest_limit PCRE2_SUFFIX(pcre2_set_parens_nest_limit_) #define pcre2_set_recursion_limit PCRE2_SUFFIX(pcre2_set_recursion_limit_) #define pcre2_set_recursion_memory_management PCRE2_SUFFIX(pcre2_set_recursion_memory_management_) +#define pcre2_substitute PCRE2_SUFFIX(pcre2_substitute_) #define pcre2_substring_copy_byname PCRE2_SUFFIX(pcre2_substring_copy_byname_) #define pcre2_substring_copy_bynumber PCRE2_SUFFIX(pcre2_substring_copy_bynumber_) #define pcre2_substring_free PCRE2_SUFFIX(pcre2_substring_free_) @@ -576,6 +592,7 @@ PCRE2_MATCH_CONTEXT_FUNCTIONS \ PCRE2_COMPILE_FUNCTIONS \ PCRE2_PATTERN_INFO_FUNCTIONS \ PCRE2_MATCH_FUNCTIONS \ +PCRE2_SUBSTITUTE_FUNCTION \ PCRE2_SUBSTRING_FUNCTIONS \ PCRE2_JIT_FUNCTIONS \ PCRE2_OTHER_FUNCTIONS diff --git a/src/pcre2_match_data.c b/src/pcre2_match_data.c index 473142e..2182326 100644 --- a/src/pcre2_match_data.c +++ b/src/pcre2_match_data.c @@ -73,7 +73,7 @@ return yield; *************************************************/ PCRE2_EXP_DEFN pcre2_match_data * PCRE2_CALL_CONVENTION -pcre2_match_data_create_from_pattern(pcre2_code *code, +pcre2_match_data_create_from_pattern(const pcre2_code *code, pcre2_general_context *gcontext) { return pcre2_match_data_create(((pcre2_real_code *)code)->top_bracket + 1, diff --git a/src/pcre2_substitute.c b/src/pcre2_substitute.c new file mode 100644 index 0000000..7bc6da9 --- /dev/null +++ b/src/pcre2_substitute.c @@ -0,0 +1,269 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Original API code Copyright (c) 1997-2012 University of Cambridge + New API code Copyright (c) 2014 University of Cambridge + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pcre2_internal.h" + + +/************************************************* +* Match and substitute * +*************************************************/ + +/* This function applies a compiled re to a subject string and creates a new +string with substitutione. The first 7 arguments are the same as for +pcre2_match(). Either string length may be PCRE2_ZERO_TERMINATED. + +Arguments: + code points to the compiled expression + subject points to the subject string + length length of subject string (may contain binary zeros) + start_offset where to start in the subject string + options option bits + match_data points to a match_data block, or is NULL + context points a PCRE2 context + replacement points to the replacement string + rlength length of replacement string + buffer where to put the substituted string + blength points to length of buffer; updated to length of string + +Returns: > 0 number of substitutions made + < 0 an error code, including PCRE2_ERROR_NOMATCH if no match +*/ + +PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION +pcre2_substitute(const pcre2_code *code, PCRE2_SPTR subject, PCRE2_SIZE length, + PCRE2_SIZE start_offset, uint32_t options, pcre2_match_data *match_data, + pcre2_match_context *mcontext, PCRE2_SPTR replacement, PCRE2_SIZE rlength, + PCRE2_UCHAR *buffer, PCRE2_SIZE *blength) +{ +int rc = 0; +int subs = 0; +uint32_t ovector_count; +uint32_t goptions = 0; +BOOL match_data_created = FALSE; +BOOL global = FALSE; +PCRE2_SIZE buff_offset, lengthleft, endlength; +PCRE2_SIZE *ovector; + +/* If no match data block is provided, create one. */ + +if (match_data == NULL) + { + pcre2_general_context *gcontext = (mcontext == NULL)? + (pcre2_general_context *)code : + (pcre2_general_context *)mcontext; + match_data = pcre2_match_data_create_from_pattern(code, gcontext); + if (match_data == NULL) return PCRE2_ERROR_NOMEMORY; + match_data_created = TRUE; + } +ovector = pcre2_get_ovector_pointer(match_data); +ovector_count = pcre2_get_ovector_count(match_data); + +/* Notice the global option and remove it from the options that are passed to +pcre2_match(). */ + +if ((options & PCRE2_SUBSTITUTE_GLOBAL) != 0) + { + options &= ~PCRE2_SUBSTITUTE_GLOBAL; + global = TRUE; + } + +/* Find lengths of zero-terminated strings. */ + +if (length == PCRE2_ZERO_TERMINATED) length = PRIV(strlen)(subject); +if (rlength == PCRE2_ZERO_TERMINATED) rlength = PRIV(strlen)(replacement); + +/* Copy up to the start offset */ + +if (start_offset > *blength) goto NOROOM; +memcpy(buffer, subject, start_offset * (PCRE2_CODE_UNIT_WIDTH/8)); +buff_offset = start_offset; +lengthleft = *blength - start_offset; + +/* Loop for global substituting. */ + +do + { + PCRE2_SIZE i; + + rc = pcre2_match(code, subject, length, start_offset, options|goptions, + match_data, mcontext); + + if (rc < 0) + { + if (goptions == 0 || rc != PCRE2_ERROR_NOMATCH || start_offset >= length) + break; + start_offset++; + if ((code->overall_options & PCRE2_UTF) != 0) + { +#if PCRE2_CODE_UNIT_WIDTH == 8 + while (start_offset < length && (subject[start_offset] & 0xc0) == 0x80) + start_offset++; +#elif PCRE2_CODE_UNIT_WIDTH == 16 + while (start_offset < length && + (subject[start_offset] & 0xfc00) == 0xdc00) + start_offset++; +#endif + } + goptions = 0; + continue; + } + + subs++; + if (rc == 0) rc = ovector_count; + endlength = ovector[0] - start_offset; + if (endlength >= lengthleft) goto NOROOM; + memcpy(buffer + buff_offset, subject + start_offset, + endlength*(PCRE2_CODE_UNIT_WIDTH/8)); + buff_offset += endlength; + lengthleft -= endlength; + + for (i = 0; i < rlength; i++) + { + if (replacement[i] == CHAR_DOLLAR_SIGN && i != rlength - 1) + { + int group = -1; + int n = 0; + BOOL inparens = FALSE; + PCRE2_SIZE j = i + 1; + PCRE2_SIZE sublength; + PCRE2_UCHAR next = replacement[j]; + PCRE2_UCHAR name[33]; + + if (next == CHAR_LEFT_CURLY_BRACKET) + { + if (j == rlength - 1) goto LITERAL; + inparens = TRUE; + next = replacement[++j]; + } + + if (next >= CHAR_0 && next <= CHAR_9) + { + group = next - CHAR_0; + while (j < rlength - 1) + { + next = replacement[++j]; + if (next < CHAR_0 || next > CHAR_9) break; + group = group * 10 + next - CHAR_0; + } + } + else + { + const uint8_t *ctypes = code->tables + ctypes_offset; + while (MAX_255(next) && (ctypes[next] & ctype_word) != 0) + { + name[n++] = next; + if (n > 32) goto LITERAL; + if (j == rlength - 1) break; + next = replacement[++j]; + } + name[n] = 0; + } + + if (inparens) + { + if (j == rlength || next != CHAR_RIGHT_CURLY_BRACKET) goto LITERAL; + } + else j--; /* Last code unit of name/number */ + + /* Have found a syntactically correct group number or name. */ + + i = j; /* Where to continue from */ + + if (group < 0) + rc = pcre2_substring_copy_byname(match_data, name, + buffer + buff_offset, &sublength); + else + rc = pcre2_substring_copy_bynumber(match_data, group, + buffer + buff_offset, &sublength); + + if (rc < 0) goto EXIT; + buff_offset += sublength; + lengthleft -= sublength; + } + + /* Handle a literal code unit */ + + else + { + LITERAL: + if (lengthleft-- < 1) goto NOROOM; + buffer[buff_offset++] = replacement[i]; + } + } + + /* The replacement has been copied to the output. Update the start offset to + point to the rest of the subject string. If we matched an empty string, + do the magic for global matches. */ + + start_offset = ovector[1]; + goptions = (ovector[0] != ovector[1])? 0 : + PCRE2_ANCHORED|PCRE2_NOTEMPTY_ATSTART; + } while (global); /* Repeat "do" loop */ + +/* No match is a "normal" end; copy the rest of the subject and return the +number of substitutions. */ + +if (rc == PCRE2_ERROR_NOMATCH) + { + rc = subs; + endlength = length - start_offset; + if (endlength + 1 >= lengthleft) goto NOROOM; + memcpy(buffer + buff_offset, subject + start_offset, + endlength*(PCRE2_CODE_UNIT_WIDTH/8)); + buff_offset += endlength; + buffer[buff_offset] = 0; + *blength = buff_offset; + } + +EXIT: +if (match_data_created) pcre2_match_data_free(match_data); +return rc; + +NOROOM: +rc = PCRE2_ERROR_NOMEMORY; +goto EXIT; +} + +/* End of pcre2_substitute.c */ diff --git a/src/pcre2_substring.c b/src/pcre2_substring.c index c4d67c1..05548ad 100644 --- a/src/pcre2_substring.c +++ b/src/pcre2_substring.c @@ -472,7 +472,8 @@ Arguments: stringname the name whose number is required Returns: the number of the named parenthesis, or a negative number - (PCRE2_ERROR_NOSUBSTRING) if not found + PCRE2_ERROR_NOSUBSTRING if not found + PCRE2_ERROR_NOUNIQUESUBSTRING if not unique */ PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION