History

moved RegEx type out of core and into RegEx.chug

Ge Wang committed 3 years ago

Commit 2cd0f171 parent c13c0ea35 files · +66 −9794Browse files

Unified · Split

35 files
  1. src/core/chuck_compile.cpp +0 −10
  2. src/core/makefile +1 −1
  3. src/core/makefile.x/makefile.win32 +63 −63
  4. src/core/regex/LICENSE.txt +0 −29
  5. src/core/regex/README.txt +0 −235
  6. src/core/regex/config.h +0 −186
  7. src/core/regex/regcomp.c +0 −129
  8. src/core/regex/regerror.c +0 −83
  9. src/core/regex/regex.h +0 −38
  10. src/core/regex/regexec.c +0 −348
  11. src/core/regex/tre-ast.c +0 −226
  12. src/core/regex/tre-ast.h +0 −128
  13. src/core/regex/tre-compile.c +0 −2296
  14. src/core/regex/tre-compile.h +0 −27
  15. src/core/regex/tre-config.h +0 −52
  16. src/core/regex/tre-filter.c +0 −73
  17. src/core/regex/tre-filter.h +0 −19
  18. src/core/regex/tre-internal.h +0 −287
  19. src/core/regex/tre-match-approx.c +0 −812
  20. src/core/regex/tre-match-backtrack.c +0 −666
  21. src/core/regex/tre-match-parallel.c +0 −501
  22. src/core/regex/tre-match-utils.h +0 −215
  23. src/core/regex/tre-mem.c +0 −155
  24. src/core/regex/tre-mem.h +0 −66
  25. src/core/regex/tre-parse.c +0 −1739
  26. src/core/regex/tre-parse.h +0 −50
  27. src/core/regex/tre-stack.c +0 −125
  28. src/core/regex/tre-stack.h +0 −76
  29. src/core/regex/tre.h +0 −276
  30. src/core/regex/xmalloc.c +0 −351
  31. src/core/regex/xmalloc.h +0 −77
  32. src/core/ulib_regex.cpp +0 −407
  33. src/core/ulib_regex.h +0 −40
  34. src/makefile +2 −2
  35. src/xcode/chuck.xcodeproj/project.pbxproj +0 −6
