Unified · Split
| @@ -54,11 +54,6 @@ · 53 unmodified lines | ||
| 54 | 54 | #include "ulib_opsc.h" |
| 55 | 55 | #endif |
| 56 | 56 | |
| 57 | −// 1.5.0.0 (ge) | chunreal | |
| 58 | −#ifndef __DISABLE_REGEX__ | |
| 59 | −#include "ulib_regex.h" | |
| 60 | −#endif | |
| 61 | − | |
| 62 | 57 | #if defined(__PLATFORM_WIN32__) |
| 63 | 58 | #include "dirent_win32.h" |
| 64 | 59 | #else |
| @@ -706,11 +701,6 @@ t_CKBOOL load_internal_modules( Chuck_Compiler * compiler ) · 641 unmodified lines | ||
| 706 | 701 | if( !load_module( compiler, env, opensoundcontrol_query, "opsc", "global" ) ) goto error; |
| 707 | 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 | 704 | // deprecated: |
| 715 | 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 | 83 | midiio_rtmidi.cpp rtmidi.cpp hidio_sdl.cpp \ |
| 84 | 84 | ugen_osc.cpp ugen_filter.cpp ugen_stk.cpp ugen_xxx.cpp \ |
| 85 | 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 | 87 | util_console.cpp util_string.cpp util_thread.cpp util_opsc.cpp \ |
| 88 | 88 | util_serial.cpp util_hid.cpp uana_xform.cpp uana_extract.cpp |
| 89 | 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 | |
| 2 | −# chuck.lex and chuck.y | |
| 3 | −# | |
| 4 | −# > flex -ochuck.yy.c chuck.lex | |
| 5 | −# > bison -dv -b chuck chuck.y | |
| 6 | −# | |
| 7 | −# to create the chuck_win32 files: | |
| 8 | −# | |
| 9 | −# > cat chuck.tab.c chuck.yy.c > chuck_win32.c | |
| 10 | −# > cp chuck.tab.h chuck_win32.h | |
| 11 | −# | |
| 12 | −# ALSO: we are actually now building in 64-bit, but yeah. | |
| 13 | − | |
| 14 | −MSBUILD=msbuild.exe | |
| 15 | −RTAUDIODIR=rtaudio | |
| 16 | − | |
| 17 | −ifneq (,$(CHUCK_DEBUG)) | |
| 18 | −CONFIG=Debug | |
| 19 | −CHUCK_BIN=Debug/chuck.exe | |
| 20 | −else | |
| 21 | −CONFIG=Release | |
| 22 | −CHUCK_BIN=Release/chuck.exe | |
| 23 | −endif | |
| 24 | − | |
| 25 | −CSRCS+= chuck_win32.c util_math.c util_network.c util_raw.c \ | |
| 26 | − util_xforms.c | |
| 27 | −CXXSRCS+= chuck_absyn.cpp chuck_parse.cpp chuck_errmsg.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 \ | |
| 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 \ | |
| 32 | − chuck_io.cpp chuck_carrier.cpp chuck.cpp \ | |
| 33 | − hidio_sdl.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 \ | |
| 36 | − ulib_opsc.cpp ulib_regex.cpp util_buffers.cpp util_console.cpp \ | |
| 37 | − util_string.cpp util_thread.cpp util_opsc.cpp util_serial.cpp \ | |
| 38 | − util_hid.cpp uana_xform.cpp uana_extract.cpp | |
| 39 | −CXXINC_HOST+= -I${RTAUDIODIR} -I${RTAUDIODIR}/include | |
| 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 \ | |
| 42 | − lo/pattern_match.c lo/send.c lo/server.c lo/server_thread.c lo/timetag.c | |
| 43 | − | |
| 44 | −CSRCS:=$(addprefix core/,$(CSRCS)) | |
| 45 | −CXXSRCS:=$(addprefix core/,$(CXXSRCS)) | |
| 46 | −CXXSRCS_HOST:=$(addprefix host/,$(CXXSRCS_HOST)) | |
| 47 | −LO_CSRCS:=$(addprefix core/,$(LO_CSRCS)) | |
| 48 | − | |
| 49 | −CXXOBJS_HOST=$(addprefix $(HOSTDIR)/,$(CXXSRCS_HOST:.cpp=.o)) | |
| 50 | − | |
| 51 | −COBJS=$(COBJS_HOST) $(addprefix $(COREDIR)/,$(COBJS_CORE)) | |
| 52 | −CXXOBJS=$(CXXOBJS_HOST) $(addprefix $(COREDIR)/,$(CXXOBJS_CORE)) | |
| 53 | −LO_COBJS=$(addprefix $(COREDIR)/,$(LO_COBJS_CORE)) | |
| 54 | −SF_COBJS=$(addprefix $(COREDIR)/,$(SF_CSRCS:.c=.o)) | |
| 55 | −OBJS=$(COBJS) $(CXXOBJS) $(LO_COBJS) $(SF_COBJS) | |
| 56 | −SRCS=$(CSRCS) $(CXXSRCS) $(CXXSRCS_HOST) $(LO_CSRCS) | |
| 57 | − | |
| 58 | −$(CHUCK_BIN): $(SRCS) | |
| 59 | − $(MSBUILD) visual-studio/chuck.sln /p:Configuration=$(CONFIG) | |
| 60 | − | |
| 61 | −clean: | |
| 62 | − $(MSBUILD) visual-studio/chuck.sln /p:Configuration=$(CONFIG) /t:Clean | |
| 63 | − | |
| 1 | +# NOTE: chuck_win32.h and chuck_win32.c come from the output of | |
| 2 | +# chuck.lex and chuck.y | |
| 3 | +# | |
| 4 | +# > flex -ochuck.yy.c chuck.lex | |
| 5 | +# > bison -dv -b chuck chuck.y | |
| 6 | +# | |
| 7 | +# to create the chuck_win32 files: | |
| 8 | +# | |
| 9 | +# > cat chuck.tab.c chuck.yy.c > chuck_win32.c | |
| 10 | +# > cp chuck.tab.h chuck_win32.h | |
| 11 | +# | |
| 12 | +# ALSO: we are actually now building in 64-bit, but yeah. | |
| 13 | + | |
| 14 | +MSBUILD=msbuild.exe | |
| 15 | +RTAUDIODIR=rtaudio | |
| 16 | + | |
| 17 | +ifneq (,$(CHUCK_DEBUG)) | |
| 18 | +CONFIG=Debug | |
| 19 | +CHUCK_BIN=Debug/chuck.exe | |
| 20 | +else | |
| 21 | +CONFIG=Release | |
| 22 | +CHUCK_BIN=Release/chuck.exe | |
| 23 | +endif | |
| 24 | + | |
| 25 | +CSRCS+= chuck_win32.c util_math.c util_network.c util_raw.c \ | |
| 26 | + util_xforms.c | |
| 27 | +CXXSRCS+= chuck_absyn.cpp chuck_parse.cpp chuck_errmsg.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 \ | |
| 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 \ | |
| 32 | + chuck_io.cpp chuck_carrier.cpp chuck.cpp \ | |
| 33 | + hidio_sdl.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 \ | |
| 36 | + ulib_opsc.cpp util_buffers.cpp util_console.cpp \ | |
| 37 | + util_string.cpp util_thread.cpp util_opsc.cpp util_serial.cpp \ | |
| 38 | + util_hid.cpp uana_xform.cpp uana_extract.cpp | |
| 39 | +CXXINC_HOST+= -I${RTAUDIODIR} -I${RTAUDIODIR}/include | |
| 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 \ | |
| 42 | + lo/pattern_match.c lo/send.c lo/server.c lo/server_thread.c lo/timetag.c | |
| 43 | + | |
| 44 | +CSRCS:=$(addprefix core/,$(CSRCS)) | |
| 45 | +CXXSRCS:=$(addprefix core/,$(CXXSRCS)) | |
| 46 | +CXXSRCS_HOST:=$(addprefix host/,$(CXXSRCS_HOST)) | |
| 47 | +LO_CSRCS:=$(addprefix core/,$(LO_CSRCS)) | |
| 48 | + | |
| 49 | +CXXOBJS_HOST=$(addprefix $(HOSTDIR)/,$(CXXSRCS_HOST:.cpp=.o)) | |
| 50 | + | |
| 51 | +COBJS=$(COBJS_HOST) $(addprefix $(COREDIR)/,$(COBJS_CORE)) | |
| 52 | +CXXOBJS=$(CXXOBJS_HOST) $(addprefix $(COREDIR)/,$(CXXOBJS_CORE)) | |
| 53 | +LO_COBJS=$(addprefix $(COREDIR)/,$(LO_COBJS_CORE)) | |
| 54 | +SF_COBJS=$(addprefix $(COREDIR)/,$(SF_CSRCS:.c=.o)) | |
| 55 | +OBJS=$(COBJS) $(CXXOBJS) $(LO_COBJS) $(SF_COBJS) | |
| 56 | +SRCS=$(CSRCS) $(CXXSRCS) $(CXXSRCS_HOST) $(LO_CSRCS) | |
| 57 | + | |
| 58 | +$(CHUCK_BIN): $(SRCS) | |
| 59 | + $(MSBUILD) visual-studio/chuck.sln /p:Configuration=$(CONFIG) | |
| 60 | + | |
| 61 | +clean: | |
| 62 | + $(MSBUILD) visual-studio/chuck.sln /p:Configuration=$(CONFIG) /t:Clean | |
| 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 | 40 | core/uana_extract.cpp core/uana_xform.cpp core/ugen_filter.cpp \ |
| 41 | 41 | core/ugen_osc.cpp core/ugen_stk.cpp core/ugen_xxx.cpp \ |
| 42 | 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 | 44 | core/util_buffers.cpp core/util_math.c core/util_raw.c \ |
| 45 | 45 | core/util_string.cpp core/util_xforms.c core/chuck_win32.c \ |
| 46 | 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 | 157 | chuck_carrier.o chuck_globals.o \ |
| 158 | 158 | hidio_sdl.o midiio_rtmidi.o rtmidi.o ugen_osc.o ugen_filter.o \ |
| 159 | 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 | 161 | util_console.o util_string.o util_thread.o util_opsc.o \ |
| 162 | 162 | util_serial.o util_hid.o uana_xform.o uana_extract.o |
| 163 | 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 | 68 | 091677411F7CAD1100248F78 /* uana_extract.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 09812B980C6E394A00D1D68C /* uana_extract.cpp */; }; |
| 69 | 69 | 091677431F7CAD1100248F78 /* util_serial.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0966E6C815CFBB0F0099CBD8 /* util_serial.cpp */; }; |
| 70 | 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 | 71 | 091677481F7CAD1100248F78 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09A40BD40A9A57C6007604EA /* Carbon.framework */; }; |
| 73 | 72 | 091677491F7CAD1100248F78 /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09A40BD50A9A57C6007604EA /* CoreAudio.framework */; }; |
| 74 | 73 | 0916774A1F7CAD1100248F78 /* CoreMIDI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09A40BD60A9A57C6007604EA /* CoreMIDI.framework */; }; |
| @@ -139,8 +138,6 @@ · 64 unmodified lines | ||
| 139 | 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 | 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 | 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 | 141 | 095D14581341476200BBD9EB /* MultitouchSupport.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MultitouchSupport.framework; path = System/Library/PrivateFrameworks/MultitouchSupport.framework; sourceTree = SDKROOT; }; |
| 145 | 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 | 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 | 929 | 09A40B4F0A9A54DA007604EA /* ulib_opsc.h */, |
| 933 | 930 | 09A40B500A9A54DA007604EA /* ulib_std.cpp */, |
| 934 | 931 | 09A40B510A9A54DA007604EA /* ulib_std.h */, |
| 935 | − 091F327C17A03A4D00686C8D /* ulib_regex.cpp */, | |
| 936 | − 091F327D17A03A4D00686C8D /* ulib_regex.h */, | |
| 937 | 932 | 09A40B520A9A54DA007604EA /* util_buffers.cpp */, |
| 938 | 933 | 09A40B530A9A54DA007604EA /* util_buffers.h */, |
| 939 | 934 | 09A40B540A9A54DA007604EA /* util_console.cpp */, |
| @@ -1169,7 +1164,6 @@ · 229 unmodified lines | ||
| 1169 | 1164 | 8615DFFA264D9AE20084EF86 /* chuck_globals.cpp in Sources */, |
| 1170 | 1165 | 091677431F7CAD1100248F78 /* util_serial.cpp in Sources */, |
| 1171 | 1166 | 091677441F7CAD1100248F78 /* chuck_io.cpp in Sources */, |
| 1172 | − 091677451F7CAD1100248F78 /* ulib_regex.cpp in Sources */, | |
| 1173 | 1167 | ); |
| 1174 | 1168 | runOnlyForDeploymentPostprocessing = 0; |
| 1175 | 1169 | }; |