Showing the changes to src (35 of 35 files). Show all
Unified · Split
| @@ -54,11 +54,6 @@ · 53 unmodified lines | |||
| 54 | #include "ulib_opsc.h" | 54 | #include "ulib_opsc.h" |
| 55 | #endif | 55 | #endif |
| 56 | 56 | ||
| 57 | // 1.5.0.0 (ge) | chunreal | ||
| 58 | #ifndef __DISABLE_REGEX__ | ||
| 59 | #include "ulib_regex.h" | ||
| 60 | #endif | ||
| 61 | |||
| 62 | #if defined(__PLATFORM_WIN32__) | 57 | #if defined(__PLATFORM_WIN32__) |
| 63 | #include "dirent_win32.h" | 58 | #include "dirent_win32.h" |
| 64 | #else | 59 | #else |
| @@ -706,11 +701,6 @@ t_CKBOOL load_internal_modules( Chuck_Compiler * compiler ) · 641 unmodified lines | |||
| 706 | if( !load_module( compiler, env, opensoundcontrol_query, "opsc", "global" ) ) goto error; | 701 | if( !load_module( compiler, env, opensoundcontrol_query, "opsc", "global" ) ) goto error; |
| 707 | #endif | 702 | #endif |
| 708 | 703 | ||
| 709 | #ifndef __DISABLE_REGEX__ | ||
| 710 | EM_log( CK_LOG_SEVERE, "module 'RegEx'" ); | ||
| 711 | if( !load_module( compiler, env, regex_query, "RegEx", "global" ) ) goto error; | ||
| 712 | #endif | ||
| 713 | |||
| 714 | // deprecated: | 704 | // deprecated: |
| 715 | // if( !load_module( compiler, env, net_query, "net", "global" ) ) goto error; | 705 | // if( !load_module( compiler, env, net_query, "net", "global" ) ) goto error; |
| 716 | 706 | ||
| @@ -83,7 +83,7 @@ CXXSRCS_CORE+= chuck_absyn.cpp chuck_parse.cpp chuck_errmsg.cpp \ · 82 unmodified lines | |||
| 83 | midiio_rtmidi.cpp rtmidi.cpp hidio_sdl.cpp \ | 83 | midiio_rtmidi.cpp rtmidi.cpp hidio_sdl.cpp \ |
| 84 | ugen_osc.cpp ugen_filter.cpp ugen_stk.cpp ugen_xxx.cpp \ | 84 | ugen_osc.cpp ugen_filter.cpp ugen_stk.cpp ugen_xxx.cpp \ |
| 85 | ulib_ai.cpp ulib_doc.cpp ulib_machine.cpp ulib_math.cpp \ | 85 | ulib_ai.cpp ulib_doc.cpp ulib_machine.cpp ulib_math.cpp \ |
| 86 | ulib_std.cpp ulib_opsc.cpp ulib_regex.cpp util_buffers.cpp \ | 86 | ulib_std.cpp ulib_opsc.cpp util_buffers.cpp \ |
| 87 | util_console.cpp util_string.cpp util_thread.cpp util_opsc.cpp \ | 87 | util_console.cpp util_string.cpp util_thread.cpp util_opsc.cpp \ |
| 88 | util_serial.cpp util_hid.cpp uana_xform.cpp uana_extract.cpp | 88 | util_serial.cpp util_hid.cpp uana_xform.cpp uana_extract.cpp |
| 89 | LO_CSRCS+= lo/address.c lo/blob.c lo/bundle.c lo/message.c lo/method.c \ | 89 | LO_CSRCS+= lo/address.c lo/blob.c lo/bundle.c lo/message.c lo/method.c \ |
| @@ -1,63 +1,63 @@ | |||
| 1 | # NOTE: chuck_win32.h and chuck_win32.c come from the output of | 1 | # NOTE: chuck_win32.h and chuck_win32.c come from the output of |
| 2 | # chuck.lex and chuck.y | 2 | # chuck.lex and chuck.y |
| 3 | # | 3 | # |
| 4 | # > flex -ochuck.yy.c chuck.lex | 4 | # > flex -ochuck.yy.c chuck.lex |
| 5 | # > bison -dv -b chuck chuck.y | 5 | # > bison -dv -b chuck chuck.y |
| 6 | # | 6 | # |
| 7 | # to create the chuck_win32 files: | 7 | # to create the chuck_win32 files: |
| 8 | # | 8 | # |
| 9 | # > cat chuck.tab.c chuck.yy.c > chuck_win32.c | 9 | # > cat chuck.tab.c chuck.yy.c > chuck_win32.c |
| 10 | # > cp chuck.tab.h chuck_win32.h | 10 | # > cp chuck.tab.h chuck_win32.h |
| 11 | # | 11 | # |
| 12 | # ALSO: we are actually now building in 64-bit, but yeah. | 12 | # ALSO: we are actually now building in 64-bit, but yeah. |
| 13 | 13 | ||
| 14 | MSBUILD=msbuild.exe | 14 | MSBUILD=msbuild.exe |
| 15 | RTAUDIODIR=rtaudio | 15 | RTAUDIODIR=rtaudio |
| 16 | 16 | ||
| 17 | ifneq (,$(CHUCK_DEBUG)) | 17 | ifneq (,$(CHUCK_DEBUG)) |
| 18 | CONFIG=Debug | 18 | CONFIG=Debug |
| 19 | CHUCK_BIN=Debug/chuck.exe | 19 | CHUCK_BIN=Debug/chuck.exe |
| 20 | else | 20 | else |
| 21 | CONFIG=Release | 21 | CONFIG=Release |
| 22 | CHUCK_BIN=Release/chuck.exe | 22 | CHUCK_BIN=Release/chuck.exe |
| 23 | endif | 23 | endif |
| 24 | 24 | ||
| 25 | CSRCS+= chuck_win32.c util_math.c util_network.c util_raw.c \ | 25 | CSRCS+= chuck_win32.c util_math.c util_network.c util_raw.c \ |
| 26 | util_xforms.c | 26 | util_xforms.c |
| 27 | CXXSRCS+= chuck_absyn.cpp chuck_parse.cpp chuck_errmsg.cpp \ | 27 | CXXSRCS+= chuck_absyn.cpp chuck_parse.cpp chuck_errmsg.cpp \ |
| 28 | chuck_frame.cpp chuck_symbol.cpp chuck_table.cpp chuck_utils.cpp \ | 28 | chuck_frame.cpp chuck_symbol.cpp chuck_table.cpp chuck_utils.cpp \ |
| 29 | chuck_vm.cpp chuck_instr.cpp chuck_scan.cpp chuck_type.cpp chuck_emit.cpp \ | 29 | chuck_vm.cpp chuck_instr.cpp chuck_scan.cpp chuck_type.cpp chuck_emit.cpp \ |
| 30 | chuck_compile.cpp chuck_dl.cpp chuck_oo.cpp chuck_lang.cpp chuck_ugen.cpp \ | 30 | chuck_compile.cpp chuck_dl.cpp chuck_oo.cpp chuck_lang.cpp chuck_ugen.cpp \ |
| 31 | chuck_otf.cpp chuck_stats.cpp chuck_shell.cpp \ | 31 | chuck_otf.cpp chuck_stats.cpp chuck_shell.cpp \ |
| 32 | chuck_io.cpp chuck_carrier.cpp chuck.cpp \ | 32 | chuck_io.cpp chuck_carrier.cpp chuck.cpp \ |
| 33 | hidio_sdl.cpp \ | 33 | hidio_sdl.cpp \ |
| 34 | midiio_rtmidi.cpp rtmidi.cpp ugen_osc.cpp ugen_filter.cpp \ | 34 | midiio_rtmidi.cpp rtmidi.cpp ugen_osc.cpp ugen_filter.cpp \ |
| 35 | ugen_stk.cpp ugen_xxx.cpp ulib_machine.cpp ulib_math.cpp ulib_std.cpp \ | 35 | ugen_stk.cpp ugen_xxx.cpp ulib_machine.cpp ulib_math.cpp ulib_std.cpp \ |
| 36 | ulib_opsc.cpp ulib_regex.cpp util_buffers.cpp util_console.cpp \ | 36 | ulib_opsc.cpp util_buffers.cpp util_console.cpp \ |
| 37 | util_string.cpp util_thread.cpp util_opsc.cpp util_serial.cpp \ | 37 | util_string.cpp util_thread.cpp util_opsc.cpp util_serial.cpp \ |
| 38 | util_hid.cpp uana_xform.cpp uana_extract.cpp | 38 | util_hid.cpp uana_xform.cpp uana_extract.cpp |
| 39 | CXXINC_HOST+= -I${RTAUDIODIR} -I${RTAUDIODIR}/include | 39 | CXXINC_HOST+= -I${RTAUDIODIR} -I${RTAUDIODIR}/include |
| 40 | CXXSRCS_HOST+= chuck_main.cpp chuck_audio.cpp chuck_console.cpp ../${RTAUDIODIR}/RtAudio.cpp | 40 | CXXSRCS_HOST+= chuck_main.cpp chuck_audio.cpp chuck_console.cpp ../${RTAUDIODIR}/RtAudio.cpp |
| 41 | LO_CSRCS+= lo/address.c lo/blob.c lo/bundle.c lo/message.c lo/method.c \ | 41 | LO_CSRCS+= lo/address.c lo/blob.c lo/bundle.c lo/message.c lo/method.c \ |
| 42 | lo/pattern_match.c lo/send.c lo/server.c lo/server_thread.c lo/timetag.c | 42 | lo/pattern_match.c lo/send.c lo/server.c lo/server_thread.c lo/timetag.c |
| 43 | 43 | ||
| 44 | CSRCS:=$(addprefix core/,$(CSRCS)) | 44 | CSRCS:=$(addprefix core/,$(CSRCS)) |
| 45 | CXXSRCS:=$(addprefix core/,$(CXXSRCS)) | 45 | CXXSRCS:=$(addprefix core/,$(CXXSRCS)) |
| 46 | CXXSRCS_HOST:=$(addprefix host/,$(CXXSRCS_HOST)) | 46 | CXXSRCS_HOST:=$(addprefix host/,$(CXXSRCS_HOST)) |
| 47 | LO_CSRCS:=$(addprefix core/,$(LO_CSRCS)) | 47 | LO_CSRCS:=$(addprefix core/,$(LO_CSRCS)) |
| 48 | 48 | ||
| 49 | CXXOBJS_HOST=$(addprefix $(HOSTDIR)/,$(CXXSRCS_HOST:.cpp=.o)) | 49 | CXXOBJS_HOST=$(addprefix $(HOSTDIR)/,$(CXXSRCS_HOST:.cpp=.o)) |
| 50 | 50 | ||
| 51 | COBJS=$(COBJS_HOST) $(addprefix $(COREDIR)/,$(COBJS_CORE)) | 51 | COBJS=$(COBJS_HOST) $(addprefix $(COREDIR)/,$(COBJS_CORE)) |
| 52 | CXXOBJS=$(CXXOBJS_HOST) $(addprefix $(COREDIR)/,$(CXXOBJS_CORE)) | 52 | CXXOBJS=$(CXXOBJS_HOST) $(addprefix $(COREDIR)/,$(CXXOBJS_CORE)) |
| 53 | LO_COBJS=$(addprefix $(COREDIR)/,$(LO_COBJS_CORE)) | 53 | LO_COBJS=$(addprefix $(COREDIR)/,$(LO_COBJS_CORE)) |
| 54 | SF_COBJS=$(addprefix $(COREDIR)/,$(SF_CSRCS:.c=.o)) | 54 | SF_COBJS=$(addprefix $(COREDIR)/,$(SF_CSRCS:.c=.o)) |
| 55 | OBJS=$(COBJS) $(CXXOBJS) $(LO_COBJS) $(SF_COBJS) | 55 | OBJS=$(COBJS) $(CXXOBJS) $(LO_COBJS) $(SF_COBJS) |
| 56 | SRCS=$(CSRCS) $(CXXSRCS) $(CXXSRCS_HOST) $(LO_CSRCS) | 56 | SRCS=$(CSRCS) $(CXXSRCS) $(CXXSRCS_HOST) $(LO_CSRCS) |
| 57 | 57 | ||
| 58 | $(CHUCK_BIN): $(SRCS) | 58 | $(CHUCK_BIN): $(SRCS) |
| 59 | $(MSBUILD) visual-studio/chuck.sln /p:Configuration=$(CONFIG) | 59 | $(MSBUILD) visual-studio/chuck.sln /p:Configuration=$(CONFIG) |
| 60 | 60 | ||
| 61 | clean: | 61 | clean: |
| 62 | $(MSBUILD) visual-studio/chuck.sln /p:Configuration=$(CONFIG) /t:Clean | 62 | $(MSBUILD) visual-studio/chuck.sln /p:Configuration=$(CONFIG) /t:Clean |
| 63 | 63 | ||
| @@ -1,29 +0,0 @@ | |||
| 1 | This is the license, copyright notice, and disclaimer for TRE, a regex | ||
| 2 | matching package (library and tools) with support for approximate | ||
| 3 | matching. | ||
| 4 | |||
| 5 | Copyright (c) 2001-2009 Ville Laurikari <vl@iki.fi> | ||
| 6 | All rights reserved. | ||
| 7 | |||
| 8 | Redistribution and use in source and binary forms, with or without | ||
| 9 | modification, are permitted provided that the following conditions | ||
| 10 | are met: | ||
| 11 | |||
| 12 | 1. Redistributions of source code must retain the above copyright | ||
| 13 | notice, this list of conditions and the following disclaimer. | ||
| 14 | |||
| 15 | 2. Redistributions in binary form must reproduce the above copyright | ||
| 16 | notice, this list of conditions and the following disclaimer in the | ||
| 17 | documentation and/or other materials provided with the distribution. | ||
| 18 | |||
| 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS | ||
| 20 | ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| 21 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
| 22 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| 23 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| 24 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
| 25 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
| 26 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
| 27 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| 28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| @@ -1,235 +0,0 @@ | |||
| 1 | Introduction | ||
| 2 | |||
| 3 | TRE is a lightweight, robust, and efficient POSIX compliant regexp | ||
| 4 | matching library with some exciting features such as approximate | ||
| 5 | (fuzzy) matching. | ||
| 6 | |||
| 7 | The matching algorithm used in TRE uses linear worst-case time in | ||
| 8 | the length of the text being searched, and quadratic worst-case | ||
| 9 | time in the length of the used regular expression. In other words, | ||
| 10 | the time complexity of the algorithm is O(M^2N), where M is the | ||
| 11 | length of the regular expression and N is the length of the | ||
| 12 | text. The used space is also quadratic on the length of the regex, | ||
| 13 | but does not depend on the searched string. This quadratic | ||
| 14 | behaviour occurs only on pathological cases which are probably very | ||
| 15 | rare in practice. | ||
| 16 | |||
| 17 | Features | ||
| 18 | |||
| 19 | TRE is not just yet another regexp matcher. TRE has some features | ||
| 20 | which are not there in most free POSIX compatible | ||
| 21 | implementations. Most of these features are not present in non-free | ||
| 22 | implementations either, for that matter. | ||
| 23 | |||
| 24 | Approximate matching | ||
| 25 | |||
| 26 | Approximate pattern matching allows matches to be approximate, that | ||
| 27 | is, allows the matches to be close to the searched pattern under | ||
| 28 | some measure of closeness. TRE uses the edit-distance measure (also | ||
| 29 | known as the Levenshtein distance) where characters can be | ||
| 30 | inserted, deleted, or substituted in the searched text in order to | ||
| 31 | get an exact match. Each insertion, deletion, or substitution adds | ||
| 32 | the distance, or cost, of the match. TRE can report the matches | ||
| 33 | which have a cost lower than some given threshold value. TRE can | ||
| 34 | also be used to search for matches with the lowest cost. | ||
| 35 | |||
| 36 | TRE includes a version of the agrep (approximate grep) command line | ||
| 37 | tool for approximate regexp matching in the style of grep. Unlike | ||
| 38 | other agrep implementations (like the one by Sun Wu and Udi Manber | ||
| 39 | from University of Arizona available here) TRE agrep allows full | ||
| 40 | regexps of any length, any number of errors, and non-uniform costs | ||
| 41 | for insertion, deletion and substitution. | ||
| 42 | |||
| 43 | Strict standard conformance | ||
| 44 | |||
| 45 | POSIX defines the behaviour of regexp functions precisely. TRE | ||
| 46 | attempts to conform to these specifications as strictly as | ||
| 47 | possible. TRE always returns the correct matches for subpatterns, | ||
| 48 | for example. Very few other implementations do this correctly. In | ||
| 49 | fact, the only other implementations besides TRE that I am aware of | ||
| 50 | (free or not) that get it right are Rx by Tom Lord, Regex++ by John | ||
| 51 | Maddock, and the AT&T ast regex by Glenn Fowler and Doug McIlroy. | ||
| 52 | |||
| 53 | The standard TRE tries to conform to is the IEEE Std 1003.1-2001, | ||
| 54 | or Open Group Base Specifications Issue 6, commonly referred to as | ||
| 55 | "POSIX". It can be found online here. The relevant parts are the | ||
| 56 | base specifications on regular expressions (and the rationale) and | ||
| 57 | the description of the regcomp() API. | ||
| 58 | |||
| 59 | For an excellent survey on POSIX regexp matchers, see the testregex | ||
| 60 | pages by Glenn Fowler of AT&T Labs Research. | ||
| 61 | |||
| 62 | Predictable matching speed | ||
| 63 | |||
| 64 | Because of the matching algorithm used in TRE, the maximum time | ||
| 65 | consumed by any regexec() call is always directly proportional to | ||
| 66 | the length of the searched string. There is one exception: if back | ||
| 67 | references are used, the matching may take time that grows | ||
| 68 | exponentially with the length of the string. This is because | ||
| 69 | matching back references is an NP complete problem, and almost | ||
| 70 | certainly requires exponential time to match in the worst case. | ||
| 71 | |||
| 72 | Predictable and modest memory consumption | ||
| 73 | |||
| 74 | A regexec() call never allocates memory from the heap. TRE | ||
| 75 | allocates all the memory it needs during a regcomp() call, and some | ||
| 76 | temporary working space from the stack frame for the duration of | ||
| 77 | the regexec() call. The amount of temporary space needed is | ||
| 78 | constant during matching and does not depend on the searched | ||
| 79 | string. For regexps of reasonable size TRE needs less than 50K of | ||
| 80 | dynamically allocated memory during the regcomp() call, less than | ||
| 81 | 20K for the compiled pattern buffer, and less than two kilobytes of | ||
| 82 | temporary working space from the stack frame during a regexec() | ||
| 83 | call. There is no time/memory tradeoff. TRE is also small in code | ||
| 84 | size; statically linking with TRE increases the executable size | ||
| 85 | less than 30K (gcc-3.2, x86, GNU/Linux). | ||
| 86 | |||
| 87 | Wide character and multibyte character set support | ||
| 88 | |||
| 89 | TRE supports multibyte character sets. This makes it possible to | ||
| 90 | use regexps seamlessly with, for example, Japanese locales. TRE | ||
| 91 | also provides a wide character API. | ||
| 92 | |||
| 93 | Binary pattern and data support | ||
| 94 | |||
| 95 | TRE provides APIs which allow binary zero characters both in | ||
| 96 | regexps and searched strings. The standard API cannot be easily | ||
| 97 | used to, for example, search for printable words from binary data | ||
| 98 | (although it is possible with some hacking). Searching for patterns | ||
| 99 | which contain binary zeroes embedded is not possible at all with | ||
| 100 | the standard API. | ||
| 101 | |||
| 102 | Completely thread safe | ||
| 103 | |||
| 104 | TRE is completely thread safe. All the exported functions are | ||
| 105 | re-entrant, and a single compiled regexp object can be used | ||
| 106 | simultaneously in multiple contexts; e.g. in main() and a signal | ||
| 107 | handler, or in many threads of a multithreaded application. | ||
| 108 | |||
| 109 | Portable | ||
| 110 | |||
| 111 | TRE is portable across multiple platforms. Here's a table of | ||
| 112 | platforms and compilers that have been successfully used to compile | ||
| 113 | and run TRE: | ||
| 114 | |||
| 115 | Platform(s) | Compiler(s) | ||
| 116 | ----------------------------------+------------ | ||
| 117 | AIX 4.3.2 - 5.3.0 | GCC, C for AIX compiler version 5 | ||
| 118 | Compaq Tru64 UNIX V5.1A/B | Compaq C V6.4-014 - V6.5-011 | ||
| 119 | Cygwin 1.3 - 1.5 | GCC | ||
| 120 | Digital UNIX V4.0 | DEC C V5.9-005 | ||
| 121 | FreeBSD 4 and above | GCC | ||
| 122 | GNU/Linux systems on x86, x86_64, | GCC | ||
| 123 | ppc64, s390 | | ||
| 124 | HP-UX 10.20- 11.00 | GCC, HP C Compiler | ||
| 125 | IRIX 6.5 | GCC, MIPSpro Compilers 7.3.1.3m | ||
| 126 | Max OS X | | ||
| 127 | NetBSD 1.5 and above | GCC, egcs | ||
| 128 | OpenBSD 3.3 and above | GCC | ||
| 129 | Solaris 2.7-10 sparc/x86 | GCC, Sun Workshop 6 compilers | ||
| 130 | Windows 98 - XP | Microsoft Visual C++ 6.0 | ||
| 131 | |||
| 132 | TRE 0.7.5 should compile without changes on all of the above | ||
| 133 | platforms. Tell me if you are using TRE on a platform that is not | ||
| 134 | listed above, and I'll add it to the list. Also let me know if TRE | ||
| 135 | does not work on a listed platform. | ||
| 136 | |||
| 137 | Depending on the platform, you may need to install libutf8 to get | ||
| 138 | wide character and multibyte character set support. | ||
| 139 | |||
| 140 | Free | ||
| 141 | |||
| 142 | TRE is released under a license which is essentially the same as | ||
| 143 | the "2 clause" BSD-style license used in NetBSD. See the file | ||
| 144 | LICENSE for details. | ||
| 145 | |||
| 146 | Roadmap | ||
| 147 | |||
| 148 | There are currently two features, both related to collating | ||
| 149 | elements, missing from 100% POSIX compliance. These are: | ||
| 150 | |||
| 151 | * Support for collating elements (e.g. [[.<X>.]], where <X> is a | ||
| 152 | collating element). It is not possible to support | ||
| 153 | multi-character collating elements portably, since POSIX does | ||
| 154 | not define a way to determine whether a character sequence is a | ||
| 155 | multi-character collating element or not. | ||
| 156 | |||
| 157 | * Support for equivalence classes, for example [[=<X>=]], where | ||
| 158 | <X> is a collating element. An equivalence class matches any | ||
| 159 | character which has the same primary collation weight as | ||
| 160 | <X>. Again, POSIX provides no portable mechanism for | ||
| 161 | determining the primary collation weight of a collating | ||
| 162 | element. | ||
| 163 | |||
| 164 | Note that other portable regexp implementations don't support | ||
| 165 | collating elements either. The single exception is Regex++, which | ||
| 166 | comes with its own database for collating elements for different | ||
| 167 | locales. Support for collating elements and equivalence classes has | ||
| 168 | not been widely requested and is not very high on the TODO list at | ||
| 169 | the moment. | ||
| 170 | |||
| 171 | These are other features I'm planning to implement real soon now: | ||
| 172 | |||
| 173 | * All the missing GNU extensions enabled in GNU regex, such as | ||
| 174 | [[:<:]] and [[:>:]] | ||
| 175 | |||
| 176 | * A REG_SHORTEST regexec() flag for returning the shortest match | ||
| 177 | instead of the longest match. | ||
| 178 | |||
| 179 | * Perl-compatible syntax | ||
| 180 | |||
| 181 | [:^class:] | ||
| 182 | Matches anything but the characters in class. Note that | ||
| 183 | [^[:class:]] works already, this would be just a | ||
| 184 | convenience shorthand. | ||
| 185 | |||
| 186 | \A | ||
| 187 | Match only at beginning of string | ||
| 188 | |||
| 189 | \Z | ||
| 190 | Match only at end of string, or before newline at the end | ||
| 191 | |||
| 192 | \z | ||
| 193 | Match only at end of string | ||
| 194 | |||
| 195 | \l | ||
| 196 | Lowercase next char (think vi) | ||
| 197 | |||
| 198 | \u | ||
| 199 | Uppercase next char (think vi) | ||
| 200 | |||
| 201 | \L | ||
| 202 | Lowercase till \E (think vi) | ||
| 203 | |||
| 204 | \U | ||
| 205 | Uppercase till \E (think vi) | ||
| 206 | |||
| 207 | (?=pattern) | ||
| 208 | Zero-width positive look-ahead assertions. | ||
| 209 | |||
| 210 | (?!pattern) | ||
| 211 | Zero-width negative look-ahead assertions. | ||
| 212 | |||
| 213 | (?<=pattern) | ||
| 214 | Zero-width positive look-behind assertions. | ||
| 215 | |||
| 216 | (?<!pattern) | ||
| 217 | Zero-width negative look-behind assertions. | ||
| 218 | |||
| 219 | Documentation especially for the nonstandard features of TRE, such | ||
| 220 | as approximate matching, is a work in progress (with "progress" | ||
| 221 | loosely defined...) | ||
| 222 | |||
| 223 | Mailing lists | ||
| 224 | |||
| 225 | tre-general@lists.laurikari.net | ||
| 226 | This list is for any discussion on the TRE software, including | ||
| 227 | reporting bugs, feature requests, requests for help, and other | ||
| 228 | things. | ||
| 229 | |||
| 230 | tre-announce@lists.laurikari.net | ||
| 231 | Subscribe to this list to get announcements of new releases of | ||
| 232 | TRE. Alternatively, you can subscribe to the freshmeat.net | ||
| 233 | project and get similar announcements that way, if you prefer. | ||
| 234 | |||
| 235 | Ville Laurikari <vl@iki.fi> | ||
| @@ -1,186 +0,0 @@ | |||
| 1 | /* config.h for Windows. */ | ||
| 2 | |||
| 3 | /* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP | ||
| 4 | systems. This function is required for `alloca.c' support on those systems. | ||
| 5 | */ | ||
| 6 | /* #undef CRAY_STACKSEG_END */ | ||
| 7 | |||
| 8 | /* Define to 1 if using `alloca.c'. */ | ||
| 9 | /* #undef C_ALLOCA */ | ||
| 10 | |||
| 11 | /* Define to 1 if translation of program messages to the user's native | ||
| 12 | language is requested. */ | ||
| 13 | /* #undef ENABLE_NLS */ | ||
| 14 | |||
| 15 | /* Define to 1 if you have `alloca', as a function or macro. */ | ||
| 16 | #define HAVE_ALLOCA 1 | ||
| 17 | |||
| 18 | /* Define to 1 if you have <alloca.h> and it should be used (not on Ultrix). | ||
| 19 | */ | ||
| 20 | /* #undef HAVE_ALLOCA_H */ | ||
| 21 | |||
| 22 | /* Define to 1 if you have <malloc.h> and it should be used. */ | ||
| 23 | #define HAVE_MALLOC_H 1 | ||
| 24 | |||
| 25 | /* Define if the GNU dcgettext() function is already present or preinstalled. | ||
| 26 | */ | ||
| 27 | /* #undef HAVE_DCGETTEXT */ | ||
| 28 | |||
| 29 | /* Define to 1 if you have the <dlfcn.h> header file. */ | ||
| 30 | #undef HAVE_DLFCN_H | ||
| 31 | |||
| 32 | /* Define to 1 if you have the <getopt.h> header file. */ | ||
| 33 | #define HAVE_GETOPT_H 1 | ||
| 34 | |||
| 35 | /* Define to 1 if you have the `getopt_long' function. */ | ||
| 36 | #define HAVE_GETOPT_LONG 1 | ||
| 37 | |||
| 38 | /* Define if the GNU gettext() function is already present or preinstalled. */ | ||
| 39 | #undef HAVE_GETTEXT | ||
| 40 | |||
| 41 | /* Define if you have the iconv() function. */ | ||
| 42 | #undef HAVE_ICONV | ||
| 43 | |||
| 44 | /* Define to 1 if you have the <inttypes.h> header file. */ | ||
| 45 | #undef HAVE_INTTYPES_H | ||
| 46 | |||
| 47 | /* Define to 1 if you have the `isascii' function. */ | ||
| 48 | #define HAVE_ISASCII 1 | ||
| 49 | |||
| 50 | /* Define to 1 if you have the `isblank' function. */ | ||
| 51 | /* #undef HAVE_ISBLANK */ | ||
| 52 | |||
| 53 | /* Define to 1 if you have the `iswctype' function. */ | ||
| 54 | #define HAVE_ISWCTYPE 1 | ||
| 55 | |||
| 56 | /* Define to 1 if you have the `iswlower' function. */ | ||
| 57 | #define HAVE_ISWLOWER 1 | ||
| 58 | |||
| 59 | /* Define to 1 if you have the `iswupper' function. */ | ||
| 60 | #define HAVE_ISWUPPER 1 | ||
| 61 | |||
| 62 | /* Define to 1 if you have the `mbrtowc' function. */ | ||
| 63 | #define HAVE_MBRTOWC 1 | ||
| 64 | |||
| 65 | /* Define to 1 if the system has the type `mbstate_t'. */ | ||
| 66 | #define HAVE_MBSTATE_T 1 | ||
| 67 | |||
| 68 | /* Define to 1 if you have the `mbtowc' function. */ | ||
| 69 | #define HAVE_MBTOWC 1 | ||
| 70 | |||
| 71 | /* Define to 1 if you have the <memory.h> header file. */ | ||
| 72 | #undef HAVE_MEMORY_H | ||
| 73 | |||
| 74 | /* Define to 1 if you have the <regex.h> header file. */ | ||
| 75 | #undef HAVE_REGEX_H | ||
| 76 | /* Define to 1 if the system has the type `reg_errcode_t'. */ | ||
| 77 | #undef HAVE_REG_ERRCODE_T | ||
| 78 | |||
| 79 | /* Define to 1 if you have the <stdint.h> header file. */ | ||
| 80 | #define HAVE_STDINT_H 1 | ||
| 81 | |||
| 82 | /* Define to 1 if you have the <stdlib.h> header file. */ | ||
| 83 | #define HAVE_STDLIB_H 1 | ||
| 84 | |||
| 85 | /* Define to 1 if you have the <strings.h> header file. */ | ||
| 86 | #define HAVE_STRINGS_H 1 | ||
| 87 | |||
| 88 | /* Define to 1 if you have the <string.h> header file. */ | ||
| 89 | #define HAVE_STRING_H 1 | ||
| 90 | |||
| 91 | /* Define to 1 if you have the <sys/stat.h> header file. */ | ||
| 92 | #define HAVE_SYS_STAT_H 1 | ||
| 93 | |||
| 94 | /* Define to 1 if you have the <sys/types.h> header file. */ | ||
| 95 | #define HAVE_SYS_TYPES_H 1 | ||
| 96 | |||
| 97 | /* Define to 1 if you have the `towlower' function. */ | ||
| 98 | #undef HAVE_TOWLOWER | ||
| 99 | |||
| 100 | /* Define to 1 if you have the `towupper' function. */ | ||
| 101 | #define HAVE_TOWUPPER 1 | ||
| 102 | |||
| 103 | /* Define to 1 if you have the <unistd.h> header file. */ | ||
| 104 | /* #undef HAVE_UNISTD_H */ | ||
| 105 | |||
| 106 | /* Define to 1 if you have the <wchar.h> header file. */ | ||
| 107 | #define HAVE_WCHAR_H 1 | ||
| 108 | |||
| 109 | /* Define to 1 if the system has the type `wchar_t'. */ | ||
| 110 | #define HAVE_WCHAR_T 1 | ||
| 111 | |||
| 112 | /* Define to 1 if you have the `wcschr' function. */ | ||
| 113 | #define HAVE_WCSCHR 1 | ||
| 114 | |||
| 115 | /* Define to 1 if you have the `wcscpy' function. */ | ||
| 116 | #define HAVE_WCSCPY 1 | ||
| 117 | |||
| 118 | /* Define to 1 if you have the `wcslen' function. */ | ||
| 119 | #define HAVE_WCSLEN 1 | ||
| 120 | |||
| 121 | /* Define to 1 if you have the `wcsncpy' function. */ | ||
| 122 | #define HAVE_WCSNCPY 1 | ||
| 123 | |||
| 124 | /* Define to 1 if you have the `wcsrtombs' function. */ | ||
| 125 | #define HAVE_WCSRTOMBS 1 | ||
| 126 | |||
| 127 | /* Define to 1 if you have the `wcstombs' function. */ | ||
| 128 | /* #undef HAVE_WCSTOMBS */ | ||
| 129 | |||
| 130 | /* Define to 1 if you have the `wctype' function. */ | ||
| 131 | #define HAVE_WCTYPE 1 | ||
| 132 | |||
| 133 | /* Define to 1 if you have the <wctype.h> header file. */ | ||
| 134 | #define HAVE_WCTYPE_H 1 | ||
| 135 | |||
| 136 | /* Define if you want to disable debug assertions. */ | ||
| 137 | /* #undef NDEBUG */ | ||
| 138 | |||
| 139 | /* Define to 1 if you have the ANSI C header files. */ | ||
| 140 | #define STDC_HEADERS 1 | ||
| 141 | |||
| 142 | /* Define if you want to enable approximate matching functionality. */ | ||
| 143 | #define TRE_APPROX 1 | ||
| 144 | |||
| 145 | /* Define if you want TRE to print debug messages to stdout. */ | ||
| 146 | /* #undef TRE_DEBUG */ | ||
| 147 | |||
| 148 | /* Define to enable multibyte character set support. */ | ||
| 149 | /* #define TRE_MULTIBYTE 1 */ | ||
| 150 | |||
| 151 | /* Define to a field in the regex_t struct where TRE should store a pointer to | ||
| 152 | the internal tre_tnfa_t structure */ | ||
| 153 | #define TRE_REGEX_T_FIELD value | ||
| 154 | |||
| 155 | /* Define to the absolute path to the system regex.h */ | ||
| 156 | /* #undef TRE_SYSTEM_REGEX_H_PATH */ | ||
| 157 | |||
| 158 | /* Define to include the system regex.h from TRE regex.h */ | ||
| 159 | /* #undef TRE_USE_SYSTEM_REGEX_H */ | ||
| 160 | |||
| 161 | /* Define to enable wide character (wchar_t) support. */ | ||
| 162 | /* #define TRE_WCHAR 1 */ | ||
| 163 | |||
| 164 | /* Define to the maximum value of wchar_t if not already defined elsewhere */ | ||
| 165 | /* #undef WCHAR_MAX */ | ||
| 166 | |||
| 167 | /* Define if wchar_t is signed */ | ||
| 168 | /* #undef WCHAR_T_SIGNED */ | ||
| 169 | |||
| 170 | /* Define if wchar_t is unsigned */ | ||
| 171 | #define WCHAR_T_UNSIGNED 1 | ||
| 172 | |||
| 173 | /* Define to empty if `const' does not conform to ANSI C. */ | ||
| 174 | /* #undef const */ | ||
| 175 | |||
| 176 | /* Define as `__inline' if that's what the C compiler calls it, or to nothing | ||
| 177 | if it is not supported. */ | ||
| 178 | #define inline __inline | ||
| 179 | |||
| 180 | /* Avoid silly warnings about "insecure" functions. */ | ||
| 181 | #define _CRT_SECURE_NO_DEPRECATE 1 | ||
| 182 | |||
| 183 | /* #define snprintf sprintf_s */ | ||
| 184 | #ifndef __WINDOWS_MODERN__ | ||
| 185 | #define snprintf _snprintf | ||
| 186 | #endif | ||
| @@ -1,129 +0,0 @@ | |||
| 1 | /* | ||
| 2 | tre_regcomp.c - TRE POSIX compatible regex compilation functions. | ||
| 3 | |||
| 4 | This software is released under a BSD-style license. | ||
| 5 | See the file LICENSE for details and copyright. | ||
| 6 | |||
| 7 | */ | ||
| 8 | |||
| 9 | #ifdef HAVE_CONFIG_H | ||
| 10 | #include <config.h> | ||
| 11 | #endif /* HAVE_CONFIG_H */ | ||
| 12 | |||
| 13 | #include <string.h> | ||
| 14 | #include <errno.h> | ||
| 15 | #include <stdlib.h> | ||
| 16 | |||
| 17 | #include "tre.h" | ||
| 18 | #include "tre-internal.h" | ||
| 19 | #include "xmalloc.h" | ||
| 20 | |||
| 21 | int | ||
| 22 | tre_regncomp(regex_t *preg, const char *regex, size_t n, int cflags) | ||
| 23 | { | ||
| 24 | int ret; | ||
| 25 | #if TRE_WCHAR | ||
| 26 | tre_char_t *wregex; | ||
| 27 | size_t wlen; | ||
| 28 | |||
| 29 | wregex = xmalloc(sizeof(tre_char_t) * (n + 1)); | ||
| 30 | if (wregex == NULL) | ||
| 31 | return REG_ESPACE; | ||
| 32 | |||
| 33 | /* If the current locale uses the standard single byte encoding of | ||
| 34 | characters, we don't do a multibyte string conversion. If we did, | ||
| 35 | many applications which use the default locale would break since | ||
| 36 | the default "C" locale uses the 7-bit ASCII character set, and | ||
| 37 | all characters with the eighth bit set would be considered invalid. */ | ||
| 38 | #if TRE_MULTIBYTE | ||
| 39 | if (TRE_MB_CUR_MAX == 1) | ||
| 40 | #endif /* TRE_MULTIBYTE */ | ||
| 41 | { | ||
| 42 | unsigned int i; | ||
| 43 | const unsigned char *str = (const unsigned char *)regex; | ||
| 44 | tre_char_t *wstr = wregex; | ||
| 45 | |||
| 46 | for (i = 0; i < n; i++) | ||
| 47 | *(wstr++) = *(str++); | ||
| 48 | wlen = n; | ||
| 49 | } | ||
| 50 | #if TRE_MULTIBYTE | ||
| 51 | else | ||
| 52 | { | ||
| 53 | int consumed; | ||
| 54 | tre_char_t *wcptr = wregex; | ||
| 55 | #ifdef HAVE_MBSTATE_T | ||
| 56 | mbstate_t state; | ||
| 57 | memset(&state, '\0', sizeof(state)); | ||
| 58 | #endif /* HAVE_MBSTATE_T */ | ||
| 59 | while (n > 0) | ||
| 60 | { | ||
| 61 | consumed = tre_mbrtowc(wcptr, regex, n, &state); | ||
| 62 | |||
| 63 | switch (consumed) | ||
| 64 | { | ||
| 65 | case 0: | ||
| 66 | if (*regex == '\0') | ||
| 67 | consumed = 1; | ||
| 68 | else | ||
| 69 | { | ||
| 70 | xfree(wregex); | ||
| 71 | return REG_BADPAT; | ||
| 72 | } | ||
| 73 | break; | ||
| 74 | case -1: | ||
| 75 | DPRINT(("mbrtowc: error %d: %s.\n", errno, strerror(errno))); | ||
| 76 | xfree(wregex); | ||
| 77 | return REG_BADPAT; | ||
| 78 | case -2: | ||
| 79 | /* The last character wasn't complete. Let's not call it a | ||
| 80 | fatal error. */ | ||
| 81 | consumed = n; | ||
| 82 | break; | ||
| 83 | } | ||
| 84 | regex += consumed; | ||
| 85 | n -= consumed; | ||
| 86 | wcptr++; | ||
| 87 | } | ||
| 88 | wlen = wcptr - wregex; | ||
| 89 | } | ||
| 90 | #endif /* TRE_MULTIBYTE */ | ||
| 91 | |||
| 92 | wregex[wlen] = L'\0'; | ||
| 93 | ret = tre_compile(preg, wregex, wlen, cflags); | ||
| 94 | xfree(wregex); | ||
| 95 | #else /* !TRE_WCHAR */ | ||
| 96 | ret = tre_compile(preg, (const tre_char_t *)regex, n, cflags); | ||
| 97 | #endif /* !TRE_WCHAR */ | ||
| 98 | |||
| 99 | return ret; | ||
| 100 | } | ||
| 101 | |||
| 102 | int | ||
| 103 | tre_regcomp(regex_t *preg, const char *regex, int cflags) | ||
| 104 | { | ||
| 105 | return tre_regncomp(preg, regex, regex ? strlen(regex) : 0, cflags); | ||
| 106 | } | ||
| 107 | |||
| 108 | |||
| 109 | #ifdef TRE_WCHAR | ||
| 110 | int | ||
| 111 | tre_regwncomp(regex_t *preg, const wchar_t *regex, size_t n, int cflags) | ||
| 112 | { | ||
| 113 | return tre_compile(preg, regex, n, cflags); | ||
| 114 | } | ||
| 115 | |||
| 116 | int | ||
| 117 | tre_regwcomp(regex_t *preg, const wchar_t *regex, int cflags) | ||
| 118 | { | ||
| 119 | return tre_compile(preg, regex, regex ? wcslen(regex) : 0, cflags); | ||
| 120 | } | ||
| 121 | #endif /* TRE_WCHAR */ | ||
| 122 | |||
| 123 | void | ||
| 124 | tre_regfree(regex_t *preg) | ||
| 125 | { | ||
| 126 | tre_free(preg); | ||
| 127 | } | ||
| 128 | |||
| 129 | /* EOF */ | ||
| @@ -1,83 +0,0 @@ | |||
| 1 | /* | ||
| 2 | tre_regerror.c - POSIX tre_regerror() implementation for TRE. | ||
| 3 | |||
| 4 | This software is released under a BSD-style license. | ||
| 5 | See the file LICENSE for details and copyright. | ||
| 6 | |||
| 7 | */ | ||
| 8 | |||
| 9 | #ifdef HAVE_CONFIG_H | ||
| 10 | #include <config.h> | ||
| 11 | #endif /* HAVE_CONFIG_H */ | ||
| 12 | |||
| 13 | #include <string.h> | ||
| 14 | #ifdef HAVE_WCHAR_H | ||
| 15 | #include <wchar.h> | ||
| 16 | #endif /* HAVE_WCHAR_H */ | ||
| 17 | #ifdef HAVE_WCTYPE_H | ||
| 18 | #include <wctype.h> | ||
| 19 | #endif /* HAVE_WCTYPE_H */ | ||
| 20 | |||
| 21 | #include "tre-internal.h" | ||
| 22 | #include "tre.h" | ||
| 23 | |||
| 24 | #ifdef HAVE_GETTEXT | ||
| 25 | #include <libintl.h> | ||
| 26 | #else | ||
| 27 | #define dgettext(p, s) s | ||
| 28 | #define gettext(s) s | ||
| 29 | #endif | ||
| 30 | |||
| 31 | #define _(String) dgettext(PACKAGE, String) | ||
| 32 | #define gettext_noop(String) String | ||
| 33 | |||
| 34 | /* Error message strings for error codes listed in `tre.h'. This list | ||
| 35 | needs to be in sync with the codes listed there, naturally. */ | ||
| 36 | static const char *tre_error_messages[] = | ||
| 37 | { gettext_noop("No error"), /* REG_OK */ | ||
| 38 | gettext_noop("No match"), /* REG_NOMATCH */ | ||
| 39 | gettext_noop("Invalid regexp"), /* REG_BADPAT */ | ||
| 40 | gettext_noop("Unknown collating element"), /* REG_ECOLLATE */ | ||
| 41 | gettext_noop("Unknown character class name"), /* REG_ECTYPE */ | ||
| 42 | gettext_noop("Trailing backslash"), /* REG_EESCAPE */ | ||
| 43 | gettext_noop("Invalid back reference"), /* REG_ESUBREG */ | ||
| 44 | gettext_noop("Missing ']'"), /* REG_EBRACK */ | ||
| 45 | gettext_noop("Missing ')'"), /* REG_EPAREN */ | ||
| 46 | gettext_noop("Missing '}'"), /* REG_EBRACE */ | ||
| 47 | gettext_noop("Invalid contents of {}"), /* REG_BADBR */ | ||
| 48 | gettext_noop("Invalid character range"), /* REG_ERANGE */ | ||
| 49 | gettext_noop("Out of memory"), /* REG_ESPACE */ | ||
| 50 | gettext_noop("Invalid use of repetition operators") /* REG_BADRPT */ | ||
| 51 | }; | ||
| 52 | |||
| 53 | size_t | ||
| 54 | tre_regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size) | ||
| 55 | { | ||
| 56 | const char *err; | ||
| 57 | size_t err_len; | ||
| 58 | |||
| 59 | /*LINTED*/(void)&preg; | ||
| 60 | if (errcode >= 0 | ||
| 61 | && errcode < (int)(sizeof(tre_error_messages) | ||
| 62 | / sizeof(*tre_error_messages))) | ||
| 63 | err = gettext(tre_error_messages[errcode]); | ||
| 64 | else | ||
| 65 | err = gettext("Unknown error"); | ||
| 66 | |||
| 67 | err_len = strlen(err) + 1; | ||
| 68 | if (errbuf_size > 0 && errbuf != NULL) | ||
| 69 | { | ||
| 70 | if (err_len > errbuf_size) | ||
| 71 | { | ||
| 72 | strncpy(errbuf, err, errbuf_size - 1); | ||
| 73 | errbuf[errbuf_size - 1] = '\0'; | ||
| 74 | } | ||
| 75 | else | ||
| 76 | { | ||
| 77 | strcpy(errbuf, err); | ||
| 78 | } | ||
| 79 | } | ||
| 80 | return err_len; | ||
| 81 | } | ||
| 82 | |||
| 83 | /* EOF */ | ||
| @@ -1,38 +0,0 @@ | |||
| 1 | /* | ||
| 2 | regex.h - TRE legacy API | ||
| 3 | |||
| 4 | This software is released under a BSD-style license. | ||
| 5 | See the file LICENSE for details and copyright. | ||
| 6 | |||
| 7 | This header is for source level compatibility with old code using | ||
| 8 | the <tre/regex.h> header which defined the TRE API functions without | ||
| 9 | a prefix. New code should include <tre/tre.h> instead. | ||
| 10 | |||
| 11 | */ | ||
| 12 | |||
| 13 | #ifndef TRE_REXEX_H | ||
| 14 | #define TRE_REGEX_H 1 | ||
| 15 | |||
| 16 | #include "tre.h" | ||
| 17 | |||
| 18 | #ifndef TRE_USE_SYSTEM_REGEX_H | ||
| 19 | #define regcomp tre_regcomp | ||
| 20 | #define regerror tre_regerror | ||
| 21 | #define regexec tre_regexec | ||
| 22 | #define regfree tre_regfree | ||
| 23 | #endif /* TRE_USE_SYSTEM_REGEX_H */ | ||
| 24 | |||
| 25 | #define regacomp tre_regacomp | ||
| 26 | #define regaexec tre_regaexec | ||
| 27 | #define regancomp tre_regancomp | ||
| 28 | #define reganexec tre_reganexec | ||
| 29 | #define regawncomp tre_regawncomp | ||
| 30 | #define regawnexec tre_regawnexec | ||
| 31 | #define regncomp tre_regncomp | ||
| 32 | #define regnexec tre_regnexec | ||
| 33 | #define regwcomp tre_regwcomp | ||
| 34 | #define regwexec tre_regwexec | ||
| 35 | #define regwncomp tre_regwncomp | ||
| 36 | #define regwnexec tre_regwnexec | ||
| 37 | |||
| 38 | #endif /* TRE_REGEX_H */ | ||
| @@ -1,348 +0,0 @@ | |||
| 1 | /* | ||
| 2 | tre_regexec.c - TRE POSIX compatible matching functions (and more). | ||
| 3 | |||
| 4 | This software is released under a BSD-style license. | ||
| 5 | See the file LICENSE for details and copyright. | ||
| 6 | |||
| 7 | */ | ||
| 8 | |||
| 9 | #ifdef HAVE_CONFIG_H | ||
| 10 | #include <config.h> | ||
| 11 | #endif /* HAVE_CONFIG_H */ | ||
| 12 | |||
| 13 | #ifdef TRE_USE_ALLOCA | ||
| 14 | /* AIX requires this to be the first thing in the file. */ | ||
| 15 | #ifndef __GNUC__ | ||
| 16 | # if HAVE_ALLOCA_H | ||
| 17 | # include <alloca.h> | ||
| 18 | # else | ||
| 19 | # ifdef _AIX | ||
| 20 | #pragma alloca | ||
| 21 | # else | ||
| 22 | # ifndef alloca /* predefined by HP cc +Olibcalls */ | ||
| 23 | char *alloca (); | ||
| 24 | # endif | ||
| 25 | # endif | ||
| 26 | # endif | ||
| 27 | #endif | ||
| 28 | #endif /* TRE_USE_ALLOCA */ | ||
| 29 | |||
| 30 | #include <assert.h> | ||
| 31 | #include <stdlib.h> | ||
| 32 | #include <string.h> | ||
| 33 | #ifdef HAVE_WCHAR_H | ||
| 34 | #include <wchar.h> | ||
| 35 | #endif /* HAVE_WCHAR_H */ | ||
| 36 | #ifdef HAVE_WCTYPE_H | ||
| 37 | #include <wctype.h> | ||
| 38 | #endif /* HAVE_WCTYPE_H */ | ||
| 39 | #ifndef TRE_WCHAR | ||
| 40 | #include <ctype.h> | ||
| 41 | #endif /* !TRE_WCHAR */ | ||
| 42 | #ifdef HAVE_MALLOC_H | ||
| 43 | #include <malloc.h> | ||
| 44 | #endif /* HAVE_MALLOC_H */ | ||
| 45 | #include <limits.h> | ||
| 46 | |||
| 47 | #include "tre-internal.h" | ||
| 48 | #include "tre.h" | ||
| 49 | #include "xmalloc.h" | ||
| 50 | |||
| 51 | |||
| 52 | /* Fills the POSIX.2 regmatch_t array according to the TNFA tag and match | ||
| 53 | endpoint values. */ | ||
| 54 | void | ||
| 55 | tre_fill_pmatch(size_t nmatch, regmatch_t pmatch[], int cflags, | ||
| 56 | const tre_tnfa_t *tnfa, int *tags, int match_eo) | ||
| 57 | { | ||
| 58 | tre_submatch_data_t *submatch_data; | ||
| 59 | unsigned int i, j; | ||
| 60 | int *parents; | ||
| 61 | |||
| 62 | i = 0; | ||
| 63 | if (match_eo >= 0 && !(cflags & REG_NOSUB)) | ||
| 64 | { | ||
| 65 | /* Construct submatch offsets from the tags. */ | ||
| 66 | DPRINT(("end tag = t%d = %d\n", tnfa->end_tag, match_eo)); | ||
| 67 | submatch_data = tnfa->submatch_data; | ||
| 68 | while (i < tnfa->num_submatches && i < nmatch) | ||
| 69 | { | ||
| 70 | if (submatch_data[i].so_tag == tnfa->end_tag) | ||
| 71 | pmatch[i].rm_so = match_eo; | ||
| 72 | else | ||
| 73 | pmatch[i].rm_so = tags[submatch_data[i].so_tag]; | ||
| 74 | |||
| 75 | if (submatch_data[i].eo_tag == tnfa->end_tag) | ||
| 76 | pmatch[i].rm_eo = match_eo; | ||
| 77 | else | ||
| 78 | pmatch[i].rm_eo = tags[submatch_data[i].eo_tag]; | ||
| 79 | |||
| 80 | /* If either of the endpoints were not used, this submatch | ||
| 81 | was not part of the match. */ | ||
| 82 | if (pmatch[i].rm_so == -1 || pmatch[i].rm_eo == -1) | ||
| 83 | pmatch[i].rm_so = pmatch[i].rm_eo = -1; | ||
| 84 | |||
| 85 | DPRINT(("pmatch[%d] = {t%d = %d, t%d = %d}\n", i, | ||
| 86 | submatch_data[i].so_tag, pmatch[i].rm_so, | ||
| 87 | submatch_data[i].eo_tag, pmatch[i].rm_eo)); | ||
| 88 | i++; | ||
| 89 | } | ||
| 90 | /* Reset all submatches that are not within all of their parent | ||
| 91 | submatches. */ | ||
| 92 | i = 0; | ||
| 93 | while (i < tnfa->num_submatches && i < nmatch) | ||
| 94 | { | ||
| 95 | if (pmatch[i].rm_eo == -1) | ||
| 96 | assert(pmatch[i].rm_so == -1); | ||
| 97 | assert(pmatch[i].rm_so <= pmatch[i].rm_eo); | ||
| 98 | |||
| 99 | parents = submatch_data[i].parents; | ||
| 100 | if (parents != NULL) | ||
| 101 | for (j = 0; parents[j] >= 0; j++) | ||
| 102 | { | ||
| 103 | DPRINT(("pmatch[%d] parent %d\n", i, parents[j])); | ||
| 104 | if (pmatch[i].rm_so < pmatch[parents[j]].rm_so | ||
| 105 | || pmatch[i].rm_eo > pmatch[parents[j]].rm_eo) | ||
| 106 | pmatch[i].rm_so = pmatch[i].rm_eo = -1; | ||
| 107 | } | ||
| 108 | i++; | ||
| 109 | } | ||
| 110 | } | ||
| 111 | |||
| 112 | while (i < nmatch) | ||
| 113 | { | ||
| 114 | pmatch[i].rm_so = -1; | ||
| 115 | pmatch[i].rm_eo = -1; | ||
| 116 | i++; | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 120 | |||
| 121 | /* | ||
| 122 | Wrapper functions for POSIX compatible regexp matching. | ||
| 123 | */ | ||
| 124 | |||
| 125 | int | ||
| 126 | tre_have_backrefs(const regex_t *preg) | ||
| 127 | { | ||
| 128 | tre_tnfa_t *tnfa = (void *)preg->TRE_REGEX_T_FIELD; | ||
| 129 | return tnfa->have_backrefs; | ||
| 130 | } | ||
| 131 | |||
| 132 | int | ||
| 133 | tre_have_approx(const regex_t *preg) | ||
| 134 | { | ||
| 135 | tre_tnfa_t *tnfa = (void *)preg->TRE_REGEX_T_FIELD; | ||
| 136 | return tnfa->have_approx; | ||
| 137 | } | ||
| 138 | |||
| 139 | static int | ||
| 140 | tre_match(const tre_tnfa_t *tnfa, const void *string, size_t len, | ||
| 141 | tre_str_type_t type, size_t nmatch, regmatch_t pmatch[], | ||
| 142 | int eflags) | ||
| 143 | { | ||
| 144 | reg_errcode_t status; | ||
| 145 | int *tags = NULL, eo; | ||
| 146 | if (tnfa->num_tags > 0 && nmatch > 0) | ||
| 147 | { | ||
| 148 | #ifdef TRE_USE_ALLOCA | ||
| 149 | tags = alloca(sizeof(*tags) * tnfa->num_tags); | ||
| 150 | #else /* !TRE_USE_ALLOCA */ | ||
| 151 | tags = xmalloc(sizeof(*tags) * tnfa->num_tags); | ||
| 152 | #endif /* !TRE_USE_ALLOCA */ | ||
| 153 | if (tags == NULL) | ||
| 154 | return REG_ESPACE; | ||
| 155 | } | ||
| 156 | |||
| 157 | /* Dispatch to the appropriate matcher. */ | ||
| 158 | if (tnfa->have_backrefs || eflags & REG_BACKTRACKING_MATCHER) | ||
| 159 | { | ||
| 160 | /* The regex has back references, use the backtracking matcher. */ | ||
| 161 | if (type == STR_USER) | ||
| 162 | { | ||
| 163 | const tre_str_source *source = string; | ||
| 164 | if (source->rewind == NULL || source->compare == NULL) | ||
| 165 | /* The backtracking matcher requires rewind and compare | ||
| 166 | capabilities from the input stream. */ | ||
| 167 | return REG_BADPAT; | ||
| 168 | } | ||
| 169 | status = tre_tnfa_run_backtrack(tnfa, string, (int)len, type, | ||
| 170 | tags, eflags, &eo); | ||
| 171 | } | ||
| 172 | #ifdef TRE_APPROX | ||
| 173 | else if (tnfa->have_approx || eflags & REG_APPROX_MATCHER) | ||
| 174 | { | ||
| 175 | /* The regex uses approximate matching, use the approximate matcher. */ | ||
| 176 | regamatch_t match; | ||
| 177 | regaparams_t params; | ||
| 178 | tre_regaparams_default(¶ms); | ||
| 179 | params.max_err = 0; | ||
| 180 | params.max_cost = 0; | ||
| 181 | status = tre_tnfa_run_approx(tnfa, string, (int)len, type, tags, | ||
| 182 | &match, params, eflags, &eo); | ||
| 183 | } | ||
| 184 | #endif /* TRE_APPROX */ | ||
| 185 | else | ||
| 186 | { | ||
| 187 | /* Exact matching, no back references, use the parallel matcher. */ | ||
| 188 | status = tre_tnfa_run_parallel(tnfa, string, (int)len, type, | ||
| 189 | tags, eflags, &eo); | ||
| 190 | } | ||
| 191 | |||
| 192 | if (status == REG_OK) | ||
| 193 | /* A match was found, so fill the submatch registers. */ | ||
| 194 | tre_fill_pmatch(nmatch, pmatch, tnfa->cflags, tnfa, tags, eo); | ||
| 195 | #ifndef TRE_USE_ALLOCA | ||
| 196 | if (tags) | ||
| 197 | xfree(tags); | ||
| 198 | #endif /* !TRE_USE_ALLOCA */ | ||
| 199 | return status; | ||
| 200 | } | ||
| 201 | |||
| 202 | int | ||
| 203 | tre_regnexec(const regex_t *preg, const char *str, size_t len, | ||
| 204 | size_t nmatch, regmatch_t pmatch[], int eflags) | ||
| 205 | { | ||
| 206 | tre_tnfa_t *tnfa = (void *)preg->TRE_REGEX_T_FIELD; | ||
| 207 | tre_str_type_t type = (TRE_MB_CUR_MAX == 1) ? STR_BYTE : STR_MBS; | ||
| 208 | |||
| 209 | return tre_match(tnfa, str, len, type, nmatch, pmatch, eflags); | ||
| 210 | } | ||
| 211 | |||
| 212 | int | ||
| 213 | tre_regexec(const regex_t *preg, const char *str, | ||
| 214 | size_t nmatch, regmatch_t pmatch[], int eflags) | ||
| 215 | { | ||
| 216 | return tre_regnexec(preg, str, (unsigned)-1, nmatch, pmatch, eflags); | ||
| 217 | } | ||
| 218 | |||
| 219 | |||
| 220 | #ifdef TRE_WCHAR | ||
| 221 | |||
| 222 | int | ||
| 223 | tre_regwnexec(const regex_t *preg, const wchar_t *str, size_t len, | ||
| 224 | size_t nmatch, regmatch_t pmatch[], int eflags) | ||
| 225 | { | ||
| 226 | tre_tnfa_t *tnfa = (void *)preg->TRE_REGEX_T_FIELD; | ||
| 227 | return tre_match(tnfa, str, len, STR_WIDE, nmatch, pmatch, eflags); | ||
| 228 | } | ||
| 229 | |||
| 230 | int | ||
| 231 | tre_regwexec(const regex_t *preg, const wchar_t *str, | ||
| 232 | size_t nmatch, regmatch_t pmatch[], int eflags) | ||
| 233 | { | ||
| 234 | return tre_regwnexec(preg, str, (unsigned)-1, nmatch, pmatch, eflags); | ||
| 235 | } | ||
| 236 | |||
| 237 | #endif /* TRE_WCHAR */ | ||
| 238 | |||
| 239 | int | ||
| 240 | tre_reguexec(const regex_t *preg, const tre_str_source *str, | ||
| 241 | size_t nmatch, regmatch_t pmatch[], int eflags) | ||
| 242 | { | ||
| 243 | tre_tnfa_t *tnfa = (void *)preg->TRE_REGEX_T_FIELD; | ||
| 244 | return tre_match(tnfa, str, (unsigned)-1, STR_USER, nmatch, pmatch, eflags); | ||
| 245 | } | ||
| 246 | |||
| 247 | |||
| 248 | #ifdef TRE_APPROX | ||
| 249 | |||
| 250 | /* | ||
| 251 | Wrapper functions for approximate regexp matching. | ||
| 252 | */ | ||
| 253 | |||
| 254 | static int | ||
| 255 | tre_match_approx(const tre_tnfa_t *tnfa, const void *string, size_t len, | ||
| 256 | tre_str_type_t type, regamatch_t *match, regaparams_t params, | ||
| 257 | int eflags) | ||
| 258 | { | ||
| 259 | reg_errcode_t status; | ||
| 260 | int *tags = NULL, eo; | ||
| 261 | |||
| 262 | /* If the regexp does not use approximate matching features, the | ||
| 263 | maximum cost is zero, and the approximate matcher isn't forced, | ||
| 264 | use the exact matcher instead. */ | ||
| 265 | if (params.max_cost == 0 && !tnfa->have_approx | ||
| 266 | && !(eflags & REG_APPROX_MATCHER)) | ||
| 267 | return tre_match(tnfa, string, len, type, match->nmatch, match->pmatch, | ||
| 268 | eflags); | ||
| 269 | |||
| 270 | /* Back references are not supported by the approximate matcher. */ | ||
| 271 | if (tnfa->have_backrefs) | ||
| 272 | return REG_BADPAT; | ||
| 273 | |||
| 274 | if (tnfa->num_tags > 0 && match->nmatch > 0) | ||
| 275 | { | ||
| 276 | #if TRE_USE_ALLOCA | ||
| 277 | tags = alloca(sizeof(*tags) * tnfa->num_tags); | ||
| 278 | #else /* !TRE_USE_ALLOCA */ | ||
| 279 | tags = xmalloc(sizeof(*tags) * tnfa->num_tags); | ||
| 280 | #endif /* !TRE_USE_ALLOCA */ | ||
| 281 | if (tags == NULL) | ||
| 282 | return REG_ESPACE; | ||
| 283 | } | ||
| 284 | status = tre_tnfa_run_approx(tnfa, string, (int)len, type, tags, | ||
| 285 | match, params, eflags, &eo); | ||
| 286 | if (status == REG_OK) | ||
| 287 | tre_fill_pmatch(match->nmatch, match->pmatch, tnfa->cflags, tnfa, tags, eo); | ||
| 288 | #ifndef TRE_USE_ALLOCA | ||
| 289 | if (tags) | ||
| 290 | xfree(tags); | ||
| 291 | #endif /* !TRE_USE_ALLOCA */ | ||
| 292 | return status; | ||
| 293 | } | ||
| 294 | |||
| 295 | int | ||
| 296 | tre_reganexec(const regex_t *preg, const char *str, size_t len, | ||
| 297 | regamatch_t *match, regaparams_t params, int eflags) | ||
| 298 | { | ||
| 299 | tre_tnfa_t *tnfa = (void *)preg->TRE_REGEX_T_FIELD; | ||
| 300 | tre_str_type_t type = (TRE_MB_CUR_MAX == 1) ? STR_BYTE : STR_MBS; | ||
| 301 | |||
| 302 | return tre_match_approx(tnfa, str, len, type, match, params, eflags); | ||
| 303 | } | ||
| 304 | |||
| 305 | int | ||
| 306 | tre_regaexec(const regex_t *preg, const char *str, | ||
| 307 | regamatch_t *match, regaparams_t params, int eflags) | ||
| 308 | { | ||
| 309 | return tre_reganexec(preg, str, (unsigned)-1, match, params, eflags); | ||
| 310 | } | ||
| 311 | |||
| 312 | #ifdef TRE_WCHAR | ||
| 313 | |||
| 314 | int | ||
| 315 | tre_regawnexec(const regex_t *preg, const wchar_t *str, size_t len, | ||
| 316 | regamatch_t *match, regaparams_t params, int eflags) | ||
| 317 | { | ||
| 318 | tre_tnfa_t *tnfa = (void *)preg->TRE_REGEX_T_FIELD; | ||
| 319 | return tre_match_approx(tnfa, str, len, STR_WIDE, | ||
| 320 | match, params, eflags); | ||
| 321 | } | ||
| 322 | |||
| 323 | int | ||
| 324 | tre_regawexec(const regex_t *preg, const wchar_t *str, | ||
| 325 | regamatch_t *match, regaparams_t params, int eflags) | ||
| 326 | { | ||
| 327 | return tre_regawnexec(preg, str, (unsigned)-1, match, params, eflags); | ||
| 328 | } | ||
| 329 | |||
| 330 | #endif /* TRE_WCHAR */ | ||
| 331 | |||
| 332 | void | ||
| 333 | tre_regaparams_default(regaparams_t *params) | ||
| 334 | { | ||
| 335 | memset(params, 0, sizeof(*params)); | ||
| 336 | params->cost_ins = 1; | ||
| 337 | params->cost_del = 1; | ||
| 338 | params->cost_subst = 1; | ||
| 339 | params->max_cost = INT_MAX; | ||
| 340 | params->max_ins = INT_MAX; | ||
| 341 | params->max_del = INT_MAX; | ||
| 342 | params->max_subst = INT_MAX; | ||
| 343 | params->max_err = INT_MAX; | ||
| 344 | } | ||
| 345 | |||
| 346 | #endif /* TRE_APPROX */ | ||
| 347 | |||
| 348 | /* EOF */ | ||
| @@ -1,226 +0,0 @@ | |||
| 1 | /* | ||
| 2 | tre-ast.c - Abstract syntax tree (AST) routines | ||
| 3 | |||
| 4 | This software is released under a BSD-style license. | ||
| 5 | See the file LICENSE for details and copyright. | ||
| 6 | |||
| 7 | */ | ||
| 8 | |||
| 9 | #ifdef HAVE_CONFIG_H | ||
| 10 | #include <config.h> | ||
| 11 | #endif /* HAVE_CONFIG_H */ | ||
| 12 | #include <assert.h> | ||
| 13 | |||
| 14 | #include "tre-ast.h" | ||
| 15 | #include "tre-mem.h" | ||
| 16 | |||
| 17 | tre_ast_node_t * | ||
| 18 | tre_ast_new_node(tre_mem_t mem, tre_ast_type_t type, size_t size) | ||
| 19 | { | ||
| 20 | tre_ast_node_t *node; | ||
| 21 | |||
| 22 | node = tre_mem_calloc(mem, sizeof(*node)); | ||
| 23 | if (!node) | ||
| 24 | return NULL; | ||
| 25 | node->obj = tre_mem_calloc(mem, size); | ||
| 26 | if (!node->obj) | ||
| 27 | return NULL; | ||
| 28 | node->type = type; | ||
| 29 | node->nullable = -1; | ||
| 30 | node->submatch_id = -1; | ||
| 31 | |||
| 32 | return node; | ||
| 33 | } | ||
| 34 | |||
| 35 | tre_ast_node_t * | ||
| 36 | tre_ast_new_literal(tre_mem_t mem, int code_min, int code_max, int position) | ||
| 37 | { | ||
| 38 | tre_ast_node_t *node; | ||
| 39 | tre_literal_t *lit; | ||
| 40 | |||
| 41 | node = tre_ast_new_node(mem, LITERAL, sizeof(tre_literal_t)); | ||
| 42 | if (!node) | ||
| 43 | return NULL; | ||
| 44 | lit = node->obj; | ||
| 45 | lit->code_min = code_min; | ||
| 46 | lit->code_max = code_max; | ||
| 47 | lit->position = position; | ||
| 48 | |||
| 49 | return node; | ||
| 50 | } | ||
| 51 | |||
| 52 | tre_ast_node_t * | ||
| 53 | tre_ast_new_iter(tre_mem_t mem, tre_ast_node_t *arg, int min, int max, | ||
| 54 | int minimal) | ||
| 55 | { | ||
| 56 | tre_ast_node_t *node; | ||
| 57 | tre_iteration_t *iter; | ||
| 58 | |||
| 59 | node = tre_ast_new_node(mem, ITERATION, sizeof(tre_iteration_t)); | ||
| 60 | if (!node) | ||
| 61 | return NULL; | ||
| 62 | iter = node->obj; | ||
| 63 | iter->arg = arg; | ||
| 64 | iter->min = min; | ||
| 65 | iter->max = max; | ||
| 66 | iter->minimal = minimal; | ||
| 67 | node->num_submatches = arg->num_submatches; | ||
| 68 | |||
| 69 | return node; | ||
| 70 | } | ||
| 71 | |||
| 72 | tre_ast_node_t * | ||
| 73 | tre_ast_new_union(tre_mem_t mem, tre_ast_node_t *left, tre_ast_node_t *right) | ||
| 74 | { | ||
| 75 | tre_ast_node_t *node; | ||
| 76 | |||
| 77 | node = tre_ast_new_node(mem, UNION, sizeof(tre_union_t)); | ||
| 78 | if (node == NULL) | ||
| 79 | return NULL; | ||
| 80 | ((tre_union_t *)node->obj)->left = left; | ||
| 81 | ((tre_union_t *)node->obj)->right = right; | ||
| 82 | node->num_submatches = left->num_submatches + right->num_submatches; | ||
| 83 | |||
| 84 | return node; | ||
| 85 | } | ||
| 86 | |||
| 87 | tre_ast_node_t * | ||
| 88 | tre_ast_new_catenation(tre_mem_t mem, tre_ast_node_t *left, | ||
| 89 | tre_ast_node_t *right) | ||
| 90 | { | ||
| 91 | tre_ast_node_t *node; | ||
| 92 | |||
| 93 | node = tre_ast_new_node(mem, CATENATION, sizeof(tre_catenation_t)); | ||
| 94 | if (node == NULL) | ||
| 95 | return NULL; | ||
| 96 | ((tre_catenation_t *)node->obj)->left = left; | ||
| 97 | ((tre_catenation_t *)node->obj)->right = right; | ||
| 98 | node->num_submatches = left->num_submatches + right->num_submatches; | ||
| 99 | |||
| 100 | return node; | ||
| 101 | } | ||
| 102 | |||
| 103 | #ifdef TRE_DEBUG | ||
| 104 | |||
| 105 | static void | ||
| 106 | tre_findent(FILE *stream, int i) | ||
| 107 | { | ||
| 108 | while (i-- > 0) | ||
| 109 | fputc(' ', stream); | ||
| 110 | } | ||
| 111 | |||
| 112 | void | ||
| 113 | tre_print_params(int *params) | ||
| 114 | { | ||
| 115 | int i; | ||
| 116 | if (params) | ||
| 117 | { | ||
| 118 | DPRINT(("params [")); | ||
| 119 | for (i = 0; i < TRE_PARAM_LAST; i++) | ||
| 120 | { | ||
| 121 | if (params[i] == TRE_PARAM_UNSET) | ||
| 122 | DPRINT(("unset")); | ||
| 123 | else if (params[i] == TRE_PARAM_DEFAULT) | ||
| 124 | DPRINT(("default")); | ||
| 125 | else | ||
| 126 | DPRINT(("%d", params[i])); | ||
| 127 | if (i < TRE_PARAM_LAST - 1) | ||
| 128 | DPRINT((", ")); | ||
| 129 | } | ||
| 130 | DPRINT(("]")); | ||
| 131 | } | ||
| 132 | } | ||
| 133 | |||
| 134 | static void | ||
| 135 | tre_do_print(FILE *stream, tre_ast_node_t *ast, int indent) | ||
| 136 | { | ||
| 137 | int code_min, code_max, pos; | ||
| 138 | int num_tags = ast->num_tags; | ||
| 139 | tre_literal_t *lit; | ||
| 140 | tre_iteration_t *iter; | ||
| 141 | |||
| 142 | tre_findent(stream, indent); | ||
| 143 | switch (ast->type) | ||
| 144 | { | ||
| 145 | case LITERAL: | ||
| 146 | lit = ast->obj; | ||
| 147 | code_min = lit->code_min; | ||
| 148 | code_max = lit->code_max; | ||
| 149 | pos = lit->position; | ||
| 150 | if (IS_EMPTY(lit)) | ||
| 151 | { | ||
| 152 | fprintf(stream, "literal empty\n"); | ||
| 153 | } | ||
| 154 | else if (IS_ASSERTION(lit)) | ||
| 155 | { | ||
| 156 | int i; | ||
| 157 | char *assertions[] = { "bol", "eol", "ctype", "!ctype", | ||
| 158 | "bow", "eow", "wb", "!wb" }; | ||
| 159 | if (code_max >= ASSERT_LAST << 1) | ||
| 160 | assert(0); | ||
| 161 | fprintf(stream, "assertions: "); | ||
| 162 | for (i = 0; (1 << i) <= ASSERT_LAST; i++) | ||
| 163 | if (code_max & (1 << i)) | ||
| 164 | fprintf(stream, "%s ", assertions[i]); | ||
| 165 | fprintf(stream, "\n"); | ||
| 166 | } | ||
| 167 | else if (IS_TAG(lit)) | ||
| 168 | { | ||
| 169 | fprintf(stream, "tag %d\n", code_max); | ||
| 170 | } | ||
| 171 | else if (IS_BACKREF(lit)) | ||
| 172 | { | ||
| 173 | fprintf(stream, "backref %d, pos %d\n", code_max, pos); | ||
| 174 | } | ||
| 175 | else if (IS_PARAMETER(lit)) | ||
| 176 | { | ||
| 177 | tre_print_params(lit->u.params); | ||
| 178 | fprintf(stream, "\n"); | ||
| 179 | } | ||
| 180 | else | ||
| 181 | { | ||
| 182 | fprintf(stream, "literal (%c, %c) (%d, %d), pos %d, sub %d, " | ||
| 183 | "%d tags\n", code_min, code_max, code_min, code_max, pos, | ||
| 184 | ast->submatch_id, num_tags); | ||
| 185 | } | ||
| 186 | break; | ||
| 187 | case ITERATION: | ||
| 188 | iter = ast->obj; | ||
| 189 | fprintf(stream, "iteration {%d, %d}, sub %d, %d tags, %s\n", | ||
| 190 | iter->min, iter->max, ast->submatch_id, num_tags, | ||
| 191 | iter->minimal ? "minimal" : "greedy"); | ||
| 192 | tre_do_print(stream, iter->arg, indent + 2); | ||
| 193 | break; | ||
| 194 | case UNION: | ||
| 195 | fprintf(stream, "union, sub %d, %d tags\n", ast->submatch_id, num_tags); | ||
| 196 | tre_do_print(stream, ((tre_union_t *)ast->obj)->left, indent + 2); | ||
| 197 | tre_do_print(stream, ((tre_union_t *)ast->obj)->right, indent + 2); | ||
| 198 | break; | ||
| 199 | case CATENATION: | ||
| 200 | fprintf(stream, "catenation, sub %d, %d tags\n", ast->submatch_id, | ||
| 201 | num_tags); | ||
| 202 | tre_do_print(stream, ((tre_catenation_t *)ast->obj)->left, indent + 2); | ||
| 203 | tre_do_print(stream, ((tre_catenation_t *)ast->obj)->right, indent + 2); | ||
| 204 | break; | ||
| 205 | default: | ||
| 206 | assert(0); | ||
| 207 | break; | ||
| 208 | } | ||
| 209 | } | ||
| 210 | |||
| 211 | static void | ||
| 212 | tre_ast_fprint(FILE *stream, tre_ast_node_t *ast) | ||
| 213 | { | ||
| 214 | tre_do_print(stream, ast, 0); | ||
| 215 | } | ||
| 216 | |||
| 217 | void | ||
| 218 | tre_ast_print(tre_ast_node_t *tree) | ||
| 219 | { | ||
| 220 | printf("AST:\n"); | ||
| 221 | tre_ast_fprint(stdout, tree); | ||
| 222 | } | ||
| 223 | |||
| 224 | #endif /* TRE_DEBUG */ | ||
| 225 | |||
| 226 | /* EOF */ | ||
| @@ -1,128 +0,0 @@ | |||
| 1 | /* | ||
| 2 | tre-ast.h - Abstract syntax tree (AST) definitions | ||
| 3 | |||
| 4 | This software is released under a BSD-style license. | ||
| 5 | See the file LICENSE for details and copyright. | ||
| 6 | |||
| 7 | */ | ||
| 8 | |||
| 9 | |||
| 10 | #ifndef TRE_AST_H | ||
| 11 | #define TRE_AST_H 1 | ||
| 12 | |||
| 13 | #include "tre-mem.h" | ||
| 14 | #include "tre-internal.h" | ||
| 15 | #include "tre-compile.h" | ||
| 16 | |||
| 17 | /* The different AST node types. */ | ||
| 18 | typedef enum { | ||
| 19 | LITERAL, | ||
| 20 | CATENATION, | ||
| 21 | ITERATION, | ||
| 22 | UNION | ||
| 23 | } tre_ast_type_t; | ||
| 24 | |||
| 25 | /* Special subtypes of TRE_LITERAL. */ | ||
| 26 | #define EMPTY -1 /* Empty leaf (denotes empty string). */ | ||
| 27 | #define ASSERTION -2 /* Assertion leaf. */ | ||
| 28 | #define TAG -3 /* Tag leaf. */ | ||
| 29 | #define BACKREF -4 /* Back reference leaf. */ | ||
| 30 | #define PARAMETER -5 /* Parameter. */ | ||
| 31 | |||
| 32 | #define IS_SPECIAL(x) ((x)->code_min < 0) | ||
| 33 | #define IS_EMPTY(x) ((x)->code_min == EMPTY) | ||
| 34 | #define IS_ASSERTION(x) ((x)->code_min == ASSERTION) | ||
| 35 | #define IS_TAG(x) ((x)->code_min == TAG) | ||
| 36 | #define IS_BACKREF(x) ((x)->code_min == BACKREF) | ||
| 37 | #define IS_PARAMETER(x) ((x)->code_min == PARAMETER) | ||
| 38 | |||
| 39 | |||
| 40 | /* A generic AST node. All AST nodes consist of this node on the top | ||
| 41 | level with `obj' pointing to the actual content. */ | ||
| 42 | typedef struct { | ||
| 43 | tre_ast_type_t type; /* Type of the node. */ | ||
| 44 | void *obj; /* Pointer to actual node. */ | ||
| 45 | int nullable; | ||
| 46 | int submatch_id; | ||
| 47 | int num_submatches; | ||
| 48 | int num_tags; | ||
| 49 | tre_pos_and_tags_t *firstpos; | ||
| 50 | tre_pos_and_tags_t *lastpos; | ||
| 51 | } tre_ast_node_t; | ||
| 52 | |||
| 53 | |||
| 54 | /* A "literal" node. These are created for assertions, back references, | ||
| 55 | tags, matching parameter settings, and all expressions that match one | ||
| 56 | character. */ | ||
| 57 | typedef struct { | ||
| 58 | long code_min; | ||
| 59 | long code_max; | ||
| 60 | int position; | ||
| 61 | union { | ||
| 62 | tre_ctype_t class; | ||
| 63 | int *params; | ||
| 64 | } u; | ||
| 65 | tre_ctype_t *neg_classes; | ||
| 66 | } tre_literal_t; | ||
| 67 | |||
| 68 | /* A "catenation" node. These are created when two regexps are concatenated. | ||
| 69 | If there are more than one subexpressions in sequence, the `left' part | ||
| 70 | holds all but the last, and `right' part holds the last subexpression | ||
| 71 | (catenation is left associative). */ | ||
| 72 | typedef struct { | ||
| 73 | tre_ast_node_t *left; | ||
| 74 | tre_ast_node_t *right; | ||
| 75 | } tre_catenation_t; | ||
| 76 | |||
| 77 | /* An "iteration" node. These are created for the "*", "+", "?", and "{m,n}" | ||
| 78 | operators. */ | ||
| 79 | typedef struct { | ||
| 80 | /* Subexpression to match. */ | ||
| 81 | tre_ast_node_t *arg; | ||
| 82 | /* Minimum number of consecutive matches. */ | ||
| 83 | int min; | ||
| 84 | /* Maximum number of consecutive matches. */ | ||
| 85 | int max; | ||
| 86 | /* If 0, match as many characters as possible, if 1 match as few as | ||
| 87 | possible. Note that this does not always mean the same thing as | ||
| 88 | matching as many/few repetitions as possible. */ | ||
| 89 | unsigned int minimal:1; | ||
| 90 | /* Approximate matching parameters (or NULL). */ | ||
| 91 | int *params; | ||
| 92 | } tre_iteration_t; | ||
| 93 | |||
| 94 | /* An "union" node. These are created for the "|" operator. */ | ||
| 95 | typedef struct { | ||
| 96 | tre_ast_node_t *left; | ||
| 97 | tre_ast_node_t *right; | ||
| 98 | } tre_union_t; | ||
| 99 | |||
| 100 | tre_ast_node_t * | ||
| 101 | tre_ast_new_node(tre_mem_t mem, tre_ast_type_t type, size_t size); | ||
| 102 | |||
| 103 | tre_ast_node_t * | ||
| 104 | tre_ast_new_literal(tre_mem_t mem, int code_min, int code_max, int position); | ||
| 105 | |||
| 106 | tre_ast_node_t * | ||
| 107 | tre_ast_new_iter(tre_mem_t mem, tre_ast_node_t *arg, int min, int max, | ||
| 108 | int minimal); | ||
| 109 | |||
| 110 | tre_ast_node_t * | ||
| 111 | tre_ast_new_union(tre_mem_t mem, tre_ast_node_t *left, tre_ast_node_t *right); | ||
| 112 | |||
| 113 | tre_ast_node_t * | ||
| 114 | tre_ast_new_catenation(tre_mem_t mem, tre_ast_node_t *left, | ||
| 115 | tre_ast_node_t *right); | ||
| 116 | |||
| 117 | #ifdef TRE_DEBUG | ||
| 118 | void | ||
| 119 | tre_ast_print(tre_ast_node_t *tree); | ||
| 120 | |||
| 121 | /* XXX - rethink AST printing API */ | ||
| 122 | void | ||
| 123 | tre_print_params(int *params); | ||
| 124 | #endif /* TRE_DEBUG */ | ||
| 125 | |||
| 126 | #endif /* TRE_AST_H */ | ||
| 127 | |||
| 128 | /* EOF */ | ||
| @@ -1,2296 +0,0 @@ | |||
| 1 | /* | ||
| 2 | tre-compile.c - TRE regex compiler | ||
| 3 | |||
| 4 | This software is released under a BSD-style license. | ||
| 5 | See the file LICENSE for details and copyright. | ||
| 6 | |||
| 7 | */ | ||
| 8 | |||
| 9 | /* | ||
| 10 | TODO: | ||
| 11 | - Fix tre_ast_to_tnfa() to recurse using a stack instead of recursive | ||
| 12 | function calls. | ||
| 13 | */ | ||
| 14 | |||
| 15 | |||
| 16 | #ifdef HAVE_CONFIG_H | ||
| 17 | #include <config.h> | ||
| 18 | #endif /* HAVE_CONFIG_H */ | ||
| 19 | #include <stdio.h> | ||
| 20 | #include <assert.h> | ||
| 21 | #include <string.h> | ||
| 22 | |||
| 23 | #include "tre-internal.h" | ||
| 24 | #include "tre-mem.h" | ||
| 25 | #include "tre-stack.h" | ||
| 26 | #include "tre-ast.h" | ||
| 27 | #include "tre-parse.h" | ||
| 28 | #include "tre-compile.h" | ||
| 29 | #include "tre.h" | ||
| 30 | #include "xmalloc.h" | ||
| 31 | |||
| 32 | /* | ||
| 33 | Algorithms to setup tags so that submatch addressing can be done. | ||
| 34 | */ | ||
| 35 | |||
| 36 | |||
| 37 | /* Inserts a catenation node to the root of the tree given in `node'. | ||
| 38 | As the left child a new tag with number `tag_id' to `node' is added, | ||
| 39 | and the right child is the old root. */ | ||
| 40 | static reg_errcode_t | ||
| 41 | tre_add_tag_left(tre_mem_t mem, tre_ast_node_t *node, int tag_id) | ||
| 42 | { | ||
| 43 | tre_catenation_t *c; | ||
| 44 | |||
| 45 | DPRINT(("add_tag_left: tag %d\n", tag_id)); | ||
| 46 | |||
| 47 | c = tre_mem_alloc(mem, sizeof(*c)); | ||
| 48 | if (c == NULL) | ||
| 49 | return REG_ESPACE; | ||
| 50 | c->left = tre_ast_new_literal(mem, TAG, tag_id, -1); | ||
| 51 | if (c->left == NULL) | ||
| 52 | return REG_ESPACE; | ||
| 53 | c->right = tre_mem_alloc(mem, sizeof(tre_ast_node_t)); | ||
| 54 | if (c->right == NULL) | ||
| 55 | return REG_ESPACE; | ||
| 56 | |||
| 57 | c->right->obj = node->obj; | ||
| 58 | c->right->type = node->type; | ||
| 59 | c->right->nullable = -1; | ||
| 60 | c->right->submatch_id = -1; | ||
| 61 | c->right->firstpos = NULL; | ||
| 62 | c->right->lastpos = NULL; | ||
| 63 | c->right->num_tags = 0; | ||
| 64 | node->obj = c; | ||
| 65 | node->type = CATENATION; | ||
| 66 | return REG_OK; | ||
| 67 | } | ||
| 68 | |||
| 69 | /* Inserts a catenation node to the root of the tree given in `node'. | ||
| 70 | As the right child a new tag with number `tag_id' to `node' is added, | ||
| 71 | and the left child is the old root. */ | ||
| 72 | static reg_errcode_t | ||
| 73 | tre_add_tag_right(tre_mem_t mem, tre_ast_node_t *node, int tag_id) | ||
| 74 | { | ||
| 75 | tre_catenation_t *c; | ||
| 76 | |||
| 77 | DPRINT(("tre_add_tag_right: tag %d\n", tag_id)); | ||
| 78 | |||
| 79 | c = tre_mem_alloc(mem, sizeof(*c)); | ||
| 80 | if (c == NULL) | ||
| 81 | return REG_ESPACE; | ||
| 82 | c->right = tre_ast_new_literal(mem, TAG, tag_id, -1); | ||
| 83 | if (c->right == NULL) | ||
| 84 | return REG_ESPACE; | ||
| 85 | c->left = tre_mem_alloc(mem, sizeof(tre_ast_node_t)); | ||
| 86 | if (c->left == NULL) | ||
| 87 | return REG_ESPACE; | ||
| 88 | |||
| 89 | c->left->obj = node->obj; | ||
| 90 | c->left->type = node->type; | ||
| 91 | c->left->nullable = -1; | ||
| 92 | c->left->submatch_id = -1; | ||
| 93 | c->left->firstpos = NULL; | ||
| 94 | c->left->lastpos = NULL; | ||
| 95 | c->left->num_tags = 0; | ||
| 96 | node->obj = c; | ||
| 97 | node->type = CATENATION; | ||
| 98 | return REG_OK; | ||
| 99 | } | ||
| 100 | |||
| 101 | typedef enum { | ||
| 102 | ADDTAGS_RECURSE, | ||
| 103 | ADDTAGS_AFTER_ITERATION, | ||
| 104 | ADDTAGS_AFTER_UNION_LEFT, | ||
| 105 | ADDTAGS_AFTER_UNION_RIGHT, | ||
| 106 | ADDTAGS_AFTER_CAT_LEFT, | ||
| 107 | ADDTAGS_AFTER_CAT_RIGHT, | ||
| 108 | ADDTAGS_SET_SUBMATCH_END | ||
| 109 | } tre_addtags_symbol_t; | ||
| 110 | |||
| 111 | |||
| 112 | typedef struct { | ||
| 113 | int tag; | ||
| 114 | int next_tag; | ||
| 115 | } tre_tag_states_t; | ||
| 116 | |||
| 117 | |||
| 118 | /* Go through `regset' and set submatch data for submatches that are | ||
| 119 | using this tag. */ | ||
| 120 | static void | ||
| 121 | tre_purge_regset(int *regset, tre_tnfa_t *tnfa, int tag) | ||
| 122 | { | ||
| 123 | int i; | ||
| 124 | |||
| 125 | for (i = 0; regset[i] >= 0; i++) | ||
| 126 | { | ||
| 127 | int id = regset[i] / 2; | ||
| 128 | int start = !(regset[i] % 2); | ||
| 129 | DPRINT((" Using tag %d for %s offset of " | ||
| 130 | "submatch %d\n", tag, | ||
| 131 | start ? "start" : "end", id)); | ||
| 132 | if (start) | ||
| 133 | tnfa->submatch_data[id].so_tag = tag; | ||
| 134 | else | ||
| 135 | tnfa->submatch_data[id].eo_tag = tag; | ||
| 136 | } | ||
| 137 | regset[0] = -1; | ||
| 138 | } | ||
| 139 | |||
| 140 | |||
| 141 | /* Adds tags to appropriate locations in the parse tree in `tree', so that | ||
| 142 | subexpressions marked for submatch addressing can be traced. */ | ||
| 143 | static reg_errcode_t | ||
| 144 | tre_add_tags(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *tree, | ||
| 145 | tre_tnfa_t *tnfa) | ||
| 146 | { | ||
| 147 | reg_errcode_t status = REG_OK; | ||
| 148 | tre_addtags_symbol_t symbol; | ||
| 149 | tre_ast_node_t *node = tree; /* Tree node we are currently looking at. */ | ||
| 150 | int bottom = tre_stack_num_objects(stack); | ||
| 151 | /* True for first pass (counting number of needed tags) */ | ||
| 152 | int first_pass = (mem == NULL || tnfa == NULL); | ||
| 153 | int *regset, *orig_regset; | ||
| 154 | int num_tags = 0; /* Total number of tags. */ | ||
| 155 | int num_minimals = 0; /* Number of special minimal tags. */ | ||
| 156 | int tag = 0; /* The tag that is to be added next. */ | ||
| 157 | int next_tag = 1; /* Next tag to use after this one. */ | ||
| 158 | int *parents; /* Stack of submatches the current submatch is | ||
| 159 | contained in. */ | ||
| 160 | int minimal_tag = -1; /* Tag that marks the beginning of a minimal match. */ | ||
| 161 | tre_tag_states_t *saved_states; | ||
| 162 | |||
| 163 | tre_tag_direction_t direction = TRE_TAG_MINIMIZE; | ||
| 164 | if (!first_pass) | ||
| 165 | { | ||
| 166 | tnfa->end_tag = 0; | ||
| 167 | tnfa->minimal_tags[0] = -1; | ||
| 168 | } | ||
| 169 | |||
| 170 | regset = xmalloc(sizeof(*regset) * ((tnfa->num_submatches + 1) * 2)); | ||
| 171 | if (regset == NULL) | ||
| 172 | return REG_ESPACE; | ||
| 173 | regset[0] = -1; | ||
| 174 | orig_regset = regset; | ||
| 175 | |||
| 176 | parents = xmalloc(sizeof(*parents) * (tnfa->num_submatches + 1)); | ||
| 177 | if (parents == NULL) | ||
| 178 | { | ||
| 179 | xfree(regset); | ||
| 180 | return REG_ESPACE; | ||
| 181 | } | ||
| 182 | parents[0] = -1; | ||
| 183 | |||
| 184 | saved_states = xmalloc(sizeof(*saved_states) * (tnfa->num_submatches + 1)); | ||
| 185 | if (saved_states == NULL) | ||
| 186 | { | ||
| 187 | xfree(regset); | ||
| 188 | xfree(parents); | ||
| 189 | return REG_ESPACE; | ||
| 190 | } | ||
| 191 | else | ||
| 192 | { | ||
| 193 | unsigned int i; | ||
| 194 | for (i = 0; i <= tnfa->num_submatches; i++) | ||
| 195 | saved_states[i].tag = -1; | ||
| 196 | } | ||
| 197 | |||
| 198 | STACK_PUSH(stack, voidptr, node); | ||
| 199 | STACK_PUSH(stack, int, ADDTAGS_RECURSE); | ||
| 200 | |||
| 201 | while (tre_stack_num_objects(stack) > bottom) | ||
| 202 | { | ||
| 203 | if (status != REG_OK) | ||
| 204 | break; | ||
| 205 | |||
| 206 | symbol = (tre_addtags_symbol_t)tre_stack_pop_int(stack); | ||
| 207 | switch (symbol) | ||
| 208 | { | ||
| 209 | |||
| 210 | case ADDTAGS_SET_SUBMATCH_END: | ||
| 211 | { | ||
| 212 | int id = tre_stack_pop_int(stack); | ||
| 213 | int i; | ||
| 214 | |||
| 215 | /* Add end of this submatch to regset. */ | ||
| 216 | for (i = 0; regset[i] >= 0; i++); | ||
| 217 | regset[i] = id * 2 + 1; | ||
| 218 | regset[i + 1] = -1; | ||
| 219 | |||
| 220 | /* Pop this submatch from the parents stack. */ | ||
| 221 | for (i = 0; parents[i] >= 0; i++); | ||
| 222 | parents[i - 1] = -1; | ||
| 223 | break; | ||
| 224 | } | ||
| 225 | |||
| 226 | case ADDTAGS_RECURSE: | ||
| 227 | node = tre_stack_pop_voidptr(stack); | ||
| 228 | |||
| 229 | if (node->submatch_id >= 0) | ||
| 230 | { | ||
| 231 | int id = node->submatch_id; | ||
| 232 | int i; | ||
| 233 | |||
| 234 | |||
| 235 | /* Add start of this submatch to regset. */ | ||
| 236 | for (i = 0; regset[i] >= 0; i++); | ||
| 237 | regset[i] = id * 2; | ||
| 238 | regset[i + 1] = -1; | ||
| 239 | |||
| 240 | if (!first_pass) | ||
| 241 | { | ||
| 242 | for (i = 0; parents[i] >= 0; i++); | ||
| 243 | tnfa->submatch_data[id].parents = NULL; | ||
| 244 | if (i > 0) | ||
| 245 | { | ||
| 246 | int *p = xmalloc(sizeof(*p) * (i + 1)); | ||
| 247 | if (p == NULL) | ||
| 248 | { | ||
| 249 | status = REG_ESPACE; | ||
| 250 | break; | ||
| 251 | } | ||
| 252 | assert(tnfa->submatch_data[id].parents == NULL); | ||
| 253 | tnfa->submatch_data[id].parents = p; | ||
| 254 | for (i = 0; parents[i] >= 0; i++) | ||
| 255 | p[i] = parents[i]; | ||
| 256 | p[i] = -1; | ||
| 257 | } | ||
| 258 | } | ||
| 259 | |||
| 260 | /* Add end of this submatch to regset after processing this | ||
| 261 | node. */ | ||
| 262 | STACK_PUSHX(stack, int, node->submatch_id); | ||
| 263 | STACK_PUSHX(stack, int, ADDTAGS_SET_SUBMATCH_END); | ||
| 264 | } | ||
| 265 | |||
| 266 | switch (node->type) | ||
| 267 | { | ||
| 268 | case LITERAL: | ||
| 269 | { | ||
| 270 | tre_literal_t *lit = node->obj; | ||
| 271 | |||
| 272 | if (!IS_SPECIAL(lit) || IS_BACKREF(lit)) | ||
| 273 | { | ||
| 274 | int i; | ||
| 275 | DPRINT(("Literal %d-%d\n", | ||
| 276 | (int)lit->code_min, (int)lit->code_max)); | ||
| 277 | if (regset[0] >= 0) | ||
| 278 | { | ||
| 279 | /* Regset is not empty, so add a tag before the | ||
| 280 | literal or backref. */ | ||
| 281 | if (!first_pass) | ||
| 282 | { | ||
| 283 | status = tre_add_tag_left(mem, node, tag); | ||
| 284 | tnfa->tag_directions[tag] = direction; | ||
| 285 | if (minimal_tag >= 0) | ||
| 286 | { | ||
| 287 | DPRINT(("Minimal %d, %d\n", minimal_tag, tag)); | ||
| 288 | for (i = 0; tnfa->minimal_tags[i] >= 0; i++); | ||
| 289 | tnfa->minimal_tags[i] = tag; | ||
| 290 | tnfa->minimal_tags[i + 1] = minimal_tag; | ||
| 291 | tnfa->minimal_tags[i + 2] = -1; | ||
| 292 | minimal_tag = -1; | ||
| 293 | num_minimals++; | ||
| 294 | } | ||
| 295 | tre_purge_regset(regset, tnfa, tag); | ||
| 296 | } | ||
| 297 | else | ||
| 298 | { | ||
| 299 | DPRINT((" num_tags = 1\n")); | ||
| 300 | node->num_tags = 1; | ||
| 301 | } | ||
| 302 | |||
| 303 | DPRINT((" num_tags++\n")); | ||
| 304 | regset[0] = -1; | ||
| 305 | tag = next_tag; | ||
| 306 | num_tags++; | ||
| 307 | next_tag++; | ||
| 308 | } | ||
| 309 | } | ||
| 310 | else | ||
| 311 | { | ||
| 312 | assert(!IS_TAG(lit)); | ||
| 313 | } | ||
| 314 | break; | ||
| 315 | } | ||
| 316 | case CATENATION: | ||
| 317 | { | ||
| 318 | tre_catenation_t *cat = node->obj; | ||
| 319 | tre_ast_node_t *left = cat->left; | ||
| 320 | tre_ast_node_t *right = cat->right; | ||
| 321 | int reserved_tag = -1; | ||
| 322 | DPRINT(("Catenation, next_tag = %d\n", next_tag)); | ||
| 323 | |||
| 324 | |||
| 325 | /* After processing right child. */ | ||
| 326 | STACK_PUSHX(stack, voidptr, node); | ||
| 327 | STACK_PUSHX(stack, int, ADDTAGS_AFTER_CAT_RIGHT); | ||
| 328 | |||
| 329 | /* Process right child. */ | ||
| 330 | STACK_PUSHX(stack, voidptr, right); | ||
| 331 | STACK_PUSHX(stack, int, ADDTAGS_RECURSE); | ||
| 332 | |||
| 333 | /* After processing left child. */ | ||
| 334 | STACK_PUSHX(stack, int, next_tag + left->num_tags); | ||
| 335 | DPRINT((" Pushing %d for after left\n", | ||
| 336 | next_tag + left->num_tags)); | ||
| 337 | if (left->num_tags > 0 && right->num_tags > 0) | ||
| 338 | { | ||
| 339 | /* Reserve the next tag to the right child. */ | ||
| 340 | DPRINT((" Reserving next_tag %d to right child\n", | ||
| 341 | next_tag)); | ||
| 342 | reserved_tag = next_tag; | ||
| 343 | next_tag++; | ||
| 344 | } | ||
| 345 | STACK_PUSHX(stack, int, reserved_tag); | ||
| 346 | STACK_PUSHX(stack, int, ADDTAGS_AFTER_CAT_LEFT); | ||
| 347 | |||
| 348 | /* Process left child. */ | ||
| 349 | STACK_PUSHX(stack, voidptr, left); | ||
| 350 | STACK_PUSHX(stack, int, ADDTAGS_RECURSE); | ||
| 351 | |||
| 352 | } | ||
| 353 | break; | ||
| 354 | case ITERATION: | ||
| 355 | { | ||
| 356 | tre_iteration_t *iter = node->obj; | ||
| 357 | DPRINT(("Iteration\n")); | ||
| 358 | |||
| 359 | if (first_pass) | ||
| 360 | { | ||
| 361 | STACK_PUSHX(stack, int, regset[0] >= 0 || iter->minimal); | ||
| 362 | } | ||
| 363 | else | ||
| 364 | { | ||
| 365 | STACK_PUSHX(stack, int, tag); | ||
| 366 | STACK_PUSHX(stack, int, iter->minimal); | ||
| 367 | } | ||
| 368 | STACK_PUSHX(stack, voidptr, node); | ||
| 369 | STACK_PUSHX(stack, int, ADDTAGS_AFTER_ITERATION); | ||
| 370 | |||
| 371 | STACK_PUSHX(stack, voidptr, iter->arg); | ||
| 372 | STACK_PUSHX(stack, int, ADDTAGS_RECURSE); | ||
| 373 | |||
| 374 | /* Regset is not empty, so add a tag here. */ | ||
| 375 | if (regset[0] >= 0 || iter->minimal) | ||
| 376 | { | ||
| 377 | if (!first_pass) | ||
| 378 | { | ||
| 379 | int i; | ||
| 380 | status = tre_add_tag_left(mem, node, tag); | ||
| 381 | if (iter->minimal) | ||
| 382 | tnfa->tag_directions[tag] = TRE_TAG_MAXIMIZE; | ||
| 383 | else | ||
| 384 | tnfa->tag_directions[tag] = direction; | ||
| 385 | if (minimal_tag >= 0) | ||
| 386 | { | ||
| 387 | DPRINT(("Minimal %d, %d\n", minimal_tag, tag)); | ||
| 388 | for (i = 0; tnfa->minimal_tags[i] >= 0; i++); | ||
| 389 | tnfa->minimal_tags[i] = tag; | ||
| 390 | tnfa->minimal_tags[i + 1] = minimal_tag; | ||
| 391 | tnfa->minimal_tags[i + 2] = -1; | ||
| 392 | minimal_tag = -1; | ||
| 393 | num_minimals++; | ||
| 394 | } | ||
| 395 | tre_purge_regset(regset, tnfa, tag); | ||
| 396 | } | ||
| 397 | |||
| 398 | DPRINT((" num_tags++\n")); | ||
| 399 | regset[0] = -1; | ||
| 400 | tag = next_tag; | ||
| 401 | num_tags++; | ||
| 402 | next_tag++; | ||
| 403 | } | ||
| 404 | direction = TRE_TAG_MINIMIZE; | ||
| 405 | } | ||
| 406 | break; | ||
| 407 | case UNION: | ||
| 408 | { | ||
| 409 | tre_union_t *uni = node->obj; | ||
| 410 | tre_ast_node_t *left = uni->left; | ||
| 411 | tre_ast_node_t *right = uni->right; | ||
| 412 | int left_tag; | ||
| 413 | int right_tag; | ||
| 414 | |||
| 415 | if (regset[0] >= 0) | ||
| 416 | { | ||
| 417 | left_tag = next_tag; | ||
| 418 | right_tag = next_tag + 1; | ||
| 419 | } | ||
| 420 | else | ||
| 421 | { | ||
| 422 | left_tag = tag; | ||
| 423 | right_tag = next_tag; | ||
| 424 | } | ||
| 425 | |||
| 426 | DPRINT(("Union\n")); | ||
| 427 | |||
| 428 | /* After processing right child. */ | ||
| 429 | STACK_PUSHX(stack, int, right_tag); | ||
| 430 | STACK_PUSHX(stack, int, left_tag); | ||
| 431 | STACK_PUSHX(stack, voidptr, regset); | ||
| 432 | STACK_PUSHX(stack, int, regset[0] >= 0); | ||
| 433 | STACK_PUSHX(stack, voidptr, node); | ||
| 434 | STACK_PUSHX(stack, voidptr, right); | ||
| 435 | STACK_PUSHX(stack, voidptr, left); | ||
| 436 | STACK_PUSHX(stack, int, ADDTAGS_AFTER_UNION_RIGHT); | ||
| 437 | |||
| 438 | /* Process right child. */ | ||
| 439 | STACK_PUSHX(stack, voidptr, right); | ||
| 440 | STACK_PUSHX(stack, int, ADDTAGS_RECURSE); | ||
| 441 | |||
| 442 | /* After processing left child. */ | ||
| 443 | STACK_PUSHX(stack, int, ADDTAGS_AFTER_UNION_LEFT); | ||
| 444 | |||
| 445 | /* Process left child. */ | ||
| 446 | STACK_PUSHX(stack, voidptr, left); | ||
| 447 | STACK_PUSHX(stack, int, ADDTAGS_RECURSE); | ||
| 448 | |||
| 449 | /* Regset is not empty, so add a tag here. */ | ||
| 450 | if (regset[0] >= 0) | ||
| 451 | { | ||
| 452 | if (!first_pass) | ||
| 453 | { | ||
| 454 | int i; | ||
| 455 | status = tre_add_tag_left(mem, node, tag); | ||
| 456 | tnfa->tag_directions[tag] = direction; | ||
| 457 | if (minimal_tag >= 0) | ||
| 458 | { | ||
| 459 | DPRINT(("Minimal %d, %d\n", minimal_tag, tag)); | ||
| 460 | for (i = 0; tnfa->minimal_tags[i] >= 0; i++); | ||
| 461 | tnfa->minimal_tags[i] = tag; | ||
| 462 | tnfa->minimal_tags[i + 1] = minimal_tag; | ||
| 463 | tnfa->minimal_tags[i + 2] = -1; | ||
| 464 | minimal_tag = -1; | ||
| 465 | num_minimals++; | ||
| 466 | } | ||
| 467 | tre_purge_regset(regset, tnfa, tag); | ||
| 468 | } | ||
| 469 | |||
| 470 | DPRINT((" num_tags++\n")); | ||
| 471 | regset[0] = -1; | ||
| 472 | tag = next_tag; | ||
| 473 | num_tags++; | ||
| 474 | next_tag++; | ||
| 475 | } | ||
| 476 | |||
| 477 | if (node->num_submatches > 0) | ||
| 478 | { | ||
| 479 | /* The next two tags are reserved for markers. */ | ||
| 480 | next_tag++; | ||
| 481 | tag = next_tag; | ||
| 482 | next_tag++; | ||
| 483 | } | ||
| 484 | |||
| 485 | break; | ||
| 486 | } | ||
| 487 | } | ||
| 488 | |||
| 489 | if (node->submatch_id >= 0) | ||
| 490 | { | ||
| 491 | int i; | ||
| 492 | /* Push this submatch on the parents stack. */ | ||
| 493 | for (i = 0; parents[i] >= 0; i++); | ||
| 494 | parents[i] = node->submatch_id; | ||
| 495 | parents[i + 1] = -1; | ||
| 496 | } | ||
| 497 | |||
| 498 | break; /* end case: ADDTAGS_RECURSE */ | ||
| 499 | |||
| 500 | case ADDTAGS_AFTER_ITERATION: | ||
| 501 | { | ||
| 502 | int minimal = 0; | ||
| 503 | int enter_tag; | ||
| 504 | node = tre_stack_pop_voidptr(stack); | ||
| 505 | if (first_pass) | ||
| 506 | { | ||
| 507 | node->num_tags = ((tre_iteration_t *)node->obj)->arg->num_tags | ||
| 508 | + tre_stack_pop_int(stack); | ||
| 509 | minimal_tag = -1; | ||
| 510 | } | ||
| 511 | else | ||
| 512 | { | ||
| 513 | minimal = tre_stack_pop_int(stack); | ||
| 514 | enter_tag = tre_stack_pop_int(stack); | ||
| 515 | if (minimal) | ||
| 516 | minimal_tag = enter_tag; | ||
| 517 | } | ||
| 518 | |||
| 519 | DPRINT(("After iteration\n")); | ||
| 520 | if (!first_pass) | ||
| 521 | { | ||
| 522 | DPRINT((" Setting direction to %s\n", | ||
| 523 | minimal ? "minimize" : "maximize")); | ||
| 524 | if (minimal) | ||
| 525 | direction = TRE_TAG_MINIMIZE; | ||
| 526 | else | ||
| 527 | direction = TRE_TAG_MAXIMIZE; | ||
| 528 | } | ||
| 529 | break; | ||
| 530 | } | ||
| 531 | |||
| 532 | case ADDTAGS_AFTER_CAT_LEFT: | ||
| 533 | { | ||
| 534 | int new_tag = tre_stack_pop_int(stack); | ||
| 535 | next_tag = tre_stack_pop_int(stack); | ||
| 536 | DPRINT(("After cat left, tag = %d, next_tag = %d\n", | ||
| 537 | tag, next_tag)); | ||
| 538 | if (new_tag >= 0) | ||
| 539 | { | ||
| 540 | DPRINT((" Setting tag to %d\n", new_tag)); | ||
| 541 | tag = new_tag; | ||
| 542 | } | ||
| 543 | break; | ||
| 544 | } | ||
| 545 | |||
| 546 | case ADDTAGS_AFTER_CAT_RIGHT: | ||
| 547 | DPRINT(("After cat right\n")); | ||
| 548 | node = tre_stack_pop_voidptr(stack); | ||
| 549 | if (first_pass) | ||
| 550 | node->num_tags = ((tre_catenation_t *)node->obj)->left->num_tags | ||
| 551 | + ((tre_catenation_t *)node->obj)->right->num_tags; | ||
| 552 | break; | ||
| 553 | |||
| 554 | case ADDTAGS_AFTER_UNION_LEFT: | ||
| 555 | DPRINT(("After union left\n")); | ||
| 556 | /* Lift the bottom of the `regset' array so that when processing | ||
| 557 | the right operand the items currently in the array are | ||
| 558 | invisible. The original bottom was saved at ADDTAGS_UNION and | ||
| 559 | will be restored at ADDTAGS_AFTER_UNION_RIGHT below. */ | ||
| 560 | while (*regset >= 0) | ||
| 561 | regset++; | ||
| 562 | break; | ||
| 563 | |||
| 564 | case ADDTAGS_AFTER_UNION_RIGHT: | ||
| 565 | { | ||
| 566 | int added_tags, tag_left, tag_right; | ||
| 567 | tre_ast_node_t *left = tre_stack_pop_voidptr(stack); | ||
| 568 | tre_ast_node_t *right = tre_stack_pop_voidptr(stack); | ||
| 569 | DPRINT(("After union right\n")); | ||
| 570 | node = tre_stack_pop_voidptr(stack); | ||
| 571 | added_tags = tre_stack_pop_int(stack); | ||
| 572 | if (first_pass) | ||
| 573 | { | ||
| 574 | node->num_tags = ((tre_union_t *)node->obj)->left->num_tags | ||
| 575 | + ((tre_union_t *)node->obj)->right->num_tags + added_tags | ||
| 576 | + ((node->num_submatches > 0) ? 2 : 0); | ||
| 577 | } | ||
| 578 | regset = tre_stack_pop_voidptr(stack); | ||
| 579 | tag_left = tre_stack_pop_int(stack); | ||
| 580 | tag_right = tre_stack_pop_int(stack); | ||
| 581 | |||
| 582 | /* Add tags after both children, the left child gets a smaller | ||
| 583 | tag than the right child. This guarantees that we prefer | ||
| 584 | the left child over the right child. */ | ||
| 585 | /* XXX - This is not always necessary (if the children have | ||
| 586 | tags which must be seen for every match of that child). */ | ||
| 587 | /* XXX - Check if this is the only place where tre_add_tag_right | ||
| 588 | is used. If so, use tre_add_tag_left (putting the tag before | ||
| 589 | the child as opposed after the child) and throw away | ||
| 590 | tre_add_tag_right. */ | ||
| 591 | if (node->num_submatches > 0) | ||
| 592 | { | ||
| 593 | if (!first_pass) | ||
| 594 | { | ||
| 595 | status = tre_add_tag_right(mem, left, tag_left); | ||
| 596 | tnfa->tag_directions[tag_left] = TRE_TAG_MAXIMIZE; | ||
| 597 | status = tre_add_tag_right(mem, right, tag_right); | ||
| 598 | tnfa->tag_directions[tag_right] = TRE_TAG_MAXIMIZE; | ||
| 599 | } | ||
| 600 | DPRINT((" num_tags += 2\n")); | ||
| 601 | num_tags += 2; | ||
| 602 | } | ||
| 603 | direction = TRE_TAG_MAXIMIZE; | ||
| 604 | break; | ||
| 605 | } | ||
| 606 | |||
| 607 | default: | ||
| 608 | assert(0); | ||
| 609 | break; | ||
| 610 | |||
| 611 | } /* end switch(symbol) */ | ||
| 612 | } /* end while(tre_stack_num_objects(stack) > bottom) */ | ||
| 613 | |||
| 614 | if (!first_pass) | ||
| 615 | tre_purge_regset(regset, tnfa, tag); | ||
| 616 | |||
| 617 | if (!first_pass && minimal_tag >= 0) | ||
| 618 | { | ||
| 619 | int i; | ||
| 620 | DPRINT(("Minimal %d, %d\n", minimal_tag, tag)); | ||
| 621 | for (i = 0; tnfa->minimal_tags[i] >= 0; i++); | ||
| 622 | tnfa->minimal_tags[i] = tag; | ||
| 623 | tnfa->minimal_tags[i + 1] = minimal_tag; | ||
| 624 | tnfa->minimal_tags[i + 2] = -1; | ||
| 625 | minimal_tag = -1; | ||
| 626 | num_minimals++; | ||
| 627 | } | ||
| 628 | |||
| 629 | DPRINT(("tre_add_tags: %s complete. Number of tags %d.\n", | ||
| 630 | first_pass? "First pass" : "Second pass", num_tags)); | ||
| 631 | |||
| 632 | assert(tree->num_tags == num_tags); | ||
| 633 | tnfa->end_tag = num_tags; | ||
| 634 | tnfa->num_tags = num_tags; | ||
| 635 | tnfa->num_minimals = num_minimals; | ||
| 636 | xfree(orig_regset); | ||
| 637 | xfree(parents); | ||
| 638 | xfree(saved_states); | ||
| 639 | return status; | ||
| 640 | } | ||
| 641 | |||
| 642 | |||
| 643 | |||
| 644 | /* | ||
| 645 | AST to TNFA compilation routines. | ||
| 646 | */ | ||
| 647 | |||
| 648 | typedef enum { | ||
| 649 | COPY_RECURSE, | ||
| 650 | COPY_SET_RESULT_PTR | ||
| 651 | } tre_copyast_symbol_t; | ||
| 652 | |||
| 653 | /* Flags for tre_copy_ast(). */ | ||
| 654 | #define COPY_REMOVE_TAGS 1 | ||
| 655 | #define COPY_MAXIMIZE_FIRST_TAG 2 | ||
| 656 | |||
| 657 | static reg_errcode_t | ||
| 658 | tre_copy_ast(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *ast, | ||
| 659 | int flags, int *pos_add, tre_tag_direction_t *tag_directions, | ||
| 660 | tre_ast_node_t **copy, int *max_pos) | ||
| 661 | { | ||
| 662 | reg_errcode_t status = REG_OK; | ||
| 663 | int bottom = tre_stack_num_objects(stack); | ||
| 664 | int num_copied = 0; | ||
| 665 | int first_tag = 1; | ||
| 666 | tre_ast_node_t **result = copy; | ||
| 667 | tre_copyast_symbol_t symbol; | ||
| 668 | |||
| 669 | STACK_PUSH(stack, voidptr, ast); | ||
| 670 | STACK_PUSH(stack, int, COPY_RECURSE); | ||
| 671 | |||
| 672 | while (status == REG_OK && tre_stack_num_objects(stack) > bottom) | ||
| 673 | { | ||
| 674 | tre_ast_node_t *node; | ||
| 675 | if (status != REG_OK) | ||
| 676 | break; | ||
| 677 | |||
| 678 | symbol = (tre_copyast_symbol_t)tre_stack_pop_int(stack); | ||
| 679 | switch (symbol) | ||
| 680 | { | ||
| 681 | case COPY_SET_RESULT_PTR: | ||
| 682 | result = tre_stack_pop_voidptr(stack); | ||
| 683 | break; | ||
| 684 | case COPY_RECURSE: | ||
| 685 | node = tre_stack_pop_voidptr(stack); | ||
| 686 | switch (node->type) | ||
| 687 | { | ||
| 688 | case LITERAL: | ||
| 689 | { | ||
| 690 | tre_literal_t *lit = node->obj; | ||
| 691 | int pos = lit->position; | ||
| 692 | int min = lit->code_min; | ||
| 693 | int max = lit->code_max; | ||
| 694 | if (!IS_SPECIAL(lit) || IS_BACKREF(lit)) | ||
| 695 | { | ||
| 696 | /* XXX - e.g. [ab] has only one position but two | ||
| 697 | nodes, so we are creating holes in the state space | ||
| 698 | here. Not fatal, just wastes memory. */ | ||
| 699 | pos += *pos_add; | ||
| 700 | num_copied++; | ||
| 701 | } | ||
| 702 | else if (IS_TAG(lit) && (flags & COPY_REMOVE_TAGS)) | ||
| 703 | { | ||
| 704 | /* Change this tag to empty. */ | ||
| 705 | min = EMPTY; | ||
| 706 | max = pos = -1; | ||
| 707 | } | ||
| 708 | else if (IS_TAG(lit) && (flags & COPY_MAXIMIZE_FIRST_TAG) | ||
| 709 | && first_tag) | ||
| 710 | { | ||
| 711 | /* Maximize the first tag. */ | ||
| 712 | tag_directions[max] = TRE_TAG_MAXIMIZE; | ||
| 713 | first_tag = 0; | ||
| 714 | } | ||
| 715 | *result = tre_ast_new_literal(mem, min, max, pos); | ||
| 716 | if (*result == NULL) | ||
| 717 | status = REG_ESPACE; | ||
| 718 | |||
| 719 | if (pos > *max_pos) | ||
| 720 | *max_pos = pos; | ||
| 721 | break; | ||
| 722 | } | ||
| 723 | case UNION: | ||
| 724 | { | ||
| 725 | tre_union_t *uni = node->obj; | ||
| 726 | tre_union_t *tmp; | ||
| 727 | *result = tre_ast_new_union(mem, uni->left, uni->right); | ||
| 728 | if (*result == NULL) | ||
| 729 | { | ||
| 730 | status = REG_ESPACE; | ||
| 731 | break; | ||
| 732 | } | ||
| 733 | tmp = (*result)->obj; | ||
| 734 | result = &tmp->left; | ||
| 735 | STACK_PUSHX(stack, voidptr, uni->right); | ||
| 736 | STACK_PUSHX(stack, int, COPY_RECURSE); | ||
| 737 | STACK_PUSHX(stack, voidptr, &tmp->right); | ||
| 738 | STACK_PUSHX(stack, int, COPY_SET_RESULT_PTR); | ||
| 739 | STACK_PUSHX(stack, voidptr, uni->left); | ||
| 740 | STACK_PUSHX(stack, int, COPY_RECURSE); | ||
| 741 | break; | ||
| 742 | } | ||
| 743 | case CATENATION: | ||
| 744 | { | ||
| 745 | tre_catenation_t *cat = node->obj; | ||
| 746 | tre_catenation_t *tmp; | ||
| 747 | *result = tre_ast_new_catenation(mem, cat->left, cat->right); | ||
| 748 | if (*result == NULL) | ||
| 749 | { | ||
| 750 | status = REG_ESPACE; | ||
| 751 | break; | ||
| 752 | } | ||
| 753 | tmp = (*result)->obj; | ||
| 754 | tmp->left = NULL; | ||
| 755 | tmp->right = NULL; | ||
| 756 | result = &tmp->left; | ||
| 757 | |||
| 758 | STACK_PUSHX(stack, voidptr, cat->right); | ||
| 759 | STACK_PUSHX(stack, int, COPY_RECURSE); | ||
| 760 | STACK_PUSHX(stack, voidptr, &tmp->right); | ||
| 761 | STACK_PUSHX(stack, int, COPY_SET_RESULT_PTR); | ||
| 762 | STACK_PUSHX(stack, voidptr, cat->left); | ||
| 763 | STACK_PUSHX(stack, int, COPY_RECURSE); | ||
| 764 | break; | ||
| 765 | } | ||
| 766 | case ITERATION: | ||
| 767 | { | ||
| 768 | tre_iteration_t *iter = node->obj; | ||
| 769 | STACK_PUSHX(stack, voidptr, iter->arg); | ||
| 770 | STACK_PUSHX(stack, int, COPY_RECURSE); | ||
| 771 | *result = tre_ast_new_iter(mem, iter->arg, iter->min, | ||
| 772 | iter->max, iter->minimal); | ||
| 773 | if (*result == NULL) | ||
| 774 | { | ||
| 775 | status = REG_ESPACE; | ||
| 776 | break; | ||
| 777 | } | ||
| 778 | iter = (*result)->obj; | ||
| 779 | result = &iter->arg; | ||
| 780 | break; | ||
| 781 | } | ||
| 782 | default: | ||
| 783 | assert(0); | ||
| 784 | break; | ||
| 785 | } | ||
| 786 | break; | ||
| 787 | } | ||
| 788 | } | ||
| 789 | *pos_add += num_copied; | ||
| 790 | return status; | ||
| 791 | } | ||
| 792 | |||
| 793 | typedef enum { | ||
| 794 | EXPAND_RECURSE, | ||
| 795 | EXPAND_AFTER_ITER | ||
| 796 | } tre_expand_ast_symbol_t; | ||
| 797 | |||
| 798 | /* Expands each iteration node that has a finite nonzero minimum or maximum | ||
| 799 | iteration count to a catenated sequence of copies of the node. */ | ||
| 800 | static reg_errcode_t | ||
| 801 | tre_expand_ast(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *ast, | ||
| 802 | int *position, tre_tag_direction_t *tag_directions, | ||
| 803 | int *max_depth) | ||
| 804 | { | ||
| 805 | reg_errcode_t status = REG_OK; | ||
| 806 | int bottom = tre_stack_num_objects(stack); | ||
| 807 | int pos_add = 0; | ||
| 808 | int pos_add_total = 0; | ||
| 809 | int max_pos = 0; | ||
| 810 | /* Current approximate matching parameters. */ | ||
| 811 | int params[TRE_PARAM_LAST]; | ||
| 812 | /* Approximate parameter nesting level. */ | ||
| 813 | int params_depth = 0; | ||
| 814 | int iter_depth = 0; | ||
| 815 | int i; | ||
| 816 | |||
| 817 | for (i = 0; i < TRE_PARAM_LAST; i++) | ||
| 818 | params[i] = TRE_PARAM_DEFAULT; | ||
| 819 | |||
| 820 | STACK_PUSHR(stack, voidptr, ast); | ||
| 821 | STACK_PUSHR(stack, int, EXPAND_RECURSE); | ||
| 822 | while (status == REG_OK && tre_stack_num_objects(stack) > bottom) | ||
| 823 | { | ||
| 824 | tre_ast_node_t *node; | ||
| 825 | tre_expand_ast_symbol_t symbol; | ||
| 826 | |||
| 827 | if (status != REG_OK) | ||
| 828 | break; | ||
| 829 | |||
| 830 | DPRINT(("pos_add %d\n", pos_add)); | ||
| 831 | |||
| 832 | symbol = (tre_expand_ast_symbol_t)tre_stack_pop_int(stack); | ||
| 833 | node = tre_stack_pop_voidptr(stack); | ||
| 834 | switch (symbol) | ||
| 835 | { | ||
| 836 | case EXPAND_RECURSE: | ||
| 837 | switch (node->type) | ||
| 838 | { | ||
| 839 | case LITERAL: | ||
| 840 | { | ||
| 841 | tre_literal_t *lit= node->obj; | ||
| 842 | if (!IS_SPECIAL(lit) || IS_BACKREF(lit)) | ||
| 843 | { | ||
| 844 | lit->position += pos_add; | ||
| 845 | if (lit->position > max_pos) | ||
| 846 | max_pos = lit->position; | ||
| 847 | } | ||
| 848 | break; | ||
| 849 | } | ||
| 850 | case UNION: | ||
| 851 | { | ||
| 852 | tre_union_t *uni = node->obj; | ||
| 853 | STACK_PUSHX(stack, voidptr, uni->right); | ||
| 854 | STACK_PUSHX(stack, int, EXPAND_RECURSE); | ||
| 855 | STACK_PUSHX(stack, voidptr, uni->left); | ||
| 856 | STACK_PUSHX(stack, int, EXPAND_RECURSE); | ||
| 857 | break; | ||
| 858 | } | ||
| 859 | case CATENATION: | ||
| 860 | { | ||
| 861 | tre_catenation_t *cat = node->obj; | ||
| 862 | STACK_PUSHX(stack, voidptr, cat->right); | ||
| 863 | STACK_PUSHX(stack, int, EXPAND_RECURSE); | ||
| 864 | STACK_PUSHX(stack, voidptr, cat->left); | ||
| 865 | STACK_PUSHX(stack, int, EXPAND_RECURSE); | ||
| 866 | break; | ||
| 867 | } | ||
| 868 | case ITERATION: | ||
| 869 | { | ||
| 870 | tre_iteration_t *iter = node->obj; | ||
| 871 | STACK_PUSHX(stack, int, pos_add); | ||
| 872 | STACK_PUSHX(stack, voidptr, node); | ||
| 873 | STACK_PUSHX(stack, int, EXPAND_AFTER_ITER); | ||
| 874 | STACK_PUSHX(stack, voidptr, iter->arg); | ||
| 875 | STACK_PUSHX(stack, int, EXPAND_RECURSE); | ||
| 876 | /* If we are going to expand this node at EXPAND_AFTER_ITER | ||
| 877 | then don't increase the `pos' fields of the nodes now, it | ||
| 878 | will get done when expanding. */ | ||
| 879 | if (iter->min > 1 || iter->max > 1) | ||
| 880 | pos_add = 0; | ||
| 881 | iter_depth++; | ||
| 882 | DPRINT(("iter\n")); | ||
| 883 | break; | ||
| 884 | } | ||
| 885 | default: | ||
| 886 | assert(0); | ||
| 887 | break; | ||
| 888 | } | ||
| 889 | break; | ||
| 890 | case EXPAND_AFTER_ITER: | ||
| 891 | { | ||
| 892 | tre_iteration_t *iter = node->obj; | ||
| 893 | int pos_add_last; | ||
| 894 | pos_add = tre_stack_pop_int(stack); | ||
| 895 | pos_add_last = pos_add; | ||
| 896 | if (iter->min > 1 || iter->max > 1) | ||
| 897 | { | ||
| 898 | tre_ast_node_t *seq1 = NULL, *seq2 = NULL; | ||
| 899 | int j; | ||
| 900 | int pos_add_save = pos_add; | ||
| 901 | |||
| 902 | /* Create a catenated sequence of copies of the node. */ | ||
| 903 | for (j = 0; j < iter->min; j++) | ||
| 904 | { | ||
| 905 | tre_ast_node_t *copy; | ||
| 906 | /* Remove tags from all but the last copy. */ | ||
| 907 | int flags = ((j + 1 < iter->min) | ||
| 908 | ? COPY_REMOVE_TAGS | ||
| 909 | : COPY_MAXIMIZE_FIRST_TAG); | ||
| 910 | DPRINT((" pos_add %d\n", pos_add)); | ||
| 911 | pos_add_save = pos_add; | ||
| 912 | status = tre_copy_ast(mem, stack, iter->arg, flags, | ||
| 913 | &pos_add, tag_directions, ©, | ||
| 914 | &max_pos); | ||
| 915 | if (status != REG_OK) | ||
| 916 | return status; | ||
| 917 | if (seq1 != NULL) | ||
| 918 | seq1 = tre_ast_new_catenation(mem, seq1, copy); | ||
| 919 | else | ||
| 920 | seq1 = copy; | ||
| 921 | if (seq1 == NULL) | ||
| 922 | return REG_ESPACE; | ||
| 923 | } | ||
| 924 | |||
| 925 | if (iter->max == -1) | ||
| 926 | { | ||
| 927 | /* No upper limit. */ | ||
| 928 | pos_add_save = pos_add; | ||
| 929 | status = tre_copy_ast(mem, stack, iter->arg, 0, | ||
| 930 | &pos_add, NULL, &seq2, &max_pos); | ||
| 931 | if (status != REG_OK) | ||
| 932 | return status; | ||
| 933 | seq2 = tre_ast_new_iter(mem, seq2, 0, -1, 0); | ||
| 934 | if (seq2 == NULL) | ||
| 935 | return REG_ESPACE; | ||
| 936 | } | ||
| 937 | else | ||
| 938 | { | ||
| 939 | for (j = iter->min; j < iter->max; j++) | ||
| 940 | { | ||
| 941 | tre_ast_node_t *tmp, *copy; | ||
| 942 | pos_add_save = pos_add; | ||
| 943 | status = tre_copy_ast(mem, stack, iter->arg, 0, | ||
| 944 | &pos_add, NULL, ©, &max_pos); | ||
| 945 | if (status != REG_OK) | ||
| 946 | return status; | ||
| 947 | if (seq2 != NULL) | ||
| 948 | seq2 = tre_ast_new_catenation(mem, copy, seq2); | ||
| 949 | else | ||
| 950 | seq2 = copy; | ||
| 951 | if (seq2 == NULL) | ||
| 952 | return REG_ESPACE; | ||
| 953 | tmp = tre_ast_new_literal(mem, EMPTY, -1, -1); | ||
| 954 | if (tmp == NULL) | ||
| 955 | return REG_ESPACE; | ||
| 956 | seq2 = tre_ast_new_union(mem, tmp, seq2); | ||
| 957 | if (seq2 == NULL) | ||
| 958 | return REG_ESPACE; | ||
| 959 | } | ||
| 960 | } | ||
| 961 | |||
| 962 | pos_add = pos_add_save; | ||
| 963 | if (seq1 == NULL) | ||
| 964 | seq1 = seq2; | ||
| 965 | else if (seq2 != NULL) | ||
| 966 | seq1 = tre_ast_new_catenation(mem, seq1, seq2); | ||
| 967 | if (seq1 == NULL) | ||
| 968 | return REG_ESPACE; | ||
| 969 | node->obj = seq1->obj; | ||
| 970 | node->type = seq1->type; | ||
| 971 | } | ||
| 972 | |||
| 973 | iter_depth--; | ||
| 974 | pos_add_total += pos_add - pos_add_last; | ||
| 975 | if (iter_depth == 0) | ||
| 976 | pos_add = pos_add_total; | ||
| 977 | |||
| 978 | /* If approximate parameters are specified, surround the result | ||
| 979 | with two parameter setting nodes. The one on the left sets | ||
| 980 | the specified parameters, and the one on the right restores | ||
| 981 | the old parameters. */ | ||
| 982 | if (iter->params) | ||
| 983 | { | ||
| 984 | tre_ast_node_t *tmp_l, *tmp_r, *tmp_node, *node_copy; | ||
| 985 | int *old_params; | ||
| 986 | |||
| 987 | tmp_l = tre_ast_new_literal(mem, PARAMETER, 0, -1); | ||
| 988 | if (!tmp_l) | ||
| 989 | return REG_ESPACE; | ||
| 990 | ((tre_literal_t *)tmp_l->obj)->u.params = iter->params; | ||
| 991 | iter->params[TRE_PARAM_DEPTH] = params_depth + 1; | ||
| 992 | tmp_r = tre_ast_new_literal(mem, PARAMETER, 0, -1); | ||
| 993 | if (!tmp_r) | ||
| 994 | return REG_ESPACE; | ||
| 995 | old_params = tre_mem_alloc(mem, sizeof(*old_params) | ||
| 996 | * TRE_PARAM_LAST); | ||
| 997 | if (!old_params) | ||
| 998 | return REG_ESPACE; | ||
| 999 | for (i = 0; i < TRE_PARAM_LAST; i++) | ||
| 1000 | old_params[i] = params[i]; | ||
| 1001 | ((tre_literal_t *)tmp_r->obj)->u.params = old_params; | ||
| 1002 | old_params[TRE_PARAM_DEPTH] = params_depth; | ||
| 1003 | /* XXX - this is the only place where ast_new_node is | ||
| 1004 | needed -- should be moved inside AST module. */ | ||
| 1005 | node_copy = tre_ast_new_node(mem, ITERATION, | ||
| 1006 | sizeof(tre_iteration_t)); | ||
| 1007 | if (!node_copy) | ||
| 1008 | return REG_ESPACE; | ||
| 1009 | node_copy->obj = node->obj; | ||
| 1010 | tmp_node = tre_ast_new_catenation(mem, tmp_l, node_copy); | ||
| 1011 | if (!tmp_node) | ||
| 1012 | return REG_ESPACE; | ||
| 1013 | tmp_node = tre_ast_new_catenation(mem, tmp_node, tmp_r); | ||
| 1014 | if (!tmp_node) | ||
| 1015 | return REG_ESPACE; | ||
| 1016 | /* Replace the contents of `node' with `tmp_node'. */ | ||
| 1017 | memcpy(node, tmp_node, sizeof(*node)); | ||
| 1018 | node->obj = tmp_node->obj; | ||
| 1019 | node->type = tmp_node->type; | ||
| 1020 | params_depth++; | ||
| 1021 | if (params_depth > *max_depth) | ||
| 1022 | *max_depth = params_depth; | ||
| 1023 | } | ||
| 1024 | break; | ||
| 1025 | } | ||
| 1026 | default: | ||
| 1027 | assert(0); | ||
| 1028 | break; | ||
| 1029 | } | ||
| 1030 | } | ||
| 1031 | |||
| 1032 | *position += pos_add_total; | ||
| 1033 | |||
| 1034 | /* `max_pos' should never be larger than `*position' if the above | ||
| 1035 | code works, but just an extra safeguard let's make sure | ||
| 1036 | `*position' is set large enough so enough memory will be | ||
| 1037 | allocated for the transition table. */ | ||
| 1038 | if (max_pos > *position) | ||
| 1039 | *position = max_pos; | ||
| 1040 | |||
| 1041 | #ifdef TRE_DEBUG | ||
| 1042 | DPRINT(("Expanded AST:\n")); | ||
| 1043 | tre_ast_print(ast); | ||
| 1044 | DPRINT(("*position %d, max_pos %d\n", *position, max_pos)); | ||
| 1045 | #endif | ||
| 1046 | |||
| 1047 | return status; | ||
| 1048 | } | ||
| 1049 | |||
| 1050 | static tre_pos_and_tags_t * | ||
| 1051 | tre_set_empty(tre_mem_t mem) | ||
| 1052 | { | ||
| 1053 | tre_pos_and_tags_t *new_set; | ||
| 1054 | |||
| 1055 | new_set = tre_mem_calloc(mem, sizeof(*new_set)); | ||
| 1056 | if (new_set == NULL) | ||
| 1057 | return NULL; | ||
| 1058 | |||
| 1059 | new_set[0].position = -1; | ||
| 1060 | new_set[0].code_min = -1; | ||
| 1061 | new_set[0].code_max = -1; | ||
| 1062 | |||
| 1063 | return new_set; | ||
| 1064 | } | ||
| 1065 | |||
| 1066 | static tre_pos_and_tags_t * | ||
| 1067 | tre_set_one(tre_mem_t mem, int position, int code_min, int code_max, | ||
| 1068 | tre_ctype_t class, tre_ctype_t *neg_classes, int backref) | ||
| 1069 | { | ||
| 1070 | tre_pos_and_tags_t *new_set; | ||
| 1071 | |||
| 1072 | new_set = tre_mem_calloc(mem, sizeof(*new_set) * 2); | ||
| 1073 | if (new_set == NULL) | ||
| 1074 | return NULL; | ||
| 1075 | |||
| 1076 | new_set[0].position = position; | ||
| 1077 | new_set[0].code_min = code_min; | ||
| 1078 | new_set[0].code_max = code_max; | ||
| 1079 | new_set[0].class = class; | ||
| 1080 | new_set[0].neg_classes = neg_classes; | ||
| 1081 | new_set[0].backref = backref; | ||
| 1082 | new_set[1].position = -1; | ||
| 1083 | new_set[1].code_min = -1; | ||
| 1084 | new_set[1].code_max = -1; | ||
| 1085 | |||
| 1086 | return new_set; | ||
| 1087 | } | ||
| 1088 | |||
| 1089 | static tre_pos_and_tags_t * | ||
| 1090 | tre_set_union(tre_mem_t mem, tre_pos_and_tags_t *set1, tre_pos_and_tags_t *set2, | ||
| 1091 | int *tags, int assertions, int *params) | ||
| 1092 | { | ||
| 1093 | int s1, s2, i, j; | ||
| 1094 | tre_pos_and_tags_t *new_set; | ||
| 1095 | int *new_tags; | ||
| 1096 | int num_tags; | ||
| 1097 | |||
| 1098 | for (num_tags = 0; tags != NULL && tags[num_tags] >= 0; num_tags++); | ||
| 1099 | for (s1 = 0; set1[s1].position >= 0; s1++); | ||
| 1100 | for (s2 = 0; set2[s2].position >= 0; s2++); | ||
| 1101 | new_set = tre_mem_calloc(mem, sizeof(*new_set) * (s1 + s2 + 1)); | ||
| 1102 | if (!new_set ) | ||
| 1103 | return NULL; | ||
| 1104 | |||
| 1105 | for (s1 = 0; set1[s1].position >= 0; s1++) | ||
| 1106 | { | ||
| 1107 | new_set[s1].position = set1[s1].position; | ||
| 1108 | new_set[s1].code_min = set1[s1].code_min; | ||
| 1109 | new_set[s1].code_max = set1[s1].code_max; | ||
| 1110 | new_set[s1].assertions = set1[s1].assertions | assertions; | ||
| 1111 | new_set[s1].class = set1[s1].class; | ||
| 1112 | new_set[s1].neg_classes = set1[s1].neg_classes; | ||
| 1113 | new_set[s1].backref = set1[s1].backref; | ||
| 1114 | if (set1[s1].tags == NULL && tags == NULL) | ||
| 1115 | new_set[s1].tags = NULL; | ||
| 1116 | else | ||
| 1117 | { | ||
| 1118 | for (i = 0; set1[s1].tags != NULL && set1[s1].tags[i] >= 0; i++); | ||
| 1119 | new_tags = tre_mem_alloc(mem, (sizeof(*new_tags) | ||
| 1120 | * (i + num_tags + 1))); | ||
| 1121 | if (new_tags == NULL) | ||
| 1122 | return NULL; | ||
| 1123 | for (j = 0; j < i; j++) | ||
| 1124 | new_tags[j] = set1[s1].tags[j]; | ||
| 1125 | for (i = 0; i < num_tags; i++) | ||
| 1126 | new_tags[j + i] = tags[i]; | ||
| 1127 | new_tags[j + i] = -1; | ||
| 1128 | new_set[s1].tags = new_tags; | ||
| 1129 | } | ||
| 1130 | if (set1[s1].params) | ||
| 1131 | new_set[s1].params = set1[s1].params; | ||
| 1132 | if (params) | ||
| 1133 | { | ||
| 1134 | if (!new_set[s1].params) | ||
| 1135 | new_set[s1].params = params; | ||
| 1136 | else | ||
| 1137 | { | ||
| 1138 | new_set[s1].params = tre_mem_alloc(mem, sizeof(*params) * | ||
| 1139 | TRE_PARAM_LAST); | ||
| 1140 | if (!new_set[s1].params) | ||
| 1141 | return NULL; | ||
| 1142 | for (i = 0; i < TRE_PARAM_LAST; i++) | ||
| 1143 | if (params[i] != TRE_PARAM_UNSET) | ||
| 1144 | new_set[s1].params[i] = params[i]; | ||
| 1145 | } | ||
| 1146 | } | ||
| 1147 | } | ||
| 1148 | |||
| 1149 | for (s2 = 0; set2[s2].position >= 0; s2++) | ||
| 1150 | { | ||
| 1151 | new_set[s1 + s2].position = set2[s2].position; | ||
| 1152 | new_set[s1 + s2].code_min = set2[s2].code_min; | ||
| 1153 | new_set[s1 + s2].code_max = set2[s2].code_max; | ||
| 1154 | /* XXX - why not | assertions here as well? */ | ||
| 1155 | new_set[s1 + s2].assertions = set2[s2].assertions; | ||
| 1156 | new_set[s1 + s2].class = set2[s2].class; | ||
| 1157 | new_set[s1 + s2].neg_classes = set2[s2].neg_classes; | ||
| 1158 | new_set[s1 + s2].backref = set2[s2].backref; | ||
| 1159 | if (set2[s2].tags == NULL) | ||
| 1160 | new_set[s1 + s2].tags = NULL; | ||
| 1161 | else | ||
| 1162 | { | ||
| 1163 | for (i = 0; set2[s2].tags[i] >= 0; i++); | ||
| 1164 | new_tags = tre_mem_alloc(mem, sizeof(*new_tags) * (i + 1)); | ||
| 1165 | if (new_tags == NULL) | ||
| 1166 | return NULL; | ||
| 1167 | for (j = 0; j < i; j++) | ||
| 1168 | new_tags[j] = set2[s2].tags[j]; | ||
| 1169 | new_tags[j] = -1; | ||
| 1170 | new_set[s1 + s2].tags = new_tags; | ||
| 1171 | } | ||
| 1172 | if (set2[s2].params) | ||
| 1173 | new_set[s1 + s2].params = set2[s2].params; | ||
| 1174 | if (params) | ||
| 1175 | { | ||
| 1176 | if (!new_set[s1 + s2].params) | ||
| 1177 | new_set[s1 + s2].params = params; | ||
| 1178 | else | ||
| 1179 | { | ||
| 1180 | new_set[s1 + s2].params = tre_mem_alloc(mem, sizeof(*params) * | ||
| 1181 | TRE_PARAM_LAST); | ||
| 1182 | if (!new_set[s1 + s2].params) | ||
| 1183 | return NULL; | ||
| 1184 | for (i = 0; i < TRE_PARAM_LAST; i++) | ||
| 1185 | if (params[i] != TRE_PARAM_UNSET) | ||
| 1186 | new_set[s1 + s2].params[i] = params[i]; | ||
| 1187 | } | ||
| 1188 | } | ||
| 1189 | } | ||
| 1190 | new_set[s1 + s2].position = -1; | ||
| 1191 | return new_set; | ||
| 1192 | } | ||
| 1193 | |||
| 1194 | /* Finds the empty path through `node' which is the one that should be | ||
| 1195 | taken according to POSIX.2 rules, and adds the tags on that path to | ||
| 1196 | `tags'. `tags' may be NULL. If `num_tags_seen' is not NULL, it is | ||
| 1197 | set to the number of tags seen on the path. */ | ||
| 1198 | static reg_errcode_t | ||
| 1199 | tre_match_empty(tre_stack_t *stack, tre_ast_node_t *node, int *tags, | ||
| 1200 | int *assertions, int *params, int *num_tags_seen, | ||
| 1201 | int *params_seen) | ||
| 1202 | { | ||
| 1203 | tre_literal_t *lit; | ||
| 1204 | tre_union_t *uni; | ||
| 1205 | tre_catenation_t *cat; | ||
| 1206 | tre_iteration_t *iter; | ||
| 1207 | int i; | ||
| 1208 | int bottom = tre_stack_num_objects(stack); | ||
| 1209 | reg_errcode_t status = REG_OK; | ||
| 1210 | if (num_tags_seen) | ||
| 1211 | *num_tags_seen = 0; | ||
| 1212 | if (params_seen) | ||
| 1213 | *params_seen = 0; | ||
| 1214 | |||
| 1215 | status = tre_stack_push_voidptr(stack, node); | ||
| 1216 | |||
| 1217 | /* Walk through the tree recursively. */ | ||
| 1218 | while (status == REG_OK && tre_stack_num_objects(stack) > bottom) | ||
| 1219 | { | ||
| 1220 | node = tre_stack_pop_voidptr(stack); | ||
| 1221 | |||
| 1222 | switch (node->type) | ||
| 1223 | { | ||
| 1224 | case LITERAL: | ||
| 1225 | lit = (tre_literal_t *)node->obj; | ||
| 1226 | switch (lit->code_min) | ||
| 1227 | { | ||
| 1228 | case TAG: | ||
| 1229 | if (lit->code_max >= 0) | ||
| 1230 | { | ||
| 1231 | if (tags != NULL) | ||
| 1232 | { | ||
| 1233 | /* Add the tag to `tags'. */ | ||
| 1234 | for (i = 0; tags[i] >= 0; i++) | ||
| 1235 | if (tags[i] == lit->code_max) | ||
| 1236 | break; | ||
| 1237 | if (tags[i] < 0) | ||
| 1238 | { | ||
| 1239 | tags[i] = lit->code_max; | ||
| 1240 | tags[i + 1] = -1; | ||
| 1241 | } | ||
| 1242 | } | ||
| 1243 | if (num_tags_seen) | ||
| 1244 | (*num_tags_seen)++; | ||
| 1245 | } | ||
| 1246 | break; | ||
| 1247 | case ASSERTION: | ||
| 1248 | assert(lit->code_max >= 1 | ||
| 1249 | || lit->code_max <= ASSERT_LAST); | ||
| 1250 | if (assertions != NULL) | ||
| 1251 | *assertions |= lit->code_max; | ||
| 1252 | break; | ||
| 1253 | case PARAMETER: | ||
| 1254 | if (params != NULL) | ||
| 1255 | for (i = 0; i < TRE_PARAM_LAST; i++) | ||
| 1256 | params[i] = lit->u.params[i]; | ||
| 1257 | if (params_seen != NULL) | ||
| 1258 | *params_seen = 1; | ||
| 1259 | break; | ||
| 1260 | case EMPTY: | ||
| 1261 | break; | ||
| 1262 | default: | ||
| 1263 | assert(0); | ||
| 1264 | break; | ||
| 1265 | } | ||
| 1266 | break; | ||
| 1267 | |||
| 1268 | case UNION: | ||
| 1269 | /* Subexpressions starting earlier take priority over ones | ||
| 1270 | starting later, so we prefer the left subexpression over the | ||
| 1271 | right subexpression. */ | ||
| 1272 | uni = (tre_union_t *)node->obj; | ||
| 1273 | if (uni->left->nullable) | ||
| 1274 | STACK_PUSHX(stack, voidptr, uni->left) | ||
| 1275 | else if (uni->right->nullable) | ||
| 1276 | STACK_PUSHX(stack, voidptr, uni->right) | ||
| 1277 | else | ||
| 1278 | assert(0); | ||
| 1279 | break; | ||
| 1280 | |||
| 1281 | case CATENATION: | ||
| 1282 | /* The path must go through both children. */ | ||
| 1283 | cat = (tre_catenation_t *)node->obj; | ||
| 1284 | assert(cat->left->nullable); | ||
| 1285 | assert(cat->right->nullable); | ||
| 1286 | STACK_PUSHX(stack, voidptr, cat->left); | ||
| 1287 | STACK_PUSHX(stack, voidptr, cat->right); | ||
| 1288 | break; | ||
| 1289 | |||
| 1290 | case ITERATION: | ||
| 1291 | /* A match with an empty string is preferred over no match at | ||
| 1292 | all, so we go through the argument if possible. */ | ||
| 1293 | iter = (tre_iteration_t *)node->obj; | ||
| 1294 | if (iter->arg->nullable) | ||
| 1295 | STACK_PUSHX(stack, voidptr, iter->arg); | ||
| 1296 | break; | ||
| 1297 | |||
| 1298 | default: | ||
| 1299 | assert(0); | ||
| 1300 | break; | ||
| 1301 | } | ||
| 1302 | } | ||
| 1303 | |||
| 1304 | return status; | ||
| 1305 | } | ||
| 1306 | |||
| 1307 | |||
| 1308 | typedef enum { | ||
| 1309 | NFL_RECURSE, | ||
| 1310 | NFL_POST_UNION, | ||
| 1311 | NFL_POST_CATENATION, | ||
| 1312 | NFL_POST_ITERATION | ||
| 1313 | } tre_nfl_stack_symbol_t; | ||
| 1314 | |||
| 1315 | |||
| 1316 | /* Computes and fills in the fields `nullable', `firstpos', and `lastpos' for | ||
| 1317 | the nodes of the AST `tree'. */ | ||
| 1318 | static reg_errcode_t | ||
| 1319 | tre_compute_nfl(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *tree) | ||
| 1320 | { | ||
| 1321 | int bottom = tre_stack_num_objects(stack); | ||
| 1322 | |||
| 1323 | STACK_PUSHR(stack, voidptr, tree); | ||
| 1324 | STACK_PUSHR(stack, int, NFL_RECURSE); | ||
| 1325 | |||
| 1326 | while (tre_stack_num_objects(stack) > bottom) | ||
| 1327 | { | ||
| 1328 | tre_nfl_stack_symbol_t symbol; | ||
| 1329 | tre_ast_node_t *node; | ||
| 1330 | |||
| 1331 | symbol = (tre_nfl_stack_symbol_t)tre_stack_pop_int(stack); | ||
| 1332 | node = tre_stack_pop_voidptr(stack); | ||
| 1333 | switch (symbol) | ||
| 1334 | { | ||
| 1335 | case NFL_RECURSE: | ||
| 1336 | switch (node->type) | ||
| 1337 | { | ||
| 1338 | case LITERAL: | ||
| 1339 | { | ||
| 1340 | tre_literal_t *lit = (tre_literal_t *)node->obj; | ||
| 1341 | if (IS_BACKREF(lit)) | ||
| 1342 | { | ||
| 1343 | /* Back references: nullable = false, firstpos = {i}, | ||
| 1344 | lastpos = {i}. */ | ||
| 1345 | node->nullable = 0; | ||
| 1346 | node->firstpos = tre_set_one(mem, lit->position, 0, | ||
| 1347 | TRE_CHAR_MAX, 0, NULL, -1); | ||
| 1348 | if (!node->firstpos) | ||
| 1349 | return REG_ESPACE; | ||
| 1350 | node->lastpos = tre_set_one(mem, lit->position, 0, | ||
| 1351 | TRE_CHAR_MAX, 0, NULL, | ||
| 1352 | (int)lit->code_max); | ||
| 1353 | if (!node->lastpos) | ||
| 1354 | return REG_ESPACE; | ||
| 1355 | } | ||
| 1356 | else if (lit->code_min < 0) | ||
| 1357 | { | ||
| 1358 | /* Tags, empty strings, params, and zero width assertions: | ||
| 1359 | nullable = true, firstpos = {}, and lastpos = {}. */ | ||
| 1360 | node->nullable = 1; | ||
| 1361 | node->firstpos = tre_set_empty(mem); | ||
| 1362 | if (!node->firstpos) | ||
| 1363 | return REG_ESPACE; | ||
| 1364 | node->lastpos = tre_set_empty(mem); | ||
| 1365 | if (!node->lastpos) | ||
| 1366 | return REG_ESPACE; | ||
| 1367 | } | ||
| 1368 | else | ||
| 1369 | { | ||
| 1370 | /* Literal at position i: nullable = false, firstpos = {i}, | ||
| 1371 | lastpos = {i}. */ | ||
| 1372 | node->nullable = 0; | ||
| 1373 | node->firstpos = | ||
| 1374 | tre_set_one(mem, lit->position, (int)lit->code_min, | ||
| 1375 | (int)lit->code_max, 0, NULL, -1); | ||
| 1376 | if (!node->firstpos) | ||
| 1377 | return REG_ESPACE; | ||
| 1378 | node->lastpos = tre_set_one(mem, lit->position, | ||
| 1379 | (int)lit->code_min, | ||
| 1380 | (int)lit->code_max, | ||
| 1381 | lit->u.class, lit->neg_classes, | ||
| 1382 | -1); | ||
| 1383 | if (!node->lastpos) | ||
| 1384 | return REG_ESPACE; | ||
| 1385 | } | ||
| 1386 | break; | ||
| 1387 | } | ||
| 1388 | |||
| 1389 | case UNION: | ||
| 1390 | /* Compute the attributes for the two subtrees, and after that | ||
| 1391 | for this node. */ | ||
| 1392 | STACK_PUSHR(stack, voidptr, node); | ||
| 1393 | STACK_PUSHR(stack, int, NFL_POST_UNION); | ||
| 1394 | STACK_PUSHR(stack, voidptr, ((tre_union_t *)node->obj)->right); | ||
| 1395 | STACK_PUSHR(stack, int, NFL_RECURSE); | ||
| 1396 | STACK_PUSHR(stack, voidptr, ((tre_union_t *)node->obj)->left); | ||
| 1397 | STACK_PUSHR(stack, int, NFL_RECURSE); | ||
| 1398 | break; | ||
| 1399 | |||
| 1400 | case CATENATION: | ||
| 1401 | /* Compute the attributes for the two subtrees, and after that | ||
| 1402 | for this node. */ | ||
| 1403 | STACK_PUSHR(stack, voidptr, node); | ||
| 1404 | STACK_PUSHR(stack, int, NFL_POST_CATENATION); | ||
| 1405 | STACK_PUSHR(stack, voidptr, ((tre_catenation_t *)node->obj)->right); | ||
| 1406 | STACK_PUSHR(stack, int, NFL_RECURSE); | ||
| 1407 | STACK_PUSHR(stack, voidptr, ((tre_catenation_t *)node->obj)->left); | ||
| 1408 | STACK_PUSHR(stack, int, NFL_RECURSE); | ||
| 1409 | break; | ||
| 1410 | |||
| 1411 | case ITERATION: | ||
| 1412 | /* Compute the attributes for the subtree, and after that for | ||
| 1413 | this node. */ | ||
| 1414 | STACK_PUSHR(stack, voidptr, node); | ||
| 1415 | STACK_PUSHR(stack, int, NFL_POST_ITERATION); | ||
| 1416 | STACK_PUSHR(stack, voidptr, ((tre_iteration_t *)node->obj)->arg); | ||
| 1417 | STACK_PUSHR(stack, int, NFL_RECURSE); | ||
| 1418 | break; | ||
| 1419 | } | ||
| 1420 | break; /* end case: NFL_RECURSE */ | ||
| 1421 | |||
| 1422 | case NFL_POST_UNION: | ||
| 1423 | { | ||
| 1424 | tre_union_t *uni = (tre_union_t *)node->obj; | ||
| 1425 | node->nullable = uni->left->nullable || uni->right->nullable; | ||
| 1426 | node->firstpos = tre_set_union(mem, uni->left->firstpos, | ||
| 1427 | uni->right->firstpos, NULL, 0, NULL); | ||
| 1428 | if (!node->firstpos) | ||
| 1429 | return REG_ESPACE; | ||
| 1430 | node->lastpos = tre_set_union(mem, uni->left->lastpos, | ||
| 1431 | uni->right->lastpos, NULL, 0, NULL); | ||
| 1432 | if (!node->lastpos) | ||
| 1433 | return REG_ESPACE; | ||
| 1434 | break; | ||
| 1435 | } | ||
| 1436 | |||
| 1437 | case NFL_POST_ITERATION: | ||
| 1438 | { | ||
| 1439 | tre_iteration_t *iter = (tre_iteration_t *)node->obj; | ||
| 1440 | |||
| 1441 | if (iter->min == 0 || iter->arg->nullable) | ||
| 1442 | node->nullable = 1; | ||
| 1443 | else | ||
| 1444 | node->nullable = 0; | ||
| 1445 | node->firstpos = iter->arg->firstpos; | ||
| 1446 | node->lastpos = iter->arg->lastpos; | ||
| 1447 | break; | ||
| 1448 | } | ||
| 1449 | |||
| 1450 | case NFL_POST_CATENATION: | ||
| 1451 | { | ||
| 1452 | int num_tags, *tags, assertions, params_seen; | ||
| 1453 | int *params; | ||
| 1454 | reg_errcode_t status; | ||
| 1455 | tre_catenation_t *cat = node->obj; | ||
| 1456 | node->nullable = cat->left->nullable && cat->right->nullable; | ||
| 1457 | |||
| 1458 | /* Compute firstpos. */ | ||
| 1459 | if (cat->left->nullable) | ||
| 1460 | { | ||
| 1461 | /* The left side matches the empty string. Make a first pass | ||
| 1462 | with tre_match_empty() to get the number of tags and | ||
| 1463 | parameters. */ | ||
| 1464 | status = tre_match_empty(stack, cat->left, | ||
| 1465 | NULL, NULL, NULL, &num_tags, | ||
| 1466 | ¶ms_seen); | ||
| 1467 | if (status != REG_OK) | ||
| 1468 | return status; | ||
| 1469 | /* Allocate arrays for the tags and parameters. */ | ||
| 1470 | tags = xmalloc(sizeof(*tags) * (num_tags + 1)); | ||
| 1471 | if (!tags) | ||
| 1472 | return REG_ESPACE; | ||
| 1473 | tags[0] = -1; | ||
| 1474 | assertions = 0; | ||
| 1475 | params = NULL; | ||
| 1476 | if (params_seen) | ||
| 1477 | { | ||
| 1478 | params = tre_mem_alloc(mem, sizeof(*params) | ||
| 1479 | * TRE_PARAM_LAST); | ||
| 1480 | if (!params) | ||
| 1481 | { | ||
| 1482 | xfree(tags); | ||
| 1483 | return REG_ESPACE; | ||
| 1484 | } | ||
| 1485 | } | ||
| 1486 | /* Second pass with tre_mach_empty() to get the list of | ||
| 1487 | tags and parameters. */ | ||
| 1488 | status = tre_match_empty(stack, cat->left, tags, | ||
| 1489 | &assertions, params, NULL, NULL); | ||
| 1490 | if (status != REG_OK) | ||
| 1491 | { | ||
| 1492 | xfree(tags); | ||
| 1493 | return status; | ||
| 1494 | } | ||
| 1495 | node->firstpos = | ||
| 1496 | tre_set_union(mem, cat->right->firstpos, cat->left->firstpos, | ||
| 1497 | tags, assertions, params); | ||
| 1498 | xfree(tags); | ||
| 1499 | if (!node->firstpos) | ||
| 1500 | return REG_ESPACE; | ||
| 1501 | } | ||
| 1502 | else | ||
| 1503 | { | ||
| 1504 | node->firstpos = cat->left->firstpos; | ||
| 1505 | } | ||
| 1506 | |||
| 1507 | /* Compute lastpos. */ | ||
| 1508 | if (cat->right->nullable) | ||
| 1509 | { | ||
| 1510 | /* The right side matches the empty string. Make a first pass | ||
| 1511 | with tre_match_empty() to get the number of tags and | ||
| 1512 | parameters. */ | ||
| 1513 | status = tre_match_empty(stack, cat->right, | ||
| 1514 | NULL, NULL, NULL, &num_tags, | ||
| 1515 | ¶ms_seen); | ||
| 1516 | if (status != REG_OK) | ||
| 1517 | return status; | ||
| 1518 | /* Allocate arrays for the tags and parameters. */ | ||
| 1519 | tags = xmalloc(sizeof(int) * (num_tags + 1)); | ||
| 1520 | if (!tags) | ||
| 1521 | return REG_ESPACE; | ||
| 1522 | tags[0] = -1; | ||
| 1523 | assertions = 0; | ||
| 1524 | params = NULL; | ||
| 1525 | if (params_seen) | ||
| 1526 | { | ||
| 1527 | params = tre_mem_alloc(mem, sizeof(*params) | ||
| 1528 | * TRE_PARAM_LAST); | ||
| 1529 | if (!params) | ||
| 1530 | { | ||
| 1531 | xfree(tags); | ||
| 1532 | return REG_ESPACE; | ||
| 1533 | } | ||
| 1534 | } | ||
| 1535 | /* Second pass with tre_mach_empty() to get the list of | ||
| 1536 | tags and parameters. */ | ||
| 1537 | status = tre_match_empty(stack, cat->right, tags, | ||
| 1538 | &assertions, params, NULL, NULL); | ||
| 1539 | if (status != REG_OK) | ||
| 1540 | { | ||
| 1541 | xfree(tags); | ||
| 1542 | return status; | ||
| 1543 | } | ||
| 1544 | node->lastpos = | ||
| 1545 | tre_set_union(mem, cat->left->lastpos, cat->right->lastpos, | ||
| 1546 | tags, assertions, params); | ||
| 1547 | xfree(tags); | ||
| 1548 | if (!node->lastpos) | ||
| 1549 | return REG_ESPACE; | ||
| 1550 | } | ||
| 1551 | else | ||
| 1552 | { | ||
| 1553 | node->lastpos = cat->right->lastpos; | ||
| 1554 | } | ||
| 1555 | break; | ||
| 1556 | } | ||
| 1557 | |||
| 1558 | default: | ||
| 1559 | assert(0); | ||
| 1560 | break; | ||
| 1561 | } | ||
| 1562 | } | ||
| 1563 | |||
| 1564 | return REG_OK; | ||
| 1565 | } | ||
| 1566 | |||
| 1567 | |||
| 1568 | /* Adds a transition from each position in `p1' to each position in `p2'. */ | ||
| 1569 | static reg_errcode_t | ||
| 1570 | tre_make_trans(tre_pos_and_tags_t *p1, tre_pos_and_tags_t *p2, | ||
| 1571 | tre_tnfa_transition_t *transitions, | ||
| 1572 | int *counts, int *offs) | ||
| 1573 | { | ||
| 1574 | tre_pos_and_tags_t *orig_p2 = p2; | ||
| 1575 | tre_tnfa_transition_t *trans; | ||
| 1576 | int i, j, k, l, dup, prev_p2_pos; | ||
| 1577 | |||
| 1578 | if (transitions != NULL) | ||
| 1579 | while (p1->position >= 0) | ||
| 1580 | { | ||
| 1581 | p2 = orig_p2; | ||
| 1582 | prev_p2_pos = -1; | ||
| 1583 | while (p2->position >= 0) | ||
| 1584 | { | ||
| 1585 | /* Optimization: if this position was already handled, skip it. */ | ||
| 1586 | if (p2->position == prev_p2_pos) | ||
| 1587 | { | ||
| 1588 | p2++; | ||
| 1589 | continue; | ||
| 1590 | } | ||
| 1591 | prev_p2_pos = p2->position; | ||
| 1592 | /* Set `trans' to point to the next unused transition from | ||
| 1593 | position `p1->position'. */ | ||
| 1594 | trans = transitions + offs[p1->position]; | ||
| 1595 | while (trans->state != NULL) | ||
| 1596 | { | ||
| 1597 | #if 0 | ||
| 1598 | /* If we find a previous transition from `p1->position' to | ||
| 1599 | `p2->position', it is overwritten. This can happen only | ||
| 1600 | if there are nested loops in the regexp, like in "((a)*)*". | ||
| 1601 | In POSIX.2 repetition using the outer loop is always | ||
| 1602 | preferred over using the inner loop. Therefore the | ||
| 1603 | transition for the inner loop is useless and can be thrown | ||
| 1604 | away. */ | ||
| 1605 | /* XXX - The same position is used for all nodes in a bracket | ||
| 1606 | expression, so this optimization cannot be used (it will | ||
| 1607 | break bracket expressions) unless I figure out a way to | ||
| 1608 | detect it here. */ | ||
| 1609 | if (trans->state_id == p2->position) | ||
| 1610 | { | ||
| 1611 | DPRINT(("*")); | ||
| 1612 | break; | ||
| 1613 | } | ||
| 1614 | #endif | ||
| 1615 | trans++; | ||
| 1616 | } | ||
| 1617 | |||
| 1618 | if (trans->state == NULL) | ||
| 1619 | (trans + 1)->state = NULL; | ||
| 1620 | /* Use the character ranges, assertions, etc. from `p1' for | ||
| 1621 | the transition from `p1' to `p2'. */ | ||
| 1622 | trans->code_min = p1->code_min; | ||
| 1623 | trans->code_max = p1->code_max; | ||
| 1624 | trans->state = transitions + offs[p2->position]; | ||
| 1625 | trans->state_id = p2->position; | ||
| 1626 | trans->assertions = p1->assertions | p2->assertions | ||
| 1627 | | (p1->class ? ASSERT_CHAR_CLASS : 0) | ||
| 1628 | | (p1->neg_classes != NULL ? ASSERT_CHAR_CLASS_NEG : 0); | ||
| 1629 | if (p1->backref >= 0) | ||
| 1630 | { | ||
| 1631 | assert((trans->assertions & ASSERT_CHAR_CLASS) == 0); | ||
| 1632 | assert(p2->backref < 0); | ||
| 1633 | trans->u.backref = p1->backref; | ||
| 1634 | trans->assertions |= ASSERT_BACKREF; | ||
| 1635 | } | ||
| 1636 | else | ||
| 1637 | trans->u.class = p1->class; | ||
| 1638 | if (p1->neg_classes != NULL) | ||
| 1639 | { | ||
| 1640 | for (i = 0; p1->neg_classes[i] != (tre_ctype_t)0; i++); | ||
| 1641 | trans->neg_classes = | ||
| 1642 | xmalloc(sizeof(*trans->neg_classes) * (i + 1)); | ||
| 1643 | if (trans->neg_classes == NULL) | ||
| 1644 | return REG_ESPACE; | ||
| 1645 | for (i = 0; p1->neg_classes[i] != (tre_ctype_t)0; i++) | ||
| 1646 | trans->neg_classes[i] = p1->neg_classes[i]; | ||
| 1647 | trans->neg_classes[i] = (tre_ctype_t)0; | ||
| 1648 | } | ||
| 1649 | else | ||
| 1650 | trans->neg_classes = NULL; | ||
| 1651 | |||
| 1652 | /* Find out how many tags this transition has. */ | ||
| 1653 | i = 0; | ||
| 1654 | if (p1->tags != NULL) | ||
| 1655 | while(p1->tags[i] >= 0) | ||
| 1656 | i++; | ||
| 1657 | j = 0; | ||
| 1658 | if (p2->tags != NULL) | ||
| 1659 | while(p2->tags[j] >= 0) | ||
| 1660 | j++; | ||
| 1661 | |||
| 1662 | /* If we are overwriting a transition, free the old tag array. */ | ||
| 1663 | if (trans->tags != NULL) | ||
| 1664 | xfree(trans->tags); | ||
| 1665 | trans->tags = NULL; | ||
| 1666 | |||
| 1667 | /* If there were any tags, allocate an array and fill it. */ | ||
| 1668 | if (i + j > 0) | ||
| 1669 | { | ||
| 1670 | trans->tags = xmalloc(sizeof(*trans->tags) * (i + j + 1)); | ||
| 1671 | if (!trans->tags) | ||
| 1672 | return REG_ESPACE; | ||
| 1673 | i = 0; | ||
| 1674 | if (p1->tags != NULL) | ||
| 1675 | while(p1->tags[i] >= 0) | ||
| 1676 | { | ||
| 1677 | trans->tags[i] = p1->tags[i]; | ||
| 1678 | i++; | ||
| 1679 | } | ||
| 1680 | l = i; | ||
| 1681 | j = 0; | ||
| 1682 | if (p2->tags != NULL) | ||
| 1683 | while (p2->tags[j] >= 0) | ||
| 1684 | { | ||
| 1685 | /* Don't add duplicates. */ | ||
| 1686 | dup = 0; | ||
| 1687 | for (k = 0; k < i; k++) | ||
| 1688 | if (trans->tags[k] == p2->tags[j]) | ||
| 1689 | { | ||
| 1690 | dup = 1; | ||
| 1691 | break; | ||
| 1692 | } | ||
| 1693 | if (!dup) | ||
| 1694 | trans->tags[l++] = p2->tags[j]; | ||
| 1695 | j++; | ||
| 1696 | } | ||
| 1697 | trans->tags[l] = -1; | ||
| 1698 | } | ||
| 1699 | |||
| 1700 | /* Set the parameter array. If both `p2' and `p1' have same | ||
| 1701 | parameters, the values in `p2' override those in `p1'. */ | ||
| 1702 | if (p1->params || p2->params) | ||
| 1703 | { | ||
| 1704 | if (!trans->params) | ||
| 1705 | trans->params = xmalloc(sizeof(*trans->params) | ||
| 1706 | * TRE_PARAM_LAST); | ||
| 1707 | if (!trans->params) | ||
| 1708 | return REG_ESPACE; | ||
| 1709 | for (i = 0; i < TRE_PARAM_LAST; i++) | ||
| 1710 | { | ||
| 1711 | trans->params[i] = TRE_PARAM_UNSET; | ||
| 1712 | if (p1->params && p1->params[i] != TRE_PARAM_UNSET) | ||
| 1713 | trans->params[i] = p1->params[i]; | ||
| 1714 | if (p2->params && p2->params[i] != TRE_PARAM_UNSET) | ||
| 1715 | trans->params[i] = p2->params[i]; | ||
| 1716 | } | ||
| 1717 | } | ||
| 1718 | else | ||
| 1719 | { | ||
| 1720 | if (trans->params) | ||
| 1721 | xfree(trans->params); | ||
| 1722 | trans->params = NULL; | ||
| 1723 | } | ||
| 1724 | |||
| 1725 | |||
| 1726 | #ifdef TRE_DEBUG | ||
| 1727 | { | ||
| 1728 | int *tags; | ||
| 1729 | |||
| 1730 | DPRINT((" %2d -> %2d on %3d", p1->position, p2->position, | ||
| 1731 | p1->code_min)); | ||
| 1732 | if (p1->code_max != p1->code_min) | ||
| 1733 | DPRINT(("-%3d", p1->code_max)); | ||
| 1734 | tags = trans->tags; | ||
| 1735 | if (tags) | ||
| 1736 | { | ||
| 1737 | DPRINT((", tags [")); | ||
| 1738 | while (*tags >= 0) | ||
| 1739 | { | ||
| 1740 | DPRINT(("%d", *tags)); | ||
| 1741 | tags++; | ||
| 1742 | if (*tags >= 0) | ||
| 1743 | DPRINT((",")); | ||
| 1744 | } | ||
| 1745 | DPRINT(("]")); | ||
| 1746 | } | ||
| 1747 | if (trans->assertions) | ||
| 1748 | DPRINT((", assert %d", trans->assertions)); | ||
| 1749 | if (trans->assertions & ASSERT_BACKREF) | ||
| 1750 | DPRINT((", backref %d", trans->u.backref)); | ||
| 1751 | else if (trans->u.class) | ||
| 1752 | DPRINT((", class %ld", (long)trans->u.class)); | ||
| 1753 | if (trans->neg_classes) | ||
| 1754 | DPRINT((", neg_classes %p", trans->neg_classes)); | ||
| 1755 | if (trans->params) | ||
| 1756 | { | ||
| 1757 | DPRINT((", ")); | ||
| 1758 | tre_print_params(trans->params); | ||
| 1759 | } | ||
| 1760 | DPRINT(("\n")); | ||
| 1761 | } | ||
| 1762 | #endif /* TRE_DEBUG */ | ||
| 1763 | p2++; | ||
| 1764 | } | ||
| 1765 | p1++; | ||
| 1766 | } | ||
| 1767 | else | ||
| 1768 | /* Compute a maximum limit for the number of transitions leaving | ||
| 1769 | from each state. */ | ||
| 1770 | while (p1->position >= 0) | ||
| 1771 | { | ||
| 1772 | p2 = orig_p2; | ||
| 1773 | while (p2->position >= 0) | ||
| 1774 | { | ||
| 1775 | counts[p1->position]++; | ||
| 1776 | p2++; | ||
| 1777 | } | ||
| 1778 | p1++; | ||
| 1779 | } | ||
| 1780 | return REG_OK; | ||
| 1781 | } | ||
| 1782 | |||
| 1783 | /* Converts the syntax tree to a TNFA. All the transitions in the TNFA are | ||
| 1784 | labelled with one character range (there are no transitions on empty | ||
| 1785 | strings). The TNFA takes O(n^2) space in the worst case, `n' is size of | ||
| 1786 | the regexp. */ | ||
| 1787 | static reg_errcode_t | ||
| 1788 | tre_ast_to_tnfa(tre_ast_node_t *node, tre_tnfa_transition_t *transitions, | ||
| 1789 | int *counts, int *offs) | ||
| 1790 | { | ||
| 1791 | tre_union_t *uni; | ||
| 1792 | tre_catenation_t *cat; | ||
| 1793 | tre_iteration_t *iter; | ||
| 1794 | reg_errcode_t errcode = REG_OK; | ||
| 1795 | |||
| 1796 | /* XXX - recurse using a stack!. */ | ||
| 1797 | switch (node->type) | ||
| 1798 | { | ||
| 1799 | case LITERAL: | ||
| 1800 | break; | ||
| 1801 | case UNION: | ||
| 1802 | uni = (tre_union_t *)node->obj; | ||
| 1803 | errcode = tre_ast_to_tnfa(uni->left, transitions, counts, offs); | ||
| 1804 | if (errcode != REG_OK) | ||
| 1805 | return errcode; | ||
| 1806 | errcode = tre_ast_to_tnfa(uni->right, transitions, counts, offs); | ||
| 1807 | break; | ||
| 1808 | |||
| 1809 | case CATENATION: | ||
| 1810 | cat = (tre_catenation_t *)node->obj; | ||
| 1811 | /* Add a transition from each position in cat->left->lastpos | ||
| 1812 | to each position in cat->right->firstpos. */ | ||
| 1813 | errcode = tre_make_trans(cat->left->lastpos, cat->right->firstpos, | ||
| 1814 | transitions, counts, offs); | ||
| 1815 | if (errcode != REG_OK) | ||
| 1816 | return errcode; | ||
| 1817 | errcode = tre_ast_to_tnfa(cat->left, transitions, counts, offs); | ||
| 1818 | if (errcode != REG_OK) | ||
| 1819 | return errcode; | ||
| 1820 | errcode = tre_ast_to_tnfa(cat->right, transitions, counts, offs); | ||
| 1821 | break; | ||
| 1822 | |||
| 1823 | case ITERATION: | ||
| 1824 | iter = (tre_iteration_t *)node->obj; | ||
| 1825 | assert(iter->max == -1 || iter->max == 1); | ||
| 1826 | |||
| 1827 | if (iter->max == -1) | ||
| 1828 | { | ||
| 1829 | assert(iter->min == 0 || iter->min == 1); | ||
| 1830 | /* Add a transition from each last position in the iterated | ||
| 1831 | expression to each first position. */ | ||
| 1832 | errcode = tre_make_trans(iter->arg->lastpos, iter->arg->firstpos, | ||
| 1833 | transitions, counts, offs); | ||
| 1834 | if (errcode != REG_OK) | ||
| 1835 | return errcode; | ||
| 1836 | } | ||
| 1837 | errcode = tre_ast_to_tnfa(iter->arg, transitions, counts, offs); | ||
| 1838 | break; | ||
| 1839 | } | ||
| 1840 | return errcode; | ||
| 1841 | } | ||
| 1842 | |||
| 1843 | |||
| 1844 | #define ERROR_EXIT(err) \ | ||
| 1845 | do \ | ||
| 1846 | { \ | ||
| 1847 | errcode = err; \ | ||
| 1848 | if (/*CONSTCOND*/1) \ | ||
| 1849 | goto error_exit; \ | ||
| 1850 | } \ | ||
| 1851 | while (/*CONSTCOND*/0) | ||
| 1852 | |||
| 1853 | |||
| 1854 | int | ||
| 1855 | tre_compile(regex_t *preg, const tre_char_t *regex, size_t n, int cflags) | ||
| 1856 | { | ||
| 1857 | tre_stack_t *stack; | ||
| 1858 | tre_ast_node_t *tree, *tmp_ast_l, *tmp_ast_r; | ||
| 1859 | tre_pos_and_tags_t *p; | ||
| 1860 | int *counts = NULL, *offs = NULL; | ||
| 1861 | int i, add = 0; | ||
| 1862 | tre_tnfa_transition_t *transitions, *initial; | ||
| 1863 | tre_tnfa_t *tnfa = NULL; | ||
| 1864 | tre_submatch_data_t *submatch_data; | ||
| 1865 | tre_tag_direction_t *tag_directions = NULL; | ||
| 1866 | reg_errcode_t errcode; | ||
| 1867 | tre_mem_t mem; | ||
| 1868 | |||
| 1869 | /* Parse context. */ | ||
| 1870 | tre_parse_ctx_t parse_ctx; | ||
| 1871 | |||
| 1872 | /* Allocate a stack used throughout the compilation process for various | ||
| 1873 | purposes. */ | ||
| 1874 | stack = tre_stack_new(512, 10240, 128); | ||
| 1875 | if (!stack) | ||
| 1876 | return REG_ESPACE; | ||
| 1877 | /* Allocate a fast memory allocator. */ | ||
| 1878 | mem = tre_mem_new(); | ||
| 1879 | if (!mem) | ||
| 1880 | { | ||
| 1881 | tre_stack_destroy(stack); | ||
| 1882 | return REG_ESPACE; | ||
| 1883 | } | ||
| 1884 | |||
| 1885 | /* Parse the regexp. */ | ||
| 1886 | memset(&parse_ctx, 0, sizeof(parse_ctx)); | ||
| 1887 | parse_ctx.mem = mem; | ||
| 1888 | parse_ctx.stack = stack; | ||
| 1889 | parse_ctx.re = regex; | ||
| 1890 | parse_ctx.len = (int)n; // 1.4.1.1 (ge) added cast to get rid of win64 warning | ||
| 1891 | parse_ctx.cflags = cflags; | ||
| 1892 | parse_ctx.max_backref = -1; | ||
| 1893 | DPRINT(("tre_compile: parsing '%.*" STRF "'\n", (int)n, regex)); | ||
| 1894 | errcode = tre_parse(&parse_ctx); | ||
| 1895 | if (errcode != REG_OK) | ||
| 1896 | ERROR_EXIT(errcode); | ||
| 1897 | preg->re_nsub = parse_ctx.submatch_id - 1; | ||
| 1898 | tree = parse_ctx.result; | ||
| 1899 | |||
| 1900 | /* Back references and approximate matching cannot currently be used | ||
| 1901 | in the same regexp. */ | ||
| 1902 | if (parse_ctx.max_backref >= 0 && parse_ctx.have_approx) | ||
| 1903 | ERROR_EXIT(REG_BADPAT); | ||
| 1904 | |||
| 1905 | #ifdef TRE_DEBUG | ||
| 1906 | tre_ast_print(tree); | ||
| 1907 | #endif /* TRE_DEBUG */ | ||
| 1908 | |||
| 1909 | /* Referring to nonexistent subexpressions is illegal. */ | ||
| 1910 | if (parse_ctx.max_backref > (int)preg->re_nsub) | ||
| 1911 | ERROR_EXIT(REG_ESUBREG); | ||
| 1912 | |||
| 1913 | /* Allocate the TNFA struct. */ | ||
| 1914 | tnfa = xcalloc(1, sizeof(tre_tnfa_t)); | ||
| 1915 | if (tnfa == NULL) | ||
| 1916 | ERROR_EXIT(REG_ESPACE); | ||
| 1917 | tnfa->have_backrefs = parse_ctx.max_backref >= 0; | ||
| 1918 | tnfa->have_approx = parse_ctx.have_approx; | ||
| 1919 | tnfa->num_submatches = parse_ctx.submatch_id; | ||
| 1920 | |||
| 1921 | /* Set up tags for submatch addressing. If REG_NOSUB is set and the | ||
| 1922 | regexp does not have back references, this can be skipped. */ | ||
| 1923 | if (tnfa->have_backrefs || !(cflags & REG_NOSUB)) | ||
| 1924 | { | ||
| 1925 | DPRINT(("tre_compile: setting up tags\n")); | ||
| 1926 | |||
| 1927 | /* Figure out how many tags we will need. */ | ||
| 1928 | errcode = tre_add_tags(NULL, stack, tree, tnfa); | ||
| 1929 | if (errcode != REG_OK) | ||
| 1930 | ERROR_EXIT(errcode); | ||
| 1931 | #ifdef TRE_DEBUG | ||
| 1932 | tre_ast_print(tree); | ||
| 1933 | #endif /* TRE_DEBUG */ | ||
| 1934 | |||
| 1935 | if (tnfa->num_tags > 0) | ||
| 1936 | { | ||
| 1937 | tag_directions = xmalloc(sizeof(*tag_directions) | ||
| 1938 | * (tnfa->num_tags + 1)); | ||
| 1939 | if (tag_directions == NULL) | ||
| 1940 | ERROR_EXIT(REG_ESPACE); | ||
| 1941 | tnfa->tag_directions = tag_directions; | ||
| 1942 | memset(tag_directions, -1, | ||
| 1943 | sizeof(*tag_directions) * (tnfa->num_tags + 1)); | ||
| 1944 | } | ||
| 1945 | tnfa->minimal_tags = xcalloc((unsigned)tnfa->num_tags * 2 + 1, | ||
| 1946 | sizeof(tnfa->minimal_tags)); | ||
| 1947 | if (tnfa->minimal_tags == NULL) | ||
| 1948 | ERROR_EXIT(REG_ESPACE); | ||
| 1949 | |||
| 1950 | submatch_data = xcalloc((unsigned)parse_ctx.submatch_id, | ||
| 1951 | sizeof(*submatch_data)); | ||
| 1952 | if (submatch_data == NULL) | ||
| 1953 | ERROR_EXIT(REG_ESPACE); | ||
| 1954 | tnfa->submatch_data = submatch_data; | ||
| 1955 | |||
| 1956 | errcode = tre_add_tags(mem, stack, tree, tnfa); | ||
| 1957 | if (errcode != REG_OK) | ||
| 1958 | ERROR_EXIT(errcode); | ||
| 1959 | |||
| 1960 | #ifdef TRE_DEBUG | ||
| 1961 | for (i = 0; i < parse_ctx.submatch_id; i++) | ||
| 1962 | DPRINT(("pmatch[%d] = {t%d, t%d}\n", | ||
| 1963 | i, submatch_data[i].so_tag, submatch_data[i].eo_tag)); | ||
| 1964 | for (i = 0; i < tnfa->num_tags; i++) | ||
| 1965 | DPRINT(("t%d is %s\n", i, | ||
| 1966 | tag_directions[i] == TRE_TAG_MINIMIZE ? | ||
| 1967 | "minimized" : "maximized")); | ||
| 1968 | #endif /* TRE_DEBUG */ | ||
| 1969 | } | ||
| 1970 | |||
| 1971 | /* Expand iteration nodes. */ | ||
| 1972 | errcode = tre_expand_ast(mem, stack, tree, &parse_ctx.position, | ||
| 1973 | tag_directions, &tnfa->params_depth); | ||
| 1974 | if (errcode != REG_OK) | ||
| 1975 | ERROR_EXIT(errcode); | ||
| 1976 | |||
| 1977 | /* Add a dummy node for the final state. | ||
| 1978 | XXX - For certain patterns this dummy node can be optimized away, | ||
| 1979 | for example "a*" or "ab*". Figure out a simple way to detect | ||
| 1980 | this possibility. */ | ||
| 1981 | tmp_ast_l = tree; | ||
| 1982 | tmp_ast_r = tre_ast_new_literal(mem, 0, 0, parse_ctx.position++); | ||
| 1983 | if (tmp_ast_r == NULL) | ||
| 1984 | ERROR_EXIT(REG_ESPACE); | ||
| 1985 | |||
| 1986 | tree = tre_ast_new_catenation(mem, tmp_ast_l, tmp_ast_r); | ||
| 1987 | if (tree == NULL) | ||
| 1988 | ERROR_EXIT(REG_ESPACE); | ||
| 1989 | |||
| 1990 | #ifdef TRE_DEBUG | ||
| 1991 | tre_ast_print(tree); | ||
| 1992 | DPRINT(("Number of states: %d\n", parse_ctx.position)); | ||
| 1993 | #endif /* TRE_DEBUG */ | ||
| 1994 | |||
| 1995 | errcode = tre_compute_nfl(mem, stack, tree); | ||
| 1996 | if (errcode != REG_OK) | ||
| 1997 | ERROR_EXIT(errcode); | ||
| 1998 | |||
| 1999 | counts = xmalloc(sizeof(int) * parse_ctx.position); | ||
| 2000 | if (counts == NULL) | ||
| 2001 | ERROR_EXIT(REG_ESPACE); | ||
| 2002 | |||
| 2003 | offs = xmalloc(sizeof(int) * parse_ctx.position); | ||
| 2004 | if (offs == NULL) | ||
| 2005 | ERROR_EXIT(REG_ESPACE); | ||
| 2006 | |||
| 2007 | for (i = 0; i < parse_ctx.position; i++) | ||
| 2008 | counts[i] = 0; | ||
| 2009 | tre_ast_to_tnfa(tree, NULL, counts, NULL); | ||
| 2010 | |||
| 2011 | add = 0; | ||
| 2012 | for (i = 0; i < parse_ctx.position; i++) | ||
| 2013 | { | ||
| 2014 | offs[i] = add; | ||
| 2015 | add += counts[i] + 1; | ||
| 2016 | counts[i] = 0; | ||
| 2017 | } | ||
| 2018 | transitions = xcalloc((unsigned)add + 1, sizeof(*transitions)); | ||
| 2019 | if (transitions == NULL) | ||
| 2020 | ERROR_EXIT(REG_ESPACE); | ||
| 2021 | tnfa->transitions = transitions; | ||
| 2022 | tnfa->num_transitions = add; | ||
| 2023 | |||
| 2024 | DPRINT(("Converting to TNFA:\n")); | ||
| 2025 | errcode = tre_ast_to_tnfa(tree, transitions, counts, offs); | ||
| 2026 | if (errcode != REG_OK) | ||
| 2027 | ERROR_EXIT(errcode); | ||
| 2028 | |||
| 2029 | /* If in eight bit mode, compute a table of characters that can be the | ||
| 2030 | first character of a match. */ | ||
| 2031 | tnfa->first_char = -1; | ||
| 2032 | if (TRE_MB_CUR_MAX == 1 && !tmp_ast_l->nullable) | ||
| 2033 | { | ||
| 2034 | int count = 0; | ||
| 2035 | tre_cint_t k; | ||
| 2036 | DPRINT(("Characters that can start a match:")); | ||
| 2037 | tnfa->firstpos_chars = xcalloc(256, sizeof(char)); | ||
| 2038 | if (tnfa->firstpos_chars == NULL) | ||
| 2039 | ERROR_EXIT(REG_ESPACE); | ||
| 2040 | for (p = tree->firstpos; p->position >= 0; p++) | ||
| 2041 | { | ||
| 2042 | tre_tnfa_transition_t *j = transitions + offs[p->position]; | ||
| 2043 | while (j->state != NULL) | ||
| 2044 | { | ||
| 2045 | for (k = j->code_min; k <= j->code_max && k < 256; k++) | ||
| 2046 | { | ||
| 2047 | DPRINT((" %d", k)); | ||
| 2048 | tnfa->firstpos_chars[k] = 1; | ||
| 2049 | count++; | ||
| 2050 | } | ||
| 2051 | j++; | ||
| 2052 | } | ||
| 2053 | } | ||
| 2054 | DPRINT(("\n")); | ||
| 2055 | #define TRE_OPTIMIZE_FIRST_CHAR 1 | ||
| 2056 | #if TRE_OPTIMIZE_FIRST_CHAR | ||
| 2057 | if (count == 1) | ||
| 2058 | { | ||
| 2059 | for (k = 0; k < 256; k++) | ||
| 2060 | if (tnfa->firstpos_chars[k]) | ||
| 2061 | { | ||
| 2062 | DPRINT(("first char must be %d\n", k)); | ||
| 2063 | tnfa->first_char = k; | ||
| 2064 | xfree(tnfa->firstpos_chars); | ||
| 2065 | tnfa->firstpos_chars = NULL; | ||
| 2066 | break; | ||
| 2067 | } | ||
| 2068 | } | ||
| 2069 | #endif | ||
| 2070 | |||
| 2071 | } | ||
| 2072 | else | ||
| 2073 | tnfa->firstpos_chars = NULL; | ||
| 2074 | |||
| 2075 | |||
| 2076 | p = tree->firstpos; | ||
| 2077 | i = 0; | ||
| 2078 | while (p->position >= 0) | ||
| 2079 | { | ||
| 2080 | i++; | ||
| 2081 | |||
| 2082 | #ifdef TRE_DEBUG | ||
| 2083 | { | ||
| 2084 | int *tags; | ||
| 2085 | DPRINT(("initial: %d", p->position)); | ||
| 2086 | tags = p->tags; | ||
| 2087 | if (tags != NULL) | ||
| 2088 | { | ||
| 2089 | if (*tags >= 0) | ||
| 2090 | DPRINT(("/")); | ||
| 2091 | while (*tags >= 0) | ||
| 2092 | { | ||
| 2093 | DPRINT(("%d", *tags)); | ||
| 2094 | tags++; | ||
| 2095 | if (*tags >= 0) | ||
| 2096 | DPRINT((",")); | ||
| 2097 | } | ||
| 2098 | } | ||
| 2099 | DPRINT((", assert %d", p->assertions)); | ||
| 2100 | if (p->params) | ||
| 2101 | { | ||
| 2102 | DPRINT((", ")); | ||
| 2103 | tre_print_params(p->params); | ||
| 2104 | } | ||
| 2105 | DPRINT(("\n")); | ||
| 2106 | } | ||
| 2107 | #endif /* TRE_DEBUG */ | ||
| 2108 | |||
| 2109 | p++; | ||
| 2110 | } | ||
| 2111 | |||
| 2112 | initial = xcalloc((unsigned)i + 1, sizeof(tre_tnfa_transition_t)); | ||
| 2113 | if (initial == NULL) | ||
| 2114 | ERROR_EXIT(REG_ESPACE); | ||
| 2115 | tnfa->initial = initial; | ||
| 2116 | |||
| 2117 | i = 0; | ||
| 2118 | for (p = tree->firstpos; p->position >= 0; p++) | ||
| 2119 | { | ||
| 2120 | initial[i].state = transitions + offs[p->position]; | ||
| 2121 | initial[i].state_id = p->position; | ||
| 2122 | initial[i].tags = NULL; | ||
| 2123 | /* Copy the arrays p->tags, and p->params, they are allocated | ||
| 2124 | from a tre_mem object. */ | ||
| 2125 | if (p->tags) | ||
| 2126 | { | ||
| 2127 | int j; | ||
| 2128 | for (j = 0; p->tags[j] >= 0; j++); | ||
| 2129 | initial[i].tags = xmalloc(sizeof(*p->tags) * (j + 1)); | ||
| 2130 | if (!initial[i].tags) | ||
| 2131 | ERROR_EXIT(REG_ESPACE); | ||
| 2132 | memcpy(initial[i].tags, p->tags, sizeof(*p->tags) * (j + 1)); | ||
| 2133 | } | ||
| 2134 | initial[i].params = NULL; | ||
| 2135 | if (p->params) | ||
| 2136 | { | ||
| 2137 | initial[i].params = xmalloc(sizeof(*p->params) * TRE_PARAM_LAST); | ||
| 2138 | if (!initial[i].params) | ||
| 2139 | ERROR_EXIT(REG_ESPACE); | ||
| 2140 | memcpy(initial[i].params, p->params, | ||
| 2141 | sizeof(*p->params) * TRE_PARAM_LAST); | ||
| 2142 | } | ||
| 2143 | initial[i].assertions = p->assertions; | ||
| 2144 | i++; | ||
| 2145 | } | ||
| 2146 | initial[i].state = NULL; | ||
| 2147 | |||
| 2148 | tnfa->num_transitions = add; | ||
| 2149 | tnfa->final = transitions + offs[tree->lastpos[0].position]; | ||
| 2150 | tnfa->num_states = parse_ctx.position; | ||
| 2151 | tnfa->cflags = cflags; | ||
| 2152 | |||
| 2153 | DPRINT(("final state %p\n", (void *)tnfa->final)); | ||
| 2154 | |||
| 2155 | tre_mem_destroy(mem); | ||
| 2156 | tre_stack_destroy(stack); | ||
| 2157 | xfree(counts); | ||
| 2158 | xfree(offs); | ||
| 2159 | |||
| 2160 | preg->TRE_REGEX_T_FIELD = (void *)tnfa; | ||
| 2161 | return REG_OK; | ||
| 2162 | |||
| 2163 | error_exit: | ||
| 2164 | /* Free everything that was allocated and return the error code. */ | ||
| 2165 | tre_mem_destroy(mem); | ||
| 2166 | if (stack != NULL) | ||
| 2167 | tre_stack_destroy(stack); | ||
| 2168 | if (counts != NULL) | ||
| 2169 | xfree(counts); | ||
| 2170 | if (offs != NULL) | ||
| 2171 | xfree(offs); | ||
| 2172 | preg->TRE_REGEX_T_FIELD = (void *)tnfa; | ||
| 2173 | tre_free(preg); | ||
| 2174 | return errcode; | ||
| 2175 | } | ||
| 2176 | |||
| 2177 | |||
| 2178 | |||
| 2179 | |||
| 2180 | void | ||
| 2181 | tre_free(regex_t *preg) | ||
| 2182 | { | ||
| 2183 | tre_tnfa_t *tnfa; | ||
| 2184 | unsigned int i; | ||
| 2185 | tre_tnfa_transition_t *trans; | ||
| 2186 | |||
| 2187 | tnfa = (void *)preg->TRE_REGEX_T_FIELD; | ||
| 2188 | if (!tnfa) | ||
| 2189 | return; | ||
| 2190 | |||
| 2191 | for (i = 0; i < tnfa->num_transitions; i++) | ||
| 2192 | if (tnfa->transitions[i].state) | ||
| 2193 | { | ||
| 2194 | if (tnfa->transitions[i].tags) | ||
| 2195 | xfree(tnfa->transitions[i].tags); | ||
| 2196 | if (tnfa->transitions[i].neg_classes) | ||
| 2197 | xfree(tnfa->transitions[i].neg_classes); | ||
| 2198 | if (tnfa->transitions[i].params) | ||
| 2199 | xfree(tnfa->transitions[i].params); | ||
| 2200 | } | ||
| 2201 | if (tnfa->transitions) | ||
| 2202 | xfree(tnfa->transitions); | ||
| 2203 | |||
| 2204 | if (tnfa->initial) | ||
| 2205 | { | ||
| 2206 | for (trans = tnfa->initial; trans->state; trans++) | ||
| 2207 | { | ||
| 2208 | if (trans->tags) | ||
| 2209 | xfree(trans->tags); | ||
| 2210 | if (trans->params) | ||
| 2211 | xfree(trans->params); | ||
| 2212 | } | ||
| 2213 | xfree(tnfa->initial); | ||
| 2214 | } | ||
| 2215 | |||
| 2216 | if (tnfa->submatch_data) | ||
| 2217 | { | ||
| 2218 | for (i = 0; i < tnfa->num_submatches; i++) | ||
| 2219 | if (tnfa->submatch_data[i].parents) | ||
| 2220 | xfree(tnfa->submatch_data[i].parents); | ||
| 2221 | xfree(tnfa->submatch_data); | ||
| 2222 | } | ||
| 2223 | |||
| 2224 | if (tnfa->tag_directions) | ||
| 2225 | xfree(tnfa->tag_directions); | ||
| 2226 | if (tnfa->firstpos_chars) | ||
| 2227 | xfree(tnfa->firstpos_chars); | ||
| 2228 | if (tnfa->minimal_tags) | ||
| 2229 | xfree(tnfa->minimal_tags); | ||
| 2230 | xfree(tnfa); | ||
| 2231 | } | ||
| 2232 | |||
| 2233 | char * | ||
| 2234 | tre_version(void) | ||
| 2235 | { | ||
| 2236 | static char str[256]; | ||
| 2237 | char *version; | ||
| 2238 | |||
| 2239 | if (str[0] == 0) | ||
| 2240 | { | ||
| 2241 | (void) tre_config(TRE_CONFIG_VERSION, &version); | ||
| 2242 | (void) snprintf(str, sizeof(str), "TRE %s (BSD)", version); | ||
| 2243 | } | ||
| 2244 | return str; | ||
| 2245 | } | ||
| 2246 | |||
| 2247 | int | ||
| 2248 | tre_config(int query, void *result) | ||
| 2249 | { | ||
| 2250 | int *int_result = result; | ||
| 2251 | const char **string_result = result; | ||
| 2252 | |||
| 2253 | switch (query) | ||
| 2254 | { | ||
| 2255 | case TRE_CONFIG_APPROX: | ||
| 2256 | #ifdef TRE_APPROX | ||
| 2257 | *int_result = 1; | ||
| 2258 | #else /* !TRE_APPROX */ | ||
| 2259 | *int_result = 0; | ||
| 2260 | #endif /* !TRE_APPROX */ | ||
| 2261 | return REG_OK; | ||
| 2262 | |||
| 2263 | case TRE_CONFIG_WCHAR: | ||
| 2264 | #ifdef TRE_WCHAR | ||
| 2265 | *int_result = 1; | ||
| 2266 | #else /* !TRE_WCHAR */ | ||
| 2267 | *int_result = 0; | ||
| 2268 | #endif /* !TRE_WCHAR */ | ||
| 2269 | return REG_OK; | ||
| 2270 | |||
| 2271 | case TRE_CONFIG_MULTIBYTE: | ||
| 2272 | #ifdef TRE_MULTIBYTE | ||
| 2273 | *int_result = 1; | ||
| 2274 | #else /* !TRE_MULTIBYTE */ | ||
| 2275 | *int_result = 0; | ||
| 2276 | #endif /* !TRE_MULTIBYTE */ | ||
| 2277 | return REG_OK; | ||
| 2278 | |||
| 2279 | case TRE_CONFIG_SYSTEM_ABI: | ||
| 2280 | #ifdef TRE_CONFIG_SYSTEM_ABI | ||
| 2281 | *int_result = 1; | ||
| 2282 | #else /* !TRE_CONFIG_SYSTEM_ABI */ | ||
| 2283 | *int_result = 0; | ||
| 2284 | #endif /* !TRE_CONFIG_SYSTEM_ABI */ | ||
| 2285 | return REG_OK; | ||
| 2286 | |||
| 2287 | case TRE_CONFIG_VERSION: | ||
| 2288 | *string_result = TRE_VERSION; | ||
| 2289 | return REG_OK; | ||
| 2290 | } | ||
| 2291 | |||
| 2292 | return REG_NOMATCH; | ||
| 2293 | } | ||
| 2294 | |||
| 2295 | |||
| 2296 | /* EOF */ | ||
| @@ -1,27 +0,0 @@ | |||
| 1 | /* | ||
| 2 | tre-compile.h: Regex compilation definitions | ||
| 3 | |||
| 4 | This software is released under a BSD-style license. | ||
| 5 | See the file LICENSE for details and copyright. | ||
| 6 | |||
| 7 | */ | ||
| 8 | |||
| 9 | |||
| 10 | #ifndef TRE_COMPILE_H | ||
| 11 | #define TRE_COMPILE_H 1 | ||
| 12 | |||
| 13 | typedef struct { | ||
| 14 | int position; | ||
| 15 | int code_min; | ||
| 16 | int code_max; | ||
| 17 | int *tags; | ||
| 18 | int assertions; | ||
| 19 | tre_ctype_t class; | ||
| 20 | tre_ctype_t *neg_classes; | ||
| 21 | int backref; | ||
| 22 | int *params; | ||
| 23 | } tre_pos_and_tags_t; | ||
| 24 | |||
| 25 | #endif /* TRE_COMPILE_H */ | ||
| 26 | |||
| 27 | /* EOF */ | ||
| @@ -1,52 +0,0 @@ | |||
| 1 | /* tre-config.h. This file defines all compile time definitions | ||
| 2 | that are needed in `regex.h' for Win32. */ | ||
| 3 | |||
| 4 | /* Define to 1 if you have `alloca', as a function or macro. */ | ||
| 5 | #define HAVE_ALLOCA 1 | ||
| 6 | |||
| 7 | /* Define to 1 if you have <alloca.h> and it should be used (not on Ultrix). | ||
| 8 | */ | ||
| 9 | #define HAVE_ALLOCA_H 1 | ||
| 10 | |||
| 11 | /* Define to 1 if you have the <libutf8.h> header file. */ | ||
| 12 | /* #undef HAVE_LIBUTF8_H */ | ||
| 13 | |||
| 14 | /* Define to 1 if the system has the type `reg_errcode_t'. */ | ||
| 15 | /* #undef HAVE_REG_ERRCODE_T */ | ||
| 16 | |||
| 17 | /* Define to 1 if you have the <sys/types.h> header file. */ | ||
| 18 | #define HAVE_SYS_TYPES_H 1 | ||
| 19 | |||
| 20 | /* Define to 1 if you have the <wchar.h> header file. */ | ||
| 21 | #define HAVE_WCHAR_H 1 | ||
| 22 | |||
| 23 | /* Define if you want to enable approximate matching functionality. */ | ||
| 24 | #define TRE_APPROX 1 | ||
| 25 | |||
| 26 | /* Define to enable multibyte character set support. */ | ||
| 27 | /* #define TRE_MULTIBYTE 1 */ | ||
| 28 | |||
| 29 | /* Define to the absolute path to the system regex.h */ | ||
| 30 | /* #undef TRE_SYSTEM_REGEX_H_PATH */ | ||
| 31 | |||
| 32 | /* Define if you want TRE to use alloca() instead of malloc() when allocating | ||
| 33 | memory needed for regexec operations. */ | ||
| 34 | #define TRE_USE_ALLOCA 1 | ||
| 35 | |||
| 36 | /* Define to include the system regex.h from TRE regex.h */ | ||
| 37 | /* #undef TRE_USE_SYSTEM_REGEX_H */ | ||
| 38 | |||
| 39 | /* Define to enable wide character (wchar_t) support. */ | ||
| 40 | /* #define TRE_WCHAR 1 */ | ||
| 41 | |||
| 42 | /* TRE version string. */ | ||
| 43 | #define TRE_VERSION "@TRE_VERSION@" | ||
| 44 | |||
| 45 | /* TRE version level 1. */ | ||
| 46 | #define TRE_VERSION_1 @TRE_VERSION_1@ | ||
| 47 | |||
| 48 | /* TRE version level 2. */ | ||
| 49 | #define TRE_VERSION_2 @TRE_VERSION_2@ | ||
| 50 | |||
| 51 | /* TRE version level 3. */ | ||
| 52 | #define TRE_VERSION_3 @TRE_VERSION_3@ | ||
| @@ -1,73 +0,0 @@ | |||
| 1 | /* | ||
| 2 | tre-filter.c: Histogram filter to quickly find regexp match candidates | ||
| 3 | |||
| 4 | This software is released under a BSD-style license. | ||
| 5 | See the file LICENSE for details and copyright. | ||
| 6 | |||
| 7 | */ | ||
| 8 | |||
| 9 | /* The idea of this filter is quite simple. First, let's assume the | ||
| 10 | search pattern is a simple string. In order for a substring of a | ||
| 11 | longer string to match the search pattern, it must have the same | ||
| 12 | numbers of different characters as the pattern, and those | ||
| 13 | characters must occur in the same order as they occur in pattern. */ | ||
| 14 | |||
| 15 | #ifdef HAVE_CONFIG_H | ||
| 16 | #include <config.h> | ||
| 17 | #endif /* HAVE_CONFIG_H */ | ||
| 18 | #include <stdio.h> | ||
| 19 | #include "tre-internal.h" | ||
| 20 | #include "tre-filter.h" | ||
| 21 | |||
| 22 | int | ||
| 23 | tre_filter_find(const unsigned char *str, size_t len, tre_filter_t *filter) | ||
| 24 | { | ||
| 25 | unsigned short counts[256]; | ||
| 26 | unsigned int i; | ||
| 27 | unsigned int window_len = filter->window_len; | ||
| 28 | tre_filter_profile_t *profile = filter->profile; | ||
| 29 | const unsigned char *str_orig = str; | ||
| 30 | |||
| 31 | DPRINT(("tre_filter_find: %.*s\n", len, str)); | ||
| 32 | |||
| 33 | for (i = 0; i < elementsof(counts); i++) | ||
| 34 | counts[i] = 0; | ||
| 35 | |||
| 36 | i = 0; | ||
| 37 | while (*str && i < window_len && i < len) | ||
| 38 | { | ||
| 39 | counts[*str]++; | ||
| 40 | i++; | ||
| 41 | str++; | ||
| 42 | len--; | ||
| 43 | } | ||
| 44 | |||
| 45 | while (len > 0) | ||
| 46 | { | ||
| 47 | tre_filter_profile_t *p; | ||
| 48 | counts[*str]++; | ||
| 49 | counts[*(str - window_len)]--; | ||
| 50 | |||
| 51 | p = profile; | ||
| 52 | while (p->ch) | ||
| 53 | { | ||
| 54 | if (counts[p->ch] < p->count) | ||
| 55 | break; | ||
| 56 | p++; | ||
| 57 | } | ||
| 58 | if (!p->ch) | ||
| 59 | { | ||
| 60 | DPRINT(("Found possible match at %d\n", | ||
| 61 | str - str_orig)); | ||
| 62 | return (int)(str - str_orig); // 1.4.1.1 (ge) added cast to get rid of win64 warning | ||
| 63 | } | ||
| 64 | else | ||
| 65 | { | ||
| 66 | DPRINT(("No match so far...\n")); | ||
| 67 | } | ||
| 68 | len--; | ||
| 69 | str++; | ||
| 70 | } | ||
| 71 | DPRINT(("This string cannot match.\n")); | ||
| 72 | return -1; | ||
| 73 | } | ||
| @@ -1,19 +0,0 @@ | |||
| 1 | |||
| 2 | |||
| 3 | |||
| 4 | |||
| 5 | typedef struct { | ||
| 6 | unsigned char ch; | ||
| 7 | unsigned char count; | ||
| 8 | } tre_filter_profile_t; | ||
| 9 | |||
| 10 | typedef struct { | ||
| 11 | /* Length of the window where the character counts are kept. */ | ||
| 12 | int window_len; | ||
| 13 | /* Required character counts table. */ | ||
| 14 | tre_filter_profile_t *profile; | ||
| 15 | } tre_filter_t; | ||
| 16 | |||
| 17 | |||
| 18 | int | ||
| 19 | tre_filter_find(const unsigned char *str, size_t len, tre_filter_t *filter); | ||
| @@ -1,287 +0,0 @@ | |||
| 1 | /* | ||
| 2 | tre-internal.h - TRE internal definitions | ||
| 3 | |||
| 4 | This software is released under a BSD-style license. | ||
| 5 | See the file LICENSE for details and copyright. | ||
| 6 | |||
| 7 | */ | ||
| 8 | |||
| 9 | #ifndef TRE_INTERNAL_H | ||
| 10 | #define TRE_INTERNAL_H 1 | ||
| 11 | |||
| 12 | #ifdef HAVE_WCHAR_H | ||
| 13 | #include <wchar.h> | ||
| 14 | #endif /* HAVE_WCHAR_H */ | ||
| 15 | |||
| 16 | #ifdef HAVE_WCTYPE_H | ||
| 17 | #include <wctype.h> | ||
| 18 | #endif /* !HAVE_WCTYPE_H */ | ||
| 19 | |||
| 20 | #include <ctype.h> | ||
| 21 | #include "tre.h" | ||
| 22 | |||
| 23 | #ifdef TRE_DEBUG | ||
| 24 | #include <stdio.h> | ||
| 25 | #define DPRINT(msg) do {printf msg; fflush(stdout);} while(/*CONSTCOND*/0) | ||
| 26 | #else /* !TRE_DEBUG */ | ||
| 27 | #define DPRINT(msg) do { } while(/*CONSTCOND*/0) | ||
| 28 | #endif /* !TRE_DEBUG */ | ||
| 29 | |||
| 30 | #define elementsof(x) ( sizeof(x) / sizeof(x[0]) ) | ||
| 31 | |||
| 32 | #ifdef HAVE_MBRTOWC | ||
| 33 | #define tre_mbrtowc(pwc, s, n, ps) (mbrtowc((pwc), (s), (n), (ps))) | ||
| 34 | #else /* !HAVE_MBRTOWC */ | ||
| 35 | #ifdef HAVE_MBTOWC | ||
| 36 | #define tre_mbrtowc(pwc, s, n, ps) (mbtowc((pwc), (s), (n))) | ||
| 37 | #endif /* HAVE_MBTOWC */ | ||
| 38 | #endif /* !HAVE_MBRTOWC */ | ||
| 39 | |||
| 40 | #ifdef TRE_MULTIBYTE | ||
| 41 | #ifdef HAVE_MBSTATE_T | ||
| 42 | #define TRE_MBSTATE | ||
| 43 | #endif /* TRE_MULTIBYTE */ | ||
| 44 | #endif /* HAVE_MBSTATE_T */ | ||
| 45 | |||
| 46 | /* Define the character types and functions. */ | ||
| 47 | #ifdef TRE_WCHAR | ||
| 48 | |||
| 49 | /* Wide characters. */ | ||
| 50 | typedef wint_t tre_cint_t; | ||
| 51 | #define TRE_CHAR_MAX WCHAR_MAX | ||
| 52 | |||
| 53 | #ifdef TRE_MULTIBYTE | ||
| 54 | #define TRE_MB_CUR_MAX MB_CUR_MAX | ||
| 55 | #else /* !TRE_MULTIBYTE */ | ||
| 56 | #define TRE_MB_CUR_MAX 1 | ||
| 57 | #endif /* !TRE_MULTIBYTE */ | ||
| 58 | |||
| 59 | #define tre_isalnum iswalnum | ||
| 60 | #define tre_isalpha iswalpha | ||
| 61 | #ifdef HAVE_ISWBLANK | ||
| 62 | #define tre_isblank iswblank | ||
| 63 | #endif /* HAVE_ISWBLANK */ | ||
| 64 | #define tre_iscntrl iswcntrl | ||
| 65 | #define tre_isdigit iswdigit | ||
| 66 | #define tre_isgraph iswgraph | ||
| 67 | #define tre_islower iswlower | ||
| 68 | #define tre_isprint iswprint | ||
| 69 | #define tre_ispunct iswpunct | ||
| 70 | #define tre_isspace iswspace | ||
| 71 | #define tre_isupper iswupper | ||
| 72 | #define tre_isxdigit iswxdigit | ||
| 73 | |||
| 74 | #define tre_tolower towlower | ||
| 75 | #define tre_toupper towupper | ||
| 76 | #define tre_strlen wcslen | ||
| 77 | |||
| 78 | #else /* !TRE_WCHAR */ | ||
| 79 | |||
| 80 | /* 8 bit characters. */ | ||
| 81 | typedef short tre_cint_t; | ||
| 82 | #define TRE_CHAR_MAX 255 | ||
| 83 | #define TRE_MB_CUR_MAX 1 | ||
| 84 | |||
| 85 | #define tre_isalnum isalnum | ||
| 86 | #define tre_isalpha isalpha | ||
| 87 | #ifdef HAVE_ISASCII | ||
| 88 | #define tre_isascii isascii | ||
| 89 | #endif /* HAVE_ISASCII */ | ||
| 90 | #ifdef HAVE_ISBLANK | ||
| 91 | #define tre_isblank isblank | ||
| 92 | #endif /* HAVE_ISBLANK */ | ||
| 93 | #define tre_iscntrl iscntrl | ||
| 94 | #define tre_isdigit isdigit | ||
| 95 | #define tre_isgraph isgraph | ||
| 96 | #define tre_islower islower | ||
| 97 | #define tre_isprint isprint | ||
| 98 | #define tre_ispunct ispunct | ||
| 99 | #define tre_isspace isspace | ||
| 100 | #define tre_isupper isupper | ||
| 101 | #define tre_isxdigit isxdigit | ||
| 102 | |||
| 103 | #define tre_tolower(c) (tre_cint_t)(tolower(c)) | ||
| 104 | #define tre_toupper(c) (tre_cint_t)(toupper(c)) | ||
| 105 | #define tre_strlen(s) (strlen((const char*)s)) | ||
| 106 | |||
| 107 | #endif /* !TRE_WCHAR */ | ||
| 108 | |||
| 109 | #if defined(TRE_WCHAR) && defined(HAVE_ISWCTYPE) && defined(HAVE_WCTYPE) | ||
| 110 | #define TRE_USE_SYSTEM_WCTYPE 1 | ||
| 111 | #endif | ||
| 112 | |||
| 113 | #ifdef TRE_USE_SYSTEM_WCTYPE | ||
| 114 | /* Use system provided iswctype() and wctype(). */ | ||
| 115 | typedef wctype_t tre_ctype_t; | ||
| 116 | #define tre_isctype iswctype | ||
| 117 | #define tre_ctype wctype | ||
| 118 | #else /* !TRE_USE_SYSTEM_WCTYPE */ | ||
| 119 | /* Define our own versions of iswctype() and wctype(). */ | ||
| 120 | typedef int (*tre_ctype_t)(tre_cint_t); | ||
| 121 | #define tre_isctype(c, type) ( (type)(c) ) | ||
| 122 | tre_ctype_t tre_ctype(const char *name); | ||
| 123 | #endif /* !TRE_USE_SYSTEM_WCTYPE */ | ||
| 124 | |||
| 125 | typedef enum { STR_WIDE, STR_BYTE, STR_MBS, STR_USER } tre_str_type_t; | ||
| 126 | |||
| 127 | /* Returns number of bytes to add to (char *)ptr to make it | ||
| 128 | properly aligned for the type. */ | ||
| 129 | #define ALIGN(ptr, type) \ | ||
| 130 | ((((long)ptr) % sizeof(type)) \ | ||
| 131 | ? (sizeof(type) - (((long)ptr) % sizeof(type))) \ | ||
| 132 | : 0) | ||
| 133 | |||
| 134 | #undef MAX | ||
| 135 | #undef MIN | ||
| 136 | #define MAX(a, b) (((a) >= (b)) ? (a) : (b)) | ||
| 137 | #define MIN(a, b) (((a) <= (b)) ? (a) : (b)) | ||
| 138 | |||
| 139 | /* Define STRF to the correct printf formatter for strings. */ | ||
| 140 | #ifdef TRE_WCHAR | ||
| 141 | #define STRF "ls" | ||
| 142 | #else /* !TRE_WCHAR */ | ||
| 143 | #define STRF "s" | ||
| 144 | #endif /* !TRE_WCHAR */ | ||
| 145 | |||
| 146 | /* TNFA transition type. A TNFA state is an array of transitions, | ||
| 147 | the terminator is a transition with NULL `state'. */ | ||
| 148 | typedef struct tnfa_transition tre_tnfa_transition_t; | ||
| 149 | |||
| 150 | struct tnfa_transition { | ||
| 151 | /* Range of accepted characters. */ | ||
| 152 | tre_cint_t code_min; | ||
| 153 | tre_cint_t code_max; | ||
| 154 | /* Pointer to the destination state. */ | ||
| 155 | tre_tnfa_transition_t *state; | ||
| 156 | /* ID number of the destination state. */ | ||
| 157 | int state_id; | ||
| 158 | /* -1 terminated array of tags (or NULL). */ | ||
| 159 | int *tags; | ||
| 160 | /* Matching parameters settings (or NULL). */ | ||
| 161 | int *params; | ||
| 162 | /* Assertion bitmap. */ | ||
| 163 | int assertions; | ||
| 164 | /* Assertion parameters. */ | ||
| 165 | union { | ||
| 166 | /* Character class assertion. */ | ||
| 167 | tre_ctype_t class; | ||
| 168 | /* Back reference assertion. */ | ||
| 169 | int backref; | ||
| 170 | } u; | ||
| 171 | /* Negative character class assertions. */ | ||
| 172 | tre_ctype_t *neg_classes; | ||
| 173 | }; | ||
| 174 | |||
| 175 | |||
| 176 | /* Assertions. */ | ||
| 177 | #define ASSERT_AT_BOL 1 /* Beginning of line. */ | ||
| 178 | #define ASSERT_AT_EOL 2 /* End of line. */ | ||
| 179 | #define ASSERT_CHAR_CLASS 4 /* Character class in `class'. */ | ||
| 180 | #define ASSERT_CHAR_CLASS_NEG 8 /* Character classes in `neg_classes'. */ | ||
| 181 | #define ASSERT_AT_BOW 16 /* Beginning of word. */ | ||
| 182 | #define ASSERT_AT_EOW 32 /* End of word. */ | ||
| 183 | #define ASSERT_AT_WB 64 /* Word boundary. */ | ||
| 184 | #define ASSERT_AT_WB_NEG 128 /* Not a word boundary. */ | ||
| 185 | #define ASSERT_BACKREF 256 /* A back reference in `backref'. */ | ||
| 186 | #define ASSERT_LAST 256 | ||
| 187 | |||
| 188 | /* Tag directions. */ | ||
| 189 | typedef enum { | ||
| 190 | TRE_TAG_MINIMIZE = 0, | ||
| 191 | TRE_TAG_MAXIMIZE = 1 | ||
| 192 | } tre_tag_direction_t; | ||
| 193 | |||
| 194 | /* Parameters that can be changed dynamically while matching. */ | ||
| 195 | typedef enum { | ||
| 196 | TRE_PARAM_COST_INS = 0, | ||
| 197 | TRE_PARAM_COST_DEL = 1, | ||
| 198 | TRE_PARAM_COST_SUBST = 2, | ||
| 199 | TRE_PARAM_COST_MAX = 3, | ||
| 200 | TRE_PARAM_MAX_INS = 4, | ||
| 201 | TRE_PARAM_MAX_DEL = 5, | ||
| 202 | TRE_PARAM_MAX_SUBST = 6, | ||
| 203 | TRE_PARAM_MAX_ERR = 7, | ||
| 204 | TRE_PARAM_DEPTH = 8, | ||
| 205 | TRE_PARAM_LAST = 9 | ||
| 206 | } tre_param_t; | ||
| 207 | |||
| 208 | /* Unset matching parameter */ | ||
| 209 | #define TRE_PARAM_UNSET -1 | ||
| 210 | |||
| 211 | /* Signifies the default matching parameter value. */ | ||
| 212 | #define TRE_PARAM_DEFAULT -2 | ||
| 213 | |||
| 214 | /* Instructions to compute submatch register values from tag values | ||
| 215 | after a successful match. */ | ||
| 216 | struct tre_submatch_data { | ||
| 217 | /* Tag that gives the value for rm_so (submatch start offset). */ | ||
| 218 | int so_tag; | ||
| 219 | /* Tag that gives the value for rm_eo (submatch end offset). */ | ||
| 220 | int eo_tag; | ||
| 221 | /* List of submatches this submatch is contained in. */ | ||
| 222 | int *parents; | ||
| 223 | }; | ||
| 224 | |||
| 225 | typedef struct tre_submatch_data tre_submatch_data_t; | ||
| 226 | |||
| 227 | |||
| 228 | /* TNFA definition. */ | ||
| 229 | typedef struct tnfa tre_tnfa_t; | ||
| 230 | |||
| 231 | struct tnfa { | ||
| 232 | tre_tnfa_transition_t *transitions; | ||
| 233 | unsigned int num_transitions; | ||
| 234 | tre_tnfa_transition_t *initial; | ||
| 235 | tre_tnfa_transition_t *final; | ||
| 236 | tre_submatch_data_t *submatch_data; | ||
| 237 | char *firstpos_chars; | ||
| 238 | int first_char; | ||
| 239 | unsigned int num_submatches; | ||
| 240 | tre_tag_direction_t *tag_directions; | ||
| 241 | int *minimal_tags; | ||
| 242 | int num_tags; | ||
| 243 | int num_minimals; | ||
| 244 | int end_tag; | ||
| 245 | int num_states; | ||
| 246 | int cflags; | ||
| 247 | int have_backrefs; | ||
| 248 | int have_approx; | ||
| 249 | int params_depth; | ||
| 250 | }; | ||
| 251 | |||
| 252 | int | ||
| 253 | tre_compile(regex_t *preg, const tre_char_t *regex, size_t n, int cflags); | ||
| 254 | |||
| 255 | void | ||
| 256 | tre_free(regex_t *preg); | ||
| 257 | |||
| 258 | void | ||
| 259 | tre_fill_pmatch(size_t nmatch, regmatch_t pmatch[], int cflags, | ||
| 260 | const tre_tnfa_t *tnfa, int *tags, int match_eo); | ||
| 261 | |||
| 262 | reg_errcode_t | ||
| 263 | tre_tnfa_run_parallel(const tre_tnfa_t *tnfa, const void *string, int len, | ||
| 264 | tre_str_type_t type, int *match_tags, int eflags, | ||
| 265 | int *match_end_ofs); | ||
| 266 | |||
| 267 | reg_errcode_t | ||
| 268 | tre_tnfa_run_parallel(const tre_tnfa_t *tnfa, const void *string, int len, | ||
| 269 | tre_str_type_t type, int *match_tags, int eflags, | ||
| 270 | int *match_end_ofs); | ||
| 271 | |||
| 272 | reg_errcode_t | ||
| 273 | tre_tnfa_run_backtrack(const tre_tnfa_t *tnfa, const void *string, | ||
| 274 | int len, tre_str_type_t type, int *match_tags, | ||
| 275 | int eflags, int *match_end_ofs); | ||
| 276 | |||
| 277 | #ifdef TRE_APPROX | ||
| 278 | reg_errcode_t | ||
| 279 | tre_tnfa_run_approx(const tre_tnfa_t *tnfa, const void *string, int len, | ||
| 280 | tre_str_type_t type, int *match_tags, | ||
| 281 | regamatch_t *match, regaparams_t params, | ||
| 282 | int eflags, int *match_end_ofs); | ||
| 283 | #endif /* TRE_APPROX */ | ||
| 284 | |||
| 285 | #endif /* TRE_INTERNAL_H */ | ||
| 286 | |||
| 287 | /* EOF */ | ||
| @@ -1,812 +0,0 @@ | |||
| 1 | /* | ||
| 2 | tre-match-approx.c - TRE approximate regex matching engine | ||
| 3 | |||
| 4 | This software is released under a BSD-style license. | ||
| 5 | See the file LICENSE for details and copyright. | ||
| 6 | |||
| 7 | */ | ||
| 8 | |||
| 9 | #ifdef HAVE_CONFIG_H | ||
| 10 | #include <config.h> | ||
| 11 | #endif /* HAVE_CONFIG_H */ | ||
| 12 | |||
| 13 | /* AIX requires this to be the first thing in the file. */ | ||
| 14 | #ifdef TRE_USE_ALLOCA | ||
| 15 | #ifndef __GNUC__ | ||
| 16 | # if HAVE_ALLOCA_H | ||
| 17 | # include <alloca.h> | ||
| 18 | # else | ||
| 19 | # ifdef _AIX | ||
| 20 | #pragma alloca | ||
| 21 | # else | ||
| 22 | # ifndef alloca /* predefined by HP cc +Olibcalls */ | ||
| 23 | char *alloca (); | ||
| 24 | # endif | ||
| 25 | # endif | ||
| 26 | # endif | ||
| 27 | #endif | ||
| 28 | #endif /* TRE_USE_ALLOCA */ | ||
| 29 | |||
| 30 | #define __USE_STRING_INLINES | ||
| 31 | #undef __NO_INLINE__ | ||
| 32 | |||
| 33 | #include <assert.h> | ||
| 34 | #include <stdlib.h> | ||
| 35 | #include <string.h> | ||
| 36 | #include <limits.h> | ||
| 37 | #ifdef HAVE_WCHAR_H | ||
| 38 | #include <wchar.h> | ||
| 39 | #endif /* HAVE_WCHAR_H */ | ||
| 40 | #ifdef HAVE_WCTYPE_H | ||
| 41 | #include <wctype.h> | ||
| 42 | #endif /* HAVE_WCTYPE_H */ | ||
| 43 | #ifndef TRE_WCHAR | ||
| 44 | #include <ctype.h> | ||
| 45 | #endif /* !TRE_WCHAR */ | ||
| 46 | #ifdef HAVE_MALLOC_H | ||
| 47 | #include <malloc.h> | ||
| 48 | #endif /* HAVE_MALLOC_H */ | ||
| 49 | |||
| 50 | #include "tre-internal.h" | ||
| 51 | #include "tre-match-utils.h" | ||
| 52 | #include "tre.h" | ||
| 53 | #include "xmalloc.h" | ||
| 54 | |||
| 55 | #define TRE_M_COST 0 | ||
| 56 | #define TRE_M_NUM_INS 1 | ||
| 57 | #define TRE_M_NUM_DEL 2 | ||
| 58 | #define TRE_M_NUM_SUBST 3 | ||
| 59 | #define TRE_M_NUM_ERR 4 | ||
| 60 | #define TRE_M_LAST 5 | ||
| 61 | |||
| 62 | #define TRE_M_MAX_DEPTH 3 | ||
| 63 | |||
| 64 | typedef struct { | ||
| 65 | /* State in the TNFA transition table. */ | ||
| 66 | tre_tnfa_transition_t *state; | ||
| 67 | /* Position in input string. */ | ||
| 68 | int pos; | ||
| 69 | /* Tag values. */ | ||
| 70 | int *tags; | ||
| 71 | /* Matching parameters. */ | ||
| 72 | regaparams_t params; | ||
| 73 | /* Nesting depth of parameters. This is used as an index in | ||
| 74 | the `costs' array. */ | ||
| 75 | int depth; | ||
| 76 | /* Costs and counter values for different parameter nesting depths. */ | ||
| 77 | int costs[TRE_M_MAX_DEPTH + 1][TRE_M_LAST]; | ||
| 78 | } tre_tnfa_approx_reach_t; | ||
| 79 | |||
| 80 | |||
| 81 | #ifdef TRE_DEBUG | ||
| 82 | /* Prints the `reach' array in a readable fashion with DPRINT. */ | ||
| 83 | static void | ||
| 84 | tre_print_reach(const tre_tnfa_t *tnfa, tre_tnfa_approx_reach_t *reach, | ||
| 85 | int pos, int num_tags) | ||
| 86 | { | ||
| 87 | int id; | ||
| 88 | |||
| 89 | /* Print each state on one line. */ | ||
| 90 | DPRINT((" reach:\n")); | ||
| 91 | for (id = 0; id < tnfa->num_states; id++) | ||
| 92 | { | ||
| 93 | int i, j; | ||
| 94 | if (reach[id].pos < pos) | ||
| 95 | continue; /* Not reached. */ | ||
| 96 | DPRINT((" %03d, costs ", id)); | ||
| 97 | for (i = 0; i <= reach[id].depth; i++) | ||
| 98 | { | ||
| 99 | DPRINT(("[")); | ||
| 100 | for (j = 0; j < TRE_M_LAST; j++) | ||
| 101 | { | ||
| 102 | DPRINT(("%2d", reach[id].costs[i][j])); | ||
| 103 | if (j + 1 < TRE_M_LAST) | ||
| 104 | DPRINT((",")); | ||
| 105 | } | ||
| 106 | DPRINT(("]")); | ||
| 107 | if (i + 1 <= reach[id].depth) | ||
| 108 | DPRINT((", ")); | ||
| 109 | } | ||
| 110 | DPRINT(("\n tags ")); | ||
| 111 | for (i = 0; i < num_tags; i++) | ||
| 112 | { | ||
| 113 | DPRINT(("%02d", reach[id].tags[i])); | ||
| 114 | if (i + 1 < num_tags) | ||
| 115 | DPRINT((",")); | ||
| 116 | } | ||
| 117 | DPRINT(("\n")); | ||
| 118 | } | ||
| 119 | DPRINT(("\n")); | ||
| 120 | } | ||
| 121 | #endif /* TRE_DEBUG */ | ||
| 122 | |||
| 123 | |||
| 124 | /* Sets the matching parameters in `reach' to the ones defined in the `pa' | ||
| 125 | array. If `pa' specifies default values, they are taken from | ||
| 126 | `default_params'. */ | ||
| 127 | inline static void | ||
| 128 | tre_set_params(tre_tnfa_approx_reach_t *reach, | ||
| 129 | int *pa, regaparams_t default_params) | ||
| 130 | { | ||
| 131 | int value; | ||
| 132 | |||
| 133 | /* If depth is increased reset costs and counters to zero for the | ||
| 134 | new levels. */ | ||
| 135 | value = pa[TRE_PARAM_DEPTH]; | ||
| 136 | assert(value <= TRE_M_MAX_DEPTH); | ||
| 137 | if (value > reach->depth) | ||
| 138 | { | ||
| 139 | int i, j; | ||
| 140 | for (i = reach->depth + 1; i <= value; i++) | ||
| 141 | for (j = 0; j < TRE_M_LAST; j++) | ||
| 142 | reach->costs[i][j] = 0; | ||
| 143 | } | ||
| 144 | reach->depth = value; | ||
| 145 | |||
| 146 | /* Set insert cost. */ | ||
| 147 | value = pa[TRE_PARAM_COST_INS]; | ||
| 148 | if (value == TRE_PARAM_DEFAULT) | ||
| 149 | reach->params.cost_ins = default_params.cost_ins; | ||
| 150 | else if (value != TRE_PARAM_UNSET) | ||
| 151 | reach->params.cost_ins = value; | ||
| 152 | |||
| 153 | /* Set delete cost. */ | ||
| 154 | value = pa[TRE_PARAM_COST_DEL]; | ||
| 155 | if (value == TRE_PARAM_DEFAULT) | ||
| 156 | reach->params.cost_del = default_params.cost_del; | ||
| 157 | else if (value != TRE_PARAM_UNSET) | ||
| 158 | reach->params.cost_del = value; | ||
| 159 | |||
| 160 | /* Set substitute cost. */ | ||
| 161 | value = pa[TRE_PARAM_COST_SUBST]; | ||
| 162 | if (value == TRE_PARAM_DEFAULT) | ||
| 163 | reach->params.cost_subst = default_params.cost_subst; | ||
| 164 | else | ||
| 165 | reach->params.cost_subst = value; | ||
| 166 | |||
| 167 | /* Set maximum cost. */ | ||
| 168 | value = pa[TRE_PARAM_COST_MAX]; | ||
| 169 | if (value == TRE_PARAM_DEFAULT) | ||
| 170 | reach->params.max_cost = default_params.max_cost; | ||
| 171 | else if (value != TRE_PARAM_UNSET) | ||
| 172 | reach->params.max_cost = value; | ||
| 173 | |||
| 174 | /* Set maximum inserts. */ | ||
| 175 | value = pa[TRE_PARAM_MAX_INS]; | ||
| 176 | if (value == TRE_PARAM_DEFAULT) | ||
| 177 | reach->params.max_ins = default_params.max_ins; | ||
| 178 | else if (value != TRE_PARAM_UNSET) | ||
| 179 | reach->params.max_ins = value; | ||
| 180 | |||
| 181 | /* Set maximum deletes. */ | ||
| 182 | value = pa[TRE_PARAM_MAX_DEL]; | ||
| 183 | if (value == TRE_PARAM_DEFAULT) | ||
| 184 | reach->params.max_del = default_params.max_del; | ||
| 185 | else if (value != TRE_PARAM_UNSET) | ||
| 186 | reach->params.max_del = value; | ||
| 187 | |||
| 188 | /* Set maximum substitutes. */ | ||
| 189 | value = pa[TRE_PARAM_MAX_SUBST]; | ||
| 190 | if (value == TRE_PARAM_DEFAULT) | ||
| 191 | reach->params.max_subst = default_params.max_subst; | ||
| 192 | else if (value != TRE_PARAM_UNSET) | ||
| 193 | reach->params.max_subst = value; | ||
| 194 | |||
| 195 | /* Set maximum number of errors. */ | ||
| 196 | value = pa[TRE_PARAM_MAX_ERR]; | ||
| 197 | if (value == TRE_PARAM_DEFAULT) | ||
| 198 | reach->params.max_err = default_params.max_err; | ||
| 199 | else if (value != TRE_PARAM_UNSET) | ||
| 200 | reach->params.max_err = value; | ||
| 201 | } | ||
| 202 | |||
| 203 | reg_errcode_t | ||
| 204 | tre_tnfa_run_approx(const tre_tnfa_t *tnfa, const void *string, int len, | ||
| 205 | tre_str_type_t type, int *match_tags, | ||
| 206 | regamatch_t *match, regaparams_t default_params, | ||
| 207 | int eflags, int *match_end_ofs) | ||
| 208 | { | ||
| 209 | /* State variables required by GET_NEXT_WCHAR. */ | ||
| 210 | tre_char_t prev_c = 0, next_c = 0; | ||
| 211 | const char *str_byte = string; | ||
| 212 | int pos = -1; | ||
| 213 | unsigned int pos_add_next = 1; | ||
| 214 | #ifdef TRE_WCHAR | ||
| 215 | const wchar_t *str_wide = string; | ||
| 216 | #ifdef TRE_MBSTATE | ||
| 217 | mbstate_t mbstate; | ||
| 218 | #endif /* !TRE_WCHAR */ | ||
| 219 | #endif /* TRE_WCHAR */ | ||
| 220 | int reg_notbol = eflags & REG_NOTBOL; | ||
| 221 | int reg_noteol = eflags & REG_NOTEOL; | ||
| 222 | int reg_newline = tnfa->cflags & REG_NEWLINE; | ||
| 223 | int str_user_end = 0; | ||
| 224 | |||
| 225 | int prev_pos; | ||
| 226 | |||
| 227 | /* Number of tags. */ | ||
| 228 | int num_tags; | ||
| 229 | /* The reach tables. */ | ||
| 230 | tre_tnfa_approx_reach_t *reach, *reach_next; | ||
| 231 | /* Tag array for temporary use. */ | ||
| 232 | int *tmp_tags; | ||
| 233 | |||
| 234 | /* End offset of best match so far, or -1 if no match found yet. */ | ||
| 235 | int match_eo = -1; | ||
| 236 | /* Costs of the match. */ | ||
| 237 | int match_costs[TRE_M_LAST]; | ||
| 238 | |||
| 239 | /* Space for temporary data required for matching. */ | ||
| 240 | unsigned char *buf; | ||
| 241 | |||
| 242 | int i, id; | ||
| 243 | |||
| 244 | if (!match_tags) | ||
| 245 | num_tags = 0; | ||
| 246 | else | ||
| 247 | num_tags = tnfa->num_tags; | ||
| 248 | |||
| 249 | #ifdef TRE_MBSTATE | ||
| 250 | memset(&mbstate, '\0', sizeof(mbstate)); | ||
| 251 | #endif /* TRE_MBSTATE */ | ||
| 252 | |||
| 253 | DPRINT(("tre_tnfa_run_approx, input type %d, len %d, eflags %d, " | ||
| 254 | "match_tags %p\n", | ||
| 255 | type, len, eflags, | ||
| 256 | match_tags)); | ||
| 257 | DPRINT(("max cost %d, ins %d, del %d, subst %d\n", | ||
| 258 | default_params.max_cost, | ||
| 259 | default_params.cost_ins, | ||
| 260 | default_params.cost_del, | ||
| 261 | default_params.cost_subst)); | ||
| 262 | |||
| 263 | /* Allocate memory for temporary data required for matching. This needs to | ||
| 264 | be done for every matching operation to be thread safe. This allocates | ||
| 265 | everything in a single large block from the stack frame using alloca() | ||
| 266 | or with malloc() if alloca is unavailable. */ | ||
| 267 | { | ||
| 268 | unsigned char *buf_cursor; | ||
| 269 | /* Space needed for one array of tags. */ | ||
| 270 | int tag_bytes = sizeof(*tmp_tags) * num_tags; | ||
| 271 | /* Space needed for one reach table. */ | ||
| 272 | int reach_bytes = sizeof(*reach_next) * tnfa->num_states; | ||
| 273 | /* Total space needed. */ | ||
| 274 | int total_bytes = reach_bytes * 2 + (tnfa->num_states * 2 + 1 ) * tag_bytes; | ||
| 275 | /* Add some extra to make sure we can align the pointers. The multiplier | ||
| 276 | used here must be equal to the number of ALIGN calls below. */ | ||
| 277 | total_bytes += (sizeof(long) - 1) * 3; | ||
| 278 | |||
| 279 | /* Allocate the memory. */ | ||
| 280 | #ifdef TRE_USE_ALLOCA | ||
| 281 | buf = alloca(total_bytes); | ||
| 282 | #else /* !TRE_USE_ALLOCA */ | ||
| 283 | buf = xmalloc((unsigned)total_bytes); | ||
| 284 | #endif /* !TRE_USE_ALLOCA */ | ||
| 285 | if (!buf) | ||
| 286 | return REG_ESPACE; | ||
| 287 | memset(buf, 0, (size_t)total_bytes); | ||
| 288 | |||
| 289 | /* Allocate `tmp_tags' from `buf'. */ | ||
| 290 | tmp_tags = (void *)buf; | ||
| 291 | buf_cursor = buf + tag_bytes; | ||
| 292 | buf_cursor += ALIGN(buf_cursor, long); | ||
| 293 | |||
| 294 | /* Allocate `reach' from `buf'. */ | ||
| 295 | reach = (void *)buf_cursor; | ||
| 296 | buf_cursor += reach_bytes; | ||
| 297 | buf_cursor += ALIGN(buf_cursor, long); | ||
| 298 | |||
| 299 | /* Allocate `reach_next' from `buf'. */ | ||
| 300 | reach_next = (void *)buf_cursor; | ||
| 301 | buf_cursor += reach_bytes; | ||
| 302 | buf_cursor += ALIGN(buf_cursor, long); | ||
| 303 | |||
| 304 | /* Allocate tag arrays for `reach' and `reach_next' from `buf'. */ | ||
| 305 | for (i = 0; i < tnfa->num_states; i++) | ||
| 306 | { | ||
| 307 | reach[i].tags = (void *)buf_cursor; | ||
| 308 | buf_cursor += tag_bytes; | ||
| 309 | reach_next[i].tags = (void *)buf_cursor; | ||
| 310 | buf_cursor += tag_bytes; | ||
| 311 | } | ||
| 312 | assert(buf_cursor <= buf + total_bytes); | ||
| 313 | } | ||
| 314 | |||
| 315 | for (i = 0; i < TRE_M_LAST; i++) | ||
| 316 | match_costs[i] = INT_MAX; | ||
| 317 | |||
| 318 | /* Mark the reach arrays empty. */ | ||
| 319 | for (i = 0; i < tnfa->num_states; i++) | ||
| 320 | reach[i].pos = reach_next[i].pos = -2; | ||
| 321 | |||
| 322 | prev_pos = pos; | ||
| 323 | GET_NEXT_WCHAR(); | ||
| 324 | pos = 0; | ||
| 325 | |||
| 326 | while (/*CONSTCOND*/1) | ||
| 327 | { | ||
| 328 | DPRINT(("%03d:%2lc/%05d\n", pos, (tre_cint_t)next_c, (int)next_c)); | ||
| 329 | |||
| 330 | /* Add initial states to `reach_next' if an exact match has not yet | ||
| 331 | been found. */ | ||
| 332 | if (match_costs[TRE_M_COST] > 0) | ||
| 333 | { | ||
| 334 | tre_tnfa_transition_t *trans; | ||
| 335 | DPRINT((" init")); | ||
| 336 | for (trans = tnfa->initial; trans->state; trans++) | ||
| 337 | { | ||
| 338 | int stateid = trans->state_id; | ||
| 339 | |||
| 340 | /* If this state is not currently in `reach_next', add it | ||
| 341 | there. */ | ||
| 342 | if (reach_next[stateid].pos < pos) | ||
| 343 | { | ||
| 344 | if (trans->assertions && CHECK_ASSERTIONS(trans->assertions)) | ||
| 345 | { | ||
| 346 | /* Assertions failed, don't add this state. */ | ||
| 347 | DPRINT((" !%d (assert)", stateid)); | ||
| 348 | continue; | ||
| 349 | } | ||
| 350 | DPRINT((" %d", stateid)); | ||
| 351 | reach_next[stateid].state = trans->state; | ||
| 352 | reach_next[stateid].pos = pos; | ||
| 353 | |||
| 354 | /* Compute tag values after this transition. */ | ||
| 355 | for (i = 0; i < num_tags; i++) | ||
| 356 | reach_next[stateid].tags[i] = -1; | ||
| 357 | |||
| 358 | if (trans->tags) | ||
| 359 | for (i = 0; trans->tags[i] >= 0; i++) | ||
| 360 | if (trans->tags[i] < num_tags) | ||
| 361 | reach_next[stateid].tags[trans->tags[i]] = pos; | ||
| 362 | |||
| 363 | /* Set the parameters, depth, and costs. */ | ||
| 364 | reach_next[stateid].params = default_params; | ||
| 365 | reach_next[stateid].depth = 0; | ||
| 366 | for (i = 0; i < TRE_M_LAST; i++) | ||
| 367 | reach_next[stateid].costs[0][i] = 0; | ||
| 368 | if (trans->params) | ||
| 369 | tre_set_params(&reach_next[stateid], trans->params, | ||
| 370 | default_params); | ||
| 371 | |||
| 372 | /* If this is the final state, mark the exact match. */ | ||
| 373 | if (trans->state == tnfa->final) | ||
| 374 | { | ||
| 375 | match_eo = pos; | ||
| 376 | for (i = 0; i < num_tags; i++) | ||
| 377 | match_tags[i] = reach_next[stateid].tags[i]; | ||
| 378 | for (i = 0; i < TRE_M_LAST; i++) | ||
| 379 | match_costs[i] = 0; | ||
| 380 | } | ||
| 381 | } | ||
| 382 | } | ||
| 383 | DPRINT(("\n")); | ||
| 384 | } | ||
| 385 | |||
| 386 | |||
| 387 | /* Handle inserts. This is done by pretending there's an epsilon | ||
| 388 | transition from each state in `reach' back to the same state. | ||
| 389 | We don't need to worry about the final state here; this will never | ||
| 390 | give a better match than what we already have. */ | ||
| 391 | for (id = 0; id < tnfa->num_states; id++) | ||
| 392 | { | ||
| 393 | int depth; | ||
| 394 | int cost, cost0; | ||
| 395 | |||
| 396 | if (reach[id].pos != prev_pos) | ||
| 397 | { | ||
| 398 | DPRINT((" insert: %d not reached\n", id)); | ||
| 399 | continue; /* Not reached. */ | ||
| 400 | } | ||
| 401 | |||
| 402 | depth = reach[id].depth; | ||
| 403 | |||
| 404 | /* Compute and check cost at current depth. */ | ||
| 405 | cost = reach[id].costs[depth][TRE_M_COST]; | ||
| 406 | if (reach[id].params.cost_ins != TRE_PARAM_UNSET) | ||
| 407 | cost += reach[id].params.cost_ins; | ||
| 408 | if (cost > reach[id].params.max_cost) | ||
| 409 | continue; /* Cost too large. */ | ||
| 410 | |||
| 411 | /* Check number of inserts at current depth. */ | ||
| 412 | if (reach[id].costs[depth][TRE_M_NUM_INS] + 1 | ||
| 413 | > reach[id].params.max_ins) | ||
| 414 | continue; /* Too many inserts. */ | ||
| 415 | |||
| 416 | /* Check total number of errors at current depth. */ | ||
| 417 | if (reach[id].costs[depth][TRE_M_NUM_ERR] + 1 | ||
| 418 | > reach[id].params.max_err) | ||
| 419 | continue; /* Too many errors. */ | ||
| 420 | |||
| 421 | /* Compute overall cost. */ | ||
| 422 | cost0 = cost; | ||
| 423 | if (depth > 0) | ||
| 424 | { | ||
| 425 | cost0 = reach[id].costs[0][TRE_M_COST]; | ||
| 426 | if (reach[id].params.cost_ins != TRE_PARAM_UNSET) | ||
| 427 | cost0 += reach[id].params.cost_ins; | ||
| 428 | else | ||
| 429 | cost0 += default_params.cost_ins; | ||
| 430 | } | ||
| 431 | |||
| 432 | DPRINT((" insert: from %d to %d, cost %d: ", id, id, | ||
| 433 | reach[id].costs[depth][TRE_M_COST])); | ||
| 434 | if (reach_next[id].pos == pos | ||
| 435 | && (cost0 >= reach_next[id].costs[0][TRE_M_COST])) | ||
| 436 | { | ||
| 437 | DPRINT(("lose\n")); | ||
| 438 | continue; | ||
| 439 | } | ||
| 440 | DPRINT(("win\n")); | ||
| 441 | |||
| 442 | /* Copy state, position, tags, parameters, and depth. */ | ||
| 443 | reach_next[id].state = reach[id].state; | ||
| 444 | reach_next[id].pos = pos; | ||
| 445 | for (i = 0; i < num_tags; i++) | ||
| 446 | reach_next[id].tags[i] = reach[id].tags[i]; | ||
| 447 | reach_next[id].params = reach[id].params; | ||
| 448 | reach_next[id].depth = reach[id].depth; | ||
| 449 | |||
| 450 | /* Set the costs after this transition. */ | ||
| 451 | memcpy(reach_next[id].costs, reach[id].costs, | ||
| 452 | sizeof(reach_next[id].costs[0][0]) | ||
| 453 | * TRE_M_LAST * (depth + 1)); | ||
| 454 | reach_next[id].costs[depth][TRE_M_COST] = cost; | ||
| 455 | reach_next[id].costs[depth][TRE_M_NUM_INS]++; | ||
| 456 | reach_next[id].costs[depth][TRE_M_NUM_ERR]++; | ||
| 457 | if (depth > 0) | ||
| 458 | { | ||
| 459 | reach_next[id].costs[0][TRE_M_COST] = cost0; | ||
| 460 | reach_next[id].costs[0][TRE_M_NUM_INS]++; | ||
| 461 | reach_next[id].costs[0][TRE_M_NUM_ERR]++; | ||
| 462 | } | ||
| 463 | |||
| 464 | } | ||
| 465 | |||
| 466 | |||
| 467 | /* Handle deletes. This is done by traversing through the whole TNFA | ||
| 468 | pretending that all transitions are epsilon transitions, until | ||
| 469 | no more states can be reached with better costs. */ | ||
| 470 | { | ||
| 471 | /* XXX - dynamic ringbuffer size */ | ||
| 472 | tre_tnfa_approx_reach_t *ringbuffer[512]; | ||
| 473 | tre_tnfa_approx_reach_t **deque_start, **deque_end; | ||
| 474 | |||
| 475 | deque_start = deque_end = ringbuffer; | ||
| 476 | |||
| 477 | /* Add all states in `reach_next' to the deque. */ | ||
| 478 | for (id = 0; id < tnfa->num_states; id++) | ||
| 479 | { | ||
| 480 | if (reach_next[id].pos != pos) | ||
| 481 | continue; | ||
| 482 | *deque_end = &reach_next[id]; | ||
| 483 | deque_end++; | ||
| 484 | assert(deque_end != deque_start); | ||
| 485 | } | ||
| 486 | |||
| 487 | /* Repeat until the deque is empty. */ | ||
| 488 | while (deque_end != deque_start) | ||
| 489 | { | ||
| 490 | tre_tnfa_approx_reach_t *reach_p; | ||
| 491 | int depth; | ||
| 492 | int cost, cost0; | ||
| 493 | tre_tnfa_transition_t *trans; | ||
| 494 | |||
| 495 | /* Pop the first item off the deque. */ | ||
| 496 | reach_p = *deque_start; | ||
| 497 | id = reach_p - reach_next; | ||
| 498 | depth = reach_p->depth; | ||
| 499 | |||
| 500 | /* Compute cost at current depth. */ | ||
| 501 | cost = reach_p->costs[depth][TRE_M_COST]; | ||
| 502 | if (reach_p->params.cost_del != TRE_PARAM_UNSET) | ||
| 503 | cost += reach_p->params.cost_del; | ||
| 504 | |||
| 505 | /* Check cost, number of deletes, and total number of errors | ||
| 506 | at current depth. */ | ||
| 507 | if (cost > reach_p->params.max_cost | ||
| 508 | || (reach_p->costs[depth][TRE_M_NUM_DEL] + 1 | ||
| 509 | > reach_p->params.max_del) | ||
| 510 | || (reach_p->costs[depth][TRE_M_NUM_ERR] + 1 | ||
| 511 | > reach_p->params.max_err)) | ||
| 512 | { | ||
| 513 | /* Too many errors or cost too large. */ | ||
| 514 | DPRINT((" delete: from %03d: cost too large\n", id)); | ||
| 515 | deque_start++; | ||
| 516 | if (deque_start >= (ringbuffer + 512)) | ||
| 517 | deque_start = ringbuffer; | ||
| 518 | continue; | ||
| 519 | } | ||
| 520 | |||
| 521 | /* Compute overall cost. */ | ||
| 522 | cost0 = cost; | ||
| 523 | if (depth > 0) | ||
| 524 | { | ||
| 525 | cost0 = reach_p->costs[0][TRE_M_COST]; | ||
| 526 | if (reach_p->params.cost_del != TRE_PARAM_UNSET) | ||
| 527 | cost0 += reach_p->params.cost_del; | ||
| 528 | else | ||
| 529 | cost0 += default_params.cost_del; | ||
| 530 | } | ||
| 531 | |||
| 532 | for (trans = reach_p->state; trans->state; trans++) | ||
| 533 | { | ||
| 534 | int dest_id = trans->state_id; | ||
| 535 | DPRINT((" delete: from %03d to %03d, cost %d (%d): ", | ||
| 536 | id, dest_id, cost0, reach_p->params.max_cost)); | ||
| 537 | |||
| 538 | if (trans->assertions && CHECK_ASSERTIONS(trans->assertions)) | ||
| 539 | { | ||
| 540 | DPRINT(("assertion failed\n")); | ||
| 541 | continue; | ||
| 542 | } | ||
| 543 | |||
| 544 | /* Compute tag values after this transition. */ | ||
| 545 | for (i = 0; i < num_tags; i++) | ||
| 546 | tmp_tags[i] = reach_p->tags[i]; | ||
| 547 | if (trans->tags) | ||
| 548 | for (i = 0; trans->tags[i] >= 0; i++) | ||
| 549 | if (trans->tags[i] < num_tags) | ||
| 550 | tmp_tags[trans->tags[i]] = pos; | ||
| 551 | |||
| 552 | /* If another path has also reached this state, choose the one | ||
| 553 | with the smallest cost or best tags if costs are equal. */ | ||
| 554 | if (reach_next[dest_id].pos == pos | ||
| 555 | && (cost0 > reach_next[dest_id].costs[0][TRE_M_COST] | ||
| 556 | || (cost0 == reach_next[dest_id].costs[0][TRE_M_COST] | ||
| 557 | && (!match_tags | ||
| 558 | || !tre_tag_order(num_tags, | ||
| 559 | tnfa->tag_directions, | ||
| 560 | tmp_tags, | ||
| 561 | reach_next[dest_id].tags))))) | ||
| 562 | { | ||
| 563 | DPRINT(("lose, cost0 %d, have %d\n", | ||
| 564 | cost0, reach_next[dest_id].costs[0][TRE_M_COST])); | ||
| 565 | continue; | ||
| 566 | } | ||
| 567 | DPRINT(("win\n")); | ||
| 568 | |||
| 569 | /* Set state, position, tags, parameters, depth, and costs. */ | ||
| 570 | reach_next[dest_id].state = trans->state; | ||
| 571 | reach_next[dest_id].pos = pos; | ||
| 572 | for (i = 0; i < num_tags; i++) | ||
| 573 | reach_next[dest_id].tags[i] = tmp_tags[i]; | ||
| 574 | |||
| 575 | reach_next[dest_id].params = reach_p->params; | ||
| 576 | if (trans->params) | ||
| 577 | tre_set_params(&reach_next[dest_id], trans->params, | ||
| 578 | default_params); | ||
| 579 | |||
| 580 | reach_next[dest_id].depth = reach_p->depth; | ||
| 581 | memcpy(&reach_next[dest_id].costs, | ||
| 582 | reach_p->costs, | ||
| 583 | sizeof(reach_p->costs[0][0]) | ||
| 584 | * TRE_M_LAST * (depth + 1)); | ||
| 585 | reach_next[dest_id].costs[depth][TRE_M_COST] = cost; | ||
| 586 | reach_next[dest_id].costs[depth][TRE_M_NUM_DEL]++; | ||
| 587 | reach_next[dest_id].costs[depth][TRE_M_NUM_ERR]++; | ||
| 588 | if (depth > 0) | ||
| 589 | { | ||
| 590 | reach_next[dest_id].costs[0][TRE_M_COST] = cost0; | ||
| 591 | reach_next[dest_id].costs[0][TRE_M_NUM_DEL]++; | ||
| 592 | reach_next[dest_id].costs[0][TRE_M_NUM_ERR]++; | ||
| 593 | } | ||
| 594 | |||
| 595 | if (trans->state == tnfa->final | ||
| 596 | && (match_eo < 0 | ||
| 597 | || match_costs[TRE_M_COST] > cost0 | ||
| 598 | || (match_costs[TRE_M_COST] == cost0 | ||
| 599 | && (num_tags > 0 | ||
| 600 | && tmp_tags[0] <= match_tags[0])))) | ||
| 601 | { | ||
| 602 | DPRINT((" setting new match at %d, cost %d\n", | ||
| 603 | pos, cost0)); | ||
| 604 | match_eo = pos; | ||
| 605 | memcpy(match_costs, reach_next[dest_id].costs[0], | ||
| 606 | sizeof(match_costs[0]) * TRE_M_LAST); | ||
| 607 | for (i = 0; i < num_tags; i++) | ||
| 608 | match_tags[i] = tmp_tags[i]; | ||
| 609 | } | ||
| 610 | |||
| 611 | /* Add to the end of the deque. */ | ||
| 612 | *deque_end = &reach_next[dest_id]; | ||
| 613 | deque_end++; | ||
| 614 | if (deque_end >= (ringbuffer + 512)) | ||
| 615 | deque_end = ringbuffer; | ||
| 616 | assert(deque_end != deque_start); | ||
| 617 | } | ||
| 618 | deque_start++; | ||
| 619 | if (deque_start >= (ringbuffer + 512)) | ||
| 620 | deque_start = ringbuffer; | ||
| 621 | } | ||
| 622 | |||
| 623 | } | ||
| 624 | |||
| 625 | #ifdef TRE_DEBUG | ||
| 626 | tre_print_reach(tnfa, reach_next, pos, num_tags); | ||
| 627 | #endif /* TRE_DEBUG */ | ||
| 628 | |||
| 629 | /* Check for end of string. */ | ||
| 630 | if (len < 0) | ||
| 631 | { | ||
| 632 | if (type == STR_USER) | ||
| 633 | { | ||
| 634 | if (str_user_end) | ||
| 635 | break; | ||
| 636 | } | ||
| 637 | else if (next_c == L'\0') | ||
| 638 | break; | ||
| 639 | } | ||
| 640 | else | ||
| 641 | { | ||
| 642 | if (pos >= len) | ||
| 643 | break; | ||
| 644 | } | ||
| 645 | |||
| 646 | prev_pos = pos; | ||
| 647 | GET_NEXT_WCHAR(); | ||
| 648 | |||
| 649 | /* Swap `reach' and `reach_next'. */ | ||
| 650 | { | ||
| 651 | tre_tnfa_approx_reach_t *tmp; | ||
| 652 | tmp = reach; | ||
| 653 | reach = reach_next; | ||
| 654 | reach_next = tmp; | ||
| 655 | } | ||
| 656 | |||
| 657 | /* Handle exact matches and substitutions. */ | ||
| 658 | for (id = 0; id < tnfa->num_states; id++) | ||
| 659 | { | ||
| 660 | tre_tnfa_transition_t *trans; | ||
| 661 | |||
| 662 | if (reach[id].pos < prev_pos) | ||
| 663 | continue; /* Not reached. */ | ||
| 664 | for (trans = reach[id].state; trans->state; trans++) | ||
| 665 | { | ||
| 666 | int dest_id; | ||
| 667 | int depth; | ||
| 668 | int cost, cost0, err; | ||
| 669 | |||
| 670 | if (trans->assertions | ||
| 671 | && (CHECK_ASSERTIONS(trans->assertions) | ||
| 672 | || CHECK_CHAR_CLASSES(trans, tnfa, eflags))) | ||
| 673 | { | ||
| 674 | DPRINT((" exact, from %d: assert failed\n", id)); | ||
| 675 | continue; | ||
| 676 | } | ||
| 677 | |||
| 678 | depth = reach[id].depth; | ||
| 679 | dest_id = trans->state_id; | ||
| 680 | |||
| 681 | cost = reach[id].costs[depth][TRE_M_COST]; | ||
| 682 | cost0 = reach[id].costs[0][TRE_M_COST]; | ||
| 683 | err = 0; | ||
| 684 | |||
| 685 | if (trans->code_min > (tre_cint_t)prev_c | ||
| 686 | || trans->code_max < (tre_cint_t)prev_c) | ||
| 687 | { | ||
| 688 | /* Handle substitutes. The required character was not in | ||
| 689 | the string, so match it in place of whatever was supposed | ||
| 690 | to be there and increase costs accordingly. */ | ||
| 691 | err = 1; | ||
| 692 | |||
| 693 | /* Compute and check cost at current depth. */ | ||
| 694 | cost = reach[id].costs[depth][TRE_M_COST]; | ||
| 695 | if (reach[id].params.cost_subst != TRE_PARAM_UNSET) | ||
| 696 | cost += reach[id].params.cost_subst; | ||
| 697 | if (cost > reach[id].params.max_cost) | ||
| 698 | continue; /* Cost too large. */ | ||
| 699 | |||
| 700 | /* Check number of substitutes at current depth. */ | ||
| 701 | if (reach[id].costs[depth][TRE_M_NUM_SUBST] + 1 | ||
| 702 | > reach[id].params.max_subst) | ||
| 703 | continue; /* Too many substitutes. */ | ||
| 704 | |||
| 705 | /* Check total number of errors at current depth. */ | ||
| 706 | if (reach[id].costs[depth][TRE_M_NUM_ERR] + 1 | ||
| 707 | > reach[id].params.max_err) | ||
| 708 | continue; /* Too many errors. */ | ||
| 709 | |||
| 710 | /* Compute overall cost. */ | ||
| 711 | cost0 = cost; | ||
| 712 | if (depth > 0) | ||
| 713 | { | ||
| 714 | cost0 = reach[id].costs[0][TRE_M_COST]; | ||
| 715 | if (reach[id].params.cost_subst != TRE_PARAM_UNSET) | ||
| 716 | cost0 += reach[id].params.cost_subst; | ||
| 717 | else | ||
| 718 | cost0 += default_params.cost_subst; | ||
| 719 | } | ||
| 720 | DPRINT((" subst, from %03d to %03d, cost %d: ", | ||
| 721 | id, dest_id, cost0)); | ||
| 722 | } | ||
| 723 | else | ||
| 724 | DPRINT((" exact, from %03d to %03d, cost %d: ", | ||
| 725 | id, dest_id, cost0)); | ||
| 726 | |||
| 727 | /* Compute tag values after this transition. */ | ||
| 728 | for (i = 0; i < num_tags; i++) | ||
| 729 | tmp_tags[i] = reach[id].tags[i]; | ||
| 730 | if (trans->tags) | ||
| 731 | for (i = 0; trans->tags[i] >= 0; i++) | ||
| 732 | if (trans->tags[i] < num_tags) | ||
| 733 | tmp_tags[trans->tags[i]] = pos; | ||
| 734 | |||
| 735 | /* If another path has also reached this state, choose the | ||
| 736 | one with the smallest cost or best tags if costs are equal. */ | ||
| 737 | if (reach_next[dest_id].pos == pos | ||
| 738 | && (cost0 > reach_next[dest_id].costs[0][TRE_M_COST] | ||
| 739 | || (cost0 == reach_next[dest_id].costs[0][TRE_M_COST] | ||
| 740 | && !tre_tag_order(num_tags, tnfa->tag_directions, | ||
| 741 | tmp_tags, | ||
| 742 | reach_next[dest_id].tags)))) | ||
| 743 | { | ||
| 744 | DPRINT(("lose\n")); | ||
| 745 | continue; | ||
| 746 | } | ||
| 747 | DPRINT(("win %d %d\n", | ||
| 748 | reach_next[dest_id].pos, | ||
| 749 | reach_next[dest_id].costs[0][TRE_M_COST])); | ||
| 750 | |||
| 751 | /* Set state, position, tags, and depth. */ | ||
| 752 | reach_next[dest_id].state = trans->state; | ||
| 753 | reach_next[dest_id].pos = pos; | ||
| 754 | for (i = 0; i < num_tags; i++) | ||
| 755 | reach_next[dest_id].tags[i] = tmp_tags[i]; | ||
| 756 | reach_next[dest_id].depth = reach[id].depth; | ||
| 757 | |||
| 758 | /* Set parameters. */ | ||
| 759 | reach_next[dest_id].params = reach[id].params; | ||
| 760 | if (trans->params) | ||
| 761 | tre_set_params(&reach_next[dest_id], trans->params, | ||
| 762 | default_params); | ||
| 763 | |||
| 764 | /* Set the costs after this transition. */ | ||
| 765 | memcpy(&reach_next[dest_id].costs, | ||
| 766 | reach[id].costs, | ||
| 767 | sizeof(reach[id].costs[0][0]) | ||
| 768 | * TRE_M_LAST * (depth + 1)); | ||
| 769 | reach_next[dest_id].costs[depth][TRE_M_COST] = cost; | ||
| 770 | reach_next[dest_id].costs[depth][TRE_M_NUM_SUBST] += err; | ||
| 771 | reach_next[dest_id].costs[depth][TRE_M_NUM_ERR] += err; | ||
| 772 | if (depth > 0) | ||
| 773 | { | ||
| 774 | reach_next[dest_id].costs[0][TRE_M_COST] = cost0; | ||
| 775 | reach_next[dest_id].costs[0][TRE_M_NUM_SUBST] += err; | ||
| 776 | reach_next[dest_id].costs[0][TRE_M_NUM_ERR] += err; | ||
| 777 | } | ||
| 778 | |||
| 779 | if (trans->state == tnfa->final | ||
| 780 | && (match_eo < 0 | ||
| 781 | || cost0 < match_costs[TRE_M_COST] | ||
| 782 | || (cost0 == match_costs[TRE_M_COST] | ||
| 783 | && num_tags > 0 && tmp_tags[0] <= match_tags[0]))) | ||
| 784 | { | ||
| 785 | DPRINT((" setting new match at %d, cost %d\n", | ||
| 786 | pos, cost0)); | ||
| 787 | match_eo = pos; | ||
| 788 | for (i = 0; i < TRE_M_LAST; i++) | ||
| 789 | match_costs[i] = reach_next[dest_id].costs[0][i]; | ||
| 790 | for (i = 0; i < num_tags; i++) | ||
| 791 | match_tags[i] = tmp_tags[i]; | ||
| 792 | } | ||
| 793 | } | ||
| 794 | } | ||
| 795 | } | ||
| 796 | |||
| 797 | DPRINT(("match end offset = %d, match cost = %d\n", match_eo, | ||
| 798 | match_costs[TRE_M_COST])); | ||
| 799 | |||
| 800 | #ifndef TRE_USE_ALLOCA | ||
| 801 | if (buf) | ||
| 802 | xfree(buf); | ||
| 803 | #endif /* !TRE_USE_ALLOCA */ | ||
| 804 | |||
| 805 | match->cost = match_costs[TRE_M_COST]; | ||
| 806 | match->num_ins = match_costs[TRE_M_NUM_INS]; | ||
| 807 | match->num_del = match_costs[TRE_M_NUM_DEL]; | ||
| 808 | match->num_subst = match_costs[TRE_M_NUM_SUBST]; | ||
| 809 | *match_end_ofs = match_eo; | ||
| 810 | |||
| 811 | return match_eo >= 0 ? REG_OK : REG_NOMATCH; | ||
| 812 | } | ||
| @@ -1,666 +0,0 @@ | |||
| 1 | /* | ||
| 2 | tre-match-backtrack.c - TRE backtracking regex matching engine | ||
| 3 | |||
| 4 | This software is released under a BSD-style license. | ||
| 5 | See the file LICENSE for details and copyright. | ||
| 6 | |||
| 7 | */ | ||
| 8 | |||
| 9 | /* | ||
| 10 | This matcher is for regexps that use back referencing. Regexp matching | ||
| 11 | with back referencing is an NP-complete problem on the number of back | ||
| 12 | references. The easiest way to match them is to use a backtracking | ||
| 13 | routine which basically goes through all possible paths in the TNFA | ||
| 14 | and chooses the one which results in the best (leftmost and longest) | ||
| 15 | match. This can be spectacularly expensive and may run out of stack | ||
| 16 | space, but there really is no better known generic algorithm. Quoting | ||
| 17 | Henry Spencer from comp.compilers: | ||
| 18 | <URL: http://compilers.iecc.com/comparch/article/93-03-102> | ||
| 19 | |||
| 20 | POSIX.2 REs require longest match, which is really exciting to | ||
| 21 | implement since the obsolete ("basic") variant also includes | ||
| 22 | \<digit>. I haven't found a better way of tackling this than doing | ||
| 23 | a preliminary match using a DFA (or simulation) on a modified RE | ||
| 24 | that just replicates subREs for \<digit>, and then doing a | ||
| 25 | backtracking match to determine whether the subRE matches were | ||
| 26 | right. This can be rather slow, but I console myself with the | ||
| 27 | thought that people who use \<digit> deserve very slow execution. | ||
| 28 | (Pun unintentional but very appropriate.) | ||
| 29 | |||
| 30 | */ | ||
| 31 | |||
| 32 | |||
| 33 | #ifdef HAVE_CONFIG_H | ||
| 34 | #include <config.h> | ||
| 35 | #endif /* HAVE_CONFIG_H */ | ||
| 36 | |||
| 37 | #ifdef TRE_USE_ALLOCA | ||
| 38 | /* AIX requires this to be the first thing in the file. */ | ||
| 39 | #ifndef __GNUC__ | ||
| 40 | # if HAVE_ALLOCA_H | ||
| 41 | # include <alloca.h> | ||
| 42 | # else | ||
| 43 | # ifdef _AIX | ||
| 44 | #pragma alloca | ||
| 45 | # else | ||
| 46 | # ifndef alloca /* predefined by HP cc +Olibcalls */ | ||
| 47 | char *alloca (); | ||
| 48 | # endif | ||
| 49 | # endif | ||
| 50 | # endif | ||
| 51 | #endif | ||
| 52 | #endif /* TRE_USE_ALLOCA */ | ||
| 53 | |||
| 54 | #include <assert.h> | ||
| 55 | #include <stdlib.h> | ||
| 56 | #include <string.h> | ||
| 57 | #ifdef HAVE_WCHAR_H | ||
| 58 | #include <wchar.h> | ||
| 59 | #endif /* HAVE_WCHAR_H */ | ||
| 60 | #ifdef HAVE_WCTYPE_H | ||
| 61 | #include <wctype.h> | ||
| 62 | #endif /* HAVE_WCTYPE_H */ | ||
| 63 | #ifndef TRE_WCHAR | ||
| 64 | #include <ctype.h> | ||
| 65 | #endif /* !TRE_WCHAR */ | ||
| 66 | #ifdef HAVE_MALLOC_H | ||
| 67 | #include <malloc.h> | ||
| 68 | #endif /* HAVE_MALLOC_H */ | ||
| 69 | |||
| 70 | #include "tre-internal.h" | ||
| 71 | #include "tre-mem.h" | ||
| 72 | #include "tre-match-utils.h" | ||
| 73 | #include "tre.h" | ||
| 74 | #include "xmalloc.h" | ||
| 75 | |||
| 76 | typedef struct { | ||
| 77 | int pos; | ||
| 78 | const char *str_byte; | ||
| 79 | #ifdef TRE_WCHAR | ||
| 80 | const wchar_t *str_wide; | ||
| 81 | #endif /* TRE_WCHAR */ | ||
| 82 | tre_tnfa_transition_t *state; | ||
| 83 | int state_id; | ||
| 84 | int next_c; | ||
| 85 | int *tags; | ||
| 86 | #ifdef TRE_MBSTATE | ||
| 87 | mbstate_t mbstate; | ||
| 88 | #endif /* TRE_MBSTATE */ | ||
| 89 | } tre_backtrack_item_t; | ||
| 90 | |||
| 91 | typedef struct tre_backtrack_struct { | ||
| 92 | tre_backtrack_item_t item; | ||
| 93 | struct tre_backtrack_struct *prev; | ||
| 94 | struct tre_backtrack_struct *next; | ||
| 95 | } *tre_backtrack_t; | ||
| 96 | |||
| 97 | #ifdef TRE_WCHAR | ||
| 98 | #define BT_STACK_WIDE_IN(_str_wide) stack->item.str_wide = (_str_wide) | ||
| 99 | #define BT_STACK_WIDE_OUT (str_wide) = stack->item.str_wide | ||
| 100 | #else /* !TRE_WCHAR */ | ||
| 101 | #define BT_STACK_WIDE_IN(_str_wide) | ||
| 102 | #define BT_STACK_WIDE_OUT | ||
| 103 | #endif /* !TRE_WCHAR */ | ||
| 104 | |||
| 105 | #ifdef TRE_MBSTATE | ||
| 106 | #define BT_STACK_MBSTATE_IN stack->item.mbstate = (mbstate) | ||
| 107 | #define BT_STACK_MBSTATE_OUT (mbstate) = stack->item.mbstate | ||
| 108 | #else /* !TRE_MBSTATE */ | ||
| 109 | #define BT_STACK_MBSTATE_IN | ||
| 110 | #define BT_STACK_MBSTATE_OUT | ||
| 111 | #endif /* !TRE_MBSTATE */ | ||
| 112 | |||
| 113 | |||
| 114 | #ifdef TRE_USE_ALLOCA | ||
| 115 | #define tre_bt_mem_new tre_mem_newa | ||
| 116 | #define tre_bt_mem_alloc tre_mem_alloca | ||
| 117 | #define tre_bt_mem_destroy(obj) do { } while (0) | ||
| 118 | #else /* !TRE_USE_ALLOCA */ | ||
| 119 | #define tre_bt_mem_new tre_mem_new | ||
| 120 | #define tre_bt_mem_alloc tre_mem_alloc | ||
| 121 | #define tre_bt_mem_destroy tre_mem_destroy | ||
| 122 | #endif /* !TRE_USE_ALLOCA */ | ||
| 123 | |||
| 124 | |||
| 125 | #define BT_STACK_PUSH(_pos, _str_byte, _str_wide, _state, _state_id, _next_c, _tags, _mbstate) \ | ||
| 126 | do \ | ||
| 127 | { \ | ||
| 128 | int i; \ | ||
| 129 | if (!stack->next) \ | ||
| 130 | { \ | ||
| 131 | tre_backtrack_t s; \ | ||
| 132 | s = tre_bt_mem_alloc(mem, sizeof(*s)); \ | ||
| 133 | if (!s) \ | ||
| 134 | { \ | ||
| 135 | tre_bt_mem_destroy(mem); \ | ||
| 136 | if (tags) \ | ||
| 137 | xfree(tags); \ | ||
| 138 | if (pmatch) \ | ||
| 139 | xfree(pmatch); \ | ||
| 140 | if (states_seen) \ | ||
| 141 | xfree(states_seen); \ | ||
| 142 | return REG_ESPACE; \ | ||
| 143 | } \ | ||
| 144 | s->prev = stack; \ | ||
| 145 | s->next = NULL; \ | ||
| 146 | s->item.tags = tre_bt_mem_alloc(mem, \ | ||
| 147 | sizeof(*tags) * tnfa->num_tags); \ | ||
| 148 | if (!s->item.tags) \ | ||
| 149 | { \ | ||
| 150 | tre_bt_mem_destroy(mem); \ | ||
| 151 | if (tags) \ | ||
| 152 | xfree(tags); \ | ||
| 153 | if (pmatch) \ | ||
| 154 | xfree(pmatch); \ | ||
| 155 | if (states_seen) \ | ||
| 156 | xfree(states_seen); \ | ||
| 157 | return REG_ESPACE; \ | ||
| 158 | } \ | ||
| 159 | stack->next = s; \ | ||
| 160 | stack = s; \ | ||
| 161 | } \ | ||
| 162 | else \ | ||
| 163 | stack = stack->next; \ | ||
| 164 | stack->item.pos = (_pos); \ | ||
| 165 | stack->item.str_byte = (_str_byte); \ | ||
| 166 | BT_STACK_WIDE_IN(_str_wide); \ | ||
| 167 | stack->item.state = (_state); \ | ||
| 168 | stack->item.state_id = (_state_id); \ | ||
| 169 | stack->item.next_c = (_next_c); \ | ||
| 170 | for (i = 0; i < tnfa->num_tags; i++) \ | ||
| 171 | stack->item.tags[i] = (_tags)[i]; \ | ||
| 172 | BT_STACK_MBSTATE_IN; \ | ||
| 173 | } \ | ||
| 174 | while (/*CONSTCOND*/0) | ||
| 175 | |||
| 176 | #define BT_STACK_POP() \ | ||
| 177 | do \ | ||
| 178 | { \ | ||
| 179 | int i; \ | ||
| 180 | assert(stack->prev); \ | ||
| 181 | pos = stack->item.pos; \ | ||
| 182 | if (type == STR_USER) \ | ||
| 183 | str_source->rewind(pos + pos_add_next, str_source->context); \ | ||
| 184 | str_byte = stack->item.str_byte; \ | ||
| 185 | BT_STACK_WIDE_OUT; \ | ||
| 186 | state = stack->item.state; \ | ||
| 187 | next_c = stack->item.next_c; \ | ||
| 188 | for (i = 0; i < tnfa->num_tags; i++) \ | ||
| 189 | tags[i] = stack->item.tags[i]; \ | ||
| 190 | BT_STACK_MBSTATE_OUT; \ | ||
| 191 | stack = stack->prev; \ | ||
| 192 | } \ | ||
| 193 | while (/*CONSTCOND*/0) | ||
| 194 | |||
| 195 | #undef MIN | ||
| 196 | #define MIN(a, b) ((a) <= (b) ? (a) : (b)) | ||
| 197 | |||
| 198 | reg_errcode_t | ||
| 199 | tre_tnfa_run_backtrack(const tre_tnfa_t *tnfa, const void *string, | ||
| 200 | int len, tre_str_type_t type, int *match_tags, | ||
| 201 | int eflags, int *match_end_ofs) | ||
| 202 | { | ||
| 203 | /* State variables required by GET_NEXT_WCHAR. */ | ||
| 204 | tre_char_t prev_c = 0, next_c = 0; | ||
| 205 | const char *str_byte = string; | ||
| 206 | int pos = 0; | ||
| 207 | unsigned int pos_add_next = 1; | ||
| 208 | #ifdef TRE_WCHAR | ||
| 209 | const wchar_t *str_wide = string; | ||
| 210 | #ifdef TRE_MBSTATE | ||
| 211 | mbstate_t mbstate; | ||
| 212 | #endif /* TRE_MBSTATE */ | ||
| 213 | #endif /* TRE_WCHAR */ | ||
| 214 | int reg_notbol = eflags & REG_NOTBOL; | ||
| 215 | int reg_noteol = eflags & REG_NOTEOL; | ||
| 216 | int reg_newline = tnfa->cflags & REG_NEWLINE; | ||
| 217 | int str_user_end = 0; | ||
| 218 | |||
| 219 | /* These are used to remember the necessary values of the above | ||
| 220 | variables to return to the position where the current search | ||
| 221 | started from. */ | ||
| 222 | int next_c_start; | ||
| 223 | const char *str_byte_start; | ||
| 224 | int pos_start = -1; | ||
| 225 | #ifdef TRE_WCHAR | ||
| 226 | const wchar_t *str_wide_start; | ||
| 227 | #endif /* TRE_WCHAR */ | ||
| 228 | #ifdef TRE_MBSTATE | ||
| 229 | mbstate_t mbstate_start; | ||
| 230 | #endif /* TRE_MBSTATE */ | ||
| 231 | |||
| 232 | /* End offset of best match so far, or -1 if no match found yet. */ | ||
| 233 | int match_eo = -1; | ||
| 234 | /* Tag arrays. */ | ||
| 235 | int *next_tags, *tags = NULL; | ||
| 236 | /* Current TNFA state. */ | ||
| 237 | tre_tnfa_transition_t *state; | ||
| 238 | int *states_seen = NULL; | ||
| 239 | |||
| 240 | /* Memory allocator to for allocating the backtracking stack. */ | ||
| 241 | tre_mem_t mem = tre_bt_mem_new(); | ||
| 242 | |||
| 243 | /* The backtracking stack. */ | ||
| 244 | tre_backtrack_t stack; | ||
| 245 | |||
| 246 | tre_tnfa_transition_t *trans_i; | ||
| 247 | regmatch_t *pmatch = NULL; | ||
| 248 | int ret; | ||
| 249 | |||
| 250 | #ifdef TRE_MBSTATE | ||
| 251 | memset(&mbstate, '\0', sizeof(mbstate)); | ||
| 252 | #endif /* TRE_MBSTATE */ | ||
| 253 | |||
| 254 | if (!mem) | ||
| 255 | return REG_ESPACE; | ||
| 256 | stack = tre_bt_mem_alloc(mem, sizeof(*stack)); | ||
| 257 | if (!stack) | ||
| 258 | { | ||
| 259 | ret = REG_ESPACE; | ||
| 260 | goto error_exit; | ||
| 261 | } | ||
| 262 | stack->prev = NULL; | ||
| 263 | stack->next = NULL; | ||
| 264 | |||
| 265 | DPRINT(("tnfa_execute_backtrack, input type %d\n", type)); | ||
| 266 | DPRINT(("len = %d\n", len)); | ||
| 267 | |||
| 268 | #ifdef TRE_USE_ALLOCA | ||
| 269 | tags = alloca(sizeof(*tags) * tnfa->num_tags); | ||
| 270 | pmatch = alloca(sizeof(*pmatch) * tnfa->num_submatches); | ||
| 271 | states_seen = alloca(sizeof(*states_seen) * tnfa->num_states); | ||
| 272 | #else /* !TRE_USE_ALLOCA */ | ||
| 273 | if (tnfa->num_tags) | ||
| 274 | { | ||
| 275 | tags = xmalloc(sizeof(*tags) * tnfa->num_tags); | ||
| 276 | if (!tags) | ||
| 277 | { | ||
| 278 | ret = REG_ESPACE; | ||
| 279 | goto error_exit; | ||
| 280 | } | ||
| 281 | } | ||
| 282 | if (tnfa->num_submatches) | ||
| 283 | { | ||
| 284 | pmatch = xmalloc(sizeof(*pmatch) * tnfa->num_submatches); | ||
| 285 | if (!pmatch) | ||
| 286 | { | ||
| 287 | ret = REG_ESPACE; | ||
| 288 | goto error_exit; | ||
| 289 | } | ||
| 290 | } | ||
| 291 | if (tnfa->num_states) | ||
| 292 | { | ||
| 293 | states_seen = xmalloc(sizeof(*states_seen) * tnfa->num_states); | ||
| 294 | if (!states_seen) | ||
| 295 | { | ||
| 296 | ret = REG_ESPACE; | ||
| 297 | goto error_exit; | ||
| 298 | } | ||
| 299 | } | ||
| 300 | #endif /* !TRE_USE_ALLOCA */ | ||
| 301 | |||
| 302 | retry: | ||
| 303 | { | ||
| 304 | int i; | ||
| 305 | for (i = 0; i < tnfa->num_tags; i++) | ||
| 306 | { | ||
| 307 | tags[i] = -1; | ||
| 308 | if (match_tags) | ||
| 309 | match_tags[i] = -1; | ||
| 310 | } | ||
| 311 | for (i = 0; i < tnfa->num_states; i++) | ||
| 312 | states_seen[i] = 0; | ||
| 313 | } | ||
| 314 | |||
| 315 | state = NULL; | ||
| 316 | pos = pos_start; | ||
| 317 | if (type == STR_USER) | ||
| 318 | str_source->rewind(pos + pos_add_next, str_source->context); | ||
| 319 | GET_NEXT_WCHAR(); | ||
| 320 | pos_start = pos; | ||
| 321 | next_c_start = next_c; | ||
| 322 | str_byte_start = str_byte; | ||
| 323 | #ifdef TRE_WCHAR | ||
| 324 | str_wide_start = str_wide; | ||
| 325 | #endif /* TRE_WCHAR */ | ||
| 326 | #ifdef TRE_MBSTATE | ||
| 327 | mbstate_start = mbstate; | ||
| 328 | #endif /* TRE_MBSTATE */ | ||
| 329 | |||
| 330 | /* Handle initial states. */ | ||
| 331 | next_tags = NULL; | ||
| 332 | for (trans_i = tnfa->initial; trans_i->state; trans_i++) | ||
| 333 | { | ||
| 334 | DPRINT(("> init %p, prev_c %lc\n", trans_i->state, (tre_cint_t)prev_c)); | ||
| 335 | if (trans_i->assertions && CHECK_ASSERTIONS(trans_i->assertions)) | ||
| 336 | { | ||
| 337 | DPRINT(("assert failed\n")); | ||
| 338 | continue; | ||
| 339 | } | ||
| 340 | if (state == NULL) | ||
| 341 | { | ||
| 342 | /* Start from this state. */ | ||
| 343 | state = trans_i->state; | ||
| 344 | next_tags = trans_i->tags; | ||
| 345 | } | ||
| 346 | else | ||
| 347 | { | ||
| 348 | /* Backtrack to this state. */ | ||
| 349 | DPRINT(("saving state %d for backtracking\n", trans_i->state_id)); | ||
| 350 | BT_STACK_PUSH(pos, str_byte, str_wide, trans_i->state, | ||
| 351 | trans_i->state_id, next_c, tags, mbstate); | ||
| 352 | { | ||
| 353 | int *tmp = trans_i->tags; | ||
| 354 | if (tmp) | ||
| 355 | while (*tmp >= 0) | ||
| 356 | stack->item.tags[*tmp++] = pos; | ||
| 357 | } | ||
| 358 | } | ||
| 359 | } | ||
| 360 | |||
| 361 | if (next_tags) | ||
| 362 | for (; *next_tags >= 0; next_tags++) | ||
| 363 | tags[*next_tags] = pos; | ||
| 364 | |||
| 365 | |||
| 366 | DPRINT(("entering match loop, pos %d, str_byte %p\n", pos, str_byte)); | ||
| 367 | DPRINT(("pos:chr/code | state and tags\n")); | ||
| 368 | DPRINT(("-------------+------------------------------------------------\n")); | ||
| 369 | |||
| 370 | if (state == NULL) | ||
| 371 | goto backtrack; | ||
| 372 | |||
| 373 | while (/*CONSTCOND*/1) | ||
| 374 | { | ||
| 375 | tre_tnfa_transition_t *next_state; | ||
| 376 | int empty_br_match; | ||
| 377 | |||
| 378 | DPRINT(("start loop\n")); | ||
| 379 | if (state == tnfa->final) | ||
| 380 | { | ||
| 381 | DPRINT((" match found, %d %d\n", match_eo, pos)); | ||
| 382 | if (match_eo < pos | ||
| 383 | || (match_eo == pos | ||
| 384 | && match_tags | ||
| 385 | && tre_tag_order(tnfa->num_tags, tnfa->tag_directions, | ||
| 386 | tags, match_tags))) | ||
| 387 | { | ||
| 388 | int i; | ||
| 389 | /* This match wins the previous match. */ | ||
| 390 | DPRINT((" win previous\n")); | ||
| 391 | match_eo = pos; | ||
| 392 | if (match_tags) | ||
| 393 | for (i = 0; i < tnfa->num_tags; i++) | ||
| 394 | match_tags[i] = tags[i]; | ||
| 395 | } | ||
| 396 | /* Our TNFAs never have transitions leaving from the final state, | ||
| 397 | so we jump right to backtracking. */ | ||
| 398 | goto backtrack; | ||
| 399 | } | ||
| 400 | |||
| 401 | #ifdef TRE_DEBUG | ||
| 402 | DPRINT(("%3d:%2lc/%05d | %p ", pos, (tre_cint_t)next_c, (int)next_c, | ||
| 403 | state)); | ||
| 404 | { | ||
| 405 | int i; | ||
| 406 | for (i = 0; i < tnfa->num_tags; i++) | ||
| 407 | DPRINT(("%d%s", tags[i], i < tnfa->num_tags - 1 ? ", " : "")); | ||
| 408 | DPRINT(("\n")); | ||
| 409 | } | ||
| 410 | #endif /* TRE_DEBUG */ | ||
| 411 | |||
| 412 | /* Go to the next character in the input string. */ | ||
| 413 | empty_br_match = 0; | ||
| 414 | trans_i = state; | ||
| 415 | if (trans_i->state && trans_i->assertions & ASSERT_BACKREF) | ||
| 416 | { | ||
| 417 | /* This is a back reference state. All transitions leaving from | ||
| 418 | this state have the same back reference "assertion". Instead | ||
| 419 | of reading the next character, we match the back reference. */ | ||
| 420 | int so, eo, bt = trans_i->u.backref; | ||
| 421 | int bt_len; | ||
| 422 | int result; | ||
| 423 | |||
| 424 | DPRINT((" should match back reference %d\n", bt)); | ||
| 425 | /* Get the substring we need to match against. Remember to | ||
| 426 | turn off REG_NOSUB temporarily. */ | ||
| 427 | tre_fill_pmatch(bt + 1, pmatch, tnfa->cflags & /*LINTED*/!REG_NOSUB, | ||
| 428 | tnfa, tags, pos); | ||
| 429 | so = pmatch[bt].rm_so; | ||
| 430 | eo = pmatch[bt].rm_eo; | ||
| 431 | bt_len = eo - so; | ||
| 432 | |||
| 433 | #ifdef TRE_DEBUG | ||
| 434 | { | ||
| 435 | int slen; | ||
| 436 | if (len < 0) | ||
| 437 | slen = bt_len; | ||
| 438 | else | ||
| 439 | slen = MIN(bt_len, len - pos); | ||
| 440 | |||
| 441 | if (type == STR_BYTE) | ||
| 442 | { | ||
| 443 | DPRINT((" substring (len %d) is [%d, %d[: '%.*s'\n", | ||
| 444 | bt_len, so, eo, bt_len, (char*)string + so)); | ||
| 445 | DPRINT((" current string is '%.*s'\n", slen, str_byte - 1)); | ||
| 446 | } | ||
| 447 | #ifdef TRE_WCHAR | ||
| 448 | else if (type == STR_WIDE) | ||
| 449 | { | ||
| 450 | DPRINT((" substring (len %d) is [%d, %d[: '%.*" STRF "'\n", | ||
| 451 | bt_len, so, eo, bt_len, (wchar_t*)string + so)); | ||
| 452 | DPRINT((" current string is '%.*" STRF "'\n", | ||
| 453 | slen, str_wide - 1)); | ||
| 454 | } | ||
| 455 | #endif /* TRE_WCHAR */ | ||
| 456 | } | ||
| 457 | #endif | ||
| 458 | |||
| 459 | if (len < 0) | ||
| 460 | { | ||
| 461 | if (type == STR_USER) | ||
| 462 | result = str_source->compare((unsigned)so, (unsigned)pos, | ||
| 463 | (unsigned)bt_len, | ||
| 464 | str_source->context); | ||
| 465 | #ifdef TRE_WCHAR | ||
| 466 | else if (type == STR_WIDE) | ||
| 467 | result = wcsncmp((const wchar_t*)string + so, str_wide - 1, | ||
| 468 | (size_t)bt_len); | ||
| 469 | #endif /* TRE_WCHAR */ | ||
| 470 | else | ||
| 471 | result = strncmp((const char*)string + so, str_byte - 1, | ||
| 472 | (size_t)bt_len); | ||
| 473 | } | ||
| 474 | else if (len - pos < bt_len) | ||
| 475 | result = 1; | ||
| 476 | #ifdef TRE_WCHAR | ||
| 477 | else if (type == STR_WIDE) | ||
| 478 | result = wmemcmp((const wchar_t*)string + so, str_wide - 1, | ||
| 479 | (size_t)bt_len); | ||
| 480 | #endif /* TRE_WCHAR */ | ||
| 481 | else | ||
| 482 | result = memcmp((const char*)string + so, str_byte - 1, | ||
| 483 | (size_t)bt_len); | ||
| 484 | |||
| 485 | if (result == 0) | ||
| 486 | { | ||
| 487 | /* Back reference matched. Check for infinite loop. */ | ||
| 488 | if (bt_len == 0) | ||
| 489 | empty_br_match = 1; | ||
| 490 | if (empty_br_match && states_seen[trans_i->state_id]) | ||
| 491 | { | ||
| 492 | DPRINT((" avoid loop\n")); | ||
| 493 | goto backtrack; | ||
| 494 | } | ||
| 495 | |||
| 496 | states_seen[trans_i->state_id] = empty_br_match; | ||
| 497 | |||
| 498 | /* Advance in input string and resync `prev_c', `next_c' | ||
| 499 | and pos. */ | ||
| 500 | DPRINT((" back reference matched\n")); | ||
| 501 | str_byte += bt_len - 1; | ||
| 502 | #ifdef TRE_WCHAR | ||
| 503 | str_wide += bt_len - 1; | ||
| 504 | #endif /* TRE_WCHAR */ | ||
| 505 | pos += bt_len - 1; | ||
| 506 | GET_NEXT_WCHAR(); | ||
| 507 | DPRINT((" pos now %d\n", pos)); | ||
| 508 | } | ||
| 509 | else | ||
| 510 | { | ||
| 511 | DPRINT((" back reference did not match\n")); | ||
| 512 | goto backtrack; | ||
| 513 | } | ||
| 514 | } | ||
| 515 | else | ||
| 516 | { | ||
| 517 | /* Check for end of string. */ | ||
| 518 | if (len < 0) | ||
| 519 | { | ||
| 520 | if (type == STR_USER) | ||
| 521 | { | ||
| 522 | if (str_user_end) | ||
| 523 | goto backtrack; | ||
| 524 | } | ||
| 525 | else if (next_c == L'\0') | ||
| 526 | goto backtrack; | ||
| 527 | } | ||
| 528 | else | ||
| 529 | { | ||
| 530 | if (pos >= len) | ||
| 531 | goto backtrack; | ||
| 532 | } | ||
| 533 | |||
| 534 | /* Read the next character. */ | ||
| 535 | GET_NEXT_WCHAR(); | ||
| 536 | } | ||
| 537 | |||
| 538 | next_state = NULL; | ||
| 539 | for (trans_i = state; trans_i->state; trans_i++) | ||
| 540 | { | ||
| 541 | DPRINT((" transition %d-%d (%c-%c) %d to %d\n", | ||
| 542 | trans_i->code_min, trans_i->code_max, | ||
| 543 | trans_i->code_min, trans_i->code_max, | ||
| 544 | trans_i->assertions, trans_i->state_id)); | ||
| 545 | if (trans_i->code_min <= (tre_cint_t)prev_c | ||
| 546 | && trans_i->code_max >= (tre_cint_t)prev_c) | ||
| 547 | { | ||
| 548 | if (trans_i->assertions | ||
| 549 | && (CHECK_ASSERTIONS(trans_i->assertions) | ||
| 550 | || CHECK_CHAR_CLASSES(trans_i, tnfa, eflags))) | ||
| 551 | { | ||
| 552 | DPRINT((" assertion failed\n")); | ||
| 553 | continue; | ||
| 554 | } | ||
| 555 | |||
| 556 | if (next_state == NULL) | ||
| 557 | { | ||
| 558 | /* First matching transition. */ | ||
| 559 | DPRINT((" Next state is %d\n", trans_i->state_id)); | ||
| 560 | next_state = trans_i->state; | ||
| 561 | next_tags = trans_i->tags; | ||
| 562 | } | ||
| 563 | else | ||
| 564 | { | ||
| 565 | /* Second matching transition. We may need to backtrack here | ||
| 566 | to take this transition instead of the first one, so we | ||
| 567 | push this transition in the backtracking stack so we can | ||
| 568 | jump back here if needed. */ | ||
| 569 | DPRINT((" saving state %d for backtracking\n", | ||
| 570 | trans_i->state_id)); | ||
| 571 | BT_STACK_PUSH(pos, str_byte, str_wide, trans_i->state, | ||
| 572 | trans_i->state_id, next_c, tags, mbstate); | ||
| 573 | { | ||
| 574 | int *tmp; | ||
| 575 | for (tmp = trans_i->tags; tmp && *tmp >= 0; tmp++) | ||
| 576 | stack->item.tags[*tmp] = pos; | ||
| 577 | } | ||
| 578 | #if 0 /* XXX - it's important not to look at all transitions here to keep | ||
| 579 | the stack small! */ | ||
| 580 | break; | ||
| 581 | #endif | ||
| 582 | } | ||
| 583 | } | ||
| 584 | } | ||
| 585 | |||
| 586 | if (next_state != NULL) | ||
| 587 | { | ||
| 588 | /* Matching transitions were found. Take the first one. */ | ||
| 589 | state = next_state; | ||
| 590 | |||
| 591 | /* Update the tag values. */ | ||
| 592 | if (next_tags) | ||
| 593 | while (*next_tags >= 0) | ||
| 594 | tags[*next_tags++] = pos; | ||
| 595 | } | ||
| 596 | else | ||
| 597 | { | ||
| 598 | backtrack: | ||
| 599 | /* A matching transition was not found. Try to backtrack. */ | ||
| 600 | if (stack->prev) | ||
| 601 | { | ||
| 602 | DPRINT((" backtracking\n")); | ||
| 603 | if (stack->item.state->assertions && ASSERT_BACKREF) | ||
| 604 | { | ||
| 605 | DPRINT((" states_seen[%d] = 0\n", | ||
| 606 | stack->item.state_id)); | ||
| 607 | states_seen[stack->item.state_id] = 0; | ||
| 608 | } | ||
| 609 | |||
| 610 | BT_STACK_POP(); | ||
| 611 | } | ||
| 612 | else if (match_eo < 0) | ||
| 613 | { | ||
| 614 | /* Try starting from a later position in the input string. */ | ||
| 615 | /* Check for end of string. */ | ||
| 616 | if (len < 0) | ||
| 617 | { | ||
| 618 | if (next_c == L'\0') | ||
| 619 | { | ||
| 620 | DPRINT(("end of string.\n")); | ||
| 621 | break; | ||
| 622 | } | ||
| 623 | } | ||
| 624 | else | ||
| 625 | { | ||
| 626 | if (pos >= len) | ||
| 627 | { | ||
| 628 | DPRINT(("end of string.\n")); | ||
| 629 | break; | ||
| 630 | } | ||
| 631 | } | ||
| 632 | DPRINT(("restarting from next start position\n")); | ||
| 633 | next_c = next_c_start; | ||
| 634 | #ifdef TRE_MBSTATE | ||
| 635 | mbstate = mbstate_start; | ||
| 636 | #endif /* TRE_MBSTATE */ | ||
| 637 | str_byte = str_byte_start; | ||
| 638 | #ifdef TRE_WCHAR | ||
| 639 | str_wide = str_wide_start; | ||
| 640 | #endif /* TRE_WCHAR */ | ||
| 641 | goto retry; | ||
| 642 | } | ||
| 643 | else | ||
| 644 | { | ||
| 645 | DPRINT(("finished\n")); | ||
| 646 | break; | ||
| 647 | } | ||
| 648 | } | ||
| 649 | } | ||
| 650 | |||
| 651 | ret = match_eo >= 0 ? REG_OK : REG_NOMATCH; | ||
| 652 | *match_end_ofs = match_eo; | ||
| 653 | |||
| 654 | error_exit: | ||
| 655 | tre_bt_mem_destroy(mem); | ||
| 656 | #ifndef TRE_USE_ALLOCA | ||
| 657 | if (tags) | ||
| 658 | xfree(tags); | ||
| 659 | if (pmatch) | ||
| 660 | xfree(pmatch); | ||
| 661 | if (states_seen) | ||
| 662 | xfree(states_seen); | ||
| 663 | #endif /* !TRE_USE_ALLOCA */ | ||
| 664 | |||
| 665 | return ret; | ||
| 666 | } | ||
| @@ -1,501 +0,0 @@ | |||
| 1 | /* | ||
| 2 | tre-match-parallel.c - TRE parallel regex matching engine | ||
| 3 | |||
| 4 | This software is released under a BSD-style license. | ||
| 5 | See the file LICENSE for details and copyright. | ||
| 6 | |||
| 7 | */ | ||
| 8 | |||
| 9 | /* | ||
| 10 | This algorithm searches for matches basically by reading characters | ||
| 11 | in the searched string one by one, starting at the beginning. All | ||
| 12 | matching paths in the TNFA are traversed in parallel. When two or | ||
| 13 | more paths reach the same state, exactly one is chosen according to | ||
| 14 | tag ordering rules; if returning submatches is not required it does | ||
| 15 | not matter which path is chosen. | ||
| 16 | |||
| 17 | The worst case time required for finding the leftmost and longest | ||
| 18 | match, or determining that there is no match, is always linearly | ||
| 19 | dependent on the length of the text being searched. | ||
| 20 | |||
| 21 | This algorithm cannot handle TNFAs with back referencing nodes. | ||
| 22 | See `tre-match-backtrack.c'. | ||
| 23 | */ | ||
| 24 | |||
| 25 | |||
| 26 | #ifdef HAVE_CONFIG_H | ||
| 27 | #include "config.h" | ||
| 28 | #endif /* HAVE_CONFIG_H */ | ||
| 29 | |||
| 30 | #ifdef TRE_USE_ALLOCA | ||
| 31 | /* AIX requires this to be the first thing in the file. */ | ||
| 32 | #ifndef __GNUC__ | ||
| 33 | # if HAVE_ALLOCA_H | ||
| 34 | # include <alloca.h> | ||
| 35 | # else | ||
| 36 | # ifdef _AIX | ||
| 37 | #pragma alloca | ||
| 38 | # else | ||
| 39 | # ifndef alloca /* predefined by HP cc +Olibcalls */ | ||
| 40 | char *alloca (); | ||
| 41 | # endif | ||
| 42 | # endif | ||
| 43 | # endif | ||
| 44 | #endif | ||
| 45 | #endif /* TRE_USE_ALLOCA */ | ||
| 46 | |||
| 47 | #include <assert.h> | ||
| 48 | #include <stdlib.h> | ||
| 49 | #include <string.h> | ||
| 50 | #ifdef HAVE_WCHAR_H | ||
| 51 | #include <wchar.h> | ||
| 52 | #endif /* HAVE_WCHAR_H */ | ||
| 53 | #ifdef HAVE_WCTYPE_H | ||
| 54 | #include <wctype.h> | ||
| 55 | #endif /* HAVE_WCTYPE_H */ | ||
| 56 | #ifndef TRE_WCHAR | ||
| 57 | #include <ctype.h> | ||
| 58 | #endif /* !TRE_WCHAR */ | ||
| 59 | #ifdef HAVE_MALLOC_H | ||
| 60 | #include <malloc.h> | ||
| 61 | #endif /* HAVE_MALLOC_H */ | ||
| 62 | |||
| 63 | #include "tre-internal.h" | ||
| 64 | #include "tre-match-utils.h" | ||
| 65 | #include "tre.h" | ||
| 66 | #include "xmalloc.h" | ||
| 67 | |||
| 68 | |||
| 69 | |||
| 70 | typedef struct { | ||
| 71 | tre_tnfa_transition_t *state; | ||
| 72 | int *tags; | ||
| 73 | } tre_tnfa_reach_t; | ||
| 74 | |||
| 75 | typedef struct { | ||
| 76 | int pos; | ||
| 77 | int **tags; | ||
| 78 | } tre_reach_pos_t; | ||
| 79 | |||
| 80 | |||
| 81 | #ifdef TRE_DEBUG | ||
| 82 | static void | ||
| 83 | tre_print_reach(const tre_tnfa_t *tnfa, tre_tnfa_reach_t *reach, int num_tags) | ||
| 84 | { | ||
| 85 | int i; | ||
| 86 | |||
| 87 | while (reach->state != NULL) | ||
| 88 | { | ||
| 89 | DPRINT((" %p", (void *)reach->state)); | ||
| 90 | if (num_tags > 0) | ||
| 91 | { | ||
| 92 | DPRINT(("/")); | ||
| 93 | for (i = 0; i < num_tags; i++) | ||
| 94 | { | ||
| 95 | DPRINT(("%d:%d", i, reach->tags[i])); | ||
| 96 | if (i < (num_tags-1)) | ||
| 97 | DPRINT((",")); | ||
| 98 | } | ||
| 99 | } | ||
| 100 | reach++; | ||
| 101 | } | ||
| 102 | DPRINT(("\n")); | ||
| 103 | |||
| 104 | } | ||
| 105 | #endif /* TRE_DEBUG */ | ||
| 106 | |||
| 107 | reg_errcode_t | ||
| 108 | tre_tnfa_run_parallel(const tre_tnfa_t *tnfa, const void *string, int len, | ||
| 109 | tre_str_type_t type, int *match_tags, int eflags, | ||
| 110 | int *match_end_ofs) | ||
| 111 | { | ||
| 112 | /* State variables required by GET_NEXT_WCHAR. */ | ||
| 113 | tre_char_t prev_c = 0, next_c = 0; | ||
| 114 | const char *str_byte = string; | ||
| 115 | int pos = -1; | ||
| 116 | unsigned int pos_add_next = 1; | ||
| 117 | #ifdef TRE_WCHAR | ||
| 118 | const wchar_t *str_wide = string; | ||
| 119 | #ifdef TRE_MBSTATE | ||
| 120 | mbstate_t mbstate; | ||
| 121 | #endif /* TRE_MBSTATE */ | ||
| 122 | #endif /* TRE_WCHAR */ | ||
| 123 | int reg_notbol = eflags & REG_NOTBOL; | ||
| 124 | int reg_noteol = eflags & REG_NOTEOL; | ||
| 125 | int reg_newline = tnfa->cflags & REG_NEWLINE; | ||
| 126 | int str_user_end = 0; | ||
| 127 | |||
| 128 | char *buf; | ||
| 129 | tre_tnfa_transition_t *trans_i; | ||
| 130 | tre_tnfa_reach_t *reach, *reach_next, *reach_i, *reach_next_i; | ||
| 131 | tre_reach_pos_t *reach_pos; | ||
| 132 | int *tag_i; | ||
| 133 | int num_tags, i; | ||
| 134 | |||
| 135 | int match_eo = -1; /* end offset of match (-1 if no match found yet) */ | ||
| 136 | int new_match = 0; | ||
| 137 | int *tmp_tags = NULL; | ||
| 138 | int *tmp_iptr; | ||
| 139 | |||
| 140 | #ifdef TRE_MBSTATE | ||
| 141 | memset(&mbstate, '\0', sizeof(mbstate)); | ||
| 142 | #endif /* TRE_MBSTATE */ | ||
| 143 | |||
| 144 | DPRINT(("tre_tnfa_run_parallel, input type %d\n", type)); | ||
| 145 | |||
| 146 | if (!match_tags) | ||
| 147 | num_tags = 0; | ||
| 148 | else | ||
| 149 | num_tags = tnfa->num_tags; | ||
| 150 | |||
| 151 | /* Allocate memory for temporary data required for matching. This needs to | ||
| 152 | be done for every matching operation to be thread safe. This allocates | ||
| 153 | everything in a single large block from the stack frame using alloca() | ||
| 154 | or with malloc() if alloca is unavailable. */ | ||
| 155 | { | ||
| 156 | int tbytes, rbytes, pbytes, xbytes, total_bytes; | ||
| 157 | char *tmp_buf; | ||
| 158 | /* Compute the length of the block we need. */ | ||
| 159 | tbytes = sizeof(*tmp_tags) * num_tags; | ||
| 160 | rbytes = sizeof(*reach_next) * (tnfa->num_states + 1); | ||
| 161 | pbytes = sizeof(*reach_pos) * tnfa->num_states; | ||
| 162 | xbytes = sizeof(int) * num_tags; | ||
| 163 | total_bytes = | ||
| 164 | (sizeof(long) - 1) * 4 /* for alignment paddings */ | ||
| 165 | + (rbytes + xbytes * tnfa->num_states) * 2 + tbytes + pbytes; | ||
| 166 | |||
| 167 | /* Allocate the memory. */ | ||
| 168 | #ifdef TRE_USE_ALLOCA | ||
| 169 | buf = alloca(total_bytes); | ||
| 170 | #else /* !TRE_USE_ALLOCA */ | ||
| 171 | buf = xmalloc((unsigned)total_bytes); | ||
| 172 | #endif /* !TRE_USE_ALLOCA */ | ||
| 173 | if (buf == NULL) | ||
| 174 | return REG_ESPACE; | ||
| 175 | memset(buf, 0, (size_t)total_bytes); | ||
| 176 | |||
| 177 | /* Get the various pointers within tmp_buf (properly aligned). */ | ||
| 178 | tmp_tags = (void *)buf; | ||
| 179 | tmp_buf = buf + tbytes; | ||
| 180 | tmp_buf += ALIGN(tmp_buf, long); | ||
| 181 | reach_next = (void *)tmp_buf; | ||
| 182 | tmp_buf += rbytes; | ||
| 183 | tmp_buf += ALIGN(tmp_buf, long); | ||
| 184 | reach = (void *)tmp_buf; | ||
| 185 | tmp_buf += rbytes; | ||
| 186 | tmp_buf += ALIGN(tmp_buf, long); | ||
| 187 | reach_pos = (void *)tmp_buf; | ||
| 188 | tmp_buf += pbytes; | ||
| 189 | tmp_buf += ALIGN(tmp_buf, long); | ||
| 190 | for (i = 0; i < tnfa->num_states; i++) | ||
| 191 | { | ||
| 192 | reach[i].tags = (void *)tmp_buf; | ||
| 193 | tmp_buf += xbytes; | ||
| 194 | reach_next[i].tags = (void *)tmp_buf; | ||
| 195 | tmp_buf += xbytes; | ||
| 196 | } | ||
| 197 | } | ||
| 198 | |||
| 199 | for (i = 0; i < tnfa->num_states; i++) | ||
| 200 | reach_pos[i].pos = -1; | ||
| 201 | |||
| 202 | /* If only one character can start a match, find it first. */ | ||
| 203 | if (tnfa->first_char >= 0 && type == STR_BYTE && str_byte) | ||
| 204 | { | ||
| 205 | const char *orig_str = str_byte; | ||
| 206 | int first = tnfa->first_char; | ||
| 207 | |||
| 208 | if (len >= 0) | ||
| 209 | str_byte = memchr(orig_str, first, (size_t)len); | ||
| 210 | else | ||
| 211 | str_byte = strchr(orig_str, first); | ||
| 212 | if (str_byte == NULL) | ||
| 213 | { | ||
| 214 | #ifndef TRE_USE_ALLOCA | ||
| 215 | if (buf) | ||
| 216 | xfree(buf); | ||
| 217 | #endif /* !TRE_USE_ALLOCA */ | ||
| 218 | return REG_NOMATCH; | ||
| 219 | } | ||
| 220 | DPRINT(("skipped %lu chars\n", (unsigned long)(str_byte - orig_str))); | ||
| 221 | if (str_byte >= orig_str + 1) | ||
| 222 | prev_c = (unsigned char)*(str_byte - 1); | ||
| 223 | next_c = (unsigned char)*str_byte; | ||
| 224 | pos = str_byte - orig_str; | ||
| 225 | if (len < 0 || pos < len) | ||
| 226 | str_byte++; | ||
| 227 | } | ||
| 228 | else | ||
| 229 | { | ||
| 230 | GET_NEXT_WCHAR(); | ||
| 231 | pos = 0; | ||
| 232 | } | ||
| 233 | |||
| 234 | #if 0 | ||
| 235 | /* Skip over characters that cannot possibly be the first character | ||
| 236 | of a match. */ | ||
| 237 | if (tnfa->firstpos_chars != NULL) | ||
| 238 | { | ||
| 239 | char *chars = tnfa->firstpos_chars; | ||
| 240 | |||
| 241 | if (len < 0) | ||
| 242 | { | ||
| 243 | const char *orig_str = str_byte; | ||
| 244 | /* XXX - use strpbrk() and wcspbrk() because they might be | ||
| 245 | optimized for the target architecture. Try also strcspn() | ||
| 246 | and wcscspn() and compare the speeds. */ | ||
| 247 | while (next_c != L'\0' && !chars[next_c]) | ||
| 248 | { | ||
| 249 | next_c = *str_byte++; | ||
| 250 | } | ||
| 251 | prev_c = *(str_byte - 2); | ||
| 252 | pos += str_byte - orig_str; | ||
| 253 | DPRINT(("skipped %d chars\n", str_byte - orig_str)); | ||
| 254 | } | ||
| 255 | else | ||
| 256 | { | ||
| 257 | while (pos <= len && !chars[next_c]) | ||
| 258 | { | ||
| 259 | prev_c = next_c; | ||
| 260 | next_c = (unsigned char)(*str_byte++); | ||
| 261 | pos++; | ||
| 262 | } | ||
| 263 | } | ||
| 264 | } | ||
| 265 | #endif | ||
| 266 | |||
| 267 | DPRINT(("length: %d\n", len)); | ||
| 268 | DPRINT(("pos:chr/code | states and tags\n")); | ||
| 269 | DPRINT(("-------------+------------------------------------------------\n")); | ||
| 270 | |||
| 271 | reach_next_i = reach_next; | ||
| 272 | while (/*CONSTCOND*/1) | ||
| 273 | { | ||
| 274 | /* If no match found yet, add the initial states to `reach_next'. */ | ||
| 275 | if (match_eo < 0) | ||
| 276 | { | ||
| 277 | DPRINT((" init >")); | ||
| 278 | trans_i = tnfa->initial; | ||
| 279 | while (trans_i->state != NULL) | ||
| 280 | { | ||
| 281 | if (reach_pos[trans_i->state_id].pos < pos) | ||
| 282 | { | ||
| 283 | if (trans_i->assertions | ||
| 284 | && CHECK_ASSERTIONS(trans_i->assertions)) | ||
| 285 | { | ||
| 286 | DPRINT(("assertion failed\n")); | ||
| 287 | trans_i++; | ||
| 288 | continue; | ||
| 289 | } | ||
| 290 | |||
| 291 | DPRINT((" %p", (void *)trans_i->state)); | ||
| 292 | reach_next_i->state = trans_i->state; | ||
| 293 | for (i = 0; i < num_tags; i++) | ||
| 294 | reach_next_i->tags[i] = -1; | ||
| 295 | tag_i = trans_i->tags; | ||
| 296 | if (tag_i) | ||
| 297 | while (*tag_i >= 0) | ||
| 298 | { | ||
| 299 | if (*tag_i < num_tags) | ||
| 300 | reach_next_i->tags[*tag_i] = pos; | ||
| 301 | tag_i++; | ||
| 302 | } | ||
| 303 | if (reach_next_i->state == tnfa->final) | ||
| 304 | { | ||
| 305 | DPRINT((" found empty match\n")); | ||
| 306 | match_eo = pos; | ||
| 307 | new_match = 1; | ||
| 308 | for (i = 0; i < num_tags; i++) | ||
| 309 | match_tags[i] = reach_next_i->tags[i]; | ||
| 310 | } | ||
| 311 | reach_pos[trans_i->state_id].pos = pos; | ||
| 312 | reach_pos[trans_i->state_id].tags = &reach_next_i->tags; | ||
| 313 | reach_next_i++; | ||
| 314 | } | ||
| 315 | trans_i++; | ||
| 316 | } | ||
| 317 | DPRINT(("\n")); | ||
| 318 | reach_next_i->state = NULL; | ||
| 319 | } | ||
| 320 | else | ||
| 321 | { | ||
| 322 | if (num_tags == 0 || reach_next_i == reach_next) | ||
| 323 | /*�We have found a match. */ | ||
| 324 | break; | ||
| 325 | } | ||
| 326 | |||
| 327 | /* Check for end of string. */ | ||
| 328 | if (len < 0) | ||
| 329 | { | ||
| 330 | if (type == STR_USER) | ||
| 331 | { | ||
| 332 | if (str_user_end) | ||
| 333 | break; | ||
| 334 | } | ||
| 335 | else if (next_c == L'\0') | ||
| 336 | break; | ||
| 337 | } | ||
| 338 | else | ||
| 339 | { | ||
| 340 | if (pos >= len) | ||
| 341 | break; | ||
| 342 | } | ||
| 343 | |||
| 344 | GET_NEXT_WCHAR(); | ||
| 345 | |||
| 346 | #ifdef TRE_DEBUG | ||
| 347 | DPRINT(("%3d:%2lc/%05d |", pos - 1, (tre_cint_t)prev_c, (int)prev_c)); | ||
| 348 | tre_print_reach(tnfa, reach_next, num_tags); | ||
| 349 | DPRINT(("%3d:%2lc/%05d |", pos, (tre_cint_t)next_c, (int)next_c)); | ||
| 350 | tre_print_reach(tnfa, reach_next, num_tags); | ||
| 351 | #endif /* TRE_DEBUG */ | ||
| 352 | |||
| 353 | /* Swap `reach' and `reach_next'. */ | ||
| 354 | reach_i = reach; | ||
| 355 | reach = reach_next; | ||
| 356 | reach_next = reach_i; | ||
| 357 | |||
| 358 | /* For each state in `reach', weed out states that don't fulfill the | ||
| 359 | minimal matching conditions. */ | ||
| 360 | if (tnfa->num_minimals && new_match) | ||
| 361 | { | ||
| 362 | new_match = 0; | ||
| 363 | reach_next_i = reach_next; | ||
| 364 | for (reach_i = reach; reach_i->state; reach_i++) | ||
| 365 | { | ||
| 366 | int skip = 0; | ||
| 367 | for (i = 0; tnfa->minimal_tags[i] >= 0; i += 2) | ||
| 368 | { | ||
| 369 | int end = tnfa->minimal_tags[i]; | ||
| 370 | int start = tnfa->minimal_tags[i + 1]; | ||
| 371 | DPRINT((" Minimal start %d, end %d\n", start, end)); | ||
| 372 | if (end >= num_tags) | ||
| 373 | { | ||
| 374 | DPRINT((" Throwing %p out.\n", reach_i->state)); | ||
| 375 | skip = 1; | ||
| 376 | break; | ||
| 377 | } | ||
| 378 | else if (reach_i->tags[start] == match_tags[start] | ||
| 379 | && reach_i->tags[end] < match_tags[end]) | ||
| 380 | { | ||
| 381 | DPRINT((" Throwing %p out because t%d < %d\n", | ||
| 382 | reach_i->state, end, match_tags[end])); | ||
| 383 | skip = 1; | ||
| 384 | break; | ||
| 385 | } | ||
| 386 | } | ||
| 387 | if (!skip) | ||
| 388 | { | ||
| 389 | reach_next_i->state = reach_i->state; | ||
| 390 | tmp_iptr = reach_next_i->tags; | ||
| 391 | reach_next_i->tags = reach_i->tags; | ||
| 392 | reach_i->tags = tmp_iptr; | ||
| 393 | reach_next_i++; | ||
| 394 | } | ||
| 395 | } | ||
| 396 | reach_next_i->state = NULL; | ||
| 397 | |||
| 398 | /* Swap `reach' and `reach_next'. */ | ||
| 399 | reach_i = reach; | ||
| 400 | reach = reach_next; | ||
| 401 | reach_next = reach_i; | ||
| 402 | } | ||
| 403 | |||
| 404 | /* For each state in `reach' see if there is a transition leaving with | ||
| 405 | the current input symbol to a state not yet in `reach_next', and | ||
| 406 | add the destination states to `reach_next'. */ | ||
| 407 | reach_next_i = reach_next; | ||
| 408 | for (reach_i = reach; reach_i->state; reach_i++) | ||
| 409 | { | ||
| 410 | for (trans_i = reach_i->state; trans_i->state; trans_i++) | ||
| 411 | { | ||
| 412 | /* Does this transition match the input symbol? */ | ||
| 413 | if (trans_i->code_min <= (tre_cint_t)prev_c && | ||
| 414 | trans_i->code_max >= (tre_cint_t)prev_c) | ||
| 415 | { | ||
| 416 | if (trans_i->assertions | ||
| 417 | && (CHECK_ASSERTIONS(trans_i->assertions) | ||
| 418 | || CHECK_CHAR_CLASSES(trans_i, tnfa, eflags))) | ||
| 419 | { | ||
| 420 | DPRINT(("assertion failed\n")); | ||
| 421 | continue; | ||
| 422 | } | ||
| 423 | |||
| 424 | /* Compute the tags after this transition. */ | ||
| 425 | for (i = 0; i < num_tags; i++) | ||
| 426 | tmp_tags[i] = reach_i->tags[i]; | ||
| 427 | tag_i = trans_i->tags; | ||
| 428 | if (tag_i != NULL) | ||
| 429 | while (*tag_i >= 0) | ||
| 430 | { | ||
| 431 | if (*tag_i < num_tags) | ||
| 432 | tmp_tags[*tag_i] = pos; | ||
| 433 | tag_i++; | ||
| 434 | } | ||
| 435 | |||
| 436 | if (reach_pos[trans_i->state_id].pos < pos) | ||
| 437 | { | ||
| 438 | /* Found an unvisited node. */ | ||
| 439 | reach_next_i->state = trans_i->state; | ||
| 440 | tmp_iptr = reach_next_i->tags; | ||
| 441 | reach_next_i->tags = tmp_tags; | ||
| 442 | tmp_tags = tmp_iptr; | ||
| 443 | reach_pos[trans_i->state_id].pos = pos; | ||
| 444 | reach_pos[trans_i->state_id].tags = &reach_next_i->tags; | ||
| 445 | |||
| 446 | if (reach_next_i->state == tnfa->final | ||
| 447 | && (match_eo == -1 | ||
| 448 | || (num_tags > 0 | ||
| 449 | && reach_next_i->tags[0] <= match_tags[0]))) | ||
| 450 | { | ||
| 451 | DPRINT((" found match %p\n", trans_i->state)); | ||
| 452 | match_eo = pos; | ||
| 453 | new_match = 1; | ||
| 454 | for (i = 0; i < num_tags; i++) | ||
| 455 | match_tags[i] = reach_next_i->tags[i]; | ||
| 456 | } | ||
| 457 | reach_next_i++; | ||
| 458 | |||
| 459 | } | ||
| 460 | else | ||
| 461 | { | ||
| 462 | assert(reach_pos[trans_i->state_id].pos == pos); | ||
| 463 | /* Another path has also reached this state. We choose | ||
| 464 | the winner by examining the tag values for both | ||
| 465 | paths. */ | ||
| 466 | if (tre_tag_order(num_tags, tnfa->tag_directions, | ||
| 467 | tmp_tags, | ||
| 468 | *reach_pos[trans_i->state_id].tags)) | ||
| 469 | { | ||
| 470 | /* The new path wins. */ | ||
| 471 | tmp_iptr = *reach_pos[trans_i->state_id].tags; | ||
| 472 | *reach_pos[trans_i->state_id].tags = tmp_tags; | ||
| 473 | if (trans_i->state == tnfa->final) | ||
| 474 | { | ||
| 475 | DPRINT((" found better match\n")); | ||
| 476 | match_eo = pos; | ||
| 477 | new_match = 1; | ||
| 478 | for (i = 0; i < num_tags; i++) | ||
| 479 | match_tags[i] = tmp_tags[i]; | ||
| 480 | } | ||
| 481 | tmp_tags = tmp_iptr; | ||
| 482 | } | ||
| 483 | } | ||
| 484 | } | ||
| 485 | } | ||
| 486 | } | ||
| 487 | reach_next_i->state = NULL; | ||
| 488 | } | ||
| 489 | |||
| 490 | DPRINT(("match end offset = %d\n", match_eo)); | ||
| 491 | |||
| 492 | #ifndef TRE_USE_ALLOCA | ||
| 493 | if (buf) | ||
| 494 | xfree(buf); | ||
| 495 | #endif /* !TRE_USE_ALLOCA */ | ||
| 496 | |||
| 497 | *match_end_ofs = match_eo; | ||
| 498 | return match_eo >= 0 ? REG_OK : REG_NOMATCH; | ||
| 499 | } | ||
| 500 | |||
| 501 | /* EOF */ | ||
| @@ -1,215 +0,0 @@ | |||
| 1 | /* | ||
| 2 | tre-match-utils.h - TRE matcher helper definitions | ||
| 3 | |||
| 4 | This software is released under a BSD-style license. | ||
| 5 | See the file LICENSE for details and copyright. | ||
| 6 | |||
| 7 | */ | ||
| 8 | |||
| 9 | #define str_source ((const tre_str_source*)string) | ||
| 10 | |||
| 11 | #ifdef TRE_WCHAR | ||
| 12 | |||
| 13 | #ifdef TRE_MULTIBYTE | ||
| 14 | |||
| 15 | /* Wide character and multibyte support. */ | ||
| 16 | |||
| 17 | #define GET_NEXT_WCHAR() \ | ||
| 18 | do { \ | ||
| 19 | prev_c = next_c; \ | ||
| 20 | if (type == STR_BYTE) \ | ||
| 21 | { \ | ||
| 22 | pos++; \ | ||
| 23 | if (len >= 0 && pos >= len) \ | ||
| 24 | next_c = '\0'; \ | ||
| 25 | else \ | ||
| 26 | next_c = (unsigned char)(*str_byte++); \ | ||
| 27 | } \ | ||
| 28 | else if (type == STR_WIDE) \ | ||
| 29 | { \ | ||
| 30 | pos++; \ | ||
| 31 | if (len >= 0 && pos >= len) \ | ||
| 32 | next_c = L'\0'; \ | ||
| 33 | else \ | ||
| 34 | next_c = *str_wide++; \ | ||
| 35 | } \ | ||
| 36 | else if (type == STR_MBS) \ | ||
| 37 | { \ | ||
| 38 | pos += pos_add_next; \ | ||
| 39 | if (str_byte == NULL) \ | ||
| 40 | next_c = L'\0'; \ | ||
| 41 | else \ | ||
| 42 | { \ | ||
| 43 | size_t w; \ | ||
| 44 | int max; \ | ||
| 45 | if (len >= 0) \ | ||
| 46 | max = len - pos; \ | ||
| 47 | else \ | ||
| 48 | max = 32; \ | ||
| 49 | if (max <= 0) \ | ||
| 50 | { \ | ||
| 51 | next_c = L'\0'; \ | ||
| 52 | pos_add_next = 1; \ | ||
| 53 | } \ | ||
| 54 | else \ | ||
| 55 | { \ | ||
| 56 | w = tre_mbrtowc(&next_c, str_byte, (size_t)max, &mbstate); \ | ||
| 57 | if (w == (size_t)-1 || w == (size_t)-2) \ | ||
| 58 | return REG_NOMATCH; \ | ||
| 59 | if (w == 0 && len >= 0) \ | ||
| 60 | { \ | ||
| 61 | pos_add_next = 1; \ | ||
| 62 | next_c = 0; \ | ||
| 63 | str_byte++; \ | ||
| 64 | } \ | ||
| 65 | else \ | ||
| 66 | { \ | ||
| 67 | pos_add_next = w; \ | ||
| 68 | str_byte += w; \ | ||
| 69 | } \ | ||
| 70 | } \ | ||
| 71 | } \ | ||
| 72 | } \ | ||
| 73 | else if (type == STR_USER) \ | ||
| 74 | { \ | ||
| 75 | pos += pos_add_next; \ | ||
| 76 | str_user_end = str_source->get_next_char(&next_c, &pos_add_next, \ | ||
| 77 | str_source->context); \ | ||
| 78 | } \ | ||
| 79 | } while(/*CONSTCOND*/0) | ||
| 80 | |||
| 81 | #else /* !TRE_MULTIBYTE */ | ||
| 82 | |||
| 83 | /* Wide character support, no multibyte support. */ | ||
| 84 | |||
| 85 | #define GET_NEXT_WCHAR() \ | ||
| 86 | do { \ | ||
| 87 | prev_c = next_c; \ | ||
| 88 | if (type == STR_BYTE) \ | ||
| 89 | { \ | ||
| 90 | pos++; \ | ||
| 91 | if (len >= 0 && pos >= len) \ | ||
| 92 | next_c = '\0'; \ | ||
| 93 | else \ | ||
| 94 | next_c = (unsigned char)(*str_byte++); \ | ||
| 95 | } \ | ||
| 96 | else if (type == STR_WIDE) \ | ||
| 97 | { \ | ||
| 98 | pos++; \ | ||
| 99 | if (len >= 0 && pos >= len) \ | ||
| 100 | next_c = L'\0'; \ | ||
| 101 | else \ | ||
| 102 | next_c = *str_wide++; \ | ||
| 103 | } \ | ||
| 104 | else if (type == STR_USER) \ | ||
| 105 | { \ | ||
| 106 | pos += pos_add_next; \ | ||
| 107 | str_user_end = str_source->get_next_char(&next_c, &pos_add_next, \ | ||
| 108 | str_source->context); \ | ||
| 109 | } \ | ||
| 110 | } while(/*CONSTCOND*/0) | ||
| 111 | |||
| 112 | #endif /* !TRE_MULTIBYTE */ | ||
| 113 | |||
| 114 | #else /* !TRE_WCHAR */ | ||
| 115 | |||
| 116 | /* No wide character or multibyte support. */ | ||
| 117 | |||
| 118 | #define GET_NEXT_WCHAR() \ | ||
| 119 | do { \ | ||
| 120 | prev_c = next_c; \ | ||
| 121 | if (type == STR_BYTE) \ | ||
| 122 | { \ | ||
| 123 | pos++; \ | ||
| 124 | if (len >= 0 && pos >= len) \ | ||
| 125 | next_c = '\0'; \ | ||
| 126 | else \ | ||
| 127 | next_c = (unsigned char)(*str_byte++); \ | ||
| 128 | } \ | ||
| 129 | else if (type == STR_USER) \ | ||
| 130 | { \ | ||
| 131 | pos += pos_add_next; \ | ||
| 132 | str_user_end = str_source->get_next_char(&next_c, &pos_add_next, \ | ||
| 133 | str_source->context); \ | ||
| 134 | } \ | ||
| 135 | } while(/*CONSTCOND*/0) | ||
| 136 | |||
| 137 | #endif /* !TRE_WCHAR */ | ||
| 138 | |||
| 139 | |||
| 140 | |||
| 141 | #define IS_WORD_CHAR(c) ((c) == L'_' || tre_isalnum(c)) | ||
| 142 | |||
| 143 | #define CHECK_ASSERTIONS(assertions) \ | ||
| 144 | (((assertions & ASSERT_AT_BOL) \ | ||
| 145 | && (pos > 0 || reg_notbol) \ | ||
| 146 | && (prev_c != L'\n' || !reg_newline)) \ | ||
| 147 | || ((assertions & ASSERT_AT_EOL) \ | ||
| 148 | && (next_c != L'\0' || reg_noteol) \ | ||
| 149 | && (next_c != L'\n' || !reg_newline)) \ | ||
| 150 | || ((assertions & ASSERT_AT_BOW) \ | ||
| 151 | && (IS_WORD_CHAR(prev_c) || !IS_WORD_CHAR(next_c))) \ | ||
| 152 | || ((assertions & ASSERT_AT_EOW) \ | ||
| 153 | && (!IS_WORD_CHAR(prev_c) || IS_WORD_CHAR(next_c))) \ | ||
| 154 | || ((assertions & ASSERT_AT_WB) \ | ||
| 155 | && (pos != 0 && next_c != L'\0' \ | ||
| 156 | && IS_WORD_CHAR(prev_c) == IS_WORD_CHAR(next_c))) \ | ||
| 157 | || ((assertions & ASSERT_AT_WB_NEG) \ | ||
| 158 | && (pos == 0 || next_c == L'\0' \ | ||
| 159 | || IS_WORD_CHAR(prev_c) != IS_WORD_CHAR(next_c)))) | ||
| 160 | |||
| 161 | #define CHECK_CHAR_CLASSES(trans_i, tnfa, eflags) \ | ||
| 162 | (((trans_i->assertions & ASSERT_CHAR_CLASS) \ | ||
| 163 | && !(tnfa->cflags & REG_ICASE) \ | ||
| 164 | && !tre_isctype((tre_cint_t)prev_c, trans_i->u.class)) \ | ||
| 165 | || ((trans_i->assertions & ASSERT_CHAR_CLASS) \ | ||
| 166 | && (tnfa->cflags & REG_ICASE) \ | ||
| 167 | && !tre_isctype(tre_tolower((tre_cint_t)prev_c),trans_i->u.class) \ | ||
| 168 | && !tre_isctype(tre_toupper((tre_cint_t)prev_c),trans_i->u.class)) \ | ||
| 169 | || ((trans_i->assertions & ASSERT_CHAR_CLASS_NEG) \ | ||
| 170 | && tre_neg_char_classes_match(trans_i->neg_classes,(tre_cint_t)prev_c, \ | ||
| 171 | tnfa->cflags & REG_ICASE))) | ||
| 172 | |||
| 173 | |||
| 174 | |||
| 175 | |||
| 176 | /* Returns 1 if `t1' wins `t2', 0 otherwise. */ | ||
| 177 | inline static int | ||
| 178 | tre_tag_order(int num_tags, tre_tag_direction_t *tag_directions, | ||
| 179 | int *t1, int *t2) | ||
| 180 | { | ||
| 181 | int i; | ||
| 182 | for (i = 0; i < num_tags; i++) | ||
| 183 | { | ||
| 184 | if (tag_directions[i] == TRE_TAG_MINIMIZE) | ||
| 185 | { | ||
| 186 | if (t1[i] < t2[i]) | ||
| 187 | return 1; | ||
| 188 | if (t1[i] > t2[i]) | ||
| 189 | return 0; | ||
| 190 | } | ||
| 191 | else | ||
| 192 | { | ||
| 193 | if (t1[i] > t2[i]) | ||
| 194 | return 1; | ||
| 195 | if (t1[i] < t2[i]) | ||
| 196 | return 0; | ||
| 197 | } | ||
| 198 | } | ||
| 199 | /* assert(0);*/ | ||
| 200 | return 0; | ||
| 201 | } | ||
| 202 | |||
| 203 | inline static int | ||
| 204 | tre_neg_char_classes_match(tre_ctype_t *classes, tre_cint_t wc, int icase) | ||
| 205 | { | ||
| 206 | DPRINT(("neg_char_classes_test: %p, %d, %d\n", classes, wc, icase)); | ||
| 207 | while (*classes != (tre_ctype_t)0) | ||
| 208 | if ((!icase && tre_isctype(wc, *classes)) | ||
| 209 | || (icase && (tre_isctype(tre_toupper(wc), *classes) | ||
| 210 | || tre_isctype(tre_tolower(wc), *classes)))) | ||
| 211 | return 1; /* Match. */ | ||
| 212 | else | ||
| 213 | classes++; | ||
| 214 | return 0; /* No match. */ | ||
| 215 | } | ||
| @@ -1,155 +0,0 @@ | |||
| 1 | /* | ||
| 2 | tre-mem.c - TRE memory allocator | ||
| 3 | |||
| 4 | This software is released under a BSD-style license. | ||
| 5 | See the file LICENSE for details and copyright. | ||
| 6 | |||
| 7 | */ | ||
| 8 | |||
| 9 | /* | ||
| 10 | This memory allocator is for allocating small memory blocks efficiently | ||
| 11 | in terms of memory overhead and execution speed. The allocated blocks | ||
| 12 | cannot be freed individually, only all at once. There can be multiple | ||
| 13 | allocators, though. | ||
| 14 | */ | ||
| 15 | |||
| 16 | #ifdef HAVE_CONFIG_H | ||
| 17 | #include <config.h> | ||
| 18 | #endif /* HAVE_CONFIG_H */ | ||
| 19 | #include <stdlib.h> | ||
| 20 | #include <string.h> | ||
| 21 | |||
| 22 | #include "tre-internal.h" | ||
| 23 | #include "tre-mem.h" | ||
| 24 | #include "xmalloc.h" | ||
| 25 | |||
| 26 | |||
| 27 | /* Returns a new memory allocator or NULL if out of memory. */ | ||
| 28 | tre_mem_t | ||
| 29 | tre_mem_new_impl(int provided, void *provided_block) | ||
| 30 | { | ||
| 31 | tre_mem_t mem; | ||
| 32 | if (provided) | ||
| 33 | { | ||
| 34 | mem = provided_block; | ||
| 35 | memset(mem, 0, sizeof(*mem)); | ||
| 36 | } | ||
| 37 | else | ||
| 38 | mem = xcalloc(1, sizeof(*mem)); | ||
| 39 | if (mem == NULL) | ||
| 40 | return NULL; | ||
| 41 | return mem; | ||
| 42 | } | ||
| 43 | |||
| 44 | |||
| 45 | /* Frees the memory allocator and all memory allocated with it. */ | ||
| 46 | void | ||
| 47 | tre_mem_destroy(tre_mem_t mem) | ||
| 48 | { | ||
| 49 | tre_list_t *tmp, *l = mem->blocks; | ||
| 50 | |||
| 51 | while (l != NULL) | ||
| 52 | { | ||
| 53 | xfree(l->data); | ||
| 54 | tmp = l->next; | ||
| 55 | xfree(l); | ||
| 56 | l = tmp; | ||
| 57 | } | ||
| 58 | xfree(mem); | ||
| 59 | } | ||
| 60 | |||
| 61 | |||
| 62 | /* Allocates a block of `size' bytes from `mem'. Returns a pointer to the | ||
| 63 | allocated block or NULL if an underlying malloc() failed. */ | ||
| 64 | void * | ||
| 65 | tre_mem_alloc_impl(tre_mem_t mem, int provided, void *provided_block, | ||
| 66 | int zero, size_t size) | ||
| 67 | { | ||
| 68 | void *ptr; | ||
| 69 | |||
| 70 | if (mem->failed) | ||
| 71 | { | ||
| 72 | DPRINT(("tre_mem_alloc: oops, called after failure?!\n")); | ||
| 73 | return NULL; | ||
| 74 | } | ||
| 75 | |||
| 76 | #ifdef MALLOC_DEBUGGING | ||
| 77 | if (!provided) | ||
| 78 | { | ||
| 79 | ptr = xmalloc(1); | ||
| 80 | if (ptr == NULL) | ||
| 81 | { | ||
| 82 | DPRINT(("tre_mem_alloc: xmalloc forced failure\n")); | ||
| 83 | mem->failed = 1; | ||
| 84 | return NULL; | ||
| 85 | } | ||
| 86 | xfree(ptr); | ||
| 87 | } | ||
| 88 | #endif /* MALLOC_DEBUGGING */ | ||
| 89 | |||
| 90 | if (mem->n < size) | ||
| 91 | { | ||
| 92 | /* We need more memory than is available in the current block. | ||
| 93 | Allocate a new block. */ | ||
| 94 | tre_list_t *l; | ||
| 95 | if (provided) | ||
| 96 | { | ||
| 97 | DPRINT(("tre_mem_alloc: using provided block\n")); | ||
| 98 | if (provided_block == NULL) | ||
| 99 | { | ||
| 100 | DPRINT(("tre_mem_alloc: provided block was NULL\n")); | ||
| 101 | mem->failed = 1; | ||
| 102 | return NULL; | ||
| 103 | } | ||
| 104 | mem->ptr = provided_block; | ||
| 105 | mem->n = TRE_MEM_BLOCK_SIZE; | ||
| 106 | } | ||
| 107 | else | ||
| 108 | { | ||
| 109 | int block_size; | ||
| 110 | if (size * 8 > TRE_MEM_BLOCK_SIZE) | ||
| 111 | block_size = size * 8; | ||
| 112 | else | ||
| 113 | block_size = TRE_MEM_BLOCK_SIZE; | ||
| 114 | DPRINT(("tre_mem_alloc: allocating new %d byte block\n", | ||
| 115 | block_size)); | ||
| 116 | l = xmalloc(sizeof(*l)); | ||
| 117 | if (l == NULL) | ||
| 118 | { | ||
| 119 | mem->failed = 1; | ||
| 120 | return NULL; | ||
| 121 | } | ||
| 122 | l->data = xmalloc(block_size); | ||
| 123 | if (l->data == NULL) | ||
| 124 | { | ||
| 125 | xfree(l); | ||
| 126 | mem->failed = 1; | ||
| 127 | return NULL; | ||
| 128 | } | ||
| 129 | l->next = NULL; | ||
| 130 | if (mem->current != NULL) | ||
| 131 | mem->current->next = l; | ||
| 132 | if (mem->blocks == NULL) | ||
| 133 | mem->blocks = l; | ||
| 134 | mem->current = l; | ||
| 135 | mem->ptr = l->data; | ||
| 136 | mem->n = block_size; | ||
| 137 | } | ||
| 138 | } | ||
| 139 | |||
| 140 | /* Make sure the next pointer will be aligned. */ | ||
| 141 | size += ALIGN(mem->ptr + size, long); | ||
| 142 | |||
| 143 | /* Allocate from current block. */ | ||
| 144 | ptr = mem->ptr; | ||
| 145 | mem->ptr += size; | ||
| 146 | mem->n -= size; | ||
| 147 | |||
| 148 | /* Set to zero if needed. */ | ||
| 149 | if (zero) | ||
| 150 | memset(ptr, 0, size); | ||
| 151 | |||
| 152 | return ptr; | ||
| 153 | } | ||
| 154 | |||
| 155 | /* EOF */ | ||
| @@ -1,66 +0,0 @@ | |||
| 1 | /* | ||
| 2 | tre-mem.h - TRE memory allocator interface | ||
| 3 | |||
| 4 | This software is released under a BSD-style license. | ||
| 5 | See the file LICENSE for details and copyright. | ||
| 6 | |||
| 7 | */ | ||
| 8 | |||
| 9 | #ifndef TRE_MEM_H | ||
| 10 | #define TRE_MEM_H 1 | ||
| 11 | |||
| 12 | #include <stdlib.h> | ||
| 13 | |||
| 14 | #define TRE_MEM_BLOCK_SIZE 1024 | ||
| 15 | |||
| 16 | typedef struct tre_list { | ||
| 17 | void *data; | ||
| 18 | struct tre_list *next; | ||
| 19 | } tre_list_t; | ||
| 20 | |||
| 21 | typedef struct tre_mem_struct { | ||
| 22 | tre_list_t *blocks; | ||
| 23 | tre_list_t *current; | ||
| 24 | char *ptr; | ||
| 25 | size_t n; | ||
| 26 | int failed; | ||
| 27 | void **provided; | ||
| 28 | } *tre_mem_t; | ||
| 29 | |||
| 30 | |||
| 31 | tre_mem_t tre_mem_new_impl(int provided, void *provided_block); | ||
| 32 | void *tre_mem_alloc_impl(tre_mem_t mem, int provided, void *provided_block, | ||
| 33 | int zero, size_t size); | ||
| 34 | |||
| 35 | /* Returns a new memory allocator or NULL if out of memory. */ | ||
| 36 | #define tre_mem_new() tre_mem_new_impl(0, NULL) | ||
| 37 | |||
| 38 | /* Allocates a block of `size' bytes from `mem'. Returns a pointer to the | ||
| 39 | allocated block or NULL if an underlying malloc() failed. */ | ||
| 40 | #define tre_mem_alloc(mem, size) tre_mem_alloc_impl(mem, 0, NULL, 0, size) | ||
| 41 | |||
| 42 | /* Allocates a block of `size' bytes from `mem'. Returns a pointer to the | ||
| 43 | allocated block or NULL if an underlying malloc() failed. The memory | ||
| 44 | is set to zero. */ | ||
| 45 | #define tre_mem_calloc(mem, size) tre_mem_alloc_impl(mem, 0, NULL, 1, size) | ||
| 46 | |||
| 47 | #ifdef TRE_USE_ALLOCA | ||
| 48 | /* alloca() versions. Like above, but memory is allocated with alloca() | ||
| 49 | instead of malloc(). */ | ||
| 50 | |||
| 51 | #define tre_mem_newa() \ | ||
| 52 | tre_mem_new_impl(1, alloca(sizeof(struct tre_mem_struct))) | ||
| 53 | |||
| 54 | #define tre_mem_alloca(mem, size) \ | ||
| 55 | ((mem)->n >= (size) \ | ||
| 56 | ? tre_mem_alloc_impl((mem), 1, NULL, 0, (size)) \ | ||
| 57 | : tre_mem_alloc_impl((mem), 1, alloca(TRE_MEM_BLOCK_SIZE), 0, (size))) | ||
| 58 | #endif /* TRE_USE_ALLOCA */ | ||
| 59 | |||
| 60 | |||
| 61 | /* Frees the memory allocator and all memory allocated with it. */ | ||
| 62 | void tre_mem_destroy(tre_mem_t mem); | ||
| 63 | |||
| 64 | #endif /* TRE_MEM_H */ | ||
| 65 | |||
| 66 | /* EOF */ | ||
| @@ -1,1739 +0,0 @@ | |||
| 1 | /* | ||
| 2 | tre-parse.c - Regexp parser | ||
| 3 | |||
| 4 | This software is released under a BSD-style license. | ||
| 5 | See the file LICENSE for details and copyright. | ||
| 6 | |||
| 7 | */ | ||
| 8 | |||
| 9 | /* | ||
| 10 | This parser is just a simple recursive descent parser for POSIX.2 | ||
| 11 | regexps. The parser supports both the obsolete default syntax and | ||
| 12 | the "extended" syntax, and some nonstandard extensions. | ||
| 13 | */ | ||
| 14 | |||
| 15 | |||
| 16 | #ifdef HAVE_CONFIG_H | ||
| 17 | #include <config.h> | ||
| 18 | #endif /* HAVE_CONFIG_H */ | ||
| 19 | #include <string.h> | ||
| 20 | #include <assert.h> | ||
| 21 | #include <limits.h> | ||
| 22 | |||
| 23 | #include "xmalloc.h" | ||
| 24 | #include "tre-mem.h" | ||
| 25 | #include "tre-ast.h" | ||
| 26 | #include "tre-stack.h" | ||
| 27 | #include "tre-parse.h" | ||
| 28 | |||
| 29 | |||
| 30 | /* Characters with special meanings in regexp syntax. */ | ||
| 31 | #define CHAR_PIPE L'|' | ||
| 32 | #define CHAR_LPAREN L'(' | ||
| 33 | #define CHAR_RPAREN L')' | ||
| 34 | #define CHAR_LBRACE L'{' | ||
| 35 | #define CHAR_RBRACE L'}' | ||
| 36 | #define CHAR_LBRACKET L'[' | ||
| 37 | #define CHAR_RBRACKET L']' | ||
| 38 | #define CHAR_MINUS L'-' | ||
| 39 | #define CHAR_STAR L'*' | ||
| 40 | #define CHAR_QUESTIONMARK L'?' | ||
| 41 | #define CHAR_PLUS L'+' | ||
| 42 | #define CHAR_PERIOD L'.' | ||
| 43 | #define CHAR_COLON L':' | ||
| 44 | #define CHAR_EQUAL L'=' | ||
| 45 | #define CHAR_COMMA L',' | ||
| 46 | #define CHAR_CARET L'^' | ||
| 47 | #define CHAR_DOLLAR L'$' | ||
| 48 | #define CHAR_BACKSLASH L'\\' | ||
| 49 | #define CHAR_HASH L'#' | ||
| 50 | #define CHAR_TILDE L'~' | ||
| 51 | |||
| 52 | |||
| 53 | /* Some macros for expanding \w, \s, etc. */ | ||
| 54 | static const struct tre_macro_struct { | ||
| 55 | const char c; | ||
| 56 | const char *expansion; | ||
| 57 | } tre_macros[] = | ||
| 58 | { {'t', "\t"}, {'n', "\n"}, {'r', "\r"}, | ||
| 59 | {'f', "\f"}, {'a', "\a"}, {'e', "\033"}, | ||
| 60 | {'w', "[[:alnum:]_]"}, {'W', "[^[:alnum:]_]"}, {'s', "[[:space:]]"}, | ||
| 61 | {'S', "[^[:space:]]"}, {'d', "[[:digit:]]"}, {'D', "[^[:digit:]]"}, | ||
| 62 | { 0, NULL } | ||
| 63 | }; | ||
| 64 | |||
| 65 | |||
| 66 | /* Expands a macro delimited by `regex' and `regex_end' to `buf', which | ||
| 67 | must have at least `len' items. Sets buf[0] to zero if the there | ||
| 68 | is no match in `tre_macros'. */ | ||
| 69 | static void | ||
| 70 | tre_expand_macro(const tre_char_t *regex, const tre_char_t *regex_end, | ||
| 71 | tre_char_t *buf, size_t buf_len) | ||
| 72 | { | ||
| 73 | int i; | ||
| 74 | |||
| 75 | buf[0] = 0; | ||
| 76 | if (regex >= regex_end) | ||
| 77 | return; | ||
| 78 | |||
| 79 | for (i = 0; tre_macros[i].expansion; i++) | ||
| 80 | { | ||
| 81 | if (tre_macros[i].c == *regex) | ||
| 82 | { | ||
| 83 | unsigned int j; | ||
| 84 | DPRINT(("Expanding macro '%c' => '%s'\n", | ||
| 85 | tre_macros[i].c, tre_macros[i].expansion)); | ||
| 86 | for (j = 0; tre_macros[i].expansion[j] && j < buf_len; j++) | ||
| 87 | buf[j] = tre_macros[i].expansion[j]; | ||
| 88 | buf[j] = 0; | ||
| 89 | break; | ||
| 90 | } | ||
| 91 | } | ||
| 92 | } | ||
| 93 | |||
| 94 | static reg_errcode_t | ||
| 95 | tre_new_item(tre_mem_t mem, int min, int max, int *i, int *max_i, | ||
| 96 | tre_ast_node_t ***items) | ||
| 97 | { | ||
| 98 | reg_errcode_t status; | ||
| 99 | tre_ast_node_t **array = *items; | ||
| 100 | /* Allocate more space if necessary. */ | ||
| 101 | if (*i >= *max_i) | ||
| 102 | { | ||
| 103 | tre_ast_node_t **new_items; | ||
| 104 | DPRINT(("out of array space, i = %d\n", *i)); | ||
| 105 | /* If the array is already 1024 items large, give up -- there's | ||
| 106 | probably an error in the regexp (e.g. not a '\0' terminated | ||
| 107 | string and missing ']') */ | ||
| 108 | if (*max_i > 1024) | ||
| 109 | return REG_ESPACE; | ||
| 110 | *max_i *= 2; | ||
| 111 | new_items = xrealloc(array, sizeof(*items) * *max_i); | ||
| 112 | if (new_items == NULL) | ||
| 113 | return REG_ESPACE; | ||
| 114 | *items = array = new_items; | ||
| 115 | } | ||
| 116 | array[*i] = tre_ast_new_literal(mem, min, max, -1); | ||
| 117 | status = array[*i] == NULL ? REG_ESPACE : REG_OK; | ||
| 118 | (*i)++; | ||
| 119 | return status; | ||
| 120 | } | ||
| 121 | |||
| 122 | |||
| 123 | /* Expands a character class to character ranges. */ | ||
| 124 | static reg_errcode_t | ||
| 125 | tre_expand_ctype(tre_mem_t mem, tre_ctype_t class, tre_ast_node_t ***items, | ||
| 126 | int *i, int *max_i, int cflags) | ||
| 127 | { | ||
| 128 | reg_errcode_t status = REG_OK; | ||
| 129 | tre_cint_t c; | ||
| 130 | int j, min = -1, max = 0; | ||
| 131 | assert(TRE_MB_CUR_MAX == 1); | ||
| 132 | |||
| 133 | DPRINT((" expanding class to character ranges\n")); | ||
| 134 | for (j = 0; (j < 256) && (status == REG_OK); j++) | ||
| 135 | { | ||
| 136 | c = j; | ||
| 137 | if (tre_isctype(c, class) | ||
| 138 | || ((cflags & REG_ICASE) | ||
| 139 | && (tre_isctype(tre_tolower(c), class) | ||
| 140 | || tre_isctype(tre_toupper(c), class)))) | ||
| 141 | { | ||
| 142 | if (min < 0) | ||
| 143 | min = c; | ||
| 144 | max = c; | ||
| 145 | } | ||
| 146 | else if (min >= 0) | ||
| 147 | { | ||
| 148 | DPRINT((" range %c (%d) to %c (%d)\n", min, min, max, max)); | ||
| 149 | status = tre_new_item(mem, min, max, i, max_i, items); | ||
| 150 | min = -1; | ||
| 151 | } | ||
| 152 | } | ||
| 153 | if (min >= 0 && status == REG_OK) | ||
| 154 | status = tre_new_item(mem, min, max, i, max_i, items); | ||
| 155 | return status; | ||
| 156 | } | ||
| 157 | |||
| 158 | |||
| 159 | static int | ||
| 160 | tre_compare_items(const void *a, const void *b) | ||
| 161 | { | ||
| 162 | const tre_ast_node_t *node_a = *(tre_ast_node_t * const *)a; | ||
| 163 | const tre_ast_node_t *node_b = *(tre_ast_node_t * const *)b; | ||
| 164 | tre_literal_t *l_a = node_a->obj, *l_b = node_b->obj; | ||
| 165 | int a_min = l_a->code_min, b_min = l_b->code_min; | ||
| 166 | |||
| 167 | if (a_min < b_min) | ||
| 168 | return -1; | ||
| 169 | else if (a_min > b_min) | ||
| 170 | return 1; | ||
| 171 | else | ||
| 172 | return 0; | ||
| 173 | } | ||
| 174 | |||
| 175 | #ifndef TRE_USE_SYSTEM_WCTYPE | ||
| 176 | |||
| 177 | /* isalnum() and the rest may be macros, so wrap them to functions. */ | ||
| 178 | int tre_isalnum_func(tre_cint_t c) { return tre_isalnum(c); } | ||
| 179 | int tre_isalpha_func(tre_cint_t c) { return tre_isalpha(c); } | ||
| 180 | |||
| 181 | #ifdef tre_isascii | ||
| 182 | int tre_isascii_func(tre_cint_t c) { return tre_isascii(c); } | ||
| 183 | #else /* !tre_isascii */ | ||
| 184 | int tre_isascii_func(tre_cint_t c) { return !(c >> 7); } | ||
| 185 | #endif /* !tre_isascii */ | ||
| 186 | |||
| 187 | #ifdef tre_isblank | ||
| 188 | int tre_isblank_func(tre_cint_t c) { return tre_isblank(c); } | ||
| 189 | #else /* !tre_isblank */ | ||
| 190 | int tre_isblank_func(tre_cint_t c) { return ((c == ' ') || (c == '\t')); } | ||
| 191 | #endif /* !tre_isblank */ | ||
| 192 | |||
| 193 | int tre_iscntrl_func(tre_cint_t c) { return tre_iscntrl(c); } | ||
| 194 | int tre_isdigit_func(tre_cint_t c) { return tre_isdigit(c); } | ||
| 195 | int tre_isgraph_func(tre_cint_t c) { return tre_isgraph(c); } | ||
| 196 | int tre_islower_func(tre_cint_t c) { return tre_islower(c); } | ||
| 197 | int tre_isprint_func(tre_cint_t c) { return tre_isprint(c); } | ||
| 198 | int tre_ispunct_func(tre_cint_t c) { return tre_ispunct(c); } | ||
| 199 | int tre_isspace_func(tre_cint_t c) { return tre_isspace(c); } | ||
| 200 | int tre_isupper_func(tre_cint_t c) { return tre_isupper(c); } | ||
| 201 | int tre_isxdigit_func(tre_cint_t c) { return tre_isxdigit(c); } | ||
| 202 | |||
| 203 | struct { | ||
| 204 | char *name; | ||
| 205 | int (*func)(tre_cint_t); | ||
| 206 | } tre_ctype_map[] = { | ||
| 207 | { "alnum", &tre_isalnum_func }, | ||
| 208 | { "alpha", &tre_isalpha_func }, | ||
| 209 | #ifdef tre_isascii | ||
| 210 | { "ascii", &tre_isascii_func }, | ||
| 211 | #endif /* tre_isascii */ | ||
| 212 | #ifdef tre_isblank | ||
| 213 | { "blank", &tre_isblank_func }, | ||
| 214 | #endif /* tre_isblank */ | ||
| 215 | { "cntrl", &tre_iscntrl_func }, | ||
| 216 | { "digit", &tre_isdigit_func }, | ||
| 217 | { "graph", &tre_isgraph_func }, | ||
| 218 | { "lower", &tre_islower_func }, | ||
| 219 | { "print", &tre_isprint_func }, | ||
| 220 | { "punct", &tre_ispunct_func }, | ||
| 221 | { "space", &tre_isspace_func }, | ||
| 222 | { "upper", &tre_isupper_func }, | ||
| 223 | { "xdigit", &tre_isxdigit_func }, | ||
| 224 | { NULL, NULL} | ||
| 225 | }; | ||
| 226 | |||
| 227 | tre_ctype_t tre_ctype(const char *name) | ||
| 228 | { | ||
| 229 | int i; | ||
| 230 | for (i = 0; tre_ctype_map[i].name != NULL; i++) | ||
| 231 | { | ||
| 232 | if (strcmp(name, tre_ctype_map[i].name) == 0) | ||
| 233 | return tre_ctype_map[i].func; | ||
| 234 | } | ||
| 235 | return (tre_ctype_t)0; | ||
| 236 | } | ||
| 237 | #endif /* !TRE_USE_SYSTEM_WCTYPE */ | ||
| 238 | |||
| 239 | /* Maximum number of character classes that can occur in a negated bracket | ||
| 240 | expression. */ | ||
| 241 | #define MAX_NEG_CLASSES 64 | ||
| 242 | |||
| 243 | /* Maximum length of character class names. */ | ||
| 244 | #define MAX_CLASS_NAME | ||
| 245 | |||
| 246 | #define REST(re) (int)(ctx->re_end - (re)), (re) | ||
| 247 | |||
| 248 | static reg_errcode_t | ||
| 249 | tre_parse_bracket_items(tre_parse_ctx_t *ctx, int negate, | ||
| 250 | tre_ctype_t neg_classes[], int *num_neg_classes, | ||
| 251 | tre_ast_node_t ***items, int *num_items, | ||
| 252 | int *items_size) | ||
| 253 | { | ||
| 254 | const tre_char_t *re = ctx->re; | ||
| 255 | reg_errcode_t status = REG_OK; | ||
| 256 | tre_ctype_t class = (tre_ctype_t)0; | ||
| 257 | int i = *num_items; | ||
| 258 | int max_i = *items_size; | ||
| 259 | int skip; | ||
| 260 | |||
| 261 | /* Build an array of the items in the bracket expression. */ | ||
| 262 | while (status == REG_OK) | ||
| 263 | { | ||
| 264 | skip = 0; | ||
| 265 | if (re == ctx->re_end) | ||
| 266 | { | ||
| 267 | status = REG_EBRACK; | ||
| 268 | } | ||
| 269 | else if (*re == CHAR_RBRACKET && re > ctx->re) | ||
| 270 | { | ||
| 271 | DPRINT(("tre_parse_bracket: done: '%.*" STRF "'\n", REST(re))); | ||
| 272 | re++; | ||
| 273 | break; | ||
| 274 | } | ||
| 275 | else | ||
| 276 | { | ||
| 277 | tre_cint_t min = 0, max = 0; | ||
| 278 | |||
| 279 | class = (tre_ctype_t)0; | ||
| 280 | if (re + 2 < ctx->re_end | ||
| 281 | && *(re + 1) == CHAR_MINUS && *(re + 2) != CHAR_RBRACKET) | ||
| 282 | { | ||
| 283 | DPRINT(("tre_parse_bracket: range: '%.*" STRF "'\n", REST(re))); | ||
| 284 | min = *re; | ||
| 285 | max = *(re + 2); | ||
| 286 | re += 3; | ||
| 287 | /* XXX - Should use collation order instead of encoding values | ||
| 288 | in character ranges. */ | ||
| 289 | if (min > max) | ||
| 290 | status = REG_ERANGE; | ||
| 291 | } | ||
| 292 | else if (re + 1 < ctx->re_end | ||
| 293 | && *re == CHAR_LBRACKET && *(re + 1) == CHAR_PERIOD) | ||
| 294 | status = REG_ECOLLATE; | ||
| 295 | else if (re + 1 < ctx->re_end | ||
| 296 | && *re == CHAR_LBRACKET && *(re + 1) == CHAR_EQUAL) | ||
| 297 | status = REG_ECOLLATE; | ||
| 298 | else if (re + 1 < ctx->re_end | ||
| 299 | && *re == CHAR_LBRACKET && *(re + 1) == CHAR_COLON) | ||
| 300 | { | ||
| 301 | char tmp_str[64]; | ||
| 302 | const tre_char_t *endptr = re + 2; | ||
| 303 | int len; | ||
| 304 | DPRINT(("tre_parse_bracket: class: '%.*" STRF "'\n", REST(re))); | ||
| 305 | while (endptr < ctx->re_end && *endptr != CHAR_COLON) | ||
| 306 | endptr++; | ||
| 307 | if (endptr != ctx->re_end) | ||
| 308 | { | ||
| 309 | len = MIN(endptr - re - 2, 63); | ||
| 310 | #ifdef TRE_WCHAR | ||
| 311 | { | ||
| 312 | tre_char_t tmp_wcs[64]; | ||
| 313 | wcsncpy(tmp_wcs, re + 2, (size_t)len); | ||
| 314 | tmp_wcs[len] = L'\0'; | ||
| 315 | #if defined HAVE_WCSRTOMBS | ||
| 316 | { | ||
| 317 | mbstate_t state; | ||
| 318 | const tre_char_t *src = tmp_wcs; | ||
| 319 | memset(&state, '\0', sizeof(state)); | ||
| 320 | len = wcsrtombs(tmp_str, &src, sizeof(tmp_str), &state); | ||
| 321 | } | ||
| 322 | #elif defined HAVE_WCSTOMBS | ||
| 323 | len = wcstombs(tmp_str, tmp_wcs, 63); | ||
| 324 | #endif /* defined HAVE_WCSTOMBS */ | ||
| 325 | } | ||
| 326 | #else /* !TRE_WCHAR */ | ||
| 327 | strncpy(tmp_str, (const char*)re + 2, len); | ||
| 328 | #endif /* !TRE_WCHAR */ | ||
| 329 | tmp_str[len] = '\0'; | ||
| 330 | DPRINT((" class name: %s\n", tmp_str)); | ||
| 331 | class = tre_ctype(tmp_str); | ||
| 332 | if (!class) | ||
| 333 | status = REG_ECTYPE; | ||
| 334 | /* Optimize character classes for 8 bit character sets. */ | ||
| 335 | if (status == REG_OK && TRE_MB_CUR_MAX == 1) | ||
| 336 | { | ||
| 337 | status = tre_expand_ctype(ctx->mem, class, items, | ||
| 338 | &i, &max_i, ctx->cflags); | ||
| 339 | class = (tre_ctype_t)0; | ||
| 340 | skip = 1; | ||
| 341 | } | ||
| 342 | re = endptr + 2; | ||
| 343 | } | ||
| 344 | else | ||
| 345 | status = REG_ECTYPE; | ||
| 346 | min = 0; | ||
| 347 | max = TRE_CHAR_MAX; | ||
| 348 | } | ||
| 349 | else | ||
| 350 | { | ||
| 351 | DPRINT(("tre_parse_bracket: char: '%.*" STRF "'\n", REST(re))); | ||
| 352 | if (*re == CHAR_MINUS && *(re + 1) != CHAR_RBRACKET | ||
| 353 | && ctx->re != re) | ||
| 354 | /* Two ranges are not allowed to share and endpoint. */ | ||
| 355 | status = REG_ERANGE; | ||
| 356 | min = max = *re++; | ||
| 357 | } | ||
| 358 | |||
| 359 | if (status != REG_OK) | ||
| 360 | break; | ||
| 361 | |||
| 362 | if (class && negate) | ||
| 363 | if (*num_neg_classes >= MAX_NEG_CLASSES) | ||
| 364 | status = REG_ESPACE; | ||
| 365 | else | ||
| 366 | neg_classes[(*num_neg_classes)++] = class; | ||
| 367 | else if (!skip) | ||
| 368 | { | ||
| 369 | status = tre_new_item(ctx->mem, min, max, &i, &max_i, items); | ||
| 370 | if (status != REG_OK) | ||
| 371 | break; | ||
| 372 | ((tre_literal_t*)((*items)[i-1])->obj)->u.class = class; | ||
| 373 | } | ||
| 374 | |||
| 375 | /* Add opposite-case counterpoints if REG_ICASE is present. | ||
| 376 | This is broken if there are more than two "same" characters. */ | ||
| 377 | if (ctx->cflags & REG_ICASE && !class && status == REG_OK && !skip) | ||
| 378 | { | ||
| 379 | tre_cint_t cmin, ccurr; | ||
| 380 | |||
| 381 | DPRINT(("adding opposite-case counterpoints\n")); | ||
| 382 | while (min <= max) | ||
| 383 | { | ||
| 384 | if (tre_islower(min)) | ||
| 385 | { | ||
| 386 | cmin = ccurr = tre_toupper(min++); | ||
| 387 | while (tre_islower(min) && tre_toupper(min) == ccurr + 1 | ||
| 388 | && min <= max) | ||
| 389 | ccurr = tre_toupper(min++); | ||
| 390 | status = tre_new_item(ctx->mem, cmin, ccurr, | ||
| 391 | &i, &max_i, items); | ||
| 392 | } | ||
| 393 | else if (tre_isupper(min)) | ||
| 394 | { | ||
| 395 | cmin = ccurr = tre_tolower(min++); | ||
| 396 | while (tre_isupper(min) && tre_tolower(min) == ccurr + 1 | ||
| 397 | && min <= max) | ||
| 398 | ccurr = tre_tolower(min++); | ||
| 399 | status = tre_new_item(ctx->mem, cmin, ccurr, | ||
| 400 | &i, &max_i, items); | ||
| 401 | } | ||
| 402 | else min++; | ||
| 403 | if (status != REG_OK) | ||
| 404 | break; | ||
| 405 | } | ||
| 406 | if (status != REG_OK) | ||
| 407 | break; | ||
| 408 | } | ||
| 409 | } | ||
| 410 | } | ||
| 411 | *num_items = i; | ||
| 412 | *items_size = max_i; | ||
| 413 | ctx->re = re; | ||
| 414 | return status; | ||
| 415 | } | ||
| 416 | |||
| 417 | static reg_errcode_t | ||
| 418 | tre_parse_bracket(tre_parse_ctx_t *ctx, tre_ast_node_t **result) | ||
| 419 | { | ||
| 420 | tre_ast_node_t *node = NULL; | ||
| 421 | int negate = 0; | ||
| 422 | reg_errcode_t status = REG_OK; | ||
| 423 | tre_ast_node_t **items, *u, *n; | ||
| 424 | int i = 0, j, max_i = 32, curr_max, curr_min; | ||
| 425 | tre_ctype_t neg_classes[MAX_NEG_CLASSES]; | ||
| 426 | int num_neg_classes = 0; | ||
| 427 | |||
| 428 | /* Start off with an array of `max_i' elements. */ | ||
| 429 | items = xmalloc(sizeof(*items) * max_i); | ||
| 430 | if (items == NULL) | ||
| 431 | return REG_ESPACE; | ||
| 432 | |||
| 433 | if (*ctx->re == CHAR_CARET) | ||
| 434 | { | ||
| 435 | DPRINT(("tre_parse_bracket: negate: '%.*" STRF "'\n", REST(ctx->re))); | ||
| 436 | negate = 1; | ||
| 437 | ctx->re++; | ||
| 438 | } | ||
| 439 | |||
| 440 | status = tre_parse_bracket_items(ctx, negate, neg_classes, &num_neg_classes, | ||
| 441 | &items, &i, &max_i); | ||
| 442 | |||
| 443 | if (status != REG_OK) | ||
| 444 | goto parse_bracket_done; | ||
| 445 | |||
| 446 | /* Sort the array if we need to negate it. */ | ||
| 447 | if (negate) | ||
| 448 | qsort(items, (unsigned)i, sizeof(*items), tre_compare_items); | ||
| 449 | |||
| 450 | curr_max = curr_min = 0; | ||
| 451 | /* Build a union of the items in the array, negated if necessary. */ | ||
| 452 | for (j = 0; j < i && status == REG_OK; j++) | ||
| 453 | { | ||
| 454 | int min, max; | ||
| 455 | tre_literal_t *l = items[j]->obj; | ||
| 456 | min = l->code_min; | ||
| 457 | max = l->code_max; | ||
| 458 | |||
| 459 | DPRINT(("item: %d - %d, class %ld, curr_max = %d\n", | ||
| 460 | (int)l->code_min, (int)l->code_max, (long)l->u.class, curr_max)); | ||
| 461 | |||
| 462 | if (negate) | ||
| 463 | { | ||
| 464 | if (min < curr_max) | ||
| 465 | { | ||
| 466 | /* Overlap. */ | ||
| 467 | curr_max = MAX(max + 1, curr_max); | ||
| 468 | DPRINT(("overlap, curr_max = %d\n", curr_max)); | ||
| 469 | l = NULL; | ||
| 470 | } | ||
| 471 | else | ||
| 472 | { | ||
| 473 | /* No overlap. */ | ||
| 474 | curr_max = min - 1; | ||
| 475 | if (curr_max >= curr_min) | ||
| 476 | { | ||
| 477 | DPRINT(("no overlap\n")); | ||
| 478 | l->code_min = curr_min; | ||
| 479 | l->code_max = curr_max; | ||
| 480 | } | ||
| 481 | else | ||
| 482 | { | ||
| 483 | DPRINT(("no overlap, zero room\n")); | ||
| 484 | l = NULL; | ||
| 485 | } | ||
| 486 | curr_min = curr_max = max + 1; | ||
| 487 | } | ||
| 488 | } | ||
| 489 | |||
| 490 | if (l != NULL) | ||
| 491 | { | ||
| 492 | int k; | ||
| 493 | DPRINT(("creating %d - %d\n", (int)l->code_min, (int)l->code_max)); | ||
| 494 | l->position = ctx->position; | ||
| 495 | if (num_neg_classes > 0) | ||
| 496 | { | ||
| 497 | l->neg_classes = tre_mem_alloc(ctx->mem, | ||
| 498 | (sizeof(l->neg_classes) | ||
| 499 | * (num_neg_classes + 1))); | ||
| 500 | if (l->neg_classes == NULL) | ||
| 501 | { | ||
| 502 | status = REG_ESPACE; | ||
| 503 | break; | ||
| 504 | } | ||
| 505 | for (k = 0; k < num_neg_classes; k++) | ||
| 506 | l->neg_classes[k] = neg_classes[k]; | ||
| 507 | l->neg_classes[k] = (tre_ctype_t)0; | ||
| 508 | } | ||
| 509 | else | ||
| 510 | l->neg_classes = NULL; | ||
| 511 | if (node == NULL) | ||
| 512 | node = items[j]; | ||
| 513 | else | ||
| 514 | { | ||
| 515 | u = tre_ast_new_union(ctx->mem, node, items[j]); | ||
| 516 | if (u == NULL) | ||
| 517 | status = REG_ESPACE; | ||
| 518 | node = u; | ||
| 519 | } | ||
| 520 | } | ||
| 521 | } | ||
| 522 | |||
| 523 | if (status != REG_OK) | ||
| 524 | goto parse_bracket_done; | ||
| 525 | |||
| 526 | if (negate) | ||
| 527 | { | ||
| 528 | int k; | ||
| 529 | DPRINT(("final: creating %d - %d\n", curr_min, (int)TRE_CHAR_MAX)); | ||
| 530 | n = tre_ast_new_literal(ctx->mem, curr_min, TRE_CHAR_MAX, ctx->position); | ||
| 531 | if (n == NULL) | ||
| 532 | status = REG_ESPACE; | ||
| 533 | else | ||
| 534 | { | ||
| 535 | tre_literal_t *l = n->obj; | ||
| 536 | if (num_neg_classes > 0) | ||
| 537 | { | ||
| 538 | l->neg_classes = tre_mem_alloc(ctx->mem, | ||
| 539 | (sizeof(l->neg_classes) | ||
| 540 | * (num_neg_classes + 1))); | ||
| 541 | if (l->neg_classes == NULL) | ||
| 542 | { | ||
| 543 | status = REG_ESPACE; | ||
| 544 | goto parse_bracket_done; | ||
| 545 | } | ||
| 546 | for (k = 0; k < num_neg_classes; k++) | ||
| 547 | l->neg_classes[k] = neg_classes[k]; | ||
| 548 | l->neg_classes[k] = (tre_ctype_t)0; | ||
| 549 | } | ||
| 550 | else | ||
| 551 | l->neg_classes = NULL; | ||
| 552 | if (node == NULL) | ||
| 553 | node = n; | ||
| 554 | else | ||
| 555 | { | ||
| 556 | u = tre_ast_new_union(ctx->mem, node, n); | ||
| 557 | if (u == NULL) | ||
| 558 | status = REG_ESPACE; | ||
| 559 | node = u; | ||
| 560 | } | ||
| 561 | } | ||
| 562 | } | ||
| 563 | |||
| 564 | if (status != REG_OK) | ||
| 565 | goto parse_bracket_done; | ||
| 566 | |||
| 567 | #ifdef TRE_DEBUG | ||
| 568 | tre_ast_print(node); | ||
| 569 | #endif /* TRE_DEBUG */ | ||
| 570 | |||
| 571 | parse_bracket_done: | ||
| 572 | xfree(items); | ||
| 573 | ctx->position++; | ||
| 574 | *result = node; | ||
| 575 | return status; | ||
| 576 | } | ||
| 577 | |||
| 578 | |||
| 579 | /* Parses a positive decimal integer. Returns -1 if the string does not | ||
| 580 | contain a valid number. */ | ||
| 581 | static int | ||
| 582 | tre_parse_int(const tre_char_t **regex, const tre_char_t *regex_end) | ||
| 583 | { | ||
| 584 | int num = -1; | ||
| 585 | const tre_char_t *r = *regex; | ||
| 586 | while (r < regex_end && *r >= L'0' && *r <= L'9') | ||
| 587 | { | ||
| 588 | if (num < 0) | ||
| 589 | num = 0; | ||
| 590 | num = num * 10 + *r - L'0'; | ||
| 591 | r++; | ||
| 592 | } | ||
| 593 | *regex = r; | ||
| 594 | return num; | ||
| 595 | } | ||
| 596 | |||
| 597 | |||
| 598 | static reg_errcode_t | ||
| 599 | tre_parse_bound(tre_parse_ctx_t *ctx, tre_ast_node_t **result) | ||
| 600 | { | ||
| 601 | int min, max, i; | ||
| 602 | int cost_ins, cost_del, cost_subst, cost_max; | ||
| 603 | int limit_ins, limit_del, limit_subst, limit_err; | ||
| 604 | const tre_char_t *r = ctx->re; | ||
| 605 | const tre_char_t *start; | ||
| 606 | int minimal = (ctx->cflags & REG_UNGREEDY) ? 1 : 0; | ||
| 607 | int approx = 0; | ||
| 608 | int costs_set = 0; | ||
| 609 | int counts_set = 0; | ||
| 610 | |||
| 611 | cost_ins = cost_del = cost_subst = cost_max = TRE_PARAM_UNSET; | ||
| 612 | limit_ins = limit_del = limit_subst = limit_err = TRE_PARAM_UNSET; | ||
| 613 | |||
| 614 | /* Parse number (minimum repetition count). */ | ||
| 615 | min = -1; | ||
| 616 | if (r < ctx->re_end && *r >= L'0' && *r <= L'9') { | ||
| 617 | DPRINT(("tre_parse: min count: '%.*" STRF "'\n", REST(r))); | ||
| 618 | min = tre_parse_int(&r, ctx->re_end); | ||
| 619 | } | ||
| 620 | |||
| 621 | /* Parse comma and second number (maximum repetition count). */ | ||
| 622 | max = min; | ||
| 623 | if (r < ctx->re_end && *r == CHAR_COMMA) | ||
| 624 | { | ||
| 625 | r++; | ||
| 626 | DPRINT(("tre_parse: max count: '%.*" STRF "'\n", REST(r))); | ||
| 627 | max = tre_parse_int(&r, ctx->re_end); | ||
| 628 | } | ||
| 629 | |||
| 630 | /* Check that the repeat counts are sane. */ | ||
| 631 | if ((max >= 0 && min > max) || max > RE_DUP_MAX) | ||
| 632 | return REG_BADBR; | ||
| 633 | |||
| 634 | |||
| 635 | /* | ||
| 636 | '{' | ||
| 637 | optionally followed immediately by a number == minimum repcount | ||
| 638 | optionally followed by , then a number == maximum repcount | ||
| 639 | + then a number == maximum insertion count | ||
| 640 | - then a number == maximum deletion count | ||
| 641 | # then a number == maximum substitution count | ||
| 642 | ~ then a number == maximum number of errors | ||
| 643 | Any of +, -, # or ~ without followed by a number means that | ||
| 644 | the maximum count/number of errors is infinite. | ||
| 645 | |||
| 646 | An equation of the form | ||
| 647 | Xi + Yd + Zs < C | ||
| 648 | can be specified to set costs and the cost limit to a value | ||
| 649 | different from the default value: | ||
| 650 | - X is the cost of an insertion | ||
| 651 | - Y is the cost of a deletion | ||
| 652 | - Z is the cost of a substitution | ||
| 653 | - C is the maximum cost | ||
| 654 | |||
| 655 | If no count limit or cost is set for an operation, the operation | ||
| 656 | is not allowed at all. | ||
| 657 | */ | ||
| 658 | |||
| 659 | |||
| 660 | do { | ||
| 661 | int done; | ||
| 662 | start = r; | ||
| 663 | |||
| 664 | /* Parse count limit settings */ | ||
| 665 | done = 0; | ||
| 666 | if (!counts_set) | ||
| 667 | while (r + 1 < ctx->re_end && !done) | ||
| 668 | { | ||
| 669 | switch (*r) | ||
| 670 | { | ||
| 671 | case CHAR_PLUS: /* Insert limit */ | ||
| 672 | DPRINT(("tre_parse: ins limit: '%.*" STRF "'\n", REST(r))); | ||
| 673 | r++; | ||
| 674 | limit_ins = tre_parse_int(&r, ctx->re_end); | ||
| 675 | if (limit_ins < 0) | ||
| 676 | limit_ins = INT_MAX; | ||
| 677 | counts_set = 1; | ||
| 678 | break; | ||
| 679 | case CHAR_MINUS: /* Delete limit */ | ||
| 680 | DPRINT(("tre_parse: del limit: '%.*" STRF "'\n", REST(r))); | ||
| 681 | r++; | ||
| 682 | limit_del = tre_parse_int(&r, ctx->re_end); | ||
| 683 | if (limit_del < 0) | ||
| 684 | limit_del = INT_MAX; | ||
| 685 | counts_set = 1; | ||
| 686 | break; | ||
| 687 | case CHAR_HASH: /* Substitute limit */ | ||
| 688 | DPRINT(("tre_parse: subst limit: '%.*" STRF "'\n", REST(r))); | ||
| 689 | r++; | ||
| 690 | limit_subst = tre_parse_int(&r, ctx->re_end); | ||
| 691 | if (limit_subst < 0) | ||
| 692 | limit_subst = INT_MAX; | ||
| 693 | counts_set = 1; | ||
| 694 | break; | ||
| 695 | case CHAR_TILDE: /* Maximum number of changes */ | ||
| 696 | DPRINT(("tre_parse: count limit: '%.*" STRF "'\n", REST(r))); | ||
| 697 | r++; | ||
| 698 | limit_err = tre_parse_int(&r, ctx->re_end); | ||
| 699 | if (limit_err < 0) | ||
| 700 | limit_err = INT_MAX; | ||
| 701 | approx = 1; | ||
| 702 | break; | ||
| 703 | case CHAR_COMMA: | ||
| 704 | r++; | ||
| 705 | break; | ||
| 706 | case L' ': | ||
| 707 | r++; | ||
| 708 | break; | ||
| 709 | case L'}': | ||
| 710 | done = 1; | ||
| 711 | break; | ||
| 712 | default: | ||
| 713 | done = 1; | ||
| 714 | break; | ||
| 715 | } | ||
| 716 | } | ||
| 717 | |||
| 718 | /* Parse cost restriction equation. */ | ||
| 719 | done = 0; | ||
| 720 | if (!costs_set) | ||
| 721 | while (r + 1 < ctx->re_end && !done) | ||
| 722 | { | ||
| 723 | switch (*r) | ||
| 724 | { | ||
| 725 | case CHAR_PLUS: | ||
| 726 | case L' ': | ||
| 727 | r++; | ||
| 728 | break; | ||
| 729 | case L'<': | ||
| 730 | DPRINT(("tre_parse: max cost: '%.*" STRF "'\n", REST(r))); | ||
| 731 | r++; | ||
| 732 | while (*r == L' ') | ||
| 733 | r++; | ||
| 734 | cost_max = tre_parse_int(&r, ctx->re_end); | ||
| 735 | if (cost_max < 0) | ||
| 736 | cost_max = INT_MAX; | ||
| 737 | else | ||
| 738 | cost_max--; | ||
| 739 | approx = 1; | ||
| 740 | break; | ||
| 741 | case CHAR_COMMA: | ||
| 742 | r++; | ||
| 743 | done = 1; | ||
| 744 | break; | ||
| 745 | default: | ||
| 746 | if (*r >= L'0' && *r <= L'9') | ||
| 747 | { | ||
| 748 | #ifdef TRE_DEBUG | ||
| 749 | const tre_char_t *sr = r; | ||
| 750 | #endif /* TRE_DEBUG */ | ||
| 751 | int cost = tre_parse_int(&r, ctx->re_end); | ||
| 752 | /* XXX - make sure r is not past end. */ | ||
| 753 | switch (*r) | ||
| 754 | { | ||
| 755 | case L'i': /* Insert cost */ | ||
| 756 | DPRINT(("tre_parse: ins cost: '%.*" STRF "'\n", | ||
| 757 | REST(sr))); | ||
| 758 | r++; | ||
| 759 | cost_ins = cost; | ||
| 760 | costs_set = 1; | ||
| 761 | break; | ||
| 762 | case L'd': /* Delete cost */ | ||
| 763 | DPRINT(("tre_parse: del cost: '%.*" STRF "'\n", | ||
| 764 | REST(sr))); | ||
| 765 | r++; | ||
| 766 | cost_del = cost; | ||
| 767 | costs_set = 1; | ||
| 768 | break; | ||
| 769 | case L's': /* Substitute cost */ | ||
| 770 | DPRINT(("tre_parse: subst cost: '%.*" STRF "'\n", | ||
| 771 | REST(sr))); | ||
| 772 | r++; | ||
| 773 | cost_subst = cost; | ||
| 774 | costs_set = 1; | ||
| 775 | break; | ||
| 776 | default: | ||
| 777 | return REG_BADBR; | ||
| 778 | } | ||
| 779 | } | ||
| 780 | else | ||
| 781 | { | ||
| 782 | done = 1; | ||
| 783 | break; | ||
| 784 | } | ||
| 785 | } | ||
| 786 | } | ||
| 787 | } while (start != r); | ||
| 788 | |||
| 789 | /* Missing }. */ | ||
| 790 | if (r >= ctx->re_end) | ||
| 791 | return REG_EBRACE; | ||
| 792 | |||
| 793 | /* Empty contents of {}. */ | ||
| 794 | if (r == ctx->re) | ||
| 795 | return REG_BADBR; | ||
| 796 | |||
| 797 | /* Parse the ending '}' or '\}'.*/ | ||
| 798 | if (ctx->cflags & REG_EXTENDED) | ||
| 799 | { | ||
| 800 | if (r >= ctx->re_end || *r != CHAR_RBRACE) | ||
| 801 | return REG_BADBR; | ||
| 802 | r++; | ||
| 803 | } | ||
| 804 | else | ||
| 805 | { | ||
| 806 | if (r + 1 >= ctx->re_end | ||
| 807 | || *r != CHAR_BACKSLASH | ||
| 808 | || *(r + 1) != CHAR_RBRACE) | ||
| 809 | return REG_BADBR; | ||
| 810 | r += 2; | ||
| 811 | } | ||
| 812 | |||
| 813 | |||
| 814 | /* Parse trailing '?' marking minimal repetition. */ | ||
| 815 | if (r < ctx->re_end) | ||
| 816 | { | ||
| 817 | if (*r == CHAR_QUESTIONMARK) | ||
| 818 | { | ||
| 819 | minimal = !(ctx->cflags & REG_UNGREEDY); | ||
| 820 | r++; | ||
| 821 | } | ||
| 822 | else if (*r == CHAR_STAR || *r == CHAR_PLUS) | ||
| 823 | { | ||
| 824 | /* These are reserved for future extensions. */ | ||
| 825 | return REG_BADRPT; | ||
| 826 | } | ||
| 827 | } | ||
| 828 | |||
| 829 | /* Create the AST node(s). */ | ||
| 830 | if (min == 0 && max == 0) | ||
| 831 | { | ||
| 832 | *result = tre_ast_new_literal(ctx->mem, EMPTY, -1, -1); | ||
| 833 | if (*result == NULL) | ||
| 834 | return REG_ESPACE; | ||
| 835 | } | ||
| 836 | else | ||
| 837 | { | ||
| 838 | if (min < 0 && max < 0) | ||
| 839 | /* Only approximate parameters set, no repetitions. */ | ||
| 840 | min = max = 1; | ||
| 841 | |||
| 842 | *result = tre_ast_new_iter(ctx->mem, *result, min, max, minimal); | ||
| 843 | if (!*result) | ||
| 844 | return REG_ESPACE; | ||
| 845 | |||
| 846 | /* If approximate matching parameters are set, add them to the | ||
| 847 | iteration node. */ | ||
| 848 | if (approx || costs_set || counts_set) | ||
| 849 | { | ||
| 850 | int *params; | ||
| 851 | tre_iteration_t *iter = (*result)->obj; | ||
| 852 | |||
| 853 | if (costs_set || counts_set) | ||
| 854 | { | ||
| 855 | if (limit_ins == TRE_PARAM_UNSET) | ||
| 856 | { | ||
| 857 | if (cost_ins == TRE_PARAM_UNSET) | ||
| 858 | limit_ins = 0; | ||
| 859 | else | ||
| 860 | limit_ins = INT_MAX; | ||
| 861 | } | ||
| 862 | |||
| 863 | if (limit_del == TRE_PARAM_UNSET) | ||
| 864 | { | ||
| 865 | if (cost_del == TRE_PARAM_UNSET) | ||
| 866 | limit_del = 0; | ||
| 867 | else | ||
| 868 | limit_del = INT_MAX; | ||
| 869 | } | ||
| 870 | |||
| 871 | if (limit_subst == TRE_PARAM_UNSET) | ||
| 872 | { | ||
| 873 | if (cost_subst == TRE_PARAM_UNSET) | ||
| 874 | limit_subst = 0; | ||
| 875 | else | ||
| 876 | limit_subst = INT_MAX; | ||
| 877 | } | ||
| 878 | } | ||
| 879 | |||
| 880 | if (cost_max == TRE_PARAM_UNSET) | ||
| 881 | cost_max = INT_MAX; | ||
| 882 | if (limit_err == TRE_PARAM_UNSET) | ||
| 883 | limit_err = INT_MAX; | ||
| 884 | |||
| 885 | ctx->have_approx = 1; | ||
| 886 | params = tre_mem_alloc(ctx->mem, sizeof(*params) * TRE_PARAM_LAST); | ||
| 887 | if (!params) | ||
| 888 | return REG_ESPACE; | ||
| 889 | for (i = 0; i < TRE_PARAM_LAST; i++) | ||
| 890 | params[i] = TRE_PARAM_UNSET; | ||
| 891 | params[TRE_PARAM_COST_INS] = cost_ins; | ||
| 892 | params[TRE_PARAM_COST_DEL] = cost_del; | ||
| 893 | params[TRE_PARAM_COST_SUBST] = cost_subst; | ||
| 894 | params[TRE_PARAM_COST_MAX] = cost_max; | ||
| 895 | params[TRE_PARAM_MAX_INS] = limit_ins; | ||
| 896 | params[TRE_PARAM_MAX_DEL] = limit_del; | ||
| 897 | params[TRE_PARAM_MAX_SUBST] = limit_subst; | ||
| 898 | params[TRE_PARAM_MAX_ERR] = limit_err; | ||
| 899 | iter->params = params; | ||
| 900 | } | ||
| 901 | } | ||
| 902 | |||
| 903 | DPRINT(("tre_parse_bound: min %d, max %d, costs [%d,%d,%d, total %d], " | ||
| 904 | "limits [%d,%d,%d, total %d]\n", | ||
| 905 | min, max, cost_ins, cost_del, cost_subst, cost_max, | ||
| 906 | limit_ins, limit_del, limit_subst, limit_err)); | ||
| 907 | |||
| 908 | |||
| 909 | ctx->re = r; | ||
| 910 | return REG_OK; | ||
| 911 | } | ||
| 912 | |||
| 913 | typedef enum { | ||
| 914 | PARSE_RE = 0, | ||
| 915 | PARSE_ATOM, | ||
| 916 | PARSE_MARK_FOR_SUBMATCH, | ||
| 917 | PARSE_BRANCH, | ||
| 918 | PARSE_PIECE, | ||
| 919 | PARSE_CATENATION, | ||
| 920 | PARSE_POST_CATENATION, | ||
| 921 | PARSE_UNION, | ||
| 922 | PARSE_POST_UNION, | ||
| 923 | PARSE_POSTFIX, | ||
| 924 | PARSE_RESTORE_CFLAGS | ||
| 925 | } tre_parse_re_stack_symbol_t; | ||
| 926 | |||
| 927 | |||
| 928 | reg_errcode_t | ||
| 929 | tre_parse(tre_parse_ctx_t *ctx) | ||
| 930 | { | ||
| 931 | tre_ast_node_t *result = NULL; | ||
| 932 | tre_parse_re_stack_symbol_t symbol; | ||
| 933 | reg_errcode_t status = REG_OK; | ||
| 934 | tre_stack_t *stack = ctx->stack; | ||
| 935 | int bottom = tre_stack_num_objects(stack); | ||
| 936 | int depth = 0; | ||
| 937 | int temporary_cflags = 0; | ||
| 938 | |||
| 939 | DPRINT(("tre_parse: parsing '%.*" STRF "', len = %d\n", | ||
| 940 | ctx->len, ctx->re, ctx->len)); | ||
| 941 | |||
| 942 | if (!ctx->nofirstsub) | ||
| 943 | { | ||
| 944 | STACK_PUSH(stack, int, ctx->submatch_id); | ||
| 945 | STACK_PUSH(stack, int, PARSE_MARK_FOR_SUBMATCH); | ||
| 946 | ctx->submatch_id++; | ||
| 947 | } | ||
| 948 | STACK_PUSH(stack, int, PARSE_RE); | ||
| 949 | ctx->re_start = ctx->re; | ||
| 950 | ctx->re_end = ctx->re + ctx->len; | ||
| 951 | |||
| 952 | |||
| 953 | /* The following is basically just a recursive descent parser. I use | ||
| 954 | an explicit stack instead of recursive functions mostly because of | ||
| 955 | two reasons: compatibility with systems which have an overflowable | ||
| 956 | call stack, and efficiency (both in lines of code and speed). */ | ||
| 957 | while (tre_stack_num_objects(stack) > bottom && status == REG_OK) | ||
| 958 | { | ||
| 959 | if (status != REG_OK) | ||
| 960 | break; | ||
| 961 | symbol = tre_stack_pop_int(stack); | ||
| 962 | switch (symbol) | ||
| 963 | { | ||
| 964 | case PARSE_RE: | ||
| 965 | /* Parse a full regexp. A regexp is one or more branches, | ||
| 966 | separated by the union operator `|'. */ | ||
| 967 | #ifdef REG_LITERAL | ||
| 968 | if (!(ctx->cflags & REG_LITERAL) | ||
| 969 | && ctx->cflags & REG_EXTENDED) | ||
| 970 | #endif /* REG_LITERAL */ | ||
| 971 | STACK_PUSHX(stack, int, PARSE_UNION); | ||
| 972 | STACK_PUSHX(stack, int, PARSE_BRANCH); | ||
| 973 | break; | ||
| 974 | |||
| 975 | case PARSE_BRANCH: | ||
| 976 | /* Parse a branch. A branch is one or more pieces, concatenated. | ||
| 977 | A piece is an atom possibly followed by a postfix operator. */ | ||
| 978 | STACK_PUSHX(stack, int, PARSE_CATENATION); | ||
| 979 | STACK_PUSHX(stack, int, PARSE_PIECE); | ||
| 980 | break; | ||
| 981 | |||
| 982 | case PARSE_PIECE: | ||
| 983 | /* Parse a piece. A piece is an atom possibly followed by one | ||
| 984 | or more postfix operators. */ | ||
| 985 | #ifdef REG_LITERAL | ||
| 986 | if (!(ctx->cflags & REG_LITERAL)) | ||
| 987 | #endif /* REG_LITERAL */ | ||
| 988 | STACK_PUSHX(stack, int, PARSE_POSTFIX); | ||
| 989 | STACK_PUSHX(stack, int, PARSE_ATOM); | ||
| 990 | break; | ||
| 991 | |||
| 992 | case PARSE_CATENATION: | ||
| 993 | /* If the expression has not ended, parse another piece. */ | ||
| 994 | { | ||
| 995 | tre_char_t c; | ||
| 996 | if (ctx->re >= ctx->re_end) | ||
| 997 | break; | ||
| 998 | c = *ctx->re; | ||
| 999 | #ifdef REG_LITERAL | ||
| 1000 | if (!(ctx->cflags & REG_LITERAL)) | ||
| 1001 | { | ||
| 1002 | #endif /* REG_LITERAL */ | ||
| 1003 | if (ctx->cflags & REG_EXTENDED && c == CHAR_PIPE) | ||
| 1004 | break; | ||
| 1005 | if ((ctx->cflags & REG_EXTENDED | ||
| 1006 | && c == CHAR_RPAREN && depth > 0) | ||
| 1007 | || (!(ctx->cflags & REG_EXTENDED) | ||
| 1008 | && (c == CHAR_BACKSLASH | ||
| 1009 | && *(ctx->re + 1) == CHAR_RPAREN))) | ||
| 1010 | { | ||
| 1011 | if (!(ctx->cflags & REG_EXTENDED) && depth == 0) | ||
| 1012 | status = REG_EPAREN; | ||
| 1013 | DPRINT(("tre_parse: group end: '%.*" STRF "'\n", | ||
| 1014 | REST(ctx->re))); | ||
| 1015 | depth--; | ||
| 1016 | if (!(ctx->cflags & REG_EXTENDED)) | ||
| 1017 | ctx->re += 2; | ||
| 1018 | break; | ||
| 1019 | } | ||
| 1020 | #ifdef REG_LITERAL | ||
| 1021 | } | ||
| 1022 | #endif /* REG_LITERAL */ | ||
| 1023 | |||
| 1024 | #ifdef REG_RIGHT_ASSOC | ||
| 1025 | if (ctx->cflags & REG_RIGHT_ASSOC) | ||
| 1026 | { | ||
| 1027 | /* Right associative concatenation. */ | ||
| 1028 | STACK_PUSHX(stack, voidptr, result); | ||
| 1029 | STACK_PUSHX(stack, int, PARSE_POST_CATENATION); | ||
| 1030 | STACK_PUSHX(stack, int, PARSE_CATENATION); | ||
| 1031 | STACK_PUSHX(stack, int, PARSE_PIECE); | ||
| 1032 | } | ||
| 1033 | else | ||
| 1034 | #endif /* REG_RIGHT_ASSOC */ | ||
| 1035 | { | ||
| 1036 | /* Default case, left associative concatenation. */ | ||
| 1037 | STACK_PUSHX(stack, int, PARSE_CATENATION); | ||
| 1038 | STACK_PUSHX(stack, voidptr, result); | ||
| 1039 | STACK_PUSHX(stack, int, PARSE_POST_CATENATION); | ||
| 1040 | STACK_PUSHX(stack, int, PARSE_PIECE); | ||
| 1041 | } | ||
| 1042 | break; | ||
| 1043 | } | ||
| 1044 | |||
| 1045 | case PARSE_POST_CATENATION: | ||
| 1046 | { | ||
| 1047 | tre_ast_node_t *tree = tre_stack_pop_voidptr(stack); | ||
| 1048 | tre_ast_node_t *tmp_node; | ||
| 1049 | tmp_node = tre_ast_new_catenation(ctx->mem, tree, result); | ||
| 1050 | if (!tmp_node) | ||
| 1051 | return REG_ESPACE; | ||
| 1052 | result = tmp_node; | ||
| 1053 | break; | ||
| 1054 | } | ||
| 1055 | |||
| 1056 | case PARSE_UNION: | ||
| 1057 | if (ctx->re >= ctx->re_end) | ||
| 1058 | break; | ||
| 1059 | #ifdef REG_LITERAL | ||
| 1060 | if (ctx->cflags & REG_LITERAL) | ||
| 1061 | break; | ||
| 1062 | #endif /* REG_LITERAL */ | ||
| 1063 | switch (*ctx->re) | ||
| 1064 | { | ||
| 1065 | case CHAR_PIPE: | ||
| 1066 | DPRINT(("tre_parse: union: '%.*" STRF "'\n", | ||
| 1067 | REST(ctx->re))); | ||
| 1068 | STACK_PUSHX(stack, int, PARSE_UNION); | ||
| 1069 | STACK_PUSHX(stack, voidptr, result); | ||
| 1070 | STACK_PUSHX(stack, int, PARSE_POST_UNION); | ||
| 1071 | STACK_PUSHX(stack, int, PARSE_BRANCH); | ||
| 1072 | ctx->re++; | ||
| 1073 | break; | ||
| 1074 | |||
| 1075 | case CHAR_RPAREN: | ||
| 1076 | ctx->re++; | ||
| 1077 | break; | ||
| 1078 | |||
| 1079 | default: | ||
| 1080 | break; | ||
| 1081 | } | ||
| 1082 | break; | ||
| 1083 | |||
| 1084 | case PARSE_POST_UNION: | ||
| 1085 | { | ||
| 1086 | tre_ast_node_t *tmp_node; | ||
| 1087 | tre_ast_node_t *tree = tre_stack_pop_voidptr(stack); | ||
| 1088 | tmp_node = tre_ast_new_union(ctx->mem, tree, result); | ||
| 1089 | if (!tmp_node) | ||
| 1090 | return REG_ESPACE; | ||
| 1091 | result = tmp_node; | ||
| 1092 | break; | ||
| 1093 | } | ||
| 1094 | |||
| 1095 | case PARSE_POSTFIX: | ||
| 1096 | /* Parse postfix operators. */ | ||
| 1097 | if (ctx->re >= ctx->re_end) | ||
| 1098 | break; | ||
| 1099 | #ifdef REG_LITERAL | ||
| 1100 | if (ctx->cflags & REG_LITERAL) | ||
| 1101 | break; | ||
| 1102 | #endif /* REG_LITERAL */ | ||
| 1103 | switch (*ctx->re) | ||
| 1104 | { | ||
| 1105 | case CHAR_PLUS: | ||
| 1106 | case CHAR_QUESTIONMARK: | ||
| 1107 | if (!(ctx->cflags & REG_EXTENDED)) | ||
| 1108 | break; | ||
| 1109 | /*FALLTHROUGH*/ | ||
| 1110 | case CHAR_STAR: | ||
| 1111 | { | ||
| 1112 | tre_ast_node_t *tmp_node; | ||
| 1113 | int minimal = (ctx->cflags & REG_UNGREEDY) ? 1 : 0; | ||
| 1114 | int rep_min = 0; | ||
| 1115 | int rep_max = -1; | ||
| 1116 | #ifdef TRE_DEBUG | ||
| 1117 | const tre_char_t *tmp_re; | ||
| 1118 | #endif | ||
| 1119 | |||
| 1120 | if (*ctx->re == CHAR_PLUS) | ||
| 1121 | rep_min = 1; | ||
| 1122 | if (*ctx->re == CHAR_QUESTIONMARK) | ||
| 1123 | rep_max = 1; | ||
| 1124 | #ifdef TRE_DEBUG | ||
| 1125 | tmp_re = ctx->re; | ||
| 1126 | #endif | ||
| 1127 | |||
| 1128 | if (ctx->re + 1 < ctx->re_end) | ||
| 1129 | { | ||
| 1130 | if (*(ctx->re + 1) == CHAR_QUESTIONMARK) | ||
| 1131 | { | ||
| 1132 | minimal = !(ctx->cflags & REG_UNGREEDY); | ||
| 1133 | ctx->re++; | ||
| 1134 | } | ||
| 1135 | else if (*(ctx->re + 1) == CHAR_STAR | ||
| 1136 | || *(ctx->re + 1) == CHAR_PLUS) | ||
| 1137 | { | ||
| 1138 | /* These are reserved for future extensions. */ | ||
| 1139 | return REG_BADRPT; | ||
| 1140 | } | ||
| 1141 | } | ||
| 1142 | |||
| 1143 | DPRINT(("tre_parse: %s star: '%.*" STRF "'\n", | ||
| 1144 | minimal ? " minimal" : "greedy", REST(tmp_re))); | ||
| 1145 | ctx->re++; | ||
| 1146 | tmp_node = tre_ast_new_iter(ctx->mem, result, rep_min, rep_max, | ||
| 1147 | minimal); | ||
| 1148 | if (tmp_node == NULL) | ||
| 1149 | return REG_ESPACE; | ||
| 1150 | result = tmp_node; | ||
| 1151 | STACK_PUSHX(stack, int, PARSE_POSTFIX); | ||
| 1152 | } | ||
| 1153 | break; | ||
| 1154 | |||
| 1155 | case CHAR_BACKSLASH: | ||
| 1156 | /* "\{" is special without REG_EXTENDED */ | ||
| 1157 | if (!(ctx->cflags & REG_EXTENDED) | ||
| 1158 | && ctx->re + 1 < ctx->re_end | ||
| 1159 | && *(ctx->re + 1) == CHAR_LBRACE) | ||
| 1160 | { | ||
| 1161 | ctx->re++; | ||
| 1162 | goto parse_brace; | ||
| 1163 | } | ||
| 1164 | else | ||
| 1165 | break; | ||
| 1166 | |||
| 1167 | case CHAR_LBRACE: | ||
| 1168 | /* "{" is literal without REG_EXTENDED */ | ||
| 1169 | if (!(ctx->cflags & REG_EXTENDED)) | ||
| 1170 | break; | ||
| 1171 | |||
| 1172 | parse_brace: | ||
| 1173 | DPRINT(("tre_parse: bound: '%.*" STRF "'\n", | ||
| 1174 | REST(ctx->re))); | ||
| 1175 | ctx->re++; | ||
| 1176 | |||
| 1177 | status = tre_parse_bound(ctx, &result); | ||
| 1178 | if (status != REG_OK) | ||
| 1179 | return status; | ||
| 1180 | STACK_PUSHX(stack, int, PARSE_POSTFIX); | ||
| 1181 | break; | ||
| 1182 | } | ||
| 1183 | break; | ||
| 1184 | |||
| 1185 | case PARSE_ATOM: | ||
| 1186 | /* Parse an atom. An atom is a regular expression enclosed in `()', | ||
| 1187 | an empty set of `()', a bracket expression, `.', `^', `$', | ||
| 1188 | a `\' followed by a character, or a single character. */ | ||
| 1189 | |||
| 1190 | /* End of regexp? (empty string). */ | ||
| 1191 | if (ctx->re >= ctx->re_end) | ||
| 1192 | goto parse_literal; | ||
| 1193 | |||
| 1194 | #ifdef REG_LITERAL | ||
| 1195 | if (ctx->cflags & REG_LITERAL) | ||
| 1196 | goto parse_literal; | ||
| 1197 | #endif /* REG_LITERAL */ | ||
| 1198 | |||
| 1199 | switch (*ctx->re) | ||
| 1200 | { | ||
| 1201 | case CHAR_LPAREN: /* parenthesized subexpression */ | ||
| 1202 | |||
| 1203 | /* Handle "(?...)" extensions. They work in a way similar | ||
| 1204 | to Perls corresponding extensions. */ | ||
| 1205 | if (ctx->cflags & REG_EXTENDED | ||
| 1206 | && *(ctx->re + 1) == CHAR_QUESTIONMARK) | ||
| 1207 | { | ||
| 1208 | int new_cflags = ctx->cflags; | ||
| 1209 | int bit = 1; | ||
| 1210 | DPRINT(("tre_parse: extension: '%.*" STRF "\n", | ||
| 1211 | REST(ctx->re))); | ||
| 1212 | ctx->re += 2; | ||
| 1213 | while (/*CONSTCOND*/1) | ||
| 1214 | { | ||
| 1215 | if (*ctx->re == L'i') | ||
| 1216 | { | ||
| 1217 | DPRINT(("tre_parse: icase: '%.*" STRF "\n", | ||
| 1218 | REST(ctx->re))); | ||
| 1219 | if (bit) | ||
| 1220 | new_cflags |= REG_ICASE; | ||
| 1221 | else | ||
| 1222 | new_cflags &= ~REG_ICASE; | ||
| 1223 | ctx->re++; | ||
| 1224 | } | ||
| 1225 | else if (*ctx->re == L'n') | ||
| 1226 | { | ||
| 1227 | DPRINT(("tre_parse: newline: '%.*" STRF "\n", | ||
| 1228 | REST(ctx->re))); | ||
| 1229 | if (bit) | ||
| 1230 | new_cflags |= REG_NEWLINE; | ||
| 1231 | else | ||
| 1232 | new_cflags &= ~REG_NEWLINE; | ||
| 1233 | ctx->re++; | ||
| 1234 | } | ||
| 1235 | #ifdef REG_RIGHT_ASSOC | ||
| 1236 | else if (*ctx->re == L'r') | ||
| 1237 | { | ||
| 1238 | DPRINT(("tre_parse: right assoc: '%.*" STRF "\n", | ||
| 1239 | REST(ctx->re))); | ||
| 1240 | if (bit) | ||
| 1241 | new_cflags |= REG_RIGHT_ASSOC; | ||
| 1242 | else | ||
| 1243 | new_cflags &= ~REG_RIGHT_ASSOC; | ||
| 1244 | ctx->re++; | ||
| 1245 | } | ||
| 1246 | #endif /* REG_RIGHT_ASSOC */ | ||
| 1247 | #ifdef REG_UNGREEDY | ||
| 1248 | else if (*ctx->re == L'U') | ||
| 1249 | { | ||
| 1250 | DPRINT(("tre_parse: ungreedy: '%.*" STRF "\n", | ||
| 1251 | REST(ctx->re))); | ||
| 1252 | if (bit) | ||
| 1253 | new_cflags |= REG_UNGREEDY; | ||
| 1254 | else | ||
| 1255 | new_cflags &= ~REG_UNGREEDY; | ||
| 1256 | ctx->re++; | ||
| 1257 | } | ||
| 1258 | #endif /* REG_UNGREEDY */ | ||
| 1259 | else if (*ctx->re == CHAR_MINUS) | ||
| 1260 | { | ||
| 1261 | DPRINT(("tre_parse: turn off: '%.*" STRF "\n", | ||
| 1262 | REST(ctx->re))); | ||
| 1263 | ctx->re++; | ||
| 1264 | bit = 0; | ||
| 1265 | } | ||
| 1266 | else if (*ctx->re == CHAR_COLON) | ||
| 1267 | { | ||
| 1268 | DPRINT(("tre_parse: no group: '%.*" STRF "\n", | ||
| 1269 | REST(ctx->re))); | ||
| 1270 | ctx->re++; | ||
| 1271 | depth++; | ||
| 1272 | break; | ||
| 1273 | } | ||
| 1274 | else if (*ctx->re == CHAR_HASH) | ||
| 1275 | { | ||
| 1276 | DPRINT(("tre_parse: comment: '%.*" STRF "\n", | ||
| 1277 | REST(ctx->re))); | ||
| 1278 | /* A comment can contain any character except a | ||
| 1279 | right parenthesis */ | ||
| 1280 | while (*ctx->re != CHAR_RPAREN | ||
| 1281 | && ctx->re < ctx->re_end) | ||
| 1282 | ctx->re++; | ||
| 1283 | if (*ctx->re == CHAR_RPAREN && ctx->re < ctx->re_end) | ||
| 1284 | { | ||
| 1285 | ctx->re++; | ||
| 1286 | break; | ||
| 1287 | } | ||
| 1288 | else | ||
| 1289 | return REG_BADPAT; | ||
| 1290 | } | ||
| 1291 | else if (*ctx->re == CHAR_RPAREN) | ||
| 1292 | { | ||
| 1293 | ctx->re++; | ||
| 1294 | break; | ||
| 1295 | } | ||
| 1296 | else | ||
| 1297 | return REG_BADPAT; | ||
| 1298 | } | ||
| 1299 | |||
| 1300 | /* Turn on the cflags changes for the rest of the | ||
| 1301 | enclosing group. */ | ||
| 1302 | STACK_PUSHX(stack, int, ctx->cflags); | ||
| 1303 | STACK_PUSHX(stack, int, PARSE_RESTORE_CFLAGS); | ||
| 1304 | STACK_PUSHX(stack, int, PARSE_RE); | ||
| 1305 | ctx->cflags = new_cflags; | ||
| 1306 | break; | ||
| 1307 | } | ||
| 1308 | |||
| 1309 | if (ctx->cflags & REG_EXTENDED | ||
| 1310 | || (ctx->re > ctx->re_start | ||
| 1311 | && *(ctx->re - 1) == CHAR_BACKSLASH)) | ||
| 1312 | { | ||
| 1313 | depth++; | ||
| 1314 | if (ctx->re + 2 < ctx->re_end | ||
| 1315 | && *(ctx->re + 1) == CHAR_QUESTIONMARK | ||
| 1316 | && *(ctx->re + 2) == CHAR_COLON) | ||
| 1317 | { | ||
| 1318 | DPRINT(("tre_parse: group begin: '%.*" STRF | ||
| 1319 | "', no submatch\n", REST(ctx->re))); | ||
| 1320 | /* Don't mark for submatching. */ | ||
| 1321 | ctx->re += 3; | ||
| 1322 | STACK_PUSHX(stack, int, PARSE_RE); | ||
| 1323 | } | ||
| 1324 | else | ||
| 1325 | { | ||
| 1326 | DPRINT(("tre_parse: group begin: '%.*" STRF | ||
| 1327 | "', submatch %d\n", REST(ctx->re), | ||
| 1328 | ctx->submatch_id)); | ||
| 1329 | ctx->re++; | ||
| 1330 | /* First parse a whole RE, then mark the resulting tree | ||
| 1331 | for submatching. */ | ||
| 1332 | STACK_PUSHX(stack, int, ctx->submatch_id); | ||
| 1333 | STACK_PUSHX(stack, int, PARSE_MARK_FOR_SUBMATCH); | ||
| 1334 | STACK_PUSHX(stack, int, PARSE_RE); | ||
| 1335 | ctx->submatch_id++; | ||
| 1336 | } | ||
| 1337 | } | ||
| 1338 | else | ||
| 1339 | goto parse_literal; | ||
| 1340 | break; | ||
| 1341 | |||
| 1342 | case CHAR_RPAREN: /* end of current subexpression */ | ||
| 1343 | if ((ctx->cflags & REG_EXTENDED && depth > 0) | ||
| 1344 | || (ctx->re > ctx->re_start | ||
| 1345 | && *(ctx->re - 1) == CHAR_BACKSLASH)) | ||
| 1346 | { | ||
| 1347 | DPRINT(("tre_parse: empty: '%.*" STRF "'\n", | ||
| 1348 | REST(ctx->re))); | ||
| 1349 | /* We were expecting an atom, but instead the current | ||
| 1350 | subexpression was closed. POSIX leaves the meaning of | ||
| 1351 | this to be implementation-defined. We interpret this as | ||
| 1352 | an empty expression (which matches an empty string). */ | ||
| 1353 | result = tre_ast_new_literal(ctx->mem, EMPTY, -1, -1); | ||
| 1354 | if (result == NULL) | ||
| 1355 | return REG_ESPACE; | ||
| 1356 | if (!(ctx->cflags & REG_EXTENDED)) | ||
| 1357 | ctx->re--; | ||
| 1358 | } | ||
| 1359 | else | ||
| 1360 | goto parse_literal; | ||
| 1361 | break; | ||
| 1362 | |||
| 1363 | case CHAR_LBRACKET: /* bracket expression */ | ||
| 1364 | DPRINT(("tre_parse: bracket: '%.*" STRF "'\n", | ||
| 1365 | REST(ctx->re))); | ||
| 1366 | ctx->re++; | ||
| 1367 | status = tre_parse_bracket(ctx, &result); | ||
| 1368 | if (status != REG_OK) | ||
| 1369 | return status; | ||
| 1370 | break; | ||
| 1371 | |||
| 1372 | case CHAR_BACKSLASH: | ||
| 1373 | /* If this is "\(" or "\)" chew off the backslash and | ||
| 1374 | try again. */ | ||
| 1375 | if (!(ctx->cflags & REG_EXTENDED) | ||
| 1376 | && ctx->re + 1 < ctx->re_end | ||
| 1377 | && (*(ctx->re + 1) == CHAR_LPAREN | ||
| 1378 | || *(ctx->re + 1) == CHAR_RPAREN)) | ||
| 1379 | { | ||
| 1380 | ctx->re++; | ||
| 1381 | STACK_PUSHX(stack, int, PARSE_ATOM); | ||
| 1382 | break; | ||
| 1383 | } | ||
| 1384 | |||
| 1385 | /* If a macro is used, parse the expanded macro recursively. */ | ||
| 1386 | { | ||
| 1387 | tre_char_t buf[64]; | ||
| 1388 | tre_expand_macro(ctx->re + 1, ctx->re_end, | ||
| 1389 | buf, elementsof(buf)); | ||
| 1390 | if (buf[0] != 0) | ||
| 1391 | { | ||
| 1392 | tre_parse_ctx_t subctx; | ||
| 1393 | memcpy(&subctx, ctx, sizeof(subctx)); | ||
| 1394 | subctx.re = buf; | ||
| 1395 | subctx.len = tre_strlen(buf); | ||
| 1396 | subctx.nofirstsub = 1; | ||
| 1397 | status = tre_parse(&subctx); | ||
| 1398 | if (status != REG_OK) | ||
| 1399 | return status; | ||
| 1400 | ctx->re += 2; | ||
| 1401 | ctx->position = subctx.position; | ||
| 1402 | result = subctx.result; | ||
| 1403 | break; | ||
| 1404 | } | ||
| 1405 | } | ||
| 1406 | |||
| 1407 | if (ctx->re + 1 >= ctx->re_end) | ||
| 1408 | /* Trailing backslash. */ | ||
| 1409 | return REG_EESCAPE; | ||
| 1410 | |||
| 1411 | #ifdef REG_LITERAL | ||
| 1412 | if (*(ctx->re + 1) == L'Q') | ||
| 1413 | { | ||
| 1414 | DPRINT(("tre_parse: tmp literal: '%.*" STRF "'\n", | ||
| 1415 | REST(ctx->re))); | ||
| 1416 | ctx->cflags |= REG_LITERAL; | ||
| 1417 | temporary_cflags |= REG_LITERAL; | ||
| 1418 | ctx->re += 2; | ||
| 1419 | STACK_PUSHX(stack, int, PARSE_ATOM); | ||
| 1420 | break; | ||
| 1421 | } | ||
| 1422 | #endif /* REG_LITERAL */ | ||
| 1423 | |||
| 1424 | DPRINT(("tre_parse: bleep: '%.*" STRF "'\n", REST(ctx->re))); | ||
| 1425 | ctx->re++; | ||
| 1426 | switch (*ctx->re) | ||
| 1427 | { | ||
| 1428 | case L'b': | ||
| 1429 | result = tre_ast_new_literal(ctx->mem, ASSERTION, | ||
| 1430 | ASSERT_AT_WB, -1); | ||
| 1431 | ctx->re++; | ||
| 1432 | break; | ||
| 1433 | case L'B': | ||
| 1434 | result = tre_ast_new_literal(ctx->mem, ASSERTION, | ||
| 1435 | ASSERT_AT_WB_NEG, -1); | ||
| 1436 | ctx->re++; | ||
| 1437 | break; | ||
| 1438 | case L'<': | ||
| 1439 | result = tre_ast_new_literal(ctx->mem, ASSERTION, | ||
| 1440 | ASSERT_AT_BOW, -1); | ||
| 1441 | ctx->re++; | ||
| 1442 | break; | ||
| 1443 | case L'>': | ||
| 1444 | result = tre_ast_new_literal(ctx->mem, ASSERTION, | ||
| 1445 | ASSERT_AT_EOW, -1); | ||
| 1446 | ctx->re++; | ||
| 1447 | break; | ||
| 1448 | case L'x': | ||
| 1449 | ctx->re++; | ||
| 1450 | if (ctx->re[0] != CHAR_LBRACE && ctx->re < ctx->re_end) | ||
| 1451 | { | ||
| 1452 | /* 8 bit hex char. */ | ||
| 1453 | char tmp[3] = {0, 0, 0}; | ||
| 1454 | long val; | ||
| 1455 | DPRINT(("tre_parse: 8 bit hex: '%.*" STRF "'\n", | ||
| 1456 | REST(ctx->re - 2))); | ||
| 1457 | |||
| 1458 | if (tre_isxdigit(ctx->re[0]) && ctx->re < ctx->re_end) | ||
| 1459 | { | ||
| 1460 | tmp[0] = (char)ctx->re[0]; | ||
| 1461 | ctx->re++; | ||
| 1462 | } | ||
| 1463 | if (tre_isxdigit(ctx->re[0]) && ctx->re < ctx->re_end) | ||
| 1464 | { | ||
| 1465 | tmp[1] = (char)ctx->re[0]; | ||
| 1466 | ctx->re++; | ||
| 1467 | } | ||
| 1468 | val = strtol(tmp, NULL, 16); | ||
| 1469 | result = tre_ast_new_literal(ctx->mem, (int)val, | ||
| 1470 | (int)val, ctx->position); | ||
| 1471 | ctx->position++; | ||
| 1472 | break; | ||
| 1473 | } | ||
| 1474 | else if (ctx->re < ctx->re_end) | ||
| 1475 | { | ||
| 1476 | /* Wide char. */ | ||
| 1477 | char tmp[32]; | ||
| 1478 | long val; | ||
| 1479 | int i = 0; | ||
| 1480 | ctx->re++; | ||
| 1481 | while (ctx->re_end - ctx->re >= 0) | ||
| 1482 | { | ||
| 1483 | if (ctx->re[0] == CHAR_RBRACE) | ||
| 1484 | break; | ||
| 1485 | if (tre_isxdigit(ctx->re[0])) | ||
| 1486 | { | ||
| 1487 | tmp[i] = (char)ctx->re[0]; | ||
| 1488 | i++; | ||
| 1489 | ctx->re++; | ||
| 1490 | continue; | ||
| 1491 | } | ||
| 1492 | return REG_EBRACE; | ||
| 1493 | } | ||
| 1494 | ctx->re++; | ||
| 1495 | tmp[i] = 0; | ||
| 1496 | val = strtol(tmp, NULL, 16); | ||
| 1497 | result = tre_ast_new_literal(ctx->mem, (int)val, (int)val, | ||
| 1498 | ctx->position); | ||
| 1499 | ctx->position++; | ||
| 1500 | break; | ||
| 1501 | } | ||
| 1502 | /*FALLTHROUGH*/ | ||
| 1503 | |||
| 1504 | default: | ||
| 1505 | if (tre_isdigit(*ctx->re)) | ||
| 1506 | { | ||
| 1507 | /* Back reference. */ | ||
| 1508 | int val = *ctx->re - L'0'; | ||
| 1509 | DPRINT(("tre_parse: backref: '%.*" STRF "'\n", | ||
| 1510 | REST(ctx->re - 1))); | ||
| 1511 | result = tre_ast_new_literal(ctx->mem, BACKREF, val, | ||
| 1512 | ctx->position); | ||
| 1513 | if (result == NULL) | ||
| 1514 | return REG_ESPACE; | ||
| 1515 | ctx->position++; | ||
| 1516 | ctx->max_backref = MAX(val, ctx->max_backref); | ||
| 1517 | ctx->re++; | ||
| 1518 | } | ||
| 1519 | else | ||
| 1520 | { | ||
| 1521 | /* Escaped character. */ | ||
| 1522 | DPRINT(("tre_parse: escaped: '%.*" STRF "'\n", | ||
| 1523 | REST(ctx->re - 1))); | ||
| 1524 | result = tre_ast_new_literal(ctx->mem, *ctx->re, *ctx->re, | ||
| 1525 | ctx->position); | ||
| 1526 | ctx->position++; | ||
| 1527 | ctx->re++; | ||
| 1528 | } | ||
| 1529 | break; | ||
| 1530 | } | ||
| 1531 | if (result == NULL) | ||
| 1532 | return REG_ESPACE; | ||
| 1533 | break; | ||
| 1534 | |||
| 1535 | case CHAR_PERIOD: /* the any-symbol */ | ||
| 1536 | DPRINT(("tre_parse: any: '%.*" STRF "'\n", | ||
| 1537 | REST(ctx->re))); | ||
| 1538 | if (ctx->cflags & REG_NEWLINE) | ||
| 1539 | { | ||
| 1540 | tre_ast_node_t *tmp1; | ||
| 1541 | tre_ast_node_t *tmp2; | ||
| 1542 | tmp1 = tre_ast_new_literal(ctx->mem, 0, L'\n' - 1, | ||
| 1543 | ctx->position); | ||
| 1544 | if (!tmp1) | ||
| 1545 | return REG_ESPACE; | ||
| 1546 | tmp2 = tre_ast_new_literal(ctx->mem, L'\n' + 1, TRE_CHAR_MAX, | ||
| 1547 | ctx->position + 1); | ||
| 1548 | if (!tmp2) | ||
| 1549 | return REG_ESPACE; | ||
| 1550 | result = tre_ast_new_union(ctx->mem, tmp1, tmp2); | ||
| 1551 | if (!result) | ||
| 1552 | return REG_ESPACE; | ||
| 1553 | ctx->position += 2; | ||
| 1554 | } | ||
| 1555 | else | ||
| 1556 | { | ||
| 1557 | result = tre_ast_new_literal(ctx->mem, 0, TRE_CHAR_MAX, | ||
| 1558 | ctx->position); | ||
| 1559 | if (!result) | ||
| 1560 | return REG_ESPACE; | ||
| 1561 | ctx->position++; | ||
| 1562 | } | ||
| 1563 | ctx->re++; | ||
| 1564 | break; | ||
| 1565 | |||
| 1566 | case CHAR_CARET: /* beginning of line assertion */ | ||
| 1567 | /* '^' has a special meaning everywhere in EREs, and in the | ||
| 1568 | beginning of the RE and after \( is BREs. */ | ||
| 1569 | if (ctx->cflags & REG_EXTENDED | ||
| 1570 | || (ctx->re - 2 >= ctx->re_start | ||
| 1571 | && *(ctx->re - 2) == CHAR_BACKSLASH | ||
| 1572 | && *(ctx->re - 1) == CHAR_LPAREN) | ||
| 1573 | || ctx->re == ctx->re_start) | ||
| 1574 | { | ||
| 1575 | DPRINT(("tre_parse: BOL: '%.*" STRF "'\n", | ||
| 1576 | REST(ctx->re))); | ||
| 1577 | result = tre_ast_new_literal(ctx->mem, ASSERTION, | ||
| 1578 | ASSERT_AT_BOL, -1); | ||
| 1579 | if (result == NULL) | ||
| 1580 | return REG_ESPACE; | ||
| 1581 | ctx->re++; | ||
| 1582 | } | ||
| 1583 | else | ||
| 1584 | goto parse_literal; | ||
| 1585 | break; | ||
| 1586 | |||
| 1587 | case CHAR_DOLLAR: /* end of line assertion. */ | ||
| 1588 | /* '$' is special everywhere in EREs, and in the end of the | ||
| 1589 | string and before \) is BREs. */ | ||
| 1590 | if (ctx->cflags & REG_EXTENDED | ||
| 1591 | || (ctx->re + 2 < ctx->re_end | ||
| 1592 | && *(ctx->re + 1) == CHAR_BACKSLASH | ||
| 1593 | && *(ctx->re + 2) == CHAR_RPAREN) | ||
| 1594 | || ctx->re + 1 == ctx->re_end) | ||
| 1595 | { | ||
| 1596 | DPRINT(("tre_parse: EOL: '%.*" STRF "'\n", | ||
| 1597 | REST(ctx->re))); | ||
| 1598 | result = tre_ast_new_literal(ctx->mem, ASSERTION, | ||
| 1599 | ASSERT_AT_EOL, -1); | ||
| 1600 | if (result == NULL) | ||
| 1601 | return REG_ESPACE; | ||
| 1602 | ctx->re++; | ||
| 1603 | } | ||
| 1604 | else | ||
| 1605 | goto parse_literal; | ||
| 1606 | break; | ||
| 1607 | |||
| 1608 | default: | ||
| 1609 | parse_literal: | ||
| 1610 | |||
| 1611 | if (temporary_cflags && ctx->re + 1 < ctx->re_end | ||
| 1612 | && *ctx->re == CHAR_BACKSLASH && *(ctx->re + 1) == L'E') | ||
| 1613 | { | ||
| 1614 | DPRINT(("tre_parse: end tmps: '%.*" STRF "'\n", | ||
| 1615 | REST(ctx->re))); | ||
| 1616 | ctx->cflags &= ~temporary_cflags; | ||
| 1617 | temporary_cflags = 0; | ||
| 1618 | ctx->re += 2; | ||
| 1619 | STACK_PUSHX(stack, int, PARSE_PIECE); | ||
| 1620 | break; | ||
| 1621 | } | ||
| 1622 | |||
| 1623 | |||
| 1624 | /* We are expecting an atom. If the subexpression (or the whole | ||
| 1625 | regexp ends here, we interpret it as an empty expression | ||
| 1626 | (which matches an empty string). */ | ||
| 1627 | if ( | ||
| 1628 | #ifdef REG_LITERAL | ||
| 1629 | !(ctx->cflags & REG_LITERAL) && | ||
| 1630 | #endif /* REG_LITERAL */ | ||
| 1631 | (ctx->re >= ctx->re_end | ||
| 1632 | || *ctx->re == CHAR_STAR | ||
| 1633 | || (ctx->cflags & REG_EXTENDED | ||
| 1634 | && (*ctx->re == CHAR_PIPE | ||
| 1635 | || *ctx->re == CHAR_LBRACE | ||
| 1636 | || *ctx->re == CHAR_PLUS | ||
| 1637 | || *ctx->re == CHAR_QUESTIONMARK)) | ||
| 1638 | /* Test for "\)" in BRE mode. */ | ||
| 1639 | || (!(ctx->cflags & REG_EXTENDED) | ||
| 1640 | && ctx->re + 1 < ctx->re_end | ||
| 1641 | && *ctx->re == CHAR_BACKSLASH | ||
| 1642 | && *(ctx->re + 1) == CHAR_LBRACE))) | ||
| 1643 | { | ||
| 1644 | DPRINT(("tre_parse: empty: '%.*" STRF "'\n", | ||
| 1645 | REST(ctx->re))); | ||
| 1646 | result = tre_ast_new_literal(ctx->mem, EMPTY, -1, -1); | ||
| 1647 | if (!result) | ||
| 1648 | return REG_ESPACE; | ||
| 1649 | break; | ||
| 1650 | } | ||
| 1651 | |||
| 1652 | DPRINT(("tre_parse: literal: '%.*" STRF "'\n", | ||
| 1653 | REST(ctx->re))); | ||
| 1654 | /* Note that we can't use an tre_isalpha() test here, since there | ||
| 1655 | may be characters which are alphabetic but neither upper or | ||
| 1656 | lower case. */ | ||
| 1657 | if (ctx->cflags & REG_ICASE | ||
| 1658 | && (tre_isupper(*ctx->re) || tre_islower(*ctx->re))) | ||
| 1659 | { | ||
| 1660 | tre_ast_node_t *tmp1; | ||
| 1661 | tre_ast_node_t *tmp2; | ||
| 1662 | |||
| 1663 | /* XXX - Can there be more than one opposite-case | ||
| 1664 | counterpoints for some character in some locale? Or | ||
| 1665 | more than two characters which all should be regarded | ||
| 1666 | the same character if case is ignored? If yes, there | ||
| 1667 | does not seem to be a portable way to detect it. I guess | ||
| 1668 | that at least for multi-character collating elements there | ||
| 1669 | could be several opposite-case counterpoints, but they | ||
| 1670 | cannot be supported portably anyway. */ | ||
| 1671 | tmp1 = tre_ast_new_literal(ctx->mem, tre_toupper(*ctx->re), | ||
| 1672 | tre_toupper(*ctx->re), | ||
| 1673 | ctx->position); | ||
| 1674 | if (!tmp1) | ||
| 1675 | return REG_ESPACE; | ||
| 1676 | tmp2 = tre_ast_new_literal(ctx->mem, tre_tolower(*ctx->re), | ||
| 1677 | tre_tolower(*ctx->re), | ||
| 1678 | ctx->position); | ||
| 1679 | if (!tmp2) | ||
| 1680 | return REG_ESPACE; | ||
| 1681 | result = tre_ast_new_union(ctx->mem, tmp1, tmp2); | ||
| 1682 | if (!result) | ||
| 1683 | return REG_ESPACE; | ||
| 1684 | } | ||
| 1685 | else | ||
| 1686 | { | ||
| 1687 | result = tre_ast_new_literal(ctx->mem, *ctx->re, *ctx->re, | ||
| 1688 | ctx->position); | ||
| 1689 | if (!result) | ||
| 1690 | return REG_ESPACE; | ||
| 1691 | } | ||
| 1692 | ctx->position++; | ||
| 1693 | ctx->re++; | ||
| 1694 | break; | ||
| 1695 | } | ||
| 1696 | break; | ||
| 1697 | |||
| 1698 | case PARSE_MARK_FOR_SUBMATCH: | ||
| 1699 | { | ||
| 1700 | int submatch_id = tre_stack_pop_int(stack); | ||
| 1701 | |||
| 1702 | if (result->submatch_id >= 0) | ||
| 1703 | { | ||
| 1704 | tre_ast_node_t *n, *tmp_node; | ||
| 1705 | n = tre_ast_new_literal(ctx->mem, EMPTY, -1, -1); | ||
| 1706 | if (n == NULL) | ||
| 1707 | return REG_ESPACE; | ||
| 1708 | tmp_node = tre_ast_new_catenation(ctx->mem, n, result); | ||
| 1709 | if (tmp_node == NULL) | ||
| 1710 | return REG_ESPACE; | ||
| 1711 | tmp_node->num_submatches = result->num_submatches; | ||
| 1712 | result = tmp_node; | ||
| 1713 | } | ||
| 1714 | result->submatch_id = submatch_id; | ||
| 1715 | result->num_submatches++; | ||
| 1716 | break; | ||
| 1717 | } | ||
| 1718 | |||
| 1719 | case PARSE_RESTORE_CFLAGS: | ||
| 1720 | ctx->cflags = tre_stack_pop_int(stack); | ||
| 1721 | break; | ||
| 1722 | |||
| 1723 | default: | ||
| 1724 | assert(0); | ||
| 1725 | break; | ||
| 1726 | } | ||
| 1727 | } | ||
| 1728 | |||
| 1729 | /* Check for missing closing parentheses. */ | ||
| 1730 | if (depth > 0) | ||
| 1731 | return REG_EPAREN; | ||
| 1732 | |||
| 1733 | if (status == REG_OK) | ||
| 1734 | ctx->result = result; | ||
| 1735 | |||
| 1736 | return status; | ||
| 1737 | } | ||
| 1738 | |||
| 1739 | /* EOF */ | ||
| @@ -1,50 +0,0 @@ | |||
| 1 | /* | ||
| 2 | tre-parse.c - Regexp parser definitions | ||
| 3 | |||
| 4 | This software is released under a BSD-style license. | ||
| 5 | See the file LICENSE for details and copyright. | ||
| 6 | |||
| 7 | */ | ||
| 8 | |||
| 9 | #ifndef TRE_PARSE_H | ||
| 10 | #define TRE_PARSE_H 1 | ||
| 11 | |||
| 12 | /* Parse context. */ | ||
| 13 | typedef struct { | ||
| 14 | /* Memory allocator. The AST is allocated using this. */ | ||
| 15 | tre_mem_t mem; | ||
| 16 | /* Stack used for keeping track of regexp syntax. */ | ||
| 17 | tre_stack_t *stack; | ||
| 18 | /* The parse result. */ | ||
| 19 | tre_ast_node_t *result; | ||
| 20 | /* The regexp to parse and its length. */ | ||
| 21 | const tre_char_t *re; | ||
| 22 | /* The first character of the entire regexp. */ | ||
| 23 | const tre_char_t *re_start; | ||
| 24 | /* The first character after the end of the regexp. */ | ||
| 25 | const tre_char_t *re_end; | ||
| 26 | int len; | ||
| 27 | /* Current submatch ID. */ | ||
| 28 | int submatch_id; | ||
| 29 | /* Current position (number of literal). */ | ||
| 30 | int position; | ||
| 31 | /* The highest back reference or -1 if none seen so far. */ | ||
| 32 | int max_backref; | ||
| 33 | /* This flag is set if the regexp uses approximate matching. */ | ||
| 34 | int have_approx; | ||
| 35 | /* Compilation flags. */ | ||
| 36 | int cflags; | ||
| 37 | /* If this flag is set the top-level submatch is not captured. */ | ||
| 38 | int nofirstsub; | ||
| 39 | /* The currently set approximate matching parameters. */ | ||
| 40 | int params[TRE_PARAM_LAST]; | ||
| 41 | } tre_parse_ctx_t; | ||
| 42 | |||
| 43 | /* Parses a wide character regexp pattern into a syntax tree. This parser | ||
| 44 | handles both syntaxes (BRE and ERE), including the TRE extensions. */ | ||
| 45 | reg_errcode_t | ||
| 46 | tre_parse(tre_parse_ctx_t *ctx); | ||
| 47 | |||
| 48 | #endif /* TRE_PARSE_H */ | ||
| 49 | |||
| 50 | /* EOF */ | ||
| @@ -1,125 +0,0 @@ | |||
| 1 | /* | ||
| 2 | tre-stack.c - Simple stack implementation | ||
| 3 | |||
| 4 | This software is released under a BSD-style license. | ||
| 5 | See the file LICENSE for details and copyright. | ||
| 6 | |||
| 7 | */ | ||
| 8 | |||
| 9 | #ifdef HAVE_CONFIG_H | ||
| 10 | #include <config.h> | ||
| 11 | #endif /* HAVE_CONFIG_H */ | ||
| 12 | #include <stdlib.h> | ||
| 13 | #include <assert.h> | ||
| 14 | |||
| 15 | #include "tre-internal.h" | ||
| 16 | #include "tre-stack.h" | ||
| 17 | #include "xmalloc.h" | ||
| 18 | |||
| 19 | union tre_stack_item { | ||
| 20 | void *voidptr_value; | ||
| 21 | int int_value; | ||
| 22 | }; | ||
| 23 | |||
| 24 | struct tre_stack_rec { | ||
| 25 | int size; | ||
| 26 | int max_size; | ||
| 27 | int increment; | ||
| 28 | int ptr; | ||
| 29 | union tre_stack_item *stack; | ||
| 30 | }; | ||
| 31 | |||
| 32 | |||
| 33 | tre_stack_t * | ||
| 34 | tre_stack_new(int size, int max_size, int increment) | ||
| 35 | { | ||
| 36 | tre_stack_t *s; | ||
| 37 | |||
| 38 | s = xmalloc(sizeof(*s)); | ||
| 39 | if (s != NULL) | ||
| 40 | { | ||
| 41 | s->stack = xmalloc(sizeof(*s->stack) * size); | ||
| 42 | if (s->stack == NULL) | ||
| 43 | { | ||
| 44 | xfree(s); | ||
| 45 | return NULL; | ||
| 46 | } | ||
| 47 | s->size = size; | ||
| 48 | s->max_size = max_size; | ||
| 49 | s->increment = increment; | ||
| 50 | s->ptr = 0; | ||
| 51 | } | ||
| 52 | return s; | ||
| 53 | } | ||
| 54 | |||
| 55 | void | ||
| 56 | tre_stack_destroy(tre_stack_t *s) | ||
| 57 | { | ||
| 58 | xfree(s->stack); | ||
| 59 | xfree(s); | ||
| 60 | } | ||
| 61 | |||
| 62 | int | ||
| 63 | tre_stack_num_objects(tre_stack_t *s) | ||
| 64 | { | ||
| 65 | return s->ptr; | ||
| 66 | } | ||
| 67 | |||
| 68 | static reg_errcode_t | ||
| 69 | tre_stack_push(tre_stack_t *s, union tre_stack_item value) | ||
| 70 | { | ||
| 71 | if (s->ptr < s->size) | ||
| 72 | { | ||
| 73 | s->stack[s->ptr] = value; | ||
| 74 | s->ptr++; | ||
| 75 | } | ||
| 76 | else | ||
| 77 | { | ||
| 78 | if (s->size >= s->max_size) | ||
| 79 | { | ||
| 80 | DPRINT(("tre_stack_push: stack full\n")); | ||
| 81 | return REG_ESPACE; | ||
| 82 | } | ||
| 83 | else | ||
| 84 | { | ||
| 85 | union tre_stack_item *new_buffer; | ||
| 86 | int new_size; | ||
| 87 | DPRINT(("tre_stack_push: trying to realloc more space\n")); | ||
| 88 | new_size = s->size + s->increment; | ||
| 89 | if (new_size > s->max_size) | ||
| 90 | new_size = s->max_size; | ||
| 91 | new_buffer = xrealloc(s->stack, sizeof(*new_buffer) * new_size); | ||
| 92 | if (new_buffer == NULL) | ||
| 93 | { | ||
| 94 | DPRINT(("tre_stack_push: realloc failed.\n")); | ||
| 95 | return REG_ESPACE; | ||
| 96 | } | ||
| 97 | DPRINT(("tre_stack_push: realloc succeeded.\n")); | ||
| 98 | assert(new_size > s->size); | ||
| 99 | s->size = new_size; | ||
| 100 | s->stack = new_buffer; | ||
| 101 | tre_stack_push(s, value); | ||
| 102 | } | ||
| 103 | } | ||
| 104 | return REG_OK; | ||
| 105 | } | ||
| 106 | |||
| 107 | #define define_pushf(typetag, type) \ | ||
| 108 | declare_pushf(typetag, type) { \ | ||
| 109 | union tre_stack_item item; \ | ||
| 110 | item.typetag ## _value = value; \ | ||
| 111 | return tre_stack_push(s, item); \ | ||
| 112 | } | ||
| 113 | |||
| 114 | define_pushf(int, int) | ||
| 115 | define_pushf(voidptr, void *) | ||
| 116 | |||
| 117 | #define define_popf(typetag, type) \ | ||
| 118 | declare_popf(typetag, type) { \ | ||
| 119 | return s->stack[--s->ptr].typetag ## _value; \ | ||
| 120 | } | ||
| 121 | |||
| 122 | define_popf(int, int) | ||
| 123 | define_popf(voidptr, void *) | ||
| 124 | |||
| 125 | /* EOF */ | ||
| @@ -1,76 +0,0 @@ | |||
| 1 | /* | ||
| 2 | tre-stack.h: Stack definitions | ||
| 3 | |||
| 4 | This software is released under a BSD-style license. | ||
| 5 | See the file LICENSE for details and copyright. | ||
| 6 | |||
| 7 | */ | ||
| 8 | |||
| 9 | |||
| 10 | #ifndef TRE_STACK_H | ||
| 11 | #define TRE_STACK_H 1 | ||
| 12 | |||
| 13 | #include "tre.h" | ||
| 14 | |||
| 15 | typedef struct tre_stack_rec tre_stack_t; | ||
| 16 | |||
| 17 | /* Creates a new stack object. `size' is initial size in bytes, `max_size' | ||
| 18 | is maximum size, and `increment' specifies how much more space will be | ||
| 19 | allocated with realloc() if all space gets used up. Returns the stack | ||
| 20 | object or NULL if out of memory. */ | ||
| 21 | tre_stack_t * | ||
| 22 | tre_stack_new(int size, int max_size, int increment); | ||
| 23 | |||
| 24 | /* Frees the stack object. */ | ||
| 25 | void | ||
| 26 | tre_stack_destroy(tre_stack_t *s); | ||
| 27 | |||
| 28 | /* Returns the current number of objects in the stack. */ | ||
| 29 | int | ||
| 30 | tre_stack_num_objects(tre_stack_t *s); | ||
| 31 | |||
| 32 | /* Each tre_stack_push_*(tre_stack_t *s, <type> value) function pushes | ||
| 33 | `value' on top of stack `s'. Returns REG_ESPACE if out of memory. | ||
| 34 | This tries to realloc() more space before failing if maximum size | ||
| 35 | has not yet been reached. Returns REG_OK if successful. */ | ||
| 36 | #define declare_pushf(typetag, type) \ | ||
| 37 | reg_errcode_t tre_stack_push_ ## typetag(tre_stack_t *s, type value) | ||
| 38 | |||
| 39 | declare_pushf(voidptr, void *); | ||
| 40 | declare_pushf(int, int); | ||
| 41 | |||
| 42 | /* Each tre_stack_pop_*(tre_stack_t *s) function pops the topmost | ||
| 43 | element off of stack `s' and returns it. The stack must not be | ||
| 44 | empty. */ | ||
| 45 | #define declare_popf(typetag, type) \ | ||
| 46 | type tre_stack_pop_ ## typetag(tre_stack_t *s) | ||
| 47 | |||
| 48 | declare_popf(voidptr, void *); | ||
| 49 | declare_popf(int, int); | ||
| 50 | |||
| 51 | /* Just to save some typing. */ | ||
| 52 | #define STACK_PUSH(s, typetag, value) \ | ||
| 53 | do \ | ||
| 54 | { \ | ||
| 55 | status = tre_stack_push_ ## typetag(s, value); \ | ||
| 56 | } \ | ||
| 57 | while (/*CONSTCOND*/0) | ||
| 58 | |||
| 59 | #define STACK_PUSHX(s, typetag, value) \ | ||
| 60 | { \ | ||
| 61 | status = tre_stack_push_ ## typetag(s, value); \ | ||
| 62 | if (status != REG_OK) \ | ||
| 63 | break; \ | ||
| 64 | } | ||
| 65 | |||
| 66 | #define STACK_PUSHR(s, typetag, value) \ | ||
| 67 | { \ | ||
| 68 | reg_errcode_t _status; \ | ||
| 69 | _status = tre_stack_push_ ## typetag(s, value); \ | ||
| 70 | if (_status != REG_OK) \ | ||
| 71 | return _status; \ | ||
| 72 | } | ||
| 73 | |||
| 74 | #endif /* TRE_STACK_H */ | ||
| 75 | |||
| 76 | /* EOF */ | ||
| @@ -1,276 +0,0 @@ | |||
| 1 | /* | ||
| 2 | tre.h - TRE public API definitions | ||
| 3 | |||
| 4 | This software is released under a BSD-style license. | ||
| 5 | See the file LICENSE for details and copyright. | ||
| 6 | |||
| 7 | */ | ||
| 8 | |||
| 9 | #ifndef TRE_H | ||
| 10 | #define TRE_H 1 | ||
| 11 | |||
| 12 | #include "tre-config.h" | ||
| 13 | |||
| 14 | #ifdef HAVE_SYS_TYPES_H | ||
| 15 | #include <sys/types.h> | ||
| 16 | #endif /* HAVE_SYS_TYPES_H */ | ||
| 17 | |||
| 18 | #ifdef HAVE_LIBUTF8_H | ||
| 19 | #include <libutf8.h> | ||
| 20 | #endif /* HAVE_LIBUTF8_H */ | ||
| 21 | |||
| 22 | #ifdef TRE_USE_SYSTEM_REGEX_H | ||
| 23 | /* Include the system regex.h to make TRE ABI compatible with the | ||
| 24 | system regex. */ | ||
| 25 | #include TRE_SYSTEM_REGEX_H_PATH | ||
| 26 | #define tre_regcomp regcomp | ||
| 27 | #define tre_regexec regexec | ||
| 28 | #define tre_regerror regerror | ||
| 29 | #define tre_regfree regfree | ||
| 30 | #endif /* TRE_USE_SYSTEM_REGEX_H */ | ||
| 31 | |||
| 32 | #ifdef __cplusplus | ||
| 33 | extern "C" { | ||
| 34 | #endif | ||
| 35 | |||
| 36 | #ifdef TRE_USE_SYSTEM_REGEX_H | ||
| 37 | |||
| 38 | #ifndef REG_OK | ||
| 39 | #define REG_OK 0 | ||
| 40 | #endif /* !REG_OK */ | ||
| 41 | |||
| 42 | #ifndef HAVE_REG_ERRCODE_T | ||
| 43 | typedef int reg_errcode_t; | ||
| 44 | #endif /* !HAVE_REG_ERRCODE_T */ | ||
| 45 | |||
| 46 | #if !defined(REG_NOSPEC) && !defined(REG_LITERAL) | ||
| 47 | #define REG_LITERAL 0x1000 | ||
| 48 | #endif | ||
| 49 | |||
| 50 | /* Extra tre_regcomp() flags. */ | ||
| 51 | #ifndef REG_BASIC | ||
| 52 | #define REG_BASIC 0 | ||
| 53 | #endif /* !REG_BASIC */ | ||
| 54 | #define REG_RIGHT_ASSOC (REG_LITERAL << 1) | ||
| 55 | #define REG_UNGREEDY (REG_RIGHT_ASSOC << 1) | ||
| 56 | |||
| 57 | /* Extra tre_regexec() flags. */ | ||
| 58 | #define REG_APPROX_MATCHER 0x1000 | ||
| 59 | #define REG_BACKTRACKING_MATCHER (REG_APPROX_MATCHER << 1) | ||
| 60 | |||
| 61 | #else /* !TRE_USE_SYSTEM_REGEX_H */ | ||
| 62 | |||
| 63 | /* If the we're not using system regex.h, we need to define the | ||
| 64 | structs and enums ourselves. */ | ||
| 65 | |||
| 66 | typedef int regoff_t; | ||
| 67 | typedef struct { | ||
| 68 | size_t re_nsub; /* Number of parenthesized subexpressions. */ | ||
| 69 | void *value; /* For internal use only. */ | ||
| 70 | } regex_t; | ||
| 71 | |||
| 72 | typedef struct { | ||
| 73 | regoff_t rm_so; | ||
| 74 | regoff_t rm_eo; | ||
| 75 | } regmatch_t; | ||
| 76 | |||
| 77 | |||
| 78 | typedef enum { | ||
| 79 | REG_OK = 0, /* No error. */ | ||
| 80 | /* POSIX tre_regcomp() return error codes. (In the order listed in the | ||
| 81 | standard.) */ | ||
| 82 | REG_NOMATCH, /* No match. */ | ||
| 83 | REG_BADPAT, /* Invalid regexp. */ | ||
| 84 | REG_ECOLLATE, /* Unknown collating element. */ | ||
| 85 | REG_ECTYPE, /* Unknown character class name. */ | ||
| 86 | REG_EESCAPE, /* Trailing backslash. */ | ||
| 87 | REG_ESUBREG, /* Invalid back reference. */ | ||
| 88 | REG_EBRACK, /* "[]" imbalance */ | ||
| 89 | REG_EPAREN, /* "\(\)" or "()" imbalance */ | ||
| 90 | REG_EBRACE, /* "\{\}" or "{}" imbalance */ | ||
| 91 | REG_BADBR, /* Invalid content of {} */ | ||
| 92 | REG_ERANGE, /* Invalid use of range operator */ | ||
| 93 | REG_ESPACE, /* Out of memory. */ | ||
| 94 | REG_BADRPT /* Invalid use of repetition operators. */ | ||
| 95 | } reg_errcode_t; | ||
| 96 | |||
| 97 | /* POSIX tre_regcomp() flags. */ | ||
| 98 | #define REG_EXTENDED 1 | ||
| 99 | #define REG_ICASE (REG_EXTENDED << 1) | ||
| 100 | #define REG_NEWLINE (REG_ICASE << 1) | ||
| 101 | #define REG_NOSUB (REG_NEWLINE << 1) | ||
| 102 | |||
| 103 | /* Extra tre_regcomp() flags. */ | ||
| 104 | #define REG_BASIC 0 | ||
| 105 | #define REG_LITERAL (REG_NOSUB << 1) | ||
| 106 | #define REG_RIGHT_ASSOC (REG_LITERAL << 1) | ||
| 107 | #define REG_UNGREEDY (REG_RIGHT_ASSOC << 1) | ||
| 108 | |||
| 109 | /* POSIX tre_regexec() flags. */ | ||
| 110 | #define REG_NOTBOL 1 | ||
| 111 | #define REG_NOTEOL (REG_NOTBOL << 1) | ||
| 112 | |||
| 113 | /* Extra tre_regexec() flags. */ | ||
| 114 | #define REG_APPROX_MATCHER (REG_NOTEOL << 1) | ||
| 115 | #define REG_BACKTRACKING_MATCHER (REG_APPROX_MATCHER << 1) | ||
| 116 | |||
| 117 | #endif /* !TRE_USE_SYSTEM_REGEX_H */ | ||
| 118 | |||
| 119 | /* REG_NOSPEC and REG_LITERAL mean the same thing. */ | ||
| 120 | #if defined(REG_LITERAL) && !defined(REG_NOSPEC) | ||
| 121 | #define REG_NOSPEC REG_LITERAL | ||
| 122 | #elif defined(REG_NOSPEC) && !defined(REG_LITERAL) | ||
| 123 | #define REG_LITERAL REG_NOSPEC | ||
| 124 | #endif /* defined(REG_NOSPEC) */ | ||
| 125 | |||
| 126 | /* The maximum number of iterations in a bound expression. */ | ||
| 127 | #undef RE_DUP_MAX | ||
| 128 | #define RE_DUP_MAX 255 | ||
| 129 | |||
| 130 | /* The POSIX.2 regexp functions */ | ||
| 131 | extern int | ||
| 132 | tre_regcomp(regex_t *preg, const char *regex, int cflags); | ||
| 133 | |||
| 134 | extern int | ||
| 135 | tre_regexec(const regex_t *preg, const char *string, size_t nmatch, | ||
| 136 | regmatch_t pmatch[], int eflags); | ||
| 137 | |||
| 138 | extern size_t | ||
| 139 | tre_regerror(int errcode, const regex_t *preg, char *errbuf, | ||
| 140 | size_t errbuf_size); | ||
| 141 | |||
| 142 | extern void | ||
| 143 | tre_regfree(regex_t *preg); | ||
| 144 | |||
| 145 | #ifdef TRE_WCHAR | ||
| 146 | #ifdef HAVE_WCHAR_H | ||
| 147 | #include <wchar.h> | ||
| 148 | #endif /* HAVE_WCHAR_H */ | ||
| 149 | |||
| 150 | /* Wide character versions (not in POSIX.2). */ | ||
| 151 | extern int | ||
| 152 | tre_regwcomp(regex_t *preg, const wchar_t *regex, int cflags); | ||
| 153 | |||
| 154 | extern int | ||
| 155 | tre_regwexec(const regex_t *preg, const wchar_t *string, | ||
| 156 | size_t nmatch, regmatch_t pmatch[], int eflags); | ||
| 157 | #endif /* TRE_WCHAR */ | ||
| 158 | |||
| 159 | /* Versions with a maximum length argument and therefore the capability to | ||
| 160 | handle null characters in the middle of the strings (not in POSIX.2). */ | ||
| 161 | extern int | ||
| 162 | tre_regncomp(regex_t *preg, const char *regex, size_t len, int cflags); | ||
| 163 | |||
| 164 | extern int | ||
| 165 | tre_regnexec(const regex_t *preg, const char *string, size_t len, | ||
| 166 | size_t nmatch, regmatch_t pmatch[], int eflags); | ||
| 167 | |||
| 168 | #ifdef TRE_WCHAR | ||
| 169 | extern int | ||
| 170 | tre_regwncomp(regex_t *preg, const wchar_t *regex, size_t len, int cflags); | ||
| 171 | |||
| 172 | extern int | ||
| 173 | tre_regwnexec(const regex_t *preg, const wchar_t *string, size_t len, | ||
| 174 | size_t nmatch, regmatch_t pmatch[], int eflags); | ||
| 175 | #endif /* TRE_WCHAR */ | ||
| 176 | |||
| 177 | #ifdef TRE_APPROX | ||
| 178 | |||
| 179 | /* Approximate matching parameter struct. */ | ||
| 180 | typedef struct { | ||
| 181 | int cost_ins; /* Default cost of an inserted character. */ | ||
| 182 | int cost_del; /* Default cost of a deleted character. */ | ||
| 183 | int cost_subst; /* Default cost of a substituted character. */ | ||
| 184 | int max_cost; /* Maximum allowed cost of a match. */ | ||
| 185 | |||
| 186 | int max_ins; /* Maximum allowed number of inserts. */ | ||
| 187 | int max_del; /* Maximum allowed number of deletes. */ | ||
| 188 | int max_subst; /* Maximum allowed number of substitutes. */ | ||
| 189 | int max_err; /* Maximum allowed number of errors total. */ | ||
| 190 | } regaparams_t; | ||
| 191 | |||
| 192 | /* Approximate matching result struct. */ | ||
| 193 | typedef struct { | ||
| 194 | size_t nmatch; /* Length of pmatch[] array. */ | ||
| 195 | regmatch_t *pmatch; /* Submatch data. */ | ||
| 196 | int cost; /* Cost of the match. */ | ||
| 197 | int num_ins; /* Number of inserts in the match. */ | ||
| 198 | int num_del; /* Number of deletes in the match. */ | ||
| 199 | int num_subst; /* Number of substitutes in the match. */ | ||
| 200 | } regamatch_t; | ||
| 201 | |||
| 202 | |||
| 203 | /* Approximate matching functions. */ | ||
| 204 | extern int | ||
| 205 | tre_regaexec(const regex_t *preg, const char *string, | ||
| 206 | regamatch_t *match, regaparams_t params, int eflags); | ||
| 207 | |||
| 208 | extern int | ||
| 209 | tre_reganexec(const regex_t *preg, const char *string, size_t len, | ||
| 210 | regamatch_t *match, regaparams_t params, int eflags); | ||
| 211 | #ifdef TRE_WCHAR | ||
| 212 | /* Wide character approximate matching. */ | ||
| 213 | extern int | ||
| 214 | tre_regawexec(const regex_t *preg, const wchar_t *string, | ||
| 215 | regamatch_t *match, regaparams_t params, int eflags); | ||
| 216 | |||
| 217 | extern int | ||
| 218 | tre_regawnexec(const regex_t *preg, const wchar_t *string, size_t len, | ||
| 219 | regamatch_t *match, regaparams_t params, int eflags); | ||
| 220 | #endif /* TRE_WCHAR */ | ||
| 221 | |||
| 222 | /* Sets the parameters to default values. */ | ||
| 223 | extern void | ||
| 224 | tre_regaparams_default(regaparams_t *params); | ||
| 225 | #endif /* TRE_APPROX */ | ||
| 226 | |||
| 227 | #ifdef TRE_WCHAR | ||
| 228 | typedef wchar_t tre_char_t; | ||
| 229 | #else /* !TRE_WCHAR */ | ||
| 230 | typedef unsigned char tre_char_t; | ||
| 231 | #endif /* !TRE_WCHAR */ | ||
| 232 | |||
| 233 | typedef struct { | ||
| 234 | int (*get_next_char)(tre_char_t *c, unsigned int *pos_add, void *context); | ||
| 235 | void (*rewind)(size_t pos, void *context); | ||
| 236 | int (*compare)(size_t pos1, size_t pos2, size_t len, void *context); | ||
| 237 | void *context; | ||
| 238 | } tre_str_source; | ||
| 239 | |||
| 240 | extern int | ||
| 241 | tre_reguexec(const regex_t *preg, const tre_str_source *string, | ||
| 242 | size_t nmatch, regmatch_t pmatch[], int eflags); | ||
| 243 | |||
| 244 | /* Returns the version string. The returned string is static. */ | ||
| 245 | extern char * | ||
| 246 | tre_version(void); | ||
| 247 | |||
| 248 | /* Returns the value for a config parameter. The type to which `result' | ||
| 249 | must point to depends of the value of `query', see documentation for | ||
| 250 | more details. */ | ||
| 251 | extern int | ||
| 252 | tre_config(int query, void *result); | ||
| 253 | |||
| 254 | enum { | ||
| 255 | TRE_CONFIG_APPROX, | ||
| 256 | TRE_CONFIG_WCHAR, | ||
| 257 | TRE_CONFIG_MULTIBYTE, | ||
| 258 | TRE_CONFIG_SYSTEM_ABI, | ||
| 259 | TRE_CONFIG_VERSION | ||
| 260 | }; | ||
| 261 | |||
| 262 | /* Returns 1 if the compiled pattern has back references, 0 if not. */ | ||
| 263 | extern int | ||
| 264 | tre_have_backrefs(const regex_t *preg); | ||
| 265 | |||
| 266 | /* Returns 1 if the compiled pattern uses approximate matching features, | ||
| 267 | 0 if not. */ | ||
| 268 | extern int | ||
| 269 | tre_have_approx(const regex_t *preg); | ||
| 270 | |||
| 271 | #ifdef __cplusplus | ||
| 272 | } | ||
| 273 | #endif | ||
| 274 | #endif /* TRE_H */ | ||
| 275 | |||
| 276 | /* EOF */ | ||
| @@ -1,351 +0,0 @@ | |||
| 1 | /* | ||
| 2 | xmalloc.c - Simple malloc debugging library implementation | ||
| 3 | |||
| 4 | This software is released under a BSD-style license. | ||
| 5 | See the file LICENSE for details and copyright. | ||
| 6 | |||
| 7 | */ | ||
| 8 | |||
| 9 | /* | ||
| 10 | TODO: | ||
| 11 | - red zones | ||
| 12 | - group dumps by source location | ||
| 13 | */ | ||
| 14 | |||
| 15 | #ifdef HAVE_CONFIG_H | ||
| 16 | #include <config.h> | ||
| 17 | #endif /* HAVE_CONFIG_H */ | ||
| 18 | |||
| 19 | #include <stdlib.h> | ||
| 20 | #include <assert.h> | ||
| 21 | #include <stdio.h> | ||
| 22 | #define XMALLOC_INTERNAL 1 | ||
| 23 | #include "xmalloc.h" | ||
| 24 | |||
| 25 | |||
| 26 | /* | ||
| 27 | Internal stuff. | ||
| 28 | */ | ||
| 29 | |||
| 30 | typedef struct hashTableItemRec { | ||
| 31 | void *ptr; | ||
| 32 | int bytes; | ||
| 33 | const char *file; | ||
| 34 | int line; | ||
| 35 | const char *func; | ||
| 36 | struct hashTableItemRec *next; | ||
| 37 | } hashTableItem; | ||
| 38 | |||
| 39 | typedef struct { | ||
| 40 | hashTableItem **table; | ||
| 41 | } hashTable; | ||
| 42 | |||
| 43 | static int xmalloc_peak; | ||
| 44 | int xmalloc_current; | ||
| 45 | static int xmalloc_peak_blocks; | ||
| 46 | int xmalloc_current_blocks; | ||
| 47 | static int xmalloc_fail_after; | ||
| 48 | |||
| 49 | #define TABLE_BITS 8 | ||
| 50 | #define TABLE_MASK ((1 << TABLE_BITS) - 1) | ||
| 51 | #define TABLE_SIZE (1 << TABLE_BITS) | ||
| 52 | |||
| 53 | static hashTable * | ||
| 54 | hash_table_new(void) | ||
| 55 | { | ||
| 56 | hashTable *tbl; | ||
| 57 | |||
| 58 | tbl = malloc(sizeof(*tbl)); | ||
| 59 | |||
| 60 | if (tbl != NULL) | ||
| 61 | { | ||
| 62 | tbl->table = calloc(TABLE_SIZE, sizeof(*tbl->table)); | ||
| 63 | |||
| 64 | if (tbl->table == NULL) | ||
| 65 | { | ||
| 66 | free(tbl); | ||
| 67 | return NULL; | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 71 | return tbl; | ||
| 72 | } | ||
| 73 | |||
| 74 | static int | ||
| 75 | hash_void_ptr(void *ptr) | ||
| 76 | { | ||
| 77 | int hash; | ||
| 78 | int i; | ||
| 79 | |||
| 80 | /* I took this hash function just off the top of my head, I have | ||
| 81 | no idea whether it is bad or very bad. */ | ||
| 82 | hash = 0; | ||
| 83 | for (i = 0; i < (int)sizeof(ptr)*8 / TABLE_BITS; i++) | ||
| 84 | { | ||
| 85 | hash ^= (unsigned long)ptr >> i*8; | ||
| 86 | hash += i * 17; | ||
| 87 | hash &= TABLE_MASK; | ||
| 88 | } | ||
| 89 | return hash; | ||
| 90 | } | ||
| 91 | |||
| 92 | static void | ||
| 93 | hash_table_add(hashTable *tbl, void *ptr, int bytes, | ||
| 94 | const char *file, int line, const char *func) | ||
| 95 | { | ||
| 96 | int i; | ||
| 97 | hashTableItem *item, *new; | ||
| 98 | |||
| 99 | i = hash_void_ptr(ptr); | ||
| 100 | |||
| 101 | item = tbl->table[i]; | ||
| 102 | if (item != NULL) | ||
| 103 | while (item->next != NULL) | ||
| 104 | item = item->next; | ||
| 105 | |||
| 106 | new = malloc(sizeof(*new)); | ||
| 107 | assert(new != NULL); | ||
| 108 | new->ptr = ptr; | ||
| 109 | new->bytes = bytes; | ||
| 110 | new->file = file; | ||
| 111 | new->line = line; | ||
| 112 | new->func = func; | ||
| 113 | new->next = NULL; | ||
| 114 | if (item != NULL) | ||
| 115 | item->next = new; | ||
| 116 | else | ||
| 117 | tbl->table[i] = new; | ||
| 118 | |||
| 119 | xmalloc_current += bytes; | ||
| 120 | if (xmalloc_current > xmalloc_peak) | ||
| 121 | xmalloc_peak = xmalloc_current; | ||
| 122 | xmalloc_current_blocks++; | ||
| 123 | if (xmalloc_current_blocks > xmalloc_peak_blocks) | ||
| 124 | xmalloc_peak_blocks = xmalloc_current_blocks; | ||
| 125 | } | ||
| 126 | |||
| 127 | static void | ||
| 128 | hash_table_del(hashTable *tbl, void *ptr) | ||
| 129 | { | ||
| 130 | int i; | ||
| 131 | hashTableItem *item, *prev; | ||
| 132 | |||
| 133 | i = hash_void_ptr(ptr); | ||
| 134 | |||
| 135 | item = tbl->table[i]; | ||
| 136 | if (item == NULL) | ||
| 137 | { | ||
| 138 | printf("xfree: invalid ptr %p\n", ptr); | ||
| 139 | abort(); | ||
| 140 | } | ||
| 141 | prev = NULL; | ||
| 142 | while (item->ptr != ptr) | ||
| 143 | { | ||
| 144 | prev = item; | ||
| 145 | item = item->next; | ||
| 146 | } | ||
| 147 | if (item->ptr != ptr) | ||
| 148 | { | ||
| 149 | printf("xfree: invalid ptr %p\n", ptr); | ||
| 150 | abort(); | ||
| 151 | } | ||
| 152 | |||
| 153 | xmalloc_current -= item->bytes; | ||
| 154 | xmalloc_current_blocks--; | ||
| 155 | |||
| 156 | if (prev != NULL) | ||
| 157 | { | ||
| 158 | prev->next = item->next; | ||
| 159 | free(item); | ||
| 160 | } | ||
| 161 | else | ||
| 162 | { | ||
| 163 | tbl->table[i] = item->next; | ||
| 164 | free(item); | ||
| 165 | } | ||
| 166 | } | ||
| 167 | |||
| 168 | static hashTable *xmalloc_table = NULL; | ||
| 169 | |||
| 170 | static void | ||
| 171 | xmalloc_init(void) | ||
| 172 | { | ||
| 173 | if (xmalloc_table == NULL) | ||
| 174 | { | ||
| 175 | xmalloc_table = hash_table_new(); | ||
| 176 | xmalloc_peak = 0; | ||
| 177 | xmalloc_peak_blocks = 0; | ||
| 178 | xmalloc_current = 0; | ||
| 179 | xmalloc_current_blocks = 0; | ||
| 180 | xmalloc_fail_after = -1; | ||
| 181 | } | ||
| 182 | assert(xmalloc_table != NULL); | ||
| 183 | assert(xmalloc_table->table != NULL); | ||
| 184 | } | ||
| 185 | |||
| 186 | |||
| 187 | |||
| 188 | /* | ||
| 189 | Public API. | ||
| 190 | */ | ||
| 191 | |||
| 192 | void | ||
| 193 | xmalloc_configure(int fail_after) | ||
| 194 | { | ||
| 195 | xmalloc_init(); | ||
| 196 | xmalloc_fail_after = fail_after; | ||
| 197 | } | ||
| 198 | |||
| 199 | int | ||
| 200 | xmalloc_dump_leaks(void) | ||
| 201 | { | ||
| 202 | int i; | ||
| 203 | int num_leaks = 0; | ||
| 204 | int leaked_bytes = 0; | ||
| 205 | hashTableItem *item; | ||
| 206 | |||
| 207 | xmalloc_init(); | ||
| 208 | |||
| 209 | for (i = 0; i < TABLE_SIZE; i++) | ||
| 210 | { | ||
| 211 | item = xmalloc_table->table[i]; | ||
| 212 | while (item != NULL) | ||
| 213 | { | ||
| 214 | printf("%s:%d: %s: %d bytes at %p not freed\n", | ||
| 215 | item->file, item->line, item->func, item->bytes, item->ptr); | ||
| 216 | num_leaks++; | ||
| 217 | leaked_bytes += item->bytes; | ||
| 218 | item = item->next; | ||
| 219 | } | ||
| 220 | } | ||
| 221 | if (num_leaks == 0) | ||
| 222 | printf("No memory leaks.\n"); | ||
| 223 | else | ||
| 224 | printf("%d unfreed memory chuncks, total %d unfreed bytes.\n", | ||
| 225 | num_leaks, leaked_bytes); | ||
| 226 | printf("Peak memory consumption %d bytes (%.1f kB, %.1f MB) in %d blocks ", | ||
| 227 | xmalloc_peak, (double)xmalloc_peak / 1024, | ||
| 228 | (double)xmalloc_peak / (1024*1024), xmalloc_peak_blocks); | ||
| 229 | printf("(average "); | ||
| 230 | if (xmalloc_peak_blocks) | ||
| 231 | printf("%d", ((xmalloc_peak + xmalloc_peak_blocks / 2) | ||
| 232 | / xmalloc_peak_blocks)); | ||
| 233 | else | ||
| 234 | printf("N/A"); | ||
| 235 | printf(" bytes per block).\n"); | ||
| 236 | |||
| 237 | return num_leaks; | ||
| 238 | } | ||
| 239 | |||
| 240 | void * | ||
| 241 | xmalloc_impl(size_t size, const char *file, int line, const char *func) | ||
| 242 | { | ||
| 243 | void *ptr; | ||
| 244 | |||
| 245 | xmalloc_init(); | ||
| 246 | assert(size > 0); | ||
| 247 | |||
| 248 | if (xmalloc_fail_after == 0) | ||
| 249 | { | ||
| 250 | xmalloc_fail_after = -2; | ||
| 251 | #if 0 | ||
| 252 | printf("xmalloc: forced failure %s:%d: %s\n", file, line, func); | ||
| 253 | #endif | ||
| 254 | return NULL; | ||
| 255 | } | ||
| 256 | else if (xmalloc_fail_after == -2) | ||
| 257 | { | ||
| 258 | printf("xmalloc: called after failure from %s:%d: %s\n", | ||
| 259 | file, line, func); | ||
| 260 | assert(0); | ||
| 261 | } | ||
| 262 | else if (xmalloc_fail_after > 0) | ||
| 263 | xmalloc_fail_after--; | ||
| 264 | |||
| 265 | ptr = malloc(size); | ||
| 266 | if (ptr != NULL) | ||
| 267 | hash_table_add(xmalloc_table, ptr, (int)size, file, line, func); | ||
| 268 | return ptr; | ||
| 269 | } | ||
| 270 | |||
| 271 | void * | ||
| 272 | xcalloc_impl(size_t nmemb, size_t size, const char *file, int line, | ||
| 273 | const char *func) | ||
| 274 | { | ||
| 275 | void *ptr; | ||
| 276 | |||
| 277 | xmalloc_init(); | ||
| 278 | assert(size > 0); | ||
| 279 | |||
| 280 | if (xmalloc_fail_after == 0) | ||
| 281 | { | ||
| 282 | xmalloc_fail_after = -2; | ||
| 283 | #if 0 | ||
| 284 | printf("xcalloc: forced failure %s:%d: %s\n", file, line, func); | ||
| 285 | #endif | ||
| 286 | return NULL; | ||
| 287 | } | ||
| 288 | else if (xmalloc_fail_after == -2) | ||
| 289 | { | ||
| 290 | printf("xcalloc: called after failure from %s:%d: %s\n", | ||
| 291 | file, line, func); | ||
| 292 | assert(0); | ||
| 293 | } | ||
| 294 | else if (xmalloc_fail_after > 0) | ||
| 295 | xmalloc_fail_after--; | ||
| 296 | |||
| 297 | ptr = calloc(nmemb, size); | ||
| 298 | if (ptr != NULL) | ||
| 299 | hash_table_add(xmalloc_table, ptr, (int)(nmemb * size), file, line, func); | ||
| 300 | return ptr; | ||
| 301 | } | ||
| 302 | |||
| 303 | void | ||
| 304 | xfree_impl(void *ptr, const char *file, int line, const char *func) | ||
| 305 | { | ||
| 306 | /*LINTED*/(void)&file; | ||
| 307 | /*LINTED*/(void)&line; | ||
| 308 | /*LINTED*/(void)&func; | ||
| 309 | xmalloc_init(); | ||
| 310 | |||
| 311 | if (ptr != NULL) | ||
| 312 | hash_table_del(xmalloc_table, ptr); | ||
| 313 | free(ptr); | ||
| 314 | } | ||
| 315 | |||
| 316 | void * | ||
| 317 | xrealloc_impl(void *ptr, size_t new_size, const char *file, int line, | ||
| 318 | const char *func) | ||
| 319 | { | ||
| 320 | void *new_ptr; | ||
| 321 | |||
| 322 | xmalloc_init(); | ||
| 323 | assert(ptr != NULL); | ||
| 324 | assert(new_size > 0); | ||
| 325 | |||
| 326 | if (xmalloc_fail_after == 0) | ||
| 327 | { | ||
| 328 | xmalloc_fail_after = -2; | ||
| 329 | return NULL; | ||
| 330 | } | ||
| 331 | else if (xmalloc_fail_after == -2) | ||
| 332 | { | ||
| 333 | printf("xrealloc: called after failure from %s:%d: %s\n", | ||
| 334 | file, line, func); | ||
| 335 | assert(0); | ||
| 336 | } | ||
| 337 | else if (xmalloc_fail_after > 0) | ||
| 338 | xmalloc_fail_after--; | ||
| 339 | |||
| 340 | new_ptr = realloc(ptr, new_size); | ||
| 341 | if (new_ptr != NULL) | ||
| 342 | { | ||
| 343 | hash_table_del(xmalloc_table, ptr); | ||
| 344 | hash_table_add(xmalloc_table, new_ptr, (int)new_size, file, line, func); | ||
| 345 | } | ||
| 346 | return new_ptr; | ||
| 347 | } | ||
| 348 | |||
| 349 | |||
| 350 | |||
| 351 | /* EOF */ | ||
| @@ -1,77 +0,0 @@ | |||
| 1 | /* | ||
| 2 | xmalloc.h - Simple malloc debugging library API | ||
| 3 | |||
| 4 | This software is released under a BSD-style license. | ||
| 5 | See the file LICENSE for details and copyright. | ||
| 6 | |||
| 7 | */ | ||
| 8 | |||
| 9 | #ifndef _XMALLOC_H | ||
| 10 | #define _XMALLOC_H 1 | ||
| 11 | |||
| 12 | void *xmalloc_impl(size_t size, const char *file, int line, const char *func); | ||
| 13 | void *xcalloc_impl(size_t nmemb, size_t size, const char *file, int line, | ||
| 14 | const char *func); | ||
| 15 | void xfree_impl(void *ptr, const char *file, int line, const char *func); | ||
| 16 | void *xrealloc_impl(void *ptr, size_t new_size, const char *file, int line, | ||
| 17 | const char *func); | ||
| 18 | int xmalloc_dump_leaks(void); | ||
| 19 | void xmalloc_configure(int fail_after); | ||
| 20 | |||
| 21 | |||
| 22 | #ifndef XMALLOC_INTERNAL | ||
| 23 | #ifdef MALLOC_DEBUGGING | ||
| 24 | |||
| 25 | /* Version 2.4 and later of GCC define a magical variable `__PRETTY_FUNCTION__' | ||
| 26 | which contains the name of the function currently being defined. | ||
| 27 | # define __XMALLOC_FUNCTION __PRETTY_FUNCTION__ | ||
| 28 | This is broken in G++ before version 2.6. | ||
| 29 | C9x has a similar variable called __func__, but prefer the GCC one since | ||
| 30 | it demangles C++ function names. */ | ||
| 31 | # ifdef __GNUC__ | ||
| 32 | # if __GNUC__ > 2 || (__GNUC__ == 2 \ | ||
| 33 | && __GNUC_MINOR__ >= (defined __cplusplus ? 6 : 4)) | ||
| 34 | # define __XMALLOC_FUNCTION __PRETTY_FUNCTION__ | ||
| 35 | # else | ||
| 36 | # define __XMALLOC_FUNCTION ((const char *) 0) | ||
| 37 | # endif | ||
| 38 | # else | ||
| 39 | # if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L | ||
| 40 | # define __XMALLOC_FUNCTION __func__ | ||
| 41 | # else | ||
| 42 | # define __XMALLOC_FUNCTION ((const char *) 0) | ||
| 43 | # endif | ||
| 44 | # endif | ||
| 45 | |||
| 46 | #define xmalloc(size) xmalloc_impl(size, __FILE__, __LINE__, \ | ||
| 47 | __XMALLOC_FUNCTION) | ||
| 48 | #define xcalloc(nmemb, size) xcalloc_impl(nmemb, size, __FILE__, __LINE__, \ | ||
| 49 | __XMALLOC_FUNCTION) | ||
| 50 | #define xfree(ptr) xfree_impl(ptr, __FILE__, __LINE__, __XMALLOC_FUNCTION) | ||
| 51 | #define xrealloc(ptr, new_size) xrealloc_impl(ptr, new_size, __FILE__, \ | ||
| 52 | __LINE__, __XMALLOC_FUNCTION) | ||
| 53 | #undef malloc | ||
| 54 | #undef calloc | ||
| 55 | #undef free | ||
| 56 | #undef realloc | ||
| 57 | |||
| 58 | #define malloc USE_XMALLOC_INSTEAD_OF_MALLOC | ||
| 59 | #define calloc USE_XCALLOC_INSTEAD_OF_CALLOC | ||
| 60 | #define free USE_XFREE_INSTEAD_OF_FREE | ||
| 61 | #define realloc USE_XREALLOC_INSTEAD_OF_REALLOC | ||
| 62 | |||
| 63 | #else /* !MALLOC_DEBUGGING */ | ||
| 64 | |||
| 65 | #include <stdlib.h> | ||
| 66 | |||
| 67 | #define xmalloc(size) malloc(size) | ||
| 68 | #define xcalloc(nmemb, size) calloc(nmemb, size) | ||
| 69 | #define xfree(ptr) free(ptr) | ||
| 70 | #define xrealloc(ptr, new_size) realloc(ptr, new_size) | ||
| 71 | |||
| 72 | #endif /* !MALLOC_DEBUGGING */ | ||
| 73 | #endif /* !XMALLOC_INTERNAL */ | ||
| 74 | |||
| 75 | #endif /* _XMALLOC_H */ | ||
| 76 | |||
| 77 | /* EOF */ | ||
| @@ -1,407 +0,0 @@ | |||
| 1 | /*---------------------------------------------------------------------------- | ||
| 2 | ChucK Concurrent, On-the-fly Audio Programming Language | ||
| 3 | Compiler and Virtual Machine | ||
| 4 | |||
| 5 | Copyright (c) 2004 Ge Wang and Perry R. Cook. All rights reserved. | ||
| 6 | http://chuck.stanford.edu/ | ||
| 7 | http://chuck.cs.princeton.edu/ | ||
| 8 | |||
| 9 | This program is free software; you can redistribute it and/or modify | ||
| 10 | it under the terms of the GNU General Public License as published by | ||
| 11 | the Free Software Foundation; either version 2 of the License, or | ||
| 12 | (at your option) any later version. | ||
| 13 | |||
| 14 | This program is distributed in the hope that it will be useful, | ||
| 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 17 | GNU General Public License for more details. | ||
| 18 | |||
| 19 | You should have received a copy of the GNU General Public License | ||
| 20 | along with this program; if not, write to the Free Software | ||
| 21 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 | ||
| 22 | U.S.A. | ||
| 23 | -----------------------------------------------------------------------------*/ | ||
| 24 | |||
| 25 | //----------------------------------------------------------------------------- | ||
| 26 | // file: ulib_regex.cpp | ||
| 27 | // desc: regex library | ||
| 28 | // | ||
| 29 | // author: Spencer Salazar (spencer@ccrma.stanford.edu) | ||
| 30 | // date: Summer 2013 | ||
| 31 | //----------------------------------------------------------------------------- | ||
| 32 | #include "ulib_regex.h" | ||
| 33 | #include "chuck_errmsg.h" | ||
| 34 | #include "chuck_instr.h" | ||
| 35 | #include "chuck_type.h" | ||
| 36 | #include "chuck_vm.h" | ||
| 37 | |||
| 38 | #ifdef WIN32 | ||
| 39 | #include "regex/regex.h" | ||
| 40 | #else | ||
| 41 | #include <regex.h> | ||
| 42 | #endif | ||
| 43 | |||
| 44 | #define CK_REGEX_MAX_MATCHES (10) | ||
| 45 | |||
| 46 | CK_DLL_SFUN( regex_match ); | ||
| 47 | CK_DLL_SFUN( regex_match2 ); | ||
| 48 | CK_DLL_SFUN( regex_replace ); | ||
| 49 | CK_DLL_SFUN( regex_replaceAll ); | ||
| 50 | |||
| 51 | DLL_QUERY regex_query( Chuck_DL_Query * QUERY ) | ||
| 52 | { | ||
| 53 | // set name | ||
| 54 | QUERY->setname( QUERY, "RegEx" ); | ||
| 55 | |||
| 56 | // begin class | ||
| 57 | QUERY->begin_class( QUERY, "RegEx", "Object" ); | ||
| 58 | QUERY->doc_class( QUERY, "Class for regular expression matching and replacing in strings. Regex style is POSIX-extended." ); | ||
| 59 | |||
| 60 | // add match | ||
| 61 | QUERY->add_sfun( QUERY, regex_match, "int", "match" ); | ||
| 62 | QUERY->add_arg( QUERY, "string", "pattern"); | ||
| 63 | QUERY->add_arg( QUERY, "string", "str"); | ||
| 64 | QUERY->doc_func( QUERY, "Return true if match for pattern is found in str, false otherwise." ); | ||
| 65 | |||
| 66 | // add match2 | ||
| 67 | QUERY->add_sfun( QUERY, regex_match2, "int", "match" ); | ||
| 68 | QUERY->add_arg( QUERY, "string", "pattern"); | ||
| 69 | QUERY->add_arg( QUERY, "string", "str"); | ||
| 70 | QUERY->add_arg( QUERY, "string[]", "matches"); | ||
| 71 | QUERY->doc_func( QUERY, "Return the match and sub-patterns in matches. matches[0] in the entire matched pattern, matches[1] is the first sub-pattern (if any), and so on." ); | ||
| 72 | |||
| 73 | // add replace | ||
| 74 | QUERY->add_sfun( QUERY, regex_replace, "string", "replace" ); | ||
| 75 | QUERY->add_arg( QUERY, "string", "pattern"); | ||
| 76 | QUERY->add_arg( QUERY, "string", "replacement"); | ||
| 77 | QUERY->add_arg( QUERY, "string", "str"); | ||
| 78 | QUERY->doc_func( QUERY, "Replace the first instance of pattern in str with replacement, returning the result." ); | ||
| 79 | |||
| 80 | // add replaceAll | ||
| 81 | QUERY->add_sfun( QUERY, regex_replaceAll, "string", "replaceAll" ); | ||
| 82 | QUERY->add_arg( QUERY, "string", "pattern"); | ||
| 83 | QUERY->add_arg( QUERY, "string", "replacement"); | ||
| 84 | QUERY->add_arg( QUERY, "string", "str"); | ||
| 85 | QUERY->doc_func( QUERY, "Replace all instances of pattern in str with replacement, returning the result."); | ||
| 86 | |||
| 87 | QUERY->end_class( QUERY ); | ||
| 88 | |||
| 89 | return TRUE; | ||
| 90 | |||
| 91 | //error: | ||
| 92 | // return FALSE; | ||
| 93 | } | ||
| 94 | |||
| 95 | |||
| 96 | CK_DLL_SFUN( regex_match ) | ||
| 97 | { | ||
| 98 | Chuck_String * pattern = GET_NEXT_STRING(ARGS); | ||
| 99 | Chuck_String * str = GET_NEXT_STRING(ARGS); | ||
| 100 | |||
| 101 | regex_t regex; | ||
| 102 | t_CKBOOL r_free = FALSE; | ||
| 103 | int result = 0; | ||
| 104 | |||
| 105 | if(pattern == NULL) | ||
| 106 | { | ||
| 107 | throw_exception(SHRED, "NullPointerException", "RegEx.match: argument 'pattern' is null"); | ||
| 108 | goto error; | ||
| 109 | } | ||
| 110 | if(str == NULL) | ||
| 111 | { | ||
| 112 | throw_exception(SHRED, "NullPointerException", "RegEx.match: argument 'str' is null"); | ||
| 113 | goto error; | ||
| 114 | } | ||
| 115 | |||
| 116 | result = regcomp(®ex, pattern->str().c_str(), REG_EXTENDED | REG_NOSUB); | ||
| 117 | if(result != 0) | ||
| 118 | goto error; | ||
| 119 | |||
| 120 | r_free = TRUE; | ||
| 121 | |||
| 122 | result = regexec(®ex, str->str().c_str(), 0, NULL, 0); | ||
| 123 | |||
| 124 | RETURN->v_int = (result == 0 ? 1 : 0); | ||
| 125 | |||
| 126 | regfree(®ex); | ||
| 127 | r_free = FALSE; | ||
| 128 | |||
| 129 | return; | ||
| 130 | |||
| 131 | error: | ||
| 132 | if(result != 0) | ||
| 133 | { | ||
| 134 | char buf[256]; | ||
| 135 | |||
| 136 | regerror(result, ®ex, buf, 256); | ||
| 137 | EM_error2( 0, "(RegEx.match): regex reported error: %s", buf); | ||
| 138 | } | ||
| 139 | |||
| 140 | if(r_free) | ||
| 141 | { | ||
| 142 | regfree(®ex); | ||
| 143 | r_free = FALSE; | ||
| 144 | } | ||
| 145 | |||
| 146 | RETURN->v_int = 0; | ||
| 147 | } | ||
| 148 | |||
| 149 | |||
| 150 | CK_DLL_SFUN( regex_match2 ) | ||
| 151 | { | ||
| 152 | Chuck_String * pattern = GET_NEXT_STRING(ARGS); | ||
| 153 | Chuck_String * str = GET_NEXT_STRING(ARGS); | ||
| 154 | Chuck_Array4 * matches = (Chuck_Array4 *) GET_NEXT_OBJECT(ARGS); | ||
| 155 | |||
| 156 | regex_t regex; | ||
| 157 | t_CKBOOL r_free = FALSE; | ||
| 158 | size_t i; | ||
| 159 | regmatch_t *matcharray = NULL; | ||
| 160 | int result = 0; | ||
| 161 | |||
| 162 | if(pattern == NULL) | ||
| 163 | { | ||
| 164 | throw_exception(SHRED, "NullPointerException", "RegEx.match: argument 'pattern' is null"); | ||
| 165 | goto error; | ||
| 166 | } | ||
| 167 | if(str == NULL) | ||
| 168 | { | ||
| 169 | throw_exception(SHRED, "NullPointerException", "RegEx.match: argument 'str' is null"); | ||
| 170 | goto error; | ||
| 171 | } | ||
| 172 | if(matches == NULL) | ||
| 173 | { | ||
| 174 | throw_exception(SHRED, "NullPointerException", "RegEx.match: argument 'matches' is null"); | ||
| 175 | goto error; | ||
| 176 | } | ||
| 177 | |||
| 178 | |||
| 179 | //matches->clear(); | ||
| 180 | // bugfix: array.clear() doesnt seem to work? | ||
| 181 | matches->set_size(0); | ||
| 182 | |||
| 183 | result = regcomp(®ex, pattern->str().c_str(), REG_EXTENDED); | ||
| 184 | if(result != 0) | ||
| 185 | goto error; | ||
| 186 | |||
| 187 | r_free = TRUE; | ||
| 188 | |||
| 189 | matcharray = new regmatch_t[regex.re_nsub+1]; | ||
| 190 | |||
| 191 | result = regexec(®ex, str->str().c_str(), regex.re_nsub+1, matcharray, 0); | ||
| 192 | |||
| 193 | RETURN->v_int = (result == 0 ? 1 : 0); | ||
| 194 | |||
| 195 | regfree(®ex); | ||
| 196 | r_free = FALSE; | ||
| 197 | |||
| 198 | if(result == 0) | ||
| 199 | { | ||
| 200 | for(i = 0; i < regex.re_nsub+1; i++) | ||
| 201 | { | ||
| 202 | Chuck_String * match = (Chuck_String *) instantiate_and_initialize_object(SHRED->vm_ref->env()->t_string, SHRED); | ||
| 203 | |||
| 204 | if(matcharray[i].rm_so >= 0 && matcharray[i].rm_eo > 0) | ||
| 205 | match->set( std::string(str->str(), matcharray[i].rm_so, | ||
| 206 | matcharray[i].rm_eo-matcharray[i].rm_so) ); | ||
| 207 | |||
| 208 | matches->push_back((t_CKUINT) match); | ||
| 209 | } | ||
| 210 | } | ||
| 211 | |||
| 212 | SAFE_DELETE(matcharray); | ||
| 213 | |||
| 214 | return; | ||
| 215 | |||
| 216 | error: | ||
| 217 | if(result != 0) | ||
| 218 | { | ||
| 219 | char errbuf[256]; | ||
| 220 | |||
| 221 | regerror(result, ®ex, errbuf, 256); | ||
| 222 | EM_error2( 0, "(RegEx.match): regex reported error: %s", errbuf); | ||
| 223 | } | ||
| 224 | |||
| 225 | if(r_free) | ||
| 226 | { | ||
| 227 | regfree(®ex); | ||
| 228 | r_free = FALSE; | ||
| 229 | } | ||
| 230 | |||
| 231 | SAFE_DELETE(matcharray); | ||
| 232 | |||
| 233 | RETURN->v_int = 0; | ||
| 234 | } | ||
| 235 | |||
| 236 | |||
| 237 | CK_DLL_SFUN( regex_replace ) | ||
| 238 | { | ||
| 239 | Chuck_String * pattern = GET_NEXT_STRING(ARGS); | ||
| 240 | Chuck_String * replace = GET_NEXT_STRING(ARGS); | ||
| 241 | Chuck_String * str = GET_NEXT_STRING(ARGS); | ||
| 242 | |||
| 243 | Chuck_String * ret = (Chuck_String *) instantiate_and_initialize_object(SHRED->vm_ref->env()->t_string, SHRED); | ||
| 244 | ret->set( str->str() ); | ||
| 245 | |||
| 246 | regex_t regex; | ||
| 247 | t_CKBOOL r_free = FALSE; | ||
| 248 | regmatch_t *matcharray = NULL; | ||
| 249 | int result = 0; | ||
| 250 | |||
| 251 | if(pattern == NULL) | ||
| 252 | { | ||
| 253 | throw_exception(SHRED, "NullPointerException", | ||
| 254 | "RegEx.match: argument 'pattern' is null"); | ||
| 255 | goto error; | ||
| 256 | } | ||
| 257 | if(str == NULL) | ||
| 258 | { | ||
| 259 | throw_exception(SHRED, "NullPointerException", | ||
| 260 | "RegEx.match: argument 'str' is null"); | ||
| 261 | goto error; | ||
| 262 | } | ||
| 263 | if(replace == NULL) | ||
| 264 | { | ||
| 265 | throw_exception(SHRED, "NullPointerException", | ||
| 266 | "RegEx.match: argument 'replace' is null"); | ||
| 267 | goto error; | ||
| 268 | } | ||
| 269 | |||
| 270 | // compile regex | ||
| 271 | result = regcomp(®ex, pattern->str().c_str(), REG_EXTENDED); | ||
| 272 | if(result != 0) | ||
| 273 | goto error; | ||
| 274 | |||
| 275 | r_free = TRUE; | ||
| 276 | |||
| 277 | // perform match | ||
| 278 | matcharray = new regmatch_t[regex.re_nsub+1]; | ||
| 279 | result = regexec(®ex, str->str().c_str(), regex.re_nsub+1, matcharray, 0); | ||
| 280 | |||
| 281 | regfree(®ex); | ||
| 282 | r_free = FALSE; | ||
| 283 | |||
| 284 | // perform substitution | ||
| 285 | if(result == 0 && matcharray[0].rm_so >= 0 && matcharray[0].rm_eo >= 0) | ||
| 286 | { | ||
| 287 | std::string s = ret->str(); | ||
| 288 | s.replace(matcharray[0].rm_so, | ||
| 289 | matcharray[0].rm_eo-matcharray[0].rm_so, replace->str()); | ||
| 290 | ret->set( s ); | ||
| 291 | } | ||
| 292 | |||
| 293 | SAFE_DELETE(matcharray); | ||
| 294 | |||
| 295 | RETURN->v_string = ret; | ||
| 296 | |||
| 297 | return; | ||
| 298 | |||
| 299 | error: | ||
| 300 | if(result != 0) | ||
| 301 | { | ||
| 302 | char errbuf[256]; | ||
| 303 | |||
| 304 | regerror(result, ®ex, errbuf, 256); | ||
| 305 | EM_error2( 0, "(RegEx.match): regex reported error: %s", errbuf); | ||
| 306 | } | ||
| 307 | |||
| 308 | if(r_free) | ||
| 309 | { | ||
| 310 | regfree(®ex); | ||
| 311 | r_free = FALSE; | ||
| 312 | } | ||
| 313 | |||
| 314 | RETURN->v_int = 0; | ||
| 315 | } | ||
| 316 | |||
| 317 | |||
| 318 | CK_DLL_SFUN( regex_replaceAll ) | ||
| 319 | { | ||
| 320 | Chuck_String * pattern = GET_NEXT_STRING(ARGS); | ||
| 321 | Chuck_String * replace = GET_NEXT_STRING(ARGS); | ||
| 322 | Chuck_String * str = GET_NEXT_STRING(ARGS); | ||
| 323 | |||
| 324 | Chuck_String * ret = (Chuck_String *) instantiate_and_initialize_object(SHRED->vm_ref->env()->t_string, SHRED); | ||
| 325 | ret->set( str->str() ); | ||
| 326 | |||
| 327 | regex_t regex; | ||
| 328 | t_CKBOOL r_free = FALSE; | ||
| 329 | regmatch_t *matcharray = NULL; | ||
| 330 | int result = 0; | ||
| 331 | size_t pos = 0; | ||
| 332 | |||
| 333 | if(pattern == NULL) | ||
| 334 | { | ||
| 335 | throw_exception(SHRED, "NullPointerException", | ||
| 336 | "RegEx.match: argument 'pattern' is null"); | ||
| 337 | goto error; | ||
| 338 | } | ||
| 339 | if(str == NULL) | ||
| 340 | { | ||
| 341 | throw_exception(SHRED, "NullPointerException", | ||
| 342 | "RegEx.match: argument 'str' is null"); | ||
| 343 | goto error; | ||
| 344 | } | ||
| 345 | if(replace == NULL) | ||
| 346 | { | ||
| 347 | throw_exception(SHRED, "NullPointerException", | ||
| 348 | "RegEx.match: argument 'replace' is null"); | ||
| 349 | goto error; | ||
| 350 | } | ||
| 351 | |||
| 352 | // compile regex | ||
| 353 | result = regcomp(®ex, pattern->str().c_str(), REG_EXTENDED); | ||
| 354 | if(result != 0) | ||
| 355 | goto error; | ||
| 356 | |||
| 357 | r_free = TRUE; | ||
| 358 | |||
| 359 | // perform match | ||
| 360 | matcharray = new regmatch_t[regex.re_nsub+1]; | ||
| 361 | |||
| 362 | while(pos < str->str().size()) | ||
| 363 | { | ||
| 364 | result = regexec(®ex, ret->str().c_str()+pos, regex.re_nsub+1, matcharray, 0); | ||
| 365 | |||
| 366 | // perform substitution | ||
| 367 | if(result != 0) | ||
| 368 | break; | ||
| 369 | else if(matcharray[0].rm_so >= 0 && matcharray[0].rm_eo >= 0) | ||
| 370 | { | ||
| 371 | std::string s = ret->str(); | ||
| 372 | s.replace(pos+matcharray[0].rm_so, | ||
| 373 | matcharray[0].rm_eo-matcharray[0].rm_so, | ||
| 374 | replace->str()); | ||
| 375 | ret->set( s ); | ||
| 376 | |||
| 377 | pos = pos + matcharray[0].rm_so + replace->str().size(); | ||
| 378 | } | ||
| 379 | } | ||
| 380 | |||
| 381 | SAFE_DELETE(matcharray); | ||
| 382 | |||
| 383 | regfree(®ex); | ||
| 384 | r_free = FALSE; | ||
| 385 | |||
| 386 | RETURN->v_string = ret; | ||
| 387 | |||
| 388 | return; | ||
| 389 | |||
| 390 | error: | ||
| 391 | if(result != 0) | ||
| 392 | { | ||
| 393 | char errbuf[256]; | ||
| 394 | |||
| 395 | regerror(result, ®ex, errbuf, 256); | ||
| 396 | EM_error2( 0, "(RegEx.match): regex reported error: %s", errbuf); | ||
| 397 | } | ||
| 398 | |||
| 399 | if(r_free) | ||
| 400 | { | ||
| 401 | regfree(®ex); | ||
| 402 | r_free = FALSE; | ||
| 403 | } | ||
| 404 | |||
| 405 | RETURN->v_int = 0; | ||
| 406 | } | ||
| 407 | |||
| @@ -1,40 +0,0 @@ | |||
| 1 | /*---------------------------------------------------------------------------- | ||
| 2 | ChucK Concurrent, On-the-fly Audio Programming Language | ||
| 3 | Compiler and Virtual Machine | ||
| 4 | |||
| 5 | Copyright (c) 2004 Ge Wang and Perry R. Cook. All rights reserved. | ||
| 6 | http://chuck.stanford.edu/ | ||
| 7 | http://chuck.cs.princeton.edu/ | ||
| 8 | |||
| 9 | This program is free software; you can redistribute it and/or modify | ||
| 10 | it under the terms of the GNU General Public License as published by | ||
| 11 | the Free Software Foundation; either version 2 of the License, or | ||
| 12 | (at your option) any later version. | ||
| 13 | |||
| 14 | This program is distributed in the hope that it will be useful, | ||
| 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 17 | GNU General Public License for more details. | ||
| 18 | |||
| 19 | You should have received a copy of the GNU General Public License | ||
| 20 | along with this program; if not, write to the Free Software | ||
| 21 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 | ||
| 22 | U.S.A. | ||
| 23 | -----------------------------------------------------------------------------*/ | ||
| 24 | |||
| 25 | //----------------------------------------------------------------------------- | ||
| 26 | // file: ulib_regex.h | ||
| 27 | // desc: regex library | ||
| 28 | // | ||
| 29 | // author: Spencer Salazar (spencer@ccrma.stanford.edu) | ||
| 30 | // date: Summer 2013 | ||
| 31 | //----------------------------------------------------------------------------- | ||
| 32 | #ifndef __ULIB_REGEX_H__ | ||
| 33 | #define __ULIB_REGEX_H__ | ||
| 34 | |||
| 35 | #include "chuck_dl.h" | ||
| 36 | |||
| 37 | DLL_QUERY regex_query( Chuck_DL_Query * QUERY ); | ||
| 38 | |||
| 39 | |||
| 40 | #endif // __ULIB_REGEX_H__ | ||
| @@ -40,7 +40,7 @@ EMSCRIPTENSRCS=host_web/chuck_emscripten.cpp \ · 39 unmodified lines | |||
| 40 | core/uana_extract.cpp core/uana_xform.cpp core/ugen_filter.cpp \ | 40 | core/uana_extract.cpp core/uana_xform.cpp core/ugen_filter.cpp \ |
| 41 | core/ugen_osc.cpp core/ugen_stk.cpp core/ugen_xxx.cpp \ | 41 | core/ugen_osc.cpp core/ugen_stk.cpp core/ugen_xxx.cpp \ |
| 42 | core/ulib_ai.cpp core/ulib_doc.cpp core/ulib_machine.cpp \ | 42 | core/ulib_ai.cpp core/ulib_doc.cpp core/ulib_machine.cpp \ |
| 43 | core/ulib_math.cpp core/ulib_regex.cpp core/ulib_std.cpp \ | 43 | core/ulib_math.cpp core/ulib_std.cpp \ |
| 44 | core/util_buffers.cpp core/util_math.c core/util_raw.c \ | 44 | core/util_buffers.cpp core/util_math.c core/util_raw.c \ |
| 45 | core/util_string.cpp core/util_xforms.c core/chuck_win32.c \ | 45 | core/util_string.cpp core/util_xforms.c core/chuck_win32.c \ |
| 46 | core/util_sndfile.c | 46 | core/util_sndfile.c |
| @@ -157,7 +157,7 @@ CXXOBJS_CORE+= chuck.o chuck_absyn.o chuck_parse.o chuck_errmsg.o \ · 110 unmodified lines | |||
| 157 | chuck_carrier.o chuck_globals.o \ | 157 | chuck_carrier.o chuck_globals.o \ |
| 158 | hidio_sdl.o midiio_rtmidi.o rtmidi.o ugen_osc.o ugen_filter.o \ | 158 | hidio_sdl.o midiio_rtmidi.o rtmidi.o ugen_osc.o ugen_filter.o \ |
| 159 | ugen_stk.o ugen_xxx.o ulib_ai.o ulib_doc.o ulib_machine.o \ | 159 | ugen_stk.o ugen_xxx.o ulib_ai.o ulib_doc.o ulib_machine.o \ |
| 160 | ulib_math.o ulib_std.o ulib_opsc.o ulib_regex.o util_buffers.o \ | 160 | ulib_math.o ulib_std.o ulib_opsc.o util_buffers.o \ |
| 161 | util_console.o util_string.o util_thread.o util_opsc.o \ | 161 | util_console.o util_string.o util_thread.o util_opsc.o \ |
| 162 | util_serial.o util_hid.o uana_xform.o uana_extract.o | 162 | util_serial.o util_hid.o uana_xform.o uana_extract.o |
| 163 | LO_COBJS_CORE+= lo/address.o lo/blob.o lo/bundle.o lo/message.o lo/method.o \ | 163 | LO_COBJS_CORE+= lo/address.o lo/blob.o lo/bundle.o lo/message.o lo/method.o \ |
| @@ -68,7 +68,6 @@ · 67 unmodified lines | |||
| 68 | 091677411F7CAD1100248F78 /* uana_extract.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 09812B980C6E394A00D1D68C /* uana_extract.cpp */; }; | 68 | 091677411F7CAD1100248F78 /* uana_extract.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 09812B980C6E394A00D1D68C /* uana_extract.cpp */; }; |
| 69 | 091677431F7CAD1100248F78 /* util_serial.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0966E6C815CFBB0F0099CBD8 /* util_serial.cpp */; }; | 69 | 091677431F7CAD1100248F78 /* util_serial.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0966E6C815CFBB0F0099CBD8 /* util_serial.cpp */; }; |
| 70 | 091677441F7CAD1100248F78 /* chuck_io.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0962DA2715D3A626002DEFF3 /* chuck_io.cpp */; }; | 70 | 091677441F7CAD1100248F78 /* chuck_io.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0962DA2715D3A626002DEFF3 /* chuck_io.cpp */; }; |
| 71 | 091677451F7CAD1100248F78 /* ulib_regex.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 091F327C17A03A4D00686C8D /* ulib_regex.cpp */; }; | ||
| 72 | 091677481F7CAD1100248F78 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09A40BD40A9A57C6007604EA /* Carbon.framework */; }; | 71 | 091677481F7CAD1100248F78 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09A40BD40A9A57C6007604EA /* Carbon.framework */; }; |
| 73 | 091677491F7CAD1100248F78 /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09A40BD50A9A57C6007604EA /* CoreAudio.framework */; }; | 72 | 091677491F7CAD1100248F78 /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09A40BD50A9A57C6007604EA /* CoreAudio.framework */; }; |
| 74 | 0916774A1F7CAD1100248F78 /* CoreMIDI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09A40BD60A9A57C6007604EA /* CoreMIDI.framework */; }; | 73 | 0916774A1F7CAD1100248F78 /* CoreMIDI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09A40BD60A9A57C6007604EA /* CoreMIDI.framework */; }; |
| @@ -139,8 +138,6 @@ · 64 unmodified lines | |||
| 139 | 090B4B1A0C4F038E00DF5577 /* uana_xform.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = uana_xform.cpp; path = ../core/uana_xform.cpp; sourceTree = SOURCE_ROOT; }; | 138 | 090B4B1A0C4F038E00DF5577 /* uana_xform.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = uana_xform.cpp; path = ../core/uana_xform.cpp; sourceTree = SOURCE_ROOT; }; |
| 140 | 090B4B1B0C4F038E00DF5577 /* uana_xform.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = uana_xform.h; path = ../core/uana_xform.h; sourceTree = SOURCE_ROOT; }; | 139 | 090B4B1B0C4F038E00DF5577 /* uana_xform.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = uana_xform.h; path = ../core/uana_xform.h; sourceTree = SOURCE_ROOT; }; |
| 141 | 091677521F7CAD1100248F78 /* libchuck.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libchuck.a; sourceTree = BUILT_PRODUCTS_DIR; }; | 140 | 091677521F7CAD1100248F78 /* libchuck.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libchuck.a; sourceTree = BUILT_PRODUCTS_DIR; }; |
| 142 | 091F327C17A03A4D00686C8D /* ulib_regex.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ulib_regex.cpp; path = ../core/ulib_regex.cpp; sourceTree = SOURCE_ROOT; }; | ||
| 143 | 091F327D17A03A4D00686C8D /* ulib_regex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ulib_regex.h; path = ../core/ulib_regex.h; sourceTree = SOURCE_ROOT; }; | ||
| 144 | 095D14581341476200BBD9EB /* MultitouchSupport.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MultitouchSupport.framework; path = System/Library/PrivateFrameworks/MultitouchSupport.framework; sourceTree = SDKROOT; }; | 141 | 095D14581341476200BBD9EB /* MultitouchSupport.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MultitouchSupport.framework; path = System/Library/PrivateFrameworks/MultitouchSupport.framework; sourceTree = SDKROOT; }; |
| 145 | 0962DA2715D3A626002DEFF3 /* chuck_io.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = chuck_io.cpp; path = ../core/chuck_io.cpp; sourceTree = SOURCE_ROOT; }; | 142 | 0962DA2715D3A626002DEFF3 /* chuck_io.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = chuck_io.cpp; path = ../core/chuck_io.cpp; sourceTree = SOURCE_ROOT; }; |
| 146 | 0962DA2815D3A626002DEFF3 /* chuck_io.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = chuck_io.h; path = ../core/chuck_io.h; sourceTree = SOURCE_ROOT; }; | 143 | 0962DA2815D3A626002DEFF3 /* chuck_io.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = chuck_io.h; path = ../core/chuck_io.h; sourceTree = SOURCE_ROOT; }; |
| @@ -932,8 +929,6 @@ · 785 unmodified lines | |||
| 932 | 09A40B4F0A9A54DA007604EA /* ulib_opsc.h */, | 929 | 09A40B4F0A9A54DA007604EA /* ulib_opsc.h */, |
| 933 | 09A40B500A9A54DA007604EA /* ulib_std.cpp */, | 930 | 09A40B500A9A54DA007604EA /* ulib_std.cpp */, |
| 934 | 09A40B510A9A54DA007604EA /* ulib_std.h */, | 931 | 09A40B510A9A54DA007604EA /* ulib_std.h */, |
| 935 | 091F327C17A03A4D00686C8D /* ulib_regex.cpp */, | ||
| 936 | 091F327D17A03A4D00686C8D /* ulib_regex.h */, | ||
| 937 | 09A40B520A9A54DA007604EA /* util_buffers.cpp */, | 932 | 09A40B520A9A54DA007604EA /* util_buffers.cpp */, |
| 938 | 09A40B530A9A54DA007604EA /* util_buffers.h */, | 933 | 09A40B530A9A54DA007604EA /* util_buffers.h */, |
| 939 | 09A40B540A9A54DA007604EA /* util_console.cpp */, | 934 | 09A40B540A9A54DA007604EA /* util_console.cpp */, |
| @@ -1169,7 +1164,6 @@ · 229 unmodified lines | |||
| 1169 | 8615DFFA264D9AE20084EF86 /* chuck_globals.cpp in Sources */, | 1164 | 8615DFFA264D9AE20084EF86 /* chuck_globals.cpp in Sources */, |
| 1170 | 091677431F7CAD1100248F78 /* util_serial.cpp in Sources */, | 1165 | 091677431F7CAD1100248F78 /* util_serial.cpp in Sources */, |
| 1171 | 091677441F7CAD1100248F78 /* chuck_io.cpp in Sources */, | 1166 | 091677441F7CAD1100248F78 /* chuck_io.cpp in Sources */, |
| 1172 | 091677451F7CAD1100248F78 /* ulib_regex.cpp in Sources */, | ||
| 1173 | ); | 1167 | ); |
| 1174 | runOnlyForDeploymentPostprocessing = 0; | 1168 | runOnlyForDeploymentPostprocessing = 0; |
| 1175 | }; | 1169 | }; |