src/core/chuck_compile.cpp+0−10
@@ -54,11 +54,6 @@ · 53 unmodified lines
54#include "ulib_opsc.h"54#include "ulib_opsc.h"
55#endif55#endif
56 56
57// 1.5.0.0 (ge) | chunreal
58#ifndef __DISABLE_REGEX__
59#include "ulib_regex.h"
60#endif
61
62#if defined(__PLATFORM_WIN32__)57#if defined(__PLATFORM_WIN32__)
63 #include "dirent_win32.h"58 #include "dirent_win32.h"
64#else59#else
@@ -706,11 +701,6 @@ t_CKBOOL load_internal_modules( Chuck_Compiler * compiler ) · 641 unmodified lines
706 if( !load_module( compiler, env, opensoundcontrol_query, "opsc", "global" ) ) goto error;701 if( !load_module( compiler, env, opensoundcontrol_query, "opsc", "global" ) ) goto error;
707 #endif702 #endif
708 703
709 #ifndef __DISABLE_REGEX__
710 EM_log( CK_LOG_SEVERE, "module 'RegEx'" );
711 if( !load_module( compiler, env, regex_query, "RegEx", "global" ) ) goto error;
712 #endif
713
714 // deprecated:704 // deprecated:
715 // if( !load_module( compiler, env, net_query, "net", "global" ) ) goto error;705 // if( !load_module( compiler, env, net_query, "net", "global" ) ) goto error;
716 706
src/core/makefile+1−1
@@ -83,7 +83,7 @@ CXXSRCS_CORE+= chuck_absyn.cpp chuck_parse.cpp chuck_errmsg.cpp \ · 82 unmodified lines
83 midiio_rtmidi.cpp rtmidi.cpp hidio_sdl.cpp \83 midiio_rtmidi.cpp rtmidi.cpp hidio_sdl.cpp \
84 ugen_osc.cpp ugen_filter.cpp ugen_stk.cpp ugen_xxx.cpp \84 ugen_osc.cpp ugen_filter.cpp ugen_stk.cpp ugen_xxx.cpp \
85 ulib_ai.cpp ulib_doc.cpp ulib_machine.cpp ulib_math.cpp \85 ulib_ai.cpp ulib_doc.cpp ulib_machine.cpp ulib_math.cpp \
86 ulib_std.cpp ulib_opsc.cpp ulib_regex.cpp util_buffers.cpp \86 ulib_std.cpp ulib_opsc.cpp util_buffers.cpp \
87 util_console.cpp util_string.cpp util_thread.cpp util_opsc.cpp \87 util_console.cpp util_string.cpp util_thread.cpp util_opsc.cpp \
88 util_serial.cpp util_hid.cpp uana_xform.cpp uana_extract.cpp88 util_serial.cpp util_hid.cpp uana_xform.cpp uana_extract.cpp
89LO_CSRCS+= lo/address.c lo/blob.c lo/bundle.c lo/message.c lo/method.c \89LO_CSRCS+= lo/address.c lo/blob.c lo/bundle.c lo/message.c lo/method.c \
src/core/makefile.x/makefile.win32+63−63
@@ -1,63 +1,63 @@
1# NOTE: chuck_win32.h and chuck_win32.c come from the output of1# NOTE: chuck_win32.h and chuck_win32.c come from the output of
2# chuck.lex and chuck.y2# chuck.lex and chuck.y
3#3#
4# > flex -ochuck.yy.c chuck.lex4# > flex -ochuck.yy.c chuck.lex
5# > bison -dv -b chuck chuck.y5# > bison -dv -b chuck chuck.y
6#6#
7# to create the chuck_win32 files:7# to create the chuck_win32 files:
8#8#
9# > cat chuck.tab.c chuck.yy.c > chuck_win32.c9# > cat chuck.tab.c chuck.yy.c > chuck_win32.c
10# > cp chuck.tab.h chuck_win32.h10# > cp chuck.tab.h chuck_win32.h
11#11#
12# ALSO: we are actually now building in 64-bit, but yeah.12# ALSO: we are actually now building in 64-bit, but yeah.
13 13
14MSBUILD=msbuild.exe14MSBUILD=msbuild.exe
15RTAUDIODIR=rtaudio15RTAUDIODIR=rtaudio
16 16
17ifneq (,$(CHUCK_DEBUG))17ifneq (,$(CHUCK_DEBUG))
18CONFIG=Debug18CONFIG=Debug
19CHUCK_BIN=Debug/chuck.exe19CHUCK_BIN=Debug/chuck.exe
20else20else
21CONFIG=Release21CONFIG=Release
22CHUCK_BIN=Release/chuck.exe22CHUCK_BIN=Release/chuck.exe
23endif23endif
24 24
25CSRCS+= chuck_win32.c util_math.c util_network.c util_raw.c \25CSRCS+= chuck_win32.c util_math.c util_network.c util_raw.c \
26 util_xforms.c26 util_xforms.c
27CXXSRCS+= chuck_absyn.cpp chuck_parse.cpp chuck_errmsg.cpp \27CXXSRCS+= chuck_absyn.cpp chuck_parse.cpp chuck_errmsg.cpp \
28 chuck_frame.cpp chuck_symbol.cpp chuck_table.cpp chuck_utils.cpp \28 chuck_frame.cpp chuck_symbol.cpp chuck_table.cpp chuck_utils.cpp \
29 chuck_vm.cpp chuck_instr.cpp chuck_scan.cpp chuck_type.cpp chuck_emit.cpp \29 chuck_vm.cpp chuck_instr.cpp chuck_scan.cpp chuck_type.cpp chuck_emit.cpp \
30 chuck_compile.cpp chuck_dl.cpp chuck_oo.cpp chuck_lang.cpp chuck_ugen.cpp \30 chuck_compile.cpp chuck_dl.cpp chuck_oo.cpp chuck_lang.cpp chuck_ugen.cpp \
31 chuck_otf.cpp chuck_stats.cpp chuck_shell.cpp \31 chuck_otf.cpp chuck_stats.cpp chuck_shell.cpp \
32 chuck_io.cpp chuck_carrier.cpp chuck.cpp \32 chuck_io.cpp chuck_carrier.cpp chuck.cpp \
33 hidio_sdl.cpp \33 hidio_sdl.cpp \
34 midiio_rtmidi.cpp rtmidi.cpp ugen_osc.cpp ugen_filter.cpp \34 midiio_rtmidi.cpp rtmidi.cpp ugen_osc.cpp ugen_filter.cpp \
35 ugen_stk.cpp ugen_xxx.cpp ulib_machine.cpp ulib_math.cpp ulib_std.cpp \35 ugen_stk.cpp ugen_xxx.cpp ulib_machine.cpp ulib_math.cpp ulib_std.cpp \
36 ulib_opsc.cpp ulib_regex.cpp util_buffers.cpp util_console.cpp \36 ulib_opsc.cpp util_buffers.cpp util_console.cpp \
37 util_string.cpp util_thread.cpp util_opsc.cpp util_serial.cpp \37 util_string.cpp util_thread.cpp util_opsc.cpp util_serial.cpp \
38 util_hid.cpp uana_xform.cpp uana_extract.cpp38 util_hid.cpp uana_xform.cpp uana_extract.cpp
39CXXINC_HOST+= -I${RTAUDIODIR} -I${RTAUDIODIR}/include39CXXINC_HOST+= -I${RTAUDIODIR} -I${RTAUDIODIR}/include
40CXXSRCS_HOST+= chuck_main.cpp chuck_audio.cpp chuck_console.cpp ../${RTAUDIODIR}/RtAudio.cpp40CXXSRCS_HOST+= chuck_main.cpp chuck_audio.cpp chuck_console.cpp ../${RTAUDIODIR}/RtAudio.cpp
41LO_CSRCS+= lo/address.c lo/blob.c lo/bundle.c lo/message.c lo/method.c \41LO_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.c42 lo/pattern_match.c lo/send.c lo/server.c lo/server_thread.c lo/timetag.c
43 43
44CSRCS:=$(addprefix core/,$(CSRCS))44CSRCS:=$(addprefix core/,$(CSRCS))
45CXXSRCS:=$(addprefix core/,$(CXXSRCS))45CXXSRCS:=$(addprefix core/,$(CXXSRCS))
46CXXSRCS_HOST:=$(addprefix host/,$(CXXSRCS_HOST))46CXXSRCS_HOST:=$(addprefix host/,$(CXXSRCS_HOST))
47LO_CSRCS:=$(addprefix core/,$(LO_CSRCS))47LO_CSRCS:=$(addprefix core/,$(LO_CSRCS))
48 48
49CXXOBJS_HOST=$(addprefix $(HOSTDIR)/,$(CXXSRCS_HOST:.cpp=.o))49CXXOBJS_HOST=$(addprefix $(HOSTDIR)/,$(CXXSRCS_HOST:.cpp=.o))
50 50
51COBJS=$(COBJS_HOST) $(addprefix $(COREDIR)/,$(COBJS_CORE))51COBJS=$(COBJS_HOST) $(addprefix $(COREDIR)/,$(COBJS_CORE))
52CXXOBJS=$(CXXOBJS_HOST) $(addprefix $(COREDIR)/,$(CXXOBJS_CORE))52CXXOBJS=$(CXXOBJS_HOST) $(addprefix $(COREDIR)/,$(CXXOBJS_CORE))
53LO_COBJS=$(addprefix $(COREDIR)/,$(LO_COBJS_CORE))53LO_COBJS=$(addprefix $(COREDIR)/,$(LO_COBJS_CORE))
54SF_COBJS=$(addprefix $(COREDIR)/,$(SF_CSRCS:.c=.o))54SF_COBJS=$(addprefix $(COREDIR)/,$(SF_CSRCS:.c=.o))
55OBJS=$(COBJS) $(CXXOBJS) $(LO_COBJS) $(SF_COBJS)55OBJS=$(COBJS) $(CXXOBJS) $(LO_COBJS) $(SF_COBJS)
56SRCS=$(CSRCS) $(CXXSRCS) $(CXXSRCS_HOST) $(LO_CSRCS)56SRCS=$(CSRCS) $(CXXSRCS) $(CXXSRCS_HOST) $(LO_CSRCS)
57 57
58$(CHUCK_BIN): $(SRCS)58$(CHUCK_BIN): $(SRCS)
59 $(MSBUILD) visual-studio/chuck.sln /p:Configuration=$(CONFIG) 59 $(MSBUILD) visual-studio/chuck.sln /p:Configuration=$(CONFIG)
60 60
61clean:61clean:
62 $(MSBUILD) visual-studio/chuck.sln /p:Configuration=$(CONFIG) /t:Clean62 $(MSBUILD) visual-studio/chuck.sln /p:Configuration=$(CONFIG) /t:Clean
63 63
src/core/regex/LICENSE.txt+0−29
@@ -1,29 +0,0 @@
1This is the license, copyright notice, and disclaimer for TRE, a regex
2matching package (library and tools) with support for approximate
3matching.
4
5Copyright (c) 2001-2009 Ville Laurikari <vl@iki.fi>
6All rights reserved.
7
8Redistribution and use in source and binary forms, with or without
9modification, are permitted provided that the following conditions
10are 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
19THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS
20``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
src/core/regex/README.txt+0−235
@@ -1,235 +0,0 @@
1Introduction
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
17Features
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
24Approximate 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
43Strict 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
62Predictable 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
72Predictable 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
87Wide 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
93Binary 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
102Completely 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
109Portable
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
146Roadmap
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
235Ville Laurikari <vl@iki.fi>
src/core/regex/config.h+0−186
@@ -1,186 +0,0 @@
1/* config.h for Windows. */
2
3/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP
4systems. 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
12language 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
152the 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
177if 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
src/core/regex/regcomp.c+0−129
@@ -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
21int
22tre_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
102int
103tre_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
110int
111tre_regwncomp(regex_t *preg, const wchar_t *regex, size_t n, int cflags)
112{
113 return tre_compile(preg, regex, n, cflags);
114}
115
116int
117tre_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
123void
124tre_regfree(regex_t *preg)
125{
126 tre_free(preg);
127}
128
129/* EOF */
src/core/regex/regerror.c+0−83
@@ -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. */
36static 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
53size_t
54tre_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 */
src/core/regex/regex.h+0−38
@@ -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 */
src/core/regex/regexec.c+0−348
@@ -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 */
23char *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. */
54void
55tre_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
125int
126tre_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
132int
133tre_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
139static int
140tre_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(&params);
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
202int
203tre_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
212int
213tre_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
222int
223tre_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
230int
231tre_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
239int
240tre_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
254static int
255tre_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
295int
296tre_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
305int
306tre_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
314int
315tre_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
323int
324tre_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
332void
333tre_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 */
src/core/regex/tre-ast.c+0−226
@@ -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
17tre_ast_node_t *
18tre_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
35tre_ast_node_t *
36tre_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
52tre_ast_node_t *
53tre_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
72tre_ast_node_t *
73tre_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
87tre_ast_node_t *
88tre_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
105static void
106tre_findent(FILE *stream, int i)
107{
108 while (i-- > 0)
109 fputc(' ', stream);
110}
111
112void
113tre_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
134static void
135tre_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
211static void
212tre_ast_fprint(FILE *stream, tre_ast_node_t *ast)
213{
214 tre_do_print(stream, ast, 0);
215}
216
217void
218tre_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 */
src/core/regex/tre-ast.h+0−128
@@ -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. */
18typedef 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. */
42typedef 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. */
57typedef 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). */
72typedef 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. */
79typedef 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. */
95typedef struct {
96 tre_ast_node_t *left;
97 tre_ast_node_t *right;
98} tre_union_t;
99
100tre_ast_node_t *
101tre_ast_new_node(tre_mem_t mem, tre_ast_type_t type, size_t size);
102
103tre_ast_node_t *
104tre_ast_new_literal(tre_mem_t mem, int code_min, int code_max, int position);
105
106tre_ast_node_t *
107tre_ast_new_iter(tre_mem_t mem, tre_ast_node_t *arg, int min, int max,
108 int minimal);
109
110tre_ast_node_t *
111tre_ast_new_union(tre_mem_t mem, tre_ast_node_t *left, tre_ast_node_t *right);
112
113tre_ast_node_t *
114tre_ast_new_catenation(tre_mem_t mem, tre_ast_node_t *left,
115 tre_ast_node_t *right);
116
117#ifdef TRE_DEBUG
118void
119tre_ast_print(tre_ast_node_t *tree);
120
121/* XXX - rethink AST printing API */
122void
123tre_print_params(int *params);
124#endif /* TRE_DEBUG */
125
126#endif /* TRE_AST_H */
127
128/* EOF */
src/core/regex/tre-compile.c+0−2296
@@ -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. */
40static reg_errcode_t
41tre_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. */
72static reg_errcode_t
73tre_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
101typedef 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
112typedef 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. */
120static void
121tre_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. */
143static reg_errcode_t
144tre_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
648typedef 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
657static reg_errcode_t
658tre_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
793typedef 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. */
800static reg_errcode_t
801tre_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, &copy,
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, &copy, &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
1050static tre_pos_and_tags_t *
1051tre_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
1066static tre_pos_and_tags_t *
1067tre_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
1089static tre_pos_and_tags_t *
1090tre_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. */
1198static reg_errcode_t
1199tre_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
1308typedef 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'. */
1318static reg_errcode_t
1319tre_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 &params_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 &params_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'. */
1569static reg_errcode_t
1570tre_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. */
1787static reg_errcode_t
1788tre_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
1854int
1855tre_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
2180void
2181tre_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
2233char *
2234tre_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
2247int
2248tre_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 */
src/core/regex/tre-compile.h+0−27
@@ -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
13typedef 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 */
src/core/regex/tre-config.h+0−52
@@ -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@
src/core/regex/tre-filter.c+0−73
@@ -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
22int
23tre_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}
src/core/regex/tre-filter.h+0−19
@@ -1,19 +0,0 @@
1
2
3
4
5typedef struct {
6 unsigned char ch;
7 unsigned char count;
8} tre_filter_profile_t;
9
10typedef 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
18int
19tre_filter_find(const unsigned char *str, size_t len, tre_filter_t *filter);
src/core/regex/tre-internal.h+0−287
@@ -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. */
50typedef 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. */
81typedef 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(). */
115typedef 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(). */
120typedef int (*tre_ctype_t)(tre_cint_t);
121#define tre_isctype(c, type) ( (type)(c) )
122tre_ctype_t tre_ctype(const char *name);
123#endif /* !TRE_USE_SYSTEM_WCTYPE */
124
125typedef 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'. */
148typedef struct tnfa_transition tre_tnfa_transition_t;
149
150struct 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. */
189typedef 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. */
195typedef 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. */
216struct 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
225typedef struct tre_submatch_data tre_submatch_data_t;
226
227
228/* TNFA definition. */
229typedef struct tnfa tre_tnfa_t;
230
231struct 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
252int
253tre_compile(regex_t *preg, const tre_char_t *regex, size_t n, int cflags);
254
255void
256tre_free(regex_t *preg);
257
258void
259tre_fill_pmatch(size_t nmatch, regmatch_t pmatch[], int cflags,
260 const tre_tnfa_t *tnfa, int *tags, int match_eo);
261
262reg_errcode_t
263tre_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
267reg_errcode_t
268tre_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
272reg_errcode_t
273tre_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
278reg_errcode_t
279tre_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 */
src/core/regex/tre-match-approx.c+0−812
@@ -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 */
23char *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
64typedef 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. */
83static void
84tre_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'. */
127inline static void
128tre_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
203reg_errcode_t
204tre_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}
src/core/regex/tre-match-backtrack.c+0−666
@@ -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 */
47char *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
76typedef 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
91typedef 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
198reg_errcode_t
199tre_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}
src/core/regex/tre-match-parallel.c+0−501
@@ -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 */
40char *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
70typedef struct {
71 tre_tnfa_transition_t *state;
72 int *tags;
73} tre_tnfa_reach_t;
74
75typedef struct {
76 int pos;
77 int **tags;
78} tre_reach_pos_t;
79
80
81#ifdef TRE_DEBUG
82static void
83tre_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
107reg_errcode_t
108tre_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 */
src/core/regex/tre-match-utils.h+0−215
@@ -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. */
177inline static int
178tre_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
203inline static int
204tre_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}
src/core/regex/tre-mem.c+0−155
@@ -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. */
28tre_mem_t
29tre_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. */
46void
47tre_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. */
64void *
65tre_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 */
src/core/regex/tre-mem.h+0−66
@@ -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
16typedef struct tre_list {
17 void *data;
18 struct tre_list *next;
19} tre_list_t;
20
21typedef 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
31tre_mem_t tre_mem_new_impl(int provided, void *provided_block);
32void *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. */
62void tre_mem_destroy(tre_mem_t mem);
63
64#endif /* TRE_MEM_H */
65
66/* EOF */
src/core/regex/tre-parse.c+0−1739
@@ -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. */
54static 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'. */
69static void
70tre_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
94static reg_errcode_t
95tre_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. */
124static reg_errcode_t
125tre_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
159static int
160tre_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. */
178int tre_isalnum_func(tre_cint_t c) { return tre_isalnum(c); }
179int tre_isalpha_func(tre_cint_t c) { return tre_isalpha(c); }
180
181#ifdef tre_isascii
182int tre_isascii_func(tre_cint_t c) { return tre_isascii(c); }
183#else /* !tre_isascii */
184int tre_isascii_func(tre_cint_t c) { return !(c >> 7); }
185#endif /* !tre_isascii */
186
187#ifdef tre_isblank
188int tre_isblank_func(tre_cint_t c) { return tre_isblank(c); }
189#else /* !tre_isblank */
190int tre_isblank_func(tre_cint_t c) { return ((c == ' ') || (c == '\t')); }
191#endif /* !tre_isblank */
192
193int tre_iscntrl_func(tre_cint_t c) { return tre_iscntrl(c); }
194int tre_isdigit_func(tre_cint_t c) { return tre_isdigit(c); }
195int tre_isgraph_func(tre_cint_t c) { return tre_isgraph(c); }
196int tre_islower_func(tre_cint_t c) { return tre_islower(c); }
197int tre_isprint_func(tre_cint_t c) { return tre_isprint(c); }
198int tre_ispunct_func(tre_cint_t c) { return tre_ispunct(c); }
199int tre_isspace_func(tre_cint_t c) { return tre_isspace(c); }
200int tre_isupper_func(tre_cint_t c) { return tre_isupper(c); }
201int tre_isxdigit_func(tre_cint_t c) { return tre_isxdigit(c); }
202
203struct {
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
227tre_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
248static reg_errcode_t
249tre_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
417static reg_errcode_t
418tre_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. */
581static int
582tre_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
598static reg_errcode_t
599tre_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
913typedef 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
928reg_errcode_t
929tre_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 */
src/core/regex/tre-parse.h+0−50
@@ -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. */
13typedef 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. */
45reg_errcode_t
46tre_parse(tre_parse_ctx_t *ctx);
47
48#endif /* TRE_PARSE_H */
49
50/* EOF */
src/core/regex/tre-stack.c+0−125
@@ -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
19union tre_stack_item {
20 void *voidptr_value;
21 int int_value;
22};
23
24struct 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
33tre_stack_t *
34tre_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
55void
56tre_stack_destroy(tre_stack_t *s)
57{
58 xfree(s->stack);
59 xfree(s);
60}
61
62int
63tre_stack_num_objects(tre_stack_t *s)
64{
65 return s->ptr;
66}
67
68static reg_errcode_t
69tre_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
114define_pushf(int, int)
115define_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
122define_popf(int, int)
123define_popf(voidptr, void *)
124
125/* EOF */
src/core/regex/tre-stack.h+0−76
@@ -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
15typedef 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. */
21tre_stack_t *
22tre_stack_new(int size, int max_size, int increment);
23
24/* Frees the stack object. */
25void
26tre_stack_destroy(tre_stack_t *s);
27
28/* Returns the current number of objects in the stack. */
29int
30tre_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
39declare_pushf(voidptr, void *);
40declare_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
48declare_popf(voidptr, void *);
49declare_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 */
src/core/regex/tre.h+0−276
@@ -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
33extern "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
43typedef 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
66typedef int regoff_t;
67typedef struct {
68 size_t re_nsub; /* Number of parenthesized subexpressions. */
69 void *value; /* For internal use only. */
70} regex_t;
71
72typedef struct {
73 regoff_t rm_so;
74 regoff_t rm_eo;
75} regmatch_t;
76
77
78typedef 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 */
131extern int
132tre_regcomp(regex_t *preg, const char *regex, int cflags);
133
134extern int
135tre_regexec(const regex_t *preg, const char *string, size_t nmatch,
136 regmatch_t pmatch[], int eflags);
137
138extern size_t
139tre_regerror(int errcode, const regex_t *preg, char *errbuf,
140 size_t errbuf_size);
141
142extern void
143tre_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). */
151extern int
152tre_regwcomp(regex_t *preg, const wchar_t *regex, int cflags);
153
154extern int
155tre_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). */
161extern int
162tre_regncomp(regex_t *preg, const char *regex, size_t len, int cflags);
163
164extern int
165tre_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
169extern int
170tre_regwncomp(regex_t *preg, const wchar_t *regex, size_t len, int cflags);
171
172extern int
173tre_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. */
180typedef 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. */
193typedef 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. */
204extern int
205tre_regaexec(const regex_t *preg, const char *string,
206 regamatch_t *match, regaparams_t params, int eflags);
207
208extern int
209tre_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. */
213extern int
214tre_regawexec(const regex_t *preg, const wchar_t *string,
215 regamatch_t *match, regaparams_t params, int eflags);
216
217extern int
218tre_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. */
223extern void
224tre_regaparams_default(regaparams_t *params);
225#endif /* TRE_APPROX */
226
227#ifdef TRE_WCHAR
228typedef wchar_t tre_char_t;
229#else /* !TRE_WCHAR */
230typedef unsigned char tre_char_t;
231#endif /* !TRE_WCHAR */
232
233typedef 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
240extern int
241tre_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. */
245extern char *
246tre_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. */
251extern int
252tre_config(int query, void *result);
253
254enum {
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. */
263extern int
264tre_have_backrefs(const regex_t *preg);
265
266/* Returns 1 if the compiled pattern uses approximate matching features,
267 0 if not. */
268extern int
269tre_have_approx(const regex_t *preg);
270
271#ifdef __cplusplus
272}
273#endif
274#endif /* TRE_H */
275
276/* EOF */
src/core/regex/xmalloc.c+0−351
@@ -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
30typedef 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
39typedef struct {
40 hashTableItem **table;
41} hashTable;
42
43static int xmalloc_peak;
44int xmalloc_current;
45static int xmalloc_peak_blocks;
46int xmalloc_current_blocks;
47static 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
53static hashTable *
54hash_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
74static int
75hash_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
92static void
93hash_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
127static void
128hash_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
168static hashTable *xmalloc_table = NULL;
169
170static void
171xmalloc_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
192void
193xmalloc_configure(int fail_after)
194{
195 xmalloc_init();
196 xmalloc_fail_after = fail_after;
197}
198
199int
200xmalloc_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
240void *
241xmalloc_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
271void *
272xcalloc_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
303void
304xfree_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
316void *
317xrealloc_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 */
src/core/regex/xmalloc.h+0−77
@@ -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
12void *xmalloc_impl(size_t size, const char *file, int line, const char *func);
13void *xcalloc_impl(size_t nmemb, size_t size, const char *file, int line,
14 const char *func);
15void xfree_impl(void *ptr, const char *file, int line, const char *func);
16void *xrealloc_impl(void *ptr, size_t new_size, const char *file, int line,
17 const char *func);
18int xmalloc_dump_leaks(void);
19void 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 */
src/core/ulib_regex.cpp+0−407
@@ -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
46CK_DLL_SFUN( regex_match );
47CK_DLL_SFUN( regex_match2 );
48CK_DLL_SFUN( regex_replace );
49CK_DLL_SFUN( regex_replaceAll );
50
51DLL_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
96CK_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(&regex, pattern->str().c_str(), REG_EXTENDED | REG_NOSUB);
117 if(result != 0)
118 goto error;
119
120 r_free = TRUE;
121
122 result = regexec(&regex, str->str().c_str(), 0, NULL, 0);
123
124 RETURN->v_int = (result == 0 ? 1 : 0);
125
126 regfree(&regex);
127 r_free = FALSE;
128
129 return;
130
131error:
132 if(result != 0)
133 {
134 char buf[256];
135
136 regerror(result, &regex, buf, 256);
137 EM_error2( 0, "(RegEx.match): regex reported error: %s", buf);
138 }
139
140 if(r_free)
141 {
142 regfree(&regex);
143 r_free = FALSE;
144 }
145
146 RETURN->v_int = 0;
147}
148
149
150CK_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(&regex, 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(&regex, str->str().c_str(), regex.re_nsub+1, matcharray, 0);
192
193 RETURN->v_int = (result == 0 ? 1 : 0);
194
195 regfree(&regex);
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
216error:
217 if(result != 0)
218 {
219 char errbuf[256];
220
221 regerror(result, &regex, errbuf, 256);
222 EM_error2( 0, "(RegEx.match): regex reported error: %s", errbuf);
223 }
224
225 if(r_free)
226 {
227 regfree(&regex);
228 r_free = FALSE;
229 }
230
231 SAFE_DELETE(matcharray);
232
233 RETURN->v_int = 0;
234}
235
236
237CK_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(&regex, 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(&regex, str->str().c_str(), regex.re_nsub+1, matcharray, 0);
280
281 regfree(&regex);
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
299error:
300 if(result != 0)
301 {
302 char errbuf[256];
303
304 regerror(result, &regex, errbuf, 256);
305 EM_error2( 0, "(RegEx.match): regex reported error: %s", errbuf);
306 }
307
308 if(r_free)
309 {
310 regfree(&regex);
311 r_free = FALSE;
312 }
313
314 RETURN->v_int = 0;
315}
316
317
318CK_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(&regex, 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(&regex, 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(&regex);
384 r_free = FALSE;
385
386 RETURN->v_string = ret;
387
388 return;
389
390error:
391 if(result != 0)
392 {
393 char errbuf[256];
394
395 regerror(result, &regex, errbuf, 256);
396 EM_error2( 0, "(RegEx.match): regex reported error: %s", errbuf);
397 }
398
399 if(r_free)
400 {
401 regfree(&regex);
402 r_free = FALSE;
403 }
404
405 RETURN->v_int = 0;
406}
407
src/core/ulib_regex.h+0−40
@@ -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
37DLL_QUERY regex_query( Chuck_DL_Query * QUERY );
38
39
40#endif // __ULIB_REGEX_H__
src/makefile+2−2
@@ -40,7 +40,7 @@ EMSCRIPTENSRCS=host_web/chuck_emscripten.cpp \ · 39 unmodified lines
40 core/uana_extract.cpp core/uana_xform.cpp core/ugen_filter.cpp \40 core/uana_extract.cpp core/uana_xform.cpp core/ugen_filter.cpp \
41 core/ugen_osc.cpp core/ugen_stk.cpp core/ugen_xxx.cpp \41 core/ugen_osc.cpp core/ugen_stk.cpp core/ugen_xxx.cpp \
42 core/ulib_ai.cpp core/ulib_doc.cpp core/ulib_machine.cpp \42 core/ulib_ai.cpp core/ulib_doc.cpp core/ulib_machine.cpp \
43 core/ulib_math.cpp core/ulib_regex.cpp core/ulib_std.cpp \43 core/ulib_math.cpp core/ulib_std.cpp \
44 core/util_buffers.cpp core/util_math.c core/util_raw.c \44 core/util_buffers.cpp core/util_math.c core/util_raw.c \
45 core/util_string.cpp core/util_xforms.c core/chuck_win32.c \45 core/util_string.cpp core/util_xforms.c core/chuck_win32.c \
46 core/util_sndfile.c46 core/util_sndfile.c
@@ -157,7 +157,7 @@ CXXOBJS_CORE+= chuck.o chuck_absyn.o chuck_parse.o chuck_errmsg.o \ · 110 unmodified lines
157 chuck_carrier.o chuck_globals.o \157 chuck_carrier.o chuck_globals.o \
158 hidio_sdl.o midiio_rtmidi.o rtmidi.o ugen_osc.o ugen_filter.o \158 hidio_sdl.o midiio_rtmidi.o rtmidi.o ugen_osc.o ugen_filter.o \
159 ugen_stk.o ugen_xxx.o ulib_ai.o ulib_doc.o ulib_machine.o \159 ugen_stk.o ugen_xxx.o ulib_ai.o ulib_doc.o ulib_machine.o \
160 ulib_math.o ulib_std.o ulib_opsc.o ulib_regex.o util_buffers.o \160 ulib_math.o ulib_std.o ulib_opsc.o util_buffers.o \
161 util_console.o util_string.o util_thread.o util_opsc.o \161 util_console.o util_string.o util_thread.o util_opsc.o \
162 util_serial.o util_hid.o uana_xform.o uana_extract.o162 util_serial.o util_hid.o uana_xform.o uana_extract.o
163LO_COBJS_CORE+= lo/address.o lo/blob.o lo/bundle.o lo/message.o lo/method.o \163LO_COBJS_CORE+= lo/address.o lo/blob.o lo/bundle.o lo/message.o lo/method.o \
src/xcode/chuck.xcodeproj/project.pbxproj+0−6
@@ -68,7 +68,6 @@ · 67 unmodified lines
68 091677411F7CAD1100248F78 /* uana_extract.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 09812B980C6E394A00D1D68C /* uana_extract.cpp */; };68 091677411F7CAD1100248F78 /* uana_extract.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 09812B980C6E394A00D1D68C /* uana_extract.cpp */; };
69 091677431F7CAD1100248F78 /* util_serial.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0966E6C815CFBB0F0099CBD8 /* util_serial.cpp */; };69 091677431F7CAD1100248F78 /* util_serial.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0966E6C815CFBB0F0099CBD8 /* util_serial.cpp */; };
70 091677441F7CAD1100248F78 /* chuck_io.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0962DA2715D3A626002DEFF3 /* chuck_io.cpp */; };70 091677441F7CAD1100248F78 /* chuck_io.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0962DA2715D3A626002DEFF3 /* chuck_io.cpp */; };
71 091677451F7CAD1100248F78 /* ulib_regex.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 091F327C17A03A4D00686C8D /* ulib_regex.cpp */; };
72 091677481F7CAD1100248F78 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09A40BD40A9A57C6007604EA /* Carbon.framework */; };71 091677481F7CAD1100248F78 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09A40BD40A9A57C6007604EA /* Carbon.framework */; };
73 091677491F7CAD1100248F78 /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09A40BD50A9A57C6007604EA /* CoreAudio.framework */; };72 091677491F7CAD1100248F78 /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09A40BD50A9A57C6007604EA /* CoreAudio.framework */; };
74 0916774A1F7CAD1100248F78 /* CoreMIDI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09A40BD60A9A57C6007604EA /* CoreMIDI.framework */; };73 0916774A1F7CAD1100248F78 /* CoreMIDI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09A40BD60A9A57C6007604EA /* CoreMIDI.framework */; };
@@ -139,8 +138,6 @@ · 64 unmodified lines
139 090B4B1A0C4F038E00DF5577 /* uana_xform.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = uana_xform.cpp; path = ../core/uana_xform.cpp; sourceTree = SOURCE_ROOT; };138 090B4B1A0C4F038E00DF5577 /* uana_xform.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = uana_xform.cpp; path = ../core/uana_xform.cpp; sourceTree = SOURCE_ROOT; };
140 090B4B1B0C4F038E00DF5577 /* uana_xform.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = uana_xform.h; path = ../core/uana_xform.h; sourceTree = SOURCE_ROOT; };139 090B4B1B0C4F038E00DF5577 /* uana_xform.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = uana_xform.h; path = ../core/uana_xform.h; sourceTree = SOURCE_ROOT; };
141 091677521F7CAD1100248F78 /* libchuck.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libchuck.a; sourceTree = BUILT_PRODUCTS_DIR; };140 091677521F7CAD1100248F78 /* libchuck.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libchuck.a; sourceTree = BUILT_PRODUCTS_DIR; };
142 091F327C17A03A4D00686C8D /* ulib_regex.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ulib_regex.cpp; path = ../core/ulib_regex.cpp; sourceTree = SOURCE_ROOT; };
143 091F327D17A03A4D00686C8D /* ulib_regex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ulib_regex.h; path = ../core/ulib_regex.h; sourceTree = SOURCE_ROOT; };
144 095D14581341476200BBD9EB /* MultitouchSupport.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MultitouchSupport.framework; path = System/Library/PrivateFrameworks/MultitouchSupport.framework; sourceTree = SDKROOT; };141 095D14581341476200BBD9EB /* MultitouchSupport.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MultitouchSupport.framework; path = System/Library/PrivateFrameworks/MultitouchSupport.framework; sourceTree = SDKROOT; };
145 0962DA2715D3A626002DEFF3 /* chuck_io.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = chuck_io.cpp; path = ../core/chuck_io.cpp; sourceTree = SOURCE_ROOT; };142 0962DA2715D3A626002DEFF3 /* chuck_io.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = chuck_io.cpp; path = ../core/chuck_io.cpp; sourceTree = SOURCE_ROOT; };
146 0962DA2815D3A626002DEFF3 /* chuck_io.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = chuck_io.h; path = ../core/chuck_io.h; sourceTree = SOURCE_ROOT; };143 0962DA2815D3A626002DEFF3 /* chuck_io.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = chuck_io.h; path = ../core/chuck_io.h; sourceTree = SOURCE_ROOT; };
@@ -932,8 +929,6 @@ · 785 unmodified lines
932 09A40B4F0A9A54DA007604EA /* ulib_opsc.h */,929 09A40B4F0A9A54DA007604EA /* ulib_opsc.h */,
933 09A40B500A9A54DA007604EA /* ulib_std.cpp */,930 09A40B500A9A54DA007604EA /* ulib_std.cpp */,
934 09A40B510A9A54DA007604EA /* ulib_std.h */,931 09A40B510A9A54DA007604EA /* ulib_std.h */,
935 091F327C17A03A4D00686C8D /* ulib_regex.cpp */,
936 091F327D17A03A4D00686C8D /* ulib_regex.h */,
937 09A40B520A9A54DA007604EA /* util_buffers.cpp */,932 09A40B520A9A54DA007604EA /* util_buffers.cpp */,
938 09A40B530A9A54DA007604EA /* util_buffers.h */,933 09A40B530A9A54DA007604EA /* util_buffers.h */,
939 09A40B540A9A54DA007604EA /* util_console.cpp */,934 09A40B540A9A54DA007604EA /* util_console.cpp */,
@@ -1169,7 +1164,6 @@ · 229 unmodified lines
1169 8615DFFA264D9AE20084EF86 /* chuck_globals.cpp in Sources */,1164 8615DFFA264D9AE20084EF86 /* chuck_globals.cpp in Sources */,
1170 091677431F7CAD1100248F78 /* util_serial.cpp in Sources */,1165 091677431F7CAD1100248F78 /* util_serial.cpp in Sources */,
1171 091677441F7CAD1100248F78 /* chuck_io.cpp in Sources */,1166 091677441F7CAD1100248F78 /* chuck_io.cpp in Sources */,
1172 091677451F7CAD1100248F78 /* ulib_regex.cpp in Sources */,
1173 );1167 );
1174 runOnlyForDeploymentPostprocessing = 0;1168 runOnlyForDeploymentPostprocessing = 0;
1175 };1169 };

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t