RPM Community Forums

Mailing List Message of <rpm-cvs>

[CVS] RPM: lua/ Makefile.am lua/local/ llocal.lua lrexlib.c lrexlib.h ...

From: Ralf S. Engelschall <rse@rpm5.org>
Date: Sat 12 Jan 2008 - 13:33:34 CET
Message-Id: <20080112123334.9140F348460@rpm5.org>
  RPM Package Manager, CVS Repository
  http://rpm5.org/cvs/
  ____________________________________________________________________________

  Server: rpm5.org                         Name:   Ralf S. Engelschall
  Root:   /v/rpm/cvs                       Email:  rse@rpm5.org
  Module: rpm lua                          Date:   12-Jan-2008 13:33:34
  Branch: HEAD                             Handle: 2008011212333301

  Added files:
    lua/local               lrexlib.txt lrexlib_algo.h lrexlib_common.c
                            lrexlib_common.h lrexlib_lpcre.c lrexlib_lpcre_f.c
                            lrexlib_lposix.c
  Modified files:
    lua                     Makefile.am
    lua/local               llocal.lua lrexlib.h
    rpm/rpmio               rpmlua.c
  Removed files:
    lua/local               lrexlib.c

  Log:
    Upgrade the RPM Lua extension "lrexlib" from ancient version 1.1x
    to latest version 2.2 in order to get a larger bunch of additional
    PCRE-based functionalities (for instance string splitting, etc). The
    source files had to be renamed to contain prefix "lrexlib_" in order to
    resolve conflict between the Lua POSIX extension "lposix.c" and the Lua
    POSIX-Regex extension "lposix.c".

  Summary:
    Revision    Changes     Path
    1.17        +6  -1      lua/Makefile.am
    1.12        +8  -8      lua/local/llocal.lua
    1.9         +0  -661    lua/local/lrexlib.c
    1.3         +3  -1      lua/local/lrexlib.h
    1.1         +655 -0     lua/local/lrexlib.txt
    1.1         +623 -0     lua/local/lrexlib_algo.h
    1.1         +244 -0     lua/local/lrexlib_common.c
    1.1         +86 -0      lua/local/lrexlib_common.h
    1.1         +446 -0     lua/local/lrexlib_lpcre.c
    1.1         +194 -0     lua/local/lrexlib_lpcre_f.c
    1.1         +308 -0     lua/local/lrexlib_lposix.c
    2.40        +2  -1      rpm/rpmio/rpmlua.c
  ____________________________________________________________________________

  patch -p0 <<'@@ .'
  Index: lua/Makefile.am
  ============================================================================
  $ cvs diff -u -r1.16 -r1.17 Makefile.am
  --- lua/Makefile.am	10 Jan 2008 20:49:36 -0000	1.16
  +++ lua/Makefile.am	12 Jan 2008 12:33:33 -0000	1.17
  @@ -29,7 +29,12 @@
   		local/lposix.h \
   		local/lposix.c \
   		local/lrexlib.h \
  -		local/lrexlib.c \
  +		local/lrexlib_algo.h \
  +		local/lrexlib_common.h \
  +		local/lrexlib_common.c \
  +		local/lrexlib_lpcre.c \
  +		local/lrexlib_lpcre_f.c \
  +		local/lrexlib_lposix.c \
   		local/luuid.h \
   		local/luuid.c \
   		linit.c \
  @@ .
  patch -p0 <<'@@ .'
  Index: lua/local/llocal.lua
  ============================================================================
  $ cvs diff -u -r1.11 -r1.12 llocal.lua
  --- lua/local/llocal.lua	11 Jan 2008 18:58:16 -0000	1.11
  +++ lua/local/llocal.lua	12 Jan 2008 12:33:33 -0000	1.12
  @@ -5,9 +5,9 @@
   
   --  provide a generic regular expression constructor
   --  based on the most powerful regular expression engine
  -rex.new = rex.newPCRE
  -if rex.new == nil then
  -   rex.new = rex.newPOSIX
  +rex = rex_posix
  +if rex_pcre ~= nil then
  +   rex = rex_pcre
   end
   
   --  provide additional "util" namespace
  @@ -26,7 +26,7 @@
   	local pat = rex.new(expr)
   	local pos = 1
   	for line in io.lines(filename) do
  -		if pat:match(line) then
  +		if pat:tfind(line) then
   			table.insert(lines, pos, line)
   		end
   		pos = pos + 1
  @@ -49,7 +49,7 @@
   	end
   	local pat = rex.new(expr)
   	for line in io.lines(filename) do
  -		if pat:match(line) then
  +		if pat:tfind(line) then
   			return true
   		end
   	end
  @@ -62,7 +62,7 @@
   
   --  regular expression based string matching
   function util.rmatch(str, regex)
  -    return rex.new(regex):match(str)
  +    return rex.new(regex):tfind(str)
   end
   
   --  regular expression based string substitution
  @@ -72,7 +72,7 @@
       local result_last = ""
       while result ~= result_last do
           result_last = result
  -        local s, e, m = re:match(result)
  +        local s, e, m = re:tfind(result)
           if s ~= nil then
               local prolog = string.sub(result, 1, s - 1)
               local epilog = string.sub(result, e + 1)
  @@ -105,7 +105,7 @@
               end
               count = count - 1
           end
  -        local s, e, m = re:match(str)
  +        local s, e, m = re:tfind(str)
           if s ~= nil then
               table.insert(result, string.sub(str, 1, s -1))
               str = string.sub(str, e + 1)
  @@ .
  rm -f lua/local/lrexlib.c <<'@@ .'
  Index: lua/local/lrexlib.c
  ============================================================================
  [NO CHANGE SUMMARY BECAUSE FILE AS A WHOLE IS JUST REMOVED]
  @@ .
  patch -p0 <<'@@ .'
  Index: lua/local/lrexlib.h
  ============================================================================
  $ cvs diff -u -r1.2 -r1.3 lrexlib.h
  --- lua/local/lrexlib.h	23 Mar 2004 05:09:14 -0000	1.2
  +++ lua/local/lrexlib.h	12 Jan 2008 12:33:33 -0000	1.3
  @@ -1,7 +1,9 @@
   #ifndef LREXLIB_H
   #define LREXLIB_H
   
  -int luaopen_rex(lua_State *L)
  +int luaopen_rex_pcre(lua_State *L)
  +	/*@modifies L @*/;
  +int luaopen_rex_posix(lua_State *L)
   	/*@modifies L @*/;
   
   #endif
  @@ .
  patch -p0 <<'@@ .'
  Index: lua/local/lrexlib.txt
  ============================================================================
  $ cvs diff -u -r0 -r1.1 lrexlib.txt
  --- /dev/null	2008-01-12 13:33:00 +0100
  +++ lrexlib.txt	2008-01-12 13:33:33 +0100
  @@ -0,0 +1,655 @@
  +Lrexlib 2.2 Reference Manual
  +
  +Table of Contents
  +
  +  * Introduction
  +  * Notes
  +  * Common (PCRE and POSIX) functions and methods
  +      + match
  +      + find
  +      + gmatch
  +      + gsub
  +      + split
  +      + flags
  +      + new
  +      + tfind
  +      + exec
  +  * PCRE-only functions and methods
  +      + dfa_exec
  +      + maketables
  +      + config
  +      + version
  +  * Other functions
  +      + plainfind
  +  * Incompatibilities with the Previous Versions
  +
  +-------------------------------------------------------------------------------
  +
  +Introduction
  +
  +Lrexlib provides bindings of the two principal regular expression library
  +interfaces (POSIX and PCRE) to Lua 5.1.
  +
  +Lrexlib builds into shared libraries called by default rex_posix.so and
  +rex_pcre.so, which can be used with require.
  +
  +Lrexlib is copyright Reuben Thomas 2000-2007 and copyright Shmuel Zeigerman
  +2004-2007, and is released under the MIT license.
  +
  +-------------------------------------------------------------------------------
  +
  +Notes
  +
  + 1. Most functions and methods in Lrexlib have mandatory and optional
  +    arguments. There are no dependencies between arguments in Lrexlib's
  +    functions and methods. Any optional argument can be supplied as nil (or
  +    omitted if it is trailing one), the library will then use the default value
  +    for that argument.
  +
  + 2. This document uses the following syntax for optional arguments: they are
  +    bracketed separately, and commas are left outside brackets, e.g.:
  +
  +    MyFunc (arg1, arg2, [arg3], [arg4])
  +
  + 3. Throughout this document, the identifier rex is used in place of either
  +    rex_posix or rex_pcre, that are the default namespaces for the
  +    corresponding libraries.
  +
  + 4. All functions receiving a regular expression pattern as an argument will
  +    generate an error if that pattern is found invalid by the used POSIX / PCRE
  +    library.
  +
  + 5. The default value for compilation flags (cf) that Lrexlib uses when the
  +    parameter is not supplied or nil, is:
  +
  +          o 0 for PCRE
  +          o REG_EXTENDED for POSIX regex library
  +
  +    For PCRE, cf may also be supplied as a string, whose characters stand for
  +    PCRE compilation flags. Combinations of the following characters (case
  +    sensitive) are supported:
  +
  +        +-------------------------+
  +        |Character|   PCRE flag   |
  +        |---------+---------------|
  +        |i        |PCRE_CASELESS  |
  +        |---------+---------------|
  +        |m        |PCRE_MULTILINE |
  +        |---------+---------------|
  +        |s        |PCRE_DOTALL    |
  +        |---------+---------------|
  +        |x        |PCRE_EXTENDED  |
  +        |---------+---------------|
  +        |U        |PCRE_UNGREEDY  |
  +        |---------+---------------|
  +        |X        |PCRE_EXTRA     |
  +        +-------------------------+
  +
  + 6. The default value for execution flags (ef) that Lrexlib uses when the
  +    parameter is not supplied or nil, is:
  +
  +          o 0 for PCRE
  +          o 0 for standard POSIX regex library
  +          o REG_STARTEND for those POSIX regex libraries that support it, e.g.
  +            Spencer's.
  +
  + 7. Parameter locale (lo) can be either a string (e.g., "French_France.1252"),
  +    or a userdata obtained from a call to maketables. The default value, used
  +    when the parameter is not supplied or nil, is the built-in PCRE set of
  +    character tables.
  +
  +-------------------------------------------------------------------------------
  +
  +Common (PCRE and POSIX) functions and methods
  +
  +match
  +
  +rex.match (subj, patt, [init], [cf], [ef], [lo])
  +
  +The function searches for the first match of the regexp patt in the string subj
  +, starting from offset init, subject to flags cf and ef.
  +
  +PCRE: A locale lo may be specified.
  +
  +    +-------------------------------------------------------------------------+
  +    |Parameter|            Description             |     Type      | Default  |
  +    |         |                                    |               |  Value   |
  +    |---------+------------------------------------+---------------+----------|
  +    |subj     |subject                             |string         |n/a       |
  +    |---------+------------------------------------+---------------+----------|
  +    |patt     |regular expression pattern          |string         |n/a       |
  +    |---------+------------------------------------+---------------+----------|
  +    |[init]   |start offset in the subject (can be |number         |1         |
  +    |         |negative)                           |               |          |
  +    |---------+------------------------------------+---------------+----------|
  +    |[cf]     |compilation flags (bitwise OR)      |number         |cf        |
  +    |---------+------------------------------------+---------------+----------|
  +    |[ef]     |execution flags (bitwise OR)        |number         |ef        |
  +    |---------+------------------------------------+---------------+----------|
  +    |[lo]     |[PCRE] locale                       |string or      |locale    |
  +    |         |                                    |userdata       |          |
  +    +-------------------------------------------------------------------------+
  +
  +Returns on success:
  +     1. All substring matches ("captures"), in the order they appear in the
  +        pattern. false is returned for sub-patterns that did not participate in
  +        the match. If the pattern specified no captures then the whole matched
  +        substring is returned.
  +Returns on failure:
  +     1. nil
  +
  +-------------------------------------------------------------------------------
  +
  +find
  +
  +rex.find (subj, patt, [init], [cf], [ef], [lo])
  +
  +The function searches for the first match of the regexp patt in the string subj
  +, starting from offset init, subject to flags cf and ef.
  +
  +PCRE: A locale lo may be specified.
  +
  +    +-------------------------------------------------------------------------+
  +    |Parameter|            Description             |     Type      | Default  |
  +    |         |                                    |               |  Value   |
  +    |---------+------------------------------------+---------------+----------|
  +    |subj     |subject                             |string         |n/a       |
  +    |---------+------------------------------------+---------------+----------|
  +    |patt     |regular expression pattern          |string         |n/a       |
  +    |---------+------------------------------------+---------------+----------|
  +    |[init]   |start offset in the subject (can be |number         |1         |
  +    |         |negative)                           |               |          |
  +    |---------+------------------------------------+---------------+----------|
  +    |[cf]     |compilation flags (bitwise OR)      |number         |cf        |
  +    |---------+------------------------------------+---------------+----------|
  +    |[ef]     |execution flags (bitwise OR)        |number         |ef        |
  +    |---------+------------------------------------+---------------+----------|
  +    |[lo]     |[PCRE] locale                       |string or      |locale    |
  +    |         |                                    |userdata       |          |
  +    +-------------------------------------------------------------------------+
  +
  +Returns on success:
  +     1. The start point of the match (a number).
  +     2. The end point of the match (a number).
  +     3. All substring matches ("captures"), in the order they appear in the
  +        pattern. false is returned for sub-patterns that did not participate in
  +        the match.
  +Returns on failure:
  +     1. nil
  +
  +-------------------------------------------------------------------------------
  +
  +gmatch
  +
  +rex.gmatch (subj, patt, [cf], [ef], [lo])
  +
  +The function is intended for use in the generic for Lua construct. It returns
  +an iterator for repeated matching of the pattern patt in the string subj,
  +subject to flags cf and ef.
  +
  +PCRE: A locale lo may be specified.
  +
  +    +-------------------------------------------------------------------------+
  +    |Parameter|         Description          |       Type       |Default Value|
  +    |---------+------------------------------+------------------+-------------|
  +    |subj     |subject                       |string            |n/a          |
  +    |---------+------------------------------+------------------+-------------|
  +    |patt     |regular expression pattern    |string            |n/a          |
  +    |---------+------------------------------+------------------+-------------|
  +    |[cf]     |compilation flags (bitwise OR)|number            |cf           |
  +    |---------+------------------------------+------------------+-------------|
  +    |[ef]     |execution flags (bitwise OR)  |number            |ef           |
  +    |---------+------------------------------+------------------+-------------|
  +    |[lo]     |[PCRE] locale                 |string or userdata|locale       |
  +    +-------------------------------------------------------------------------+
  +
  +The iterator function is called by Lua. On every iteration (that is, on every
  +match), it returns all captures in the order they appear in the pattern (or the
  +entire match if the pattern specified no captures). The iteration will continue
  +till the subject fails to match.
  +
  +-------------------------------------------------------------------------------
  +
  +gsub
  +
  +rex.gsub (subj, patt, repl, [n], [cf], [ef], [lo])
  +
  +This function searches for all matches of the pattern patt in the string subj
  +and replaces them according to the parameters repl and n (see details below).
  +
  +PCRE: A locale lo may be specified.
  +
  +    +-------------------------------------------------------------------------+
  +    |Parameter|              Description              |     Type     |Default |
  +    |         |                                       |              | Value  |
  +    |---------+---------------------------------------+--------------+--------|
  +    |subj     |subject                                |string        |n/a     |
  +    |---------+---------------------------------------+--------------+--------|
  +    |patt     |regular expression pattern             |string        |n/a     |
  +    |---------+---------------------------------------+--------------+--------|
  +    |         |                                       |string,       |        |
  +    |repl     |substitution source                    |function or   |n/a     |
  +    |         |                                       |table         |        |
  +    |---------+---------------------------------------+--------------+--------|
  +    |[n]      |maximum number of matches to search    |number or     |nil     |
  +    |         |for, or control function, or nil       |function      |        |
  +    |---------+---------------------------------------+--------------+--------|
  +    |[cf]     |compilation flags (bitwise OR)         |number        |cf      |
  +    |---------+---------------------------------------+--------------+--------|
  +    |[ef]     |execution flags (bitwise OR)           |number        |ef      |
  +    |---------+---------------------------------------+--------------+--------|
  +    |[lo]     |[PCRE] locale                          |string or     |locale  |
  +    |         |                                       |userdata      |        |
  +    +-------------------------------------------------------------------------+
  +
  +Returns:
  +     1. The subject string with the substitutions made.
  +     2. Number of matches found.
  +     3. Number of substitutions made.
  +Details:
  +
  +    The parameter repl can be either a string, a function or a table. On each
  +    match made, it is converted into a value repl_out that may be used for the
  +    replacement.
  +
  +    repl_out is generated differently depending on the type of repl:
  +
  +     1. If repl is a string then it is treated as a template for substitution,
  +        where the %X occurences in repl are handled in a special way, depending
  +        on the value of the character X:
  +
  +          o if X represents a digit, then each %X occurence is substituted by
  +            the value of the X-th submatch (capture), with the following cases
  +            handled specially:
  +              # each %0 is substituted by the entire match
  +              # if the pattern contains no captures, then each %1 is
  +                substituted by the entire match
  +              # any other %X where X is greater than the number of captures in
  +                the pattern will generate an error ("invalid capture index")
  +              # if the pattern does contain a capture with number X but that
  +                capture didn't participate in the match, then %X is substituted
  +                by an empty string
  +          o if X is any non-digit character then %X is substituted by X
  +
  +        All parts of repl other than %X are copied to repl_out verbatim.
  +
  +     2. If repl is a function then it is called on each match with the
  +        submatches passed as parameters (if there are no submatches then the
  +        entire match is passed as the only parameter). repl_out is the return
  +        value of the repl call, and is interpreted as follows:
  +
  +          o if it is a string or a number (coerced to a string), then the
  +            replacement value is that string;
  +          o if it is a nil or a false, then no replacement is to be done;
  +
  +     3. If repl is a table then repl_out is repl [m1], where m1 is the first
  +        submatch (or the entire match if there are no submatches), following
  +        the same rules as for the return value of repl call, described in the
  +        above paragraph.
  +
  +    Note: Under some circumstances, the value of repl_out may be ignored; see
  +    below.
  +
  +    gsub behaves differently depending on the type of n:
  +
  +     1. If n is a number then it is treated as the maximum number of matches to
  +        search for (an omitted or nil value means an unlimited number of
  +        matches). On each match, the replacement value is the repl_out string
  +        (see above).
  +
  +     2. If n is a function, then it is called on each match, after repl_out is
  +        produced (so if repl is a function, it will be called prior to the n
  +        call).
  +
  +        n receives 3 arguments and returns 2 values. Its arguments are:
  +
  +             1. The start offset of the match (a number)
  +             2. The end offset of the match (a number)
  +             3. repl_out
  +
  +        The type of its first return controls the replacement produced by gsub
  +        for the current match:
  +
  +              # true -- replace/don't replace, according to repl_out;
  +              # nil/false -- don't replace;
  +              # a string (or a number coerced to a string) -- replace by that
  +                string;
  +
  +        The type of its second return controls gsub behavior after the current
  +        match is handled:
  +
  +              # nil/false -- no changes: n will be called on the next match;
  +              # true -- search for an unlimited number of matches; n will not
  +                be called again;
  +              # a number -- maximum number of matches to search for, beginning
  +                from the next match; n will not be called again;
  +
  +-------------------------------------------------------------------------------
  +
  +split
  +
  +rex.split (subj, sep, [cf], [ef], [lo])
  +
  +The function is intended for use in the generic for Lua construct. It is used
  +for splitting a subject string subj into parts (sections). The sep parameter is
  +a regular expression pattern representing separators between the sections.
  +
  +The function returns an iterator for repeated matching of the pattern sep in
  +the string subj, subject to flags cf and ef.
  +
  +PCRE: A locale lo may be specified.
  +
  +    +-------------------------------------------------------------------------+
  +    |Parameter|           Description            |      Type      |  Default  |
  +    |         |                                  |                |   Value   |
  +    |---------+----------------------------------+----------------+-----------|
  +    |subj     |subject                           |string          |n/a        |
  +    |---------+----------------------------------+----------------+-----------|
  +    |sep      |separator (regular expression     |string          |n/a        |
  +    |         |pattern)                          |                |           |
  +    |---------+----------------------------------+----------------+-----------|
  +    |[cf]     |compilation flags (bitwise OR)    |number          |cf         |
  +    |---------+----------------------------------+----------------+-----------|
  +    |[ef]     |execution flags (bitwise OR)      |number          |ef         |
  +    |---------+----------------------------------+----------------+-----------|
  +    |[lo]     |[PCRE] locale                     |string or       |locale     |
  +    |         |                                  |userdata        |           |
  +    +-------------------------------------------------------------------------+
  +
  +On every iteration pass, the iterator returns:
  +
  +     1. A subject section (can be an empty string), followed by
  +     2. All captures in the order they appear in the sep pattern (or the entire
  +        match if the sep pattern specified no captures). If there is no match
  +        (this can occur only in the last iteration), then nothing is returned
  +        after the subject section.
  +
  +The iteration will continue till the end of the subject. Unlike gmatch, there
  +will always be at least one iteration pass, even if there's no matches in the
  +subject.
  +
  +-------------------------------------------------------------------------------
  +
  +flags
  +
  +rex.flags ([tb])
  +
  +This function returns a table containing numeric values of the constants
  +defined by the used regex library (either PCRE or POSIX). Those constants are
  +keyed by their names (strings). If the table argument tb is supplied then it is
  +used as the output table, else a new table is created.
  +
  +The constants contained in the returned table can then be used in most
  +functions and methods where compilation flags or execution flags can be
  +specified. They can also be used for comparing with return codes of some
  +functions and methods for determining the reason of failure. For details, see
  +PCRE and POSIX documentation.
  +
  +    +--------------------------------------------------------------+
  +    |Parameter|          Description           |Type |Default Value|
  +    |---------+--------------------------------+-----+-------------|
  +    |[tb]     |a table for placing results into|table|nil          |
  +    +--------------------------------------------------------------+
  +
  +Returns:
  +     1. A table filled with the results.
  +
  +-------------------------------------------------------------------------------
  +
  +new
  +
  +rex.new (patt, [cf], [lo])
  +
  +The functions compiles regular expression patt into a regular expression object
  +whose internal representation is correspondent to the library used (PCRE or
  +POSIX regex). The returned result then can be used by the methods tfind, exec
  +and dfa_exec. Regular expression objects are automatically garbage collected.
  +
  +PCRE: A locale lo may be specified.
  +
  +    +-------------------------------------------------------------------------+
  +    |Parameter|         Description          |       Type       |Default Value|
  +    |---------+------------------------------+------------------+-------------|
  +    |patt     |regular expression pattern    |string            |n/a          |
  +    |---------+------------------------------+------------------+-------------|
  +    |[cf]     |compilation flags (bitwise OR)|number            |cf           |
  +    |---------+------------------------------+------------------+-------------|
  +    |[lo]     |[PCRE] locale                 |string or userdata|locale       |
  +    +-------------------------------------------------------------------------+
  +
  +Returns:
  +     1. Compiled regular expression (a userdata).
  +
  +-------------------------------------------------------------------------------
  +
  +tfind
  +
  +r:tfind (subj, [init], [ef])
  +
  +The method searches for the first match of the compiled regexp r in the string
  +subj, starting from offset init, subject to execution flags ef.
  +
  +    +-------------------------------------------------------------------------+
  +    |Parameter|               Description               |  Type  |  Default   |
  +    |         |                                         |        |   Value    |
  +    |---------+-----------------------------------------+--------+------------|
  +    |r        |regex object produced by new             |userdata|n/a         |
  +    |---------+-----------------------------------------+--------+------------|
  +    |subj     |subject                                  |string  |n/a         |
  +    |---------+-----------------------------------------+--------+------------|
  +    |[init]   |start offset in the subject (can be      |number  |1           |
  +    |         |negative)                                |        |            |
  +    |---------+-----------------------------------------+--------+------------|
  +    |[ef]     |execution flags (bitwise OR)             |number  |ef          |
  +    +-------------------------------------------------------------------------+
  +
  +Returns on success:
  +     1. The start point of the match (a number).
  +     2. The end point of the match (a number).
  +     3. Substring matches ("captures" in Lua terminology) are returned as a
  +        third result, in a table. This table contains false in the positions
  +        where the corresponding sub-pattern did not participate in the match.
  +         1. PCRE: if named subpatterns are used then the table also contains
  +            substring matches keyed by their correspondent subpattern names
  +            (strings).
  +Returns on failure:
  +     1. nil
  +Notes:
  +     1. If named subpatterns (see PCRE docs) are used then the returned table
  +        also contains substring matches keyed by their correspondent subpattern
  +        names (strings).
  +
  +-------------------------------------------------------------------------------
  +
  +exec
  +
  +r:exec (subj, [init], [ef])
  +
  +The method searches for the first match of the compiled regexp r in the string
  +subj, starting from offset init, subject to execution flags ef.
  +
  +    +-------------------------------------------------------------------------+
  +    |Parameter|               Description               |  Type  |  Default   |
  +    |         |                                         |        |   Value    |
  +    |---------+-----------------------------------------+--------+------------|
  +    |r        |regex object produced by new             |userdata|n/a         |
  +    |---------+-----------------------------------------+--------+------------|
  +    |subj     |subject                                  |string  |n/a         |
  +    |---------+-----------------------------------------+--------+------------|
  +    |[init]   |start offset in the subject (can be      |number  |1           |
  +    |         |negative)                                |        |            |
  +    |---------+-----------------------------------------+--------+------------|
  +    |[ef]     |execution flags (bitwise OR)             |number  |ef          |
  +    +-------------------------------------------------------------------------+
  +
  +Returns on success:
  +     1. The start point of the first match (a number).
  +     2. The end point of the first match (a number).
  +     3. The offsets of substring matches ("captures" in Lua terminology) are
  +        returned as a third result, in a table. This table contains false in
  +        the positions where the corresponding sub-pattern did not participate
  +        in the match.
  +         1. PCRE: if named subpatterns are used then the table also contains
  +            substring matches keyed by their correspondent subpattern names
  +            (strings).
  +Returns on failure:
  +     1. nil
  +Example:
  +    If the whole match is at offsets 10,20 and substring matches are at offsets
  +    12,14 and 16,19 then the function returns the following: 10, 20, {
  +    12,14,16,19 }.
  +
  +-------------------------------------------------------------------------------
  +
  +PCRE-only functions and methods
  +
  +dfa_exec
  +
  +[PCRE 6.0 and later. See pcre_dfa_exec in the PCRE docs.]
  +
  +r:dfa_exec (subj, [init], [ef], [ovecsize], [wscount])
  +
  +The method matches a compiled regular expression r against a given subject
  +string subj, using a DFA matching algorithm.
  +
  +    +-------------------------------------------------------------------------+
  +    |Parameter |              Description               |  Type  |  Default   |
  +    |          |                                        |        |   Value    |
  +    |----------+----------------------------------------+--------+------------|
  +    |r         |regex object produced by new            |userdata|n/a         |
  +    |----------+----------------------------------------+--------+------------|
  +    |subj      |subject                                 |string  |n/a         |
  +    |----------+----------------------------------------+--------+------------|
  +    |[init]    |start offset in the subject (can be     |number  |1           |
  +    |          |negative)                               |        |            |
  +    |----------+----------------------------------------+--------+------------|
  +    |[ef]      |execution flags (bitwise OR)            |number  |ef          |
  +    |----------+----------------------------------------+--------+------------|
  +    |[ovecsize]|size of the array for result offsets    |number  |100         |
  +    |----------+----------------------------------------+--------+------------|
  +    |[wscount] |number of elements in the working space |number  |50          |
  +    |          |array                                   |        |            |
  +    +-------------------------------------------------------------------------+
  +
  +Returns on success (either full or partial match):
  +     1. The start point of the matches found (a number).
  +     2. A table containing the end points of the matches found, the longer
  +        matches first.
  +     3. The return value of the underlying pcre_dfa_exec call (a number).
  +Returns on failure (no match):
  +     1. nil
  +Example:
  +    If there are 3 matches found starting at offset 10 and ending at offsets
  +    15, 20 and 25 then the function returns the following: 10, { 25,20,15 }, 3.
  +
  +-------------------------------------------------------------------------------
  +
  +maketables
  +
  +[PCRE only. See pcre_maketables in the PCRE docs.]
  +
  +rex.maketables ()
  +
  +Creates a set of character tables corresponding to the current locale and
  +returns it as a userdata. The returned value can be passed to any Lrexlib
  +function accepting the locale parameter.
  +
  +-------------------------------------------------------------------------------
  +
  +config
  +
  +[PCRE 4.0 and later. See pcre_config in the PCRE docs.]
  +
  +rex.config ([tb])
  +
  +This function returns a table containing the values of the configuration
  +parameters used at PCRE library build-time. Those parameters (numbers) are
  +keyed by their names (strings). If the table argument tb is supplied then it is
  +used as the output table, else a new table is created.
  +
  +    +--------------------------------------------------------------+
  +    |Parameter|          Description           |Type |Default Value|
  +    |---------+--------------------------------+-----+-------------|
  +    |[tb]     |a table for placing results into|table|nil          |
  +    +--------------------------------------------------------------+
  +
  +Returns:
  +     1. A table filled with the results.
  +
  +-------------------------------------------------------------------------------
  +
  +version
  +
  +[PCRE only. See pcre_version in the PCRE docs.]
  +
  +rex.version ()
  +
  +This function returns a string containing the version of the used PCRE library
  +and its release date.
  +
  +-------------------------------------------------------------------------------
  +
  +Other functions
  +
  +plainfind
  +
  +rex.plainfind (subj, patt, [init], [ci])
  +
  +The function searches for the first match of the string patt in the subject
  +subj, starting from offset init.
  +
  +      + The string patt is not regular expression, all its characters stand for
  +        themselves.
  +      + Both strings subj and patt can have embedded zeros.
  +      + The flag ci specifies case-insensitive search (current locale is used).
  +      + This function uses neither PCRE nor POSIX regex library.
  +    +-------------------------------------------------------------------------+
  +    |Parameter|               Description                | Type  |  Default   |
  +    |         |                                          |       |   Value    |
  +    |---------+------------------------------------------+-------+------------|
  +    |subj     |subject                                   |string |n/a         |
  +    |---------+------------------------------------------+-------+------------|
  +    |patt     |text to find                              |string |n/a         |
  +    |---------+------------------------------------------+-------+------------|
  +    |[init]   |start offset in the subject (can be       |number |1           |
  +    |         |negative)                                 |       |            |
  +    |---------+------------------------------------------+-------+------------|
  +    |[ci]     |case insensitive search                   |boolean|false       |
  +    +-------------------------------------------------------------------------+
  +
  +Returns on success:
  +     1. The start point of the match (a number).
  +     2. The end point of the match (a number).
  +Returns on failure:
  +     1. nil
  +
  +-------------------------------------------------------------------------------
  +
  +Incompatibilities with the Previous Versions
  +
  +Incompatibilities between the versions 2.0 and 1.19:
  +
  +     1. Lua 5.1 is required
  +     2. Functions newPCRE and newPOSIX renamed to new
  +     3. Functions flagsPCRE and flagsPOSIX renamed to flags
  +     4. Function versionPCRE renamed to version
  +     5. Method match renamed to tfind
  +     6. Method gmatch removed (similar functionality is provided by function
  +        gmatch)
  +     7. Methods tfind and exec: 2 values are returned on failure
  +     8. (PCRE) exec: the returned table may additionally contain named
  +        subpatterns
  +
  +Incompatibilities between the versions 2.1 and 2.0:
  +
  +     1. match, find, tfind, exec, dfa_exec: only one value (a nil) is returned
  +        when the subject does not match the pattern. Any other failure
  +        generates an error.
  +
  +Incompatibilities between the versions 2.2 and 2.1:
  +
  +     1. gsub: a special "break" return of repl function is deprecated.
  +     2. (PCRE) gsub, gmatch: after finding an empty match at the current
  +        position, the functions try to find a non-empty match anchored to the
  +        same position.
  +
  @@ .
  patch -p0 <<'@@ .'
  Index: lua/local/lrexlib_algo.h
  ============================================================================
  $ cvs diff -u -r0 -r1.1 lrexlib_algo.h
  --- /dev/null	2008-01-12 13:33:00 +0100
  +++ lrexlib_algo.h	2008-01-12 13:33:33 +0100
  @@ -0,0 +1,623 @@
  +/* algo.h */
  +/* See Copyright Notice in the file LICENSE */
  +
  +#include "lrexlib_common.h"
  +
  +#define REX_VERSION "Lrexlib 2.2.2"
  +
  +/* Forward declarations */
  +static void gmatch_pushsubject (lua_State *L, TArgExec *argE);
  +static int findmatch_exec  (TUserdata *ud, TArgExec *argE);
  +static int tfind_exec      (TUserdata *ud, TArgExec *argE);
  +static int split_exec      (TUserdata *ud, TArgExec *argE, int offset);
  +static int compile_regex   (lua_State *L, const TArgComp *argC, TUserdata **pud);
  +static int generate_error  (lua_State *L, const TUserdata *ud, int errcode);
  +
  +#ifndef ALG_OPTLOCALE
  +#  define ALG_OPTLOCALE(a,b,c)  ((void)a)
  +#endif
  +
  +
  +/*  When doing an iterative search, there can occur a situation of a zero-length
  + *  match at the current position, that prevents further advance on the subject
  + *  string.
  + *  There are two ways to handle that (AFAIK):
  + *    a) Advance by one character (continue the search from the next position),
  + *       or
  + *    b) Search for a non-zero-length match that begins from the current
  + *       position ("retry" the search). If the match is not found then advance
  + *       by one character.
  + *  The "b)" seems more correct, but most regex libraries expose no API for that.
  + *  The known exception is PCRE that has flags PCRE_NOTEMPTY and PCRE_ANCHORED.
  + */
  +#ifdef ALG_USERETRY
  +  #define SET_RETRY(a,b) (a=b)
  +  static int gsub_exec (TUserdata *ud, TArgExec *argE, int offset, int retry);
  +  static int gmatch_exec (TUserdata *ud, TArgExec *argE, int retry);
  +  #define GSUB_EXEC gsub_exec
  +  #define GMATCH_EXEC gmatch_exec
  +#else
  +  #define SET_RETRY(a,b) ((void)a)
  +  static int gsub_exec (TUserdata *ud, TArgExec *argE, int offset);
  +  static int gmatch_exec (TUserdata *ud, TArgExec *argE);
  +  #define GSUB_EXEC(a,b,c,d) gsub_exec(a,b,c)
  +  #define GMATCH_EXEC(a,b,c) gmatch_exec(a,b)
  +#endif
  +
  +
  +static int OptLimit (lua_State *L, int pos) {
  +  if (lua_isnoneornil (L, pos))
  +    return GSUB_UNLIMITED;
  +  if (lua_isfunction (L, pos))
  +    return GSUB_CONDITIONAL;
  +  if (lua_isnumber (L, pos)) {
  +    int a = lua_tointeger (L, pos);
  +    return a < 0 ? 0 : a;
  +  }
  +  return luaL_argerror (L, pos, "number or function expected");
  +}
  +
  +
  +static int get_startoffset(lua_State *L, int stackpos, size_t len) {
  +  int startoffset = luaL_optint(L, stackpos, 1);
  +  if(startoffset > 0)
  +    startoffset--;
  +  else if(startoffset < 0) {
  +    startoffset += len;
  +    if(startoffset < 0)
  +      startoffset = 0;
  +  }
  +  return startoffset;
  +}
  +
  +
  +static TUserdata* check_ud (lua_State *L)
  +{
  +  TUserdata *ud;
  +  if (lua_getmetatable(L, 1) &&
  +      lua_rawequal(L, -1, LUA_ENVIRONINDEX) &&
  +      (ud = (TUserdata *)lua_touserdata(L, 1)) != NULL) {
  +    lua_pop(L, 1);
  +    return ud;
  +  }
  +  luaL_argerror(L, 1, "incorrect type");
  +  return NULL;
  +}
  +
  +
  +static void checkarg_new (lua_State *L, TArgComp *argC) {
  +  argC->pattern = luaL_checklstring (L, 1, &argC->patlen);
  +  argC->cflags = ALG_GETCFLAGS (L, 2);
  +  ALG_OPTLOCALE (argC, L, 3);
  +}
  +
  +
  +/* function gsub (s, patt, f, [n], [cf], [ef], [lo]) */
  +static void checkarg_gsub (lua_State *L, TArgComp *argC, TArgExec *argE) {
  +  argE->text = luaL_checklstring (L, 1, &argE->textlen);
  +  argC->pattern = luaL_checklstring (L, 2, &argC->patlen);
  +  lua_tostring (L, 3);    /* converts number (if any) to string */
  +  argE->reptype = lua_type (L, 3);
  +  if (argE->reptype != LUA_TSTRING && argE->reptype != LUA_TTABLE &&
  +      argE->reptype != LUA_TFUNCTION) {
  +    luaL_argerror (L, 3, "must be string, table or function");
  +  }
  +  argE->funcpos = 3;
  +  argE->funcpos2 = 4;
  +  argE->maxmatch = OptLimit (L, 4);
  +  argC->cflags = ALG_GETCFLAGS (L, 5);
  +  argE->eflags = luaL_optint (L, 6, ALG_EFLAGS_DFLT);
  +  ALG_OPTLOCALE (argC, L, 7);
  +}
  +
  +
  +/* function find  (s, patt, [st], [cf], [ef], [lo]) */
  +/* function match (s, patt, [st], [cf], [ef], [lo]) */
  +static void checkarg_find_f (lua_State *L, TArgComp *argC, TArgExec *argE) {
  +  argE->text = luaL_checklstring (L, 1, &argE->textlen);
  +  argC->pattern = luaL_checklstring (L, 2, &argC->patlen);
  +  argE->startoffset = get_startoffset (L, 3, argE->textlen);
  +  argC->cflags = ALG_GETCFLAGS (L, 4);
  +  argE->eflags = luaL_optint (L, 5, ALG_EFLAGS_DFLT);
  +  ALG_OPTLOCALE (argC, L, 6);
  +}
  +
  +
  +/* function gmatch (s, patt, [cf], [ef], [lo]) */
  +/* function split  (s, patt, [cf], [ef], [lo]) */
  +static void checkarg_gmatch_split (lua_State *L, TArgComp *argC, TArgExec *argE) {
  +  argE->text = luaL_checklstring (L, 1, &argE->textlen);
  +  argC->pattern = luaL_checklstring (L, 2, &argC->patlen);
  +  argC->cflags = ALG_GETCFLAGS (L, 3);
  +  argE->eflags = luaL_optint (L, 4, ALG_EFLAGS_DFLT);
  +  ALG_OPTLOCALE (argC, L, 5);
  +}
  +
  +
  +/* method r:tfind (s, [st], [ef]) */
  +/* method r:exec  (s, [st], [ef]) */
  +static void checkarg_tfind (lua_State *L, TArgExec *argE, TUserdata **ud) {
  +  *ud = check_ud (L);
  +  argE->text = luaL_checklstring (L, 2, &argE->textlen);
  +  argE->startoffset = get_startoffset (L, 3, argE->textlen);
  +  argE->eflags = luaL_optint (L, 4, ALG_EFLAGS_DFLT);
  +}
  +
  +
  +static int ud_new (lua_State *L) {
  +  TArgComp argC;
  +  checkarg_new (L, &argC);
  +  return compile_regex (L, &argC, NULL);
  +}
  +
  +static void push_substrings (lua_State *L, TUserdata *ud, const char *text,
  +                             TFreeList *freelist) {
  +  int i;
  +  if (lua_checkstack (L, ALG_NSUB(ud)) == 0) {
  +    if (freelist)
  +      freelist_free (freelist);
  +    luaL_error (L, "cannot add %d stack slots", ALG_NSUB(ud));
  +  }
  +  for (i = 1; i <= ALG_NSUB(ud); i++) {
  +    ALG_PUSHSUB_OR_FALSE (L, ud, text, i);
  +  }
  +}
  +
  +static int gsub (lua_State *L) {
  +  TUserdata *ud;
  +  TArgComp argC;
  +  TArgExec argE;
  +  int n_match = 0, n_subst = 0, st = 0, retry;
  +  TBuffer BufOut, BufRep, BufTemp, *pBuf = &BufOut;
  +  TFreeList freelist;
  +  /*------------------------------------------------------------------*/
  +  checkarg_gsub (L, &argC, &argE);
  +  compile_regex (L, &argC, &ud);
  +  freelist_init (&freelist);
  +  /*------------------------------------------------------------------*/
  +  if (argE.reptype == LUA_TSTRING) {
  +    buffer_init (&BufRep, 256, L, &freelist);
  +    bufferZ_putrepstring (&BufRep, argE.funcpos, ALG_NSUB(ud));
  +  }
  +  /*------------------------------------------------------------------*/
  +  if (argE.maxmatch == GSUB_CONDITIONAL) {
  +    buffer_init (&BufTemp, 1024, L, &freelist);
  +    pBuf = &BufTemp;
  +  }
  +  /*------------------------------------------------------------------*/
  +  buffer_init (&BufOut, 1024, L, &freelist);
  +  SET_RETRY (retry, 0);
  +  while ((argE.maxmatch < 0 || n_match < argE.maxmatch) && st <= (int)argE.textlen) {
  +    int from, to, res;
  +    int curr_subst = 0;
  +    res = GSUB_EXEC (ud, &argE, st, retry);
  +    if (res == ALG_NOMATCH) {
  +#ifdef ALG_USERETRY
  +      if (retry) {
  +        if (st < (int)argE.textlen) {  /* advance by 1 char (not replaced) */
  +          buffer_addlstring (&BufOut, argE.text + st, 1);
  +          ++st;
  +          retry = 0;
  +          continue;
  +        }
  +      }
  +#endif
  +      break;
  +    }
  +    else if (!ALG_ISMATCH (res)) {
  +      freelist_free (&freelist);
  +      return generate_error (L, ud, res);
  +    }
  +    ++n_match;
  +    from = ALG_BASE(st) + ALG_SUBBEG(ud,0);
  +    to = ALG_BASE(st) + ALG_SUBEND(ud,0);
  +    if (st < from) {
  +      buffer_addlstring (&BufOut, argE.text + st, from - st);
  +#ifdef ALG_PULL
  +      st = from;
  +#endif
  +    }
  +    /*----------------------------------------------------------------*/
  +    if (argE.reptype == LUA_TSTRING) {
  +      size_t iter = 0, num;
  +      const char *str;
  +      while (bufferZ_next (&BufRep, &iter, &num, &str)) {
  +        if (str)
  +          buffer_addlstring (pBuf, str, num);
  +        else if (num == 0 || ALG_SUBVALID (ud,num))
  +          buffer_addlstring (pBuf, argE.text + ALG_BASE(st) + ALG_SUBBEG(ud,num), ALG_SUBLEN(ud,num));
  +      }
  +      curr_subst = 1;
  +    }
  +    /*----------------------------------------------------------------*/
  +    else if (argE.reptype == LUA_TTABLE) {
  +      if (ALG_NSUB(ud) > 0)
  +        ALG_PUSHSUB_OR_FALSE (L, ud, argE.text + ALG_BASE(st), 1);
  +      else
  +        lua_pushlstring (L, argE.text + from, to - from);
  +      lua_gettable (L, argE.funcpos);
  +    }
  +    /*----------------------------------------------------------------*/
  +    else if (argE.reptype == LUA_TFUNCTION) {
  +      int narg;
  +      lua_pushvalue (L, argE.funcpos);
  +      if (ALG_NSUB(ud) > 0) {
  +        push_substrings (L, ud, argE.text + ALG_BASE(st), &freelist);
  +        narg = ALG_NSUB(ud);
  +      }
  +      else {
  +        lua_pushlstring (L, argE.text + from, to - from);
  +        narg = 1;
  +      }
  +      if (0 != lua_pcall (L, narg, 1, 0)) {
  +        freelist_free (&freelist);
  +        return lua_error (L);  /* re-raise the error */
  +      }
  +    }
  +    /*----------------------------------------------------------------*/
  +    if (argE.reptype != LUA_TSTRING) {
  +      if (lua_tostring (L, -1)) {
  +        buffer_addvalue (pBuf, -1);
  +        curr_subst = 1;
  +      }
  +      else if (!lua_toboolean (L, -1))
  +        buffer_addlstring (pBuf, argE.text + from, to - from);
  +      else {
  +        freelist_free (&freelist);
  +        luaL_error (L, "invalid replacement value (a %s)", luaL_typename (L, -1));
  +      }
  +      if (argE.maxmatch != GSUB_CONDITIONAL)
  +        lua_pop (L, 1);
  +    }
  +    /*----------------------------------------------------------------*/
  +    if (argE.maxmatch == GSUB_CONDITIONAL) {
  +      /* Call the function */
  +      lua_pushvalue (L, argE.funcpos2);
  +      lua_pushinteger (L, from + 1);
  +      lua_pushinteger (L, to);
  +      if (argE.reptype == LUA_TSTRING)
  +        buffer_pushresult (&BufTemp);
  +      else {
  +        lua_pushvalue (L, -4);
  +        lua_remove (L, -5);
  +      }
  +      if (0 != lua_pcall (L, 3, 2, 0)) {
  +        freelist_free (&freelist);
  +        lua_error (L);  /* re-raise the error */
  +      }
  +      /* Handle the 1-st return value */
  +      if (lua_isstring (L, -2)) {               /* coercion is allowed here */
  +        buffer_addvalue (&BufOut, -2);          /* rep2 */
  +        curr_subst = 1;
  +      }
  +      else if (lua_toboolean (L, -2))
  +        buffer_addbuffer (&BufOut, &BufTemp);   /* rep1 */
  +      else {
  +        buffer_addlstring (&BufOut, argE.text + from, to - from); /* "no" */
  +        curr_subst = 0;
  +      }
  +      /* Handle the 2-nd return value */
  +      if (lua_type (L, -1) == LUA_TNUMBER) {    /* no coercion is allowed here */
  +        int n = lua_tointeger (L, -1);
  +        if (n < 0)                              /* n */
  +          n = 0;
  +        argE.maxmatch = n_match + n;
  +      }
  +      else if (lua_toboolean (L, -1))           /* "yes to all" */
  +        argE.maxmatch = GSUB_UNLIMITED;
  +      else
  +        buffer_clear (&BufTemp);
  +
  +      lua_pop (L, 2);
  +      if (argE.maxmatch != GSUB_CONDITIONAL)
  +        pBuf = &BufOut;
  +    }
  +    /*----------------------------------------------------------------*/
  +    n_subst += curr_subst;
  +    if (st < to) {
  +      st = to;
  +      SET_RETRY (retry, 0);
  +    }
  +    else if (st < (int)argE.textlen) {
  +#ifdef ALG_USERETRY
  +      retry = 1;
  +#else
  +      /* advance by 1 char (not replaced) */
  +      buffer_addlstring (&BufOut, argE.text + st, 1);
  +      ++st;
  +#endif
  +    }
  +    else break;
  +  }
  +  /*------------------------------------------------------------------*/
  +  buffer_addlstring (&BufOut, argE.text + st, argE.textlen - st);
  +  buffer_pushresult (&BufOut);
  +  lua_pushinteger (L, n_match);
  +  lua_pushinteger (L, n_subst);
  +  freelist_free (&freelist);
  +  return 3;
  +}
  +
  +
  +static int generic_find (lua_State *L, int find) {
  +  int res;
  +  TUserdata *ud;
  +  TArgComp argC;
  +  TArgExec argE;
  +
  +  checkarg_find_f (L, &argC, &argE);
  +  if (argE.startoffset > (int)argE.textlen)
  +    return lua_pushnil(L), 1;
  +
  +  compile_regex (L, &argC, &ud);
  +  res = findmatch_exec (ud, &argE);
  +  if (ALG_ISMATCH (res)) {
  +    if (find)
  +      ALG_PUSHOFFSETS (L, ud, ALG_BASE(argE.startoffset), 0);
  +    if (ALG_NSUB(ud))    /* push captures */
  +      push_substrings (L, ud, argE.text, NULL);
  +    else if (!find) {
  +      ALG_PUSHSUB (L, ud, argE.text, 0);
  +      return 1;
  +    }
  +    return find ? ALG_NSUB(ud) + 2 : ALG_NSUB(ud);
  +  }
  +  else if (res == ALG_NOMATCH)
  +    return lua_pushnil (L), 1;
  +  else
  +    return generate_error (L, ud, res);
  +}
  +
  +
  +static int find (lua_State *L) {
  +  return generic_find (L, 1);
  +}
  +
  +
  +static int match (lua_State *L) {
  +  return generic_find (L, 0);
  +}
  +
  +
  +static int gmatch_iter (lua_State *L) {
  +  int retry;
  +  TArgExec argE;
  +  TUserdata *ud    = (TUserdata*) lua_touserdata (L, lua_upvalueindex (1));
  +  argE.text        = lua_tolstring (L, lua_upvalueindex (2), &argE.textlen);
  +  argE.eflags      = lua_tointeger (L, lua_upvalueindex (3));
  +  argE.startoffset = lua_tointeger (L, lua_upvalueindex (4));
  +#ifdef ALG_USERETRY
  +  retry            = lua_tointeger (L, lua_upvalueindex (5));
  +#endif
  +
  +  if (argE.startoffset > (int)argE.textlen)
  +    return 0;
  +
  +  while (1) {
  +    int res = GMATCH_EXEC (ud, &argE, retry);
  +    if (ALG_ISMATCH (res)) {
  +      int incr = 0;
  +      if (ALG_SUBLEN(ud,0)) {
  +        SET_RETRY (retry, 0);
  +      }
  +      else { /* no progress: prevent endless loop */
  +#ifdef ALG_USERETRY
  +        SET_RETRY (retry, 1);
  +#else
  +        incr = 1;
  +#endif
  +      }
  +      ALG_PUSHEND (L, ud, ALG_BASE(argE.startoffset)+incr, 0); /* update start offset */
  +      lua_replace (L, lua_upvalueindex (4));
  +#ifdef ALG_USERETRY
  +      lua_pushinteger (L, retry);
  +      lua_replace (L, lua_upvalueindex (5));         /* update retry */
  +#endif
  +      /* push either captures or entire match */
  +      if (ALG_NSUB(ud)) {
  +        push_substrings (L, ud, argE.text, NULL);
  +        return ALG_NSUB(ud);
  +      }
  +      else {
  +        ALG_PUSHSUB (L, ud, argE.text, 0);
  +        return 1;
  +      }
  +    }
  +    else if (res == ALG_NOMATCH) {
  +#ifdef ALG_USERETRY
  +      if (retry) {
  +        if (argE.startoffset < (int)argE.textlen) {
  +          ++argE.startoffset;   /* advance by 1 char */
  +          SET_RETRY (retry, 0);
  +          continue;
  +        }
  +      }
  +#endif
  +      return 0;
  +    }
  +    else
  +      return generate_error (L, ud, res);
  +  }
  +}
  +
  +
  +static int split_iter (lua_State *L) {
  +  int incr, newoffset, res;
  +  TArgExec argE;
  +  TUserdata *ud    = (TUserdata*) lua_touserdata (L, lua_upvalueindex (1));
  +  argE.text        = lua_tolstring (L, lua_upvalueindex (2), &argE.textlen);
  +  argE.eflags      = lua_tointeger (L, lua_upvalueindex (3));
  +  argE.startoffset = lua_tointeger (L, lua_upvalueindex (4));
  +  incr             = lua_tointeger (L, lua_upvalueindex (5));
  +
  +  if (argE.startoffset > (int)argE.textlen)
  +    return 0;
  +
  +  newoffset = argE.startoffset + incr;
  +  res = split_exec (ud, &argE, newoffset);
  +  if (ALG_ISMATCH (res)) {
  +    ALG_PUSHEND (L, ud, ALG_BASE(newoffset), 0);          /* update start offset */
  +    lua_replace (L, lua_upvalueindex (4));
  +    lua_pushinteger (L, ALG_SUBLEN(ud,0) ? 0 : 1);    /* update incr */
  +    lua_replace (L, lua_upvalueindex (5));
  +    /* push text preceding the match */
  +    lua_pushlstring (L, argE.text + argE.startoffset,
  +                     ALG_SUBBEG(ud,0) + ALG_BASE(newoffset) - argE.startoffset);
  +    /* push either captures or entire match */
  +    if (ALG_NSUB(ud)) {
  +      push_substrings (L, ud, argE.text + ALG_BASE(newoffset), NULL);
  +      return 1 + ALG_NSUB(ud);
  +    }
  +    else {
  +      ALG_PUSHSUB (L, ud, argE.text + ALG_BASE(newoffset), 0);
  +      return 2;
  +    }
  +  }
  +  else if (res == ALG_NOMATCH) {
  +    lua_pushinteger (L, argE.textlen + 1);    /* mark as last iteration */
  +    lua_replace (L, lua_upvalueindex (4));    /* update start offset */
  +    lua_pushlstring (L, argE.text+argE.startoffset, argE.textlen-argE.startoffset);
  +    return 1;
  +  }
  +  else
  +    return generate_error (L, ud, res);
  +}
  +
  +
  +static int gmatch (lua_State *L)
  +{
  +  TArgComp argC;
  +  TArgExec argE;
  +  TUserdata *ud;
  +  checkarg_gmatch_split (L, &argC, &argE);
  +  compile_regex (L, &argC, &ud);              /* 1-st upvalue: ud */
  +  gmatch_pushsubject (L, &argE);              /* 2-nd upvalue: s  */
  +  lua_pushinteger (L, argE.eflags);           /* 3-rd upvalue: ef */
  +  lua_pushinteger (L, 0);                     /* 4-th upvalue: startoffset */
  +#ifdef ALG_USERETRY
  +  lua_pushinteger (L, 0);                     /* 5-th upvalue: retry */
  +  lua_pushcclosure (L, gmatch_iter, 5);
  +#else
  +  lua_pushcclosure (L, gmatch_iter, 4);
  +#endif
  +  return 1;
  +}
  +
  +static int split (lua_State *L)
  +{
  +  TArgComp argC;
  +  TArgExec argE;
  +  TUserdata *ud;
  +  checkarg_gmatch_split (L, &argC, &argE);
  +  compile_regex (L, &argC, &ud);              /* 1-st upvalue: ud */
  +  gmatch_pushsubject (L, &argE);              /* 2-nd upvalue: s  */
  +  lua_pushinteger (L, argE.eflags);           /* 3-rd upvalue: ef */
  +  lua_pushinteger (L, 0);                     /* 4-th upvalue: startoffset */
  +  lua_pushinteger (L, 0);                     /* 5-th upvalue: incr */
  +  lua_pushcclosure (L, split_iter, 5);
  +  return 1;
  +}
  +
  +
  +static void push_substring_table (lua_State *L, TUserdata *ud, const char *text) {
  +  int i;
  +  lua_newtable (L);
  +  for (i = 1; i <= ALG_NSUB(ud); i++) {
  +    ALG_PUSHSUB_OR_FALSE (L, ud, text, i);
  +    lua_rawseti (L, -2, i);
  +  }
  +}
  +
  +
  +static void push_offset_table (lua_State *L, TUserdata *ud, int startoffset) {
  +  int i, j;
  +  lua_newtable (L);
  +  for (i=1, j=1; i <= ALG_NSUB(ud); i++) {
  +    if (ALG_SUBVALID (ud,i)) {
  +      ALG_PUSHSTART (L, ud, startoffset, i);
  +      lua_rawseti (L, -2, j++);
  +      ALG_PUSHEND (L, ud, startoffset, i);
  +      lua_rawseti (L, -2, j++);
  +    }
  +    else {
  +      lua_pushboolean (L, 0);
  +      lua_rawseti (L, -2, j++);
  +      lua_pushboolean (L, 0);
  +      lua_rawseti (L, -2, j++);
  +    }
  +  }
  +}
  +
  +
  +static int generic_tfind (lua_State *L, int tfind) {
  +  TUserdata *ud;
  +  TArgExec argE;
  +  int res;
  +
  +  checkarg_tfind (L, &argE, &ud);
  +  if (argE.startoffset > (int)argE.textlen)
  +    return lua_pushnil(L), 1;
  +
  +  res = tfind_exec (ud, &argE);
  +  if (ALG_ISMATCH (res)) {
  +    ALG_PUSHOFFSETS (L, ud, ALG_BASE(argE.startoffset), 0);
  +    if (tfind)
  +      push_substring_table (L, ud, argE.text);
  +    else
  +      push_offset_table (L, ud, ALG_BASE(argE.startoffset));
  +#ifdef DO_NAMED_SUBPATTERNS
  +    DO_NAMED_SUBPATTERNS (L, ud, argE.text);
  +#endif
  +    return 3;
  +  }
  +  else if (res == ALG_NOMATCH)
  +    return lua_pushnil (L), 1;
  +  else
  +    return generate_error(L, ud, res);
  +}
  +
  +
  +static int ud_tfind (lua_State *L) {
  +  return generic_tfind (L, 1);
  +}
  +
  +
  +static int ud_exec (lua_State *L) {
  +  return generic_tfind (L, 0);
  +}
  +
  +
  +/* function plainfind (s, p, [st], [ci]) */
  +static int plainfind_func (lua_State *L) {
  +  size_t textlen, patlen;
  +  const char *text = luaL_checklstring (L, 1, &textlen);
  +  const char *pattern = luaL_checklstring (L, 2, &patlen);
  +  const char *from = text + get_startoffset (L, 3, textlen);
  +  int ci = lua_toboolean (L, 4);
  +  const char *end = text + textlen;
  +
  +  for (; from + patlen <= end; ++from) {
  +    const char *f = from, *p = pattern;
  +    size_t len = patlen + 1;
  +    if (ci) {
  +      while (--len) {
  +        if (toupper (*f++) != toupper (*p++))
  +          break;
  +      }
  +    }
  +    else {
  +      while (--len) {
  +        if (*f++ != *p++)
  +          break;
  +      }
  +    }
  +    if (len == 0) {
  +      lua_pushinteger (L, from - text + 1);
  +      lua_pushinteger (L, from - text + patlen);
  +      return 2;
  +    }
  +  }
  +  lua_pushnil (L);
  +  return 1;
  +}
  +
  @@ .
  patch -p0 <<'@@ .'
  Index: lua/local/lrexlib_common.c
  ============================================================================
  $ cvs diff -u -r0 -r1.1 lrexlib_common.c
  --- /dev/null	2008-01-12 13:33:00 +0100
  +++ lrexlib_common.c	2008-01-12 13:33:34 +0100
  @@ -0,0 +1,244 @@
  +/* common.c */
  +/* See Copyright Notice in the file LICENSE */
  +
  +#if defined(LUA_USE_POSIX) || defined(LUA_USE_PCRE)
  +
  +#include <stdlib.h>
  +#include <ctype.h>
  +#include <string.h>
  +#include "lua.h"
  +#include "lauxlib.h"
  +#include "lrexlib_common.h"
  +
  +/* the table must be on Lua stack top */
  +int get_int_field (lua_State *L, const char* field)
  +{
  +  int val;
  +  lua_getfield (L, -1, field);
  +  val = lua_tointeger (L, -1);
  +  lua_pop (L, 1);
  +  return val;
  +}
  +
  +/* the table must be on Lua stack top */
  +void set_int_field (lua_State *L, const char* field, int val)
  +{
  +  lua_pushinteger (L, val);
  +  lua_setfield (L, -2, field);
  +}
  +
  +void *Lmalloc(lua_State *L, size_t size) {
  +  void *p = malloc(size);
  +  if(p == NULL)
  +    luaL_error(L, "malloc failed");
  +  return p;
  +}
  +
  +/* This function fills a table with string-number pairs.
  +   The table can be passed as the 1-st lua-function parameter,
  +   otherwise it is created. The return value is the filled table.
  +*/
  +int get_flags (lua_State *L, const flag_pair **arrs) {
  +  const flag_pair *p;
  +  const flag_pair **pp;
  +  int nparams = lua_gettop(L);
  +
  +  if(nparams == 0)
  +    lua_newtable(L);
  +  else {
  +    if(!lua_istable(L, 1))
  +      luaL_argerror(L, 1, "not a table");
  +    if(nparams > 1)
  +      lua_pushvalue(L, 1);
  +  }
  +
  +  for(pp=arrs; *pp; ++pp) {
  +    for(p=*pp; p->key; ++p) {
  +      lua_pushstring(L, p->key);
  +      lua_pushinteger(L, p->val);
  +      lua_rawset(L, -3);
  +    }
  +  }
  +  return 1;
  +}
  +
  +const char *get_flag_key (const flag_pair *fp, int val) {
  +  for (; fp->key; ++fp) {
  +    if (fp->val == val)
  +      return fp->key;
  +  }
  +  return NULL;
  +}
  +
  +/* Classes */
  +
  +/*
  + *  class TFreeList
  + *  ***************
  + *  Simple array of pointers to TBuffer's.
  + *  The array has fixed capacity (not expanded automatically).
  + */
  +
  +void freelist_init (TFreeList *fl) {
  +  fl->top = 0;
  +}
  +
  +void freelist_add (TFreeList *fl, TBuffer *buf) {
  +  fl->list[fl->top++] = buf;
  +}
  +
  +void freelist_free (TFreeList *fl) {
  +  while (fl->top > 0)
  +    buffer_free (fl->list[--fl->top]);
  +}
  +
  +/*
  + *  class TBuffer
  + *  *************
  + *  Auto-extensible array of characters for building long strings incrementally.
  + *    * Differs from luaL_Buffer in that:
  + *       *  it does not use Lua facilities (except luaL_error when malloc fails)
  + *       *  its operations do not change Lua stack top position
  + *       *  buffer_addvalue does not extract the value from Lua stack
  + *       *  buffer_pushresult does not have to be the last operation
  + *    * Uses TFreeList class:
  + *       *  for inserting itself into a TFreeList instance for future clean-up
  + *       *  calls freelist_free prior to calling luaL_error.
  + *    * Has specialized "Z-operations" for maintaining mixed string/integer
  + *      array:  bufferZ_addlstring, bufferZ_addnum and bufferZ_next.
  + *       *  if the array is intended to be "mixed", then the methods
  + *          buffer_addlstring and buffer_addvalue must not be used
  + *          (the application will crash on bufferZ_next).
  + *       *  conversely, if the array is not intended to be "mixed",
  + *          then the method bufferZ_next must not be used.
  + */
  +
  +enum { ID_NUMBER, ID_STRING };
  +
  +void buffer_init (TBuffer *buf, size_t sz, lua_State *L, TFreeList *fl) {
  +  buf->arr = (char*) malloc (sz);
  +  if (!buf->arr) {
  +    freelist_free (fl);
  +    luaL_error (L, "malloc failed");
  +  }
  +  buf->size = sz;
  +  buf->top = 0;
  +  buf->L = L;
  +  buf->freelist = fl;
  +  freelist_add (fl, buf);
  +}
  +
  +void buffer_free (TBuffer *buf) {
  +  free (buf->arr);
  +}
  +
  +void buffer_clear (TBuffer *buf) {
  +  buf->top = 0;
  +}
  +
  +void buffer_pushresult (TBuffer *buf) {
  +  lua_pushlstring (buf->L, buf->arr, buf->top);
  +}
  +
  +void buffer_addbuffer (TBuffer *trg, TBuffer *src) {
  +  buffer_addlstring (trg, src->arr, src->top);
  +}
  +
  +void buffer_addlstring (TBuffer *buf, const void *src, size_t sz) {
  +  size_t newtop = buf->top + sz;
  +  if (newtop > buf->size) {
  +    char *p = (char*) realloc (buf->arr, 2 * newtop);   /* 2x expansion */
  +    if (!p) {
  +      freelist_free (buf->freelist);
  +      luaL_error (buf->L, "realloc failed");
  +    }
  +    buf->arr = p;
  +    buf->size = 2 * newtop;
  +  }
  +  memcpy (buf->arr + buf->top, src, sz);
  +  buf->top = newtop;
  +}
  +
  +void buffer_addvalue (TBuffer *buf, int stackpos) {
  +  size_t len;
  +  const char *p = lua_tolstring (buf->L, stackpos, &len);
  +  buffer_addlstring (buf, p, len);
  +}
  +
  +static void bufferZ_addlstring (TBuffer *buf, const void *src, size_t len) {
  +  size_t header[2] = { ID_STRING };
  +  header[1] = len;
  +  buffer_addlstring (buf, header, sizeof (header));
  +  buffer_addlstring (buf, src, len);
  +}
  +
  +static void bufferZ_addnum (TBuffer *buf, size_t num) {
  +  size_t header[2] = { ID_NUMBER };
  +  header[1] = num;
  +  buffer_addlstring (buf, header, sizeof (header));
  +}
  +
  +/* 1. When called repeatedly on the same TBuffer, its existing data
  +      is discarded and overwritten by the new data.
  +   2. The TBuffer's array is never shrunk by this function.
  +*/
  +void bufferZ_putrepstring (TBuffer *BufRep, int reppos, int nsub) {
  +  char dbuf[] = { 0, 0 };
  +  size_t replen;
  +  const char *p = lua_tolstring (BufRep->L, reppos, &replen);
  +  const char *end = p + replen;
  +  BufRep->top = 0;
  +  while (p < end) {
  +    const char *q;
  +    for (q = p; q < end && *q != '%'; ++q)
  +      {}
  +    if (q != p)
  +      bufferZ_addlstring (BufRep, p, q - p);
  +    if (q < end) {
  +      if (++q < end) {  /* skip % */
  +        if (isdigit (*q)) {
  +          int num;
  +          *dbuf = *q;
  +          num = atoi (dbuf);
  +          if (num == 1 && nsub == 0)
  +            num = 0;
  +          else if (num > nsub) {
  +            freelist_free (BufRep->freelist);
  +            luaL_error (BufRep->L, "invalid capture index");
  +          }
  +          bufferZ_addnum (BufRep, num);
  +        }
  +        else bufferZ_addlstring (BufRep, q, 1);
  +      }
  +      p = q + 1;
  +    }
  +    else break;
  +  }
  +}
  +
  +/******************************************************************************
  +  The intended use of this function is as follows:
  +        size_t iter = 0;
  +        while (bufferZ_next (buf, &iter, &num, &str)) {
  +          if (str) do_something_with_string (str, num);
  +          else     do_something_with_number (num);
  +        }
  +*******************************************************************************
  +*/
  +int bufferZ_next (TBuffer *buf, size_t *iter, size_t *num, const char **str) {
  +  if (*iter < buf->top) {
  +    size_t *ptr_header = (size_t*)(buf->arr + *iter);
  +    *num = ptr_header[1];
  +    *iter += 2 * sizeof (size_t);
  +    *str = NULL;
  +    if (*ptr_header == ID_STRING) {
  +      *str = buf->arr + *iter;
  +      *iter += *num;
  +    }
  +    return 1;
  +  }
  +  return 0;
  +}
  +
  +#endif
  +
  @@ .
  patch -p0 <<'@@ .'
  Index: lua/local/lrexlib_common.h
  ============================================================================
  $ cvs diff -u -r0 -r1.1 lrexlib_common.h
  --- /dev/null	2008-01-12 13:33:00 +0100
  +++ lrexlib_common.h	2008-01-12 13:33:34 +0100
  @@ -0,0 +1,86 @@
  +/* common.h */
  +/* See Copyright Notice in the file LICENSE */
  +
  +#ifndef COMMON_H
  +#define COMMON_H
  +
  +#include "lua.h"
  +
  +/* REX_API can be overridden from the command line or Makefile */
  +#ifndef REX_API
  +#  define REX_API LUALIB_API
  +#endif
  +
  +/* Special values for maxmatch in gsub. They all must be negative. */
  +#define GSUB_UNLIMITED   -1
  +#define GSUB_CONDITIONAL -2
  +
  +/* Common structs and functions */
  +
  +typedef struct {
  +  const char* key;
  +  int val;
  +} flag_pair;
  +
  +typedef struct {            /* compile arguments */
  +  const char * pattern;
  +  size_t       patlen;
  +  int          cflags;
  +  const char * locale;
  +  const unsigned char * tables;
  +  int          tablespos;
  +} TArgComp;
  +
  +typedef struct {            /* exec arguments */
  +  const char * text;
  +  size_t       textlen;
  +  int          startoffset;
  +  int          eflags;
  +  int          funcpos;
  +  int          maxmatch;
  +  int          funcpos2;      /* used with gsub */
  +  int          reptype;       /* used with gsub */
  +  size_t       ovecsize;      /* used with dfa_exec */
  +  size_t       wscount;       /* used with dfa_exec */
  +} TArgExec;
  +
  +struct tagFreeList; /* forward declaration */
  +
  +struct tagBuffer {
  +  size_t      size;
  +  size_t      top;
  +  char      * arr;
  +  lua_State * L;
  +  struct tagFreeList * freelist;
  +};
  +
  +struct tagFreeList {
  +  struct tagBuffer * list[16];
  +  int top;
  +};
  +
  +typedef struct tagBuffer TBuffer;
  +typedef struct tagFreeList TFreeList;
  +
  +void freelist_init (TFreeList *fl);
  +void freelist_add (TFreeList *fl, TBuffer *buf);
  +void freelist_free (TFreeList *fl);
  +
  +void buffer_init (TBuffer *buf, size_t sz, lua_State *L, TFreeList *fl);
  +void buffer_free (TBuffer *buf);
  +void buffer_clear (TBuffer *buf);
  +void buffer_addbuffer (TBuffer *trg, TBuffer *src);
  +void buffer_addlstring (TBuffer *buf, const void *src, size_t sz);
  +void buffer_addvalue (TBuffer *buf, int stackpos);
  +void buffer_pushresult (TBuffer *buf);
  +
  +void bufferZ_putrepstring (TBuffer *buf, int reppos, int nsub);
  +int  bufferZ_next (TBuffer *buf, size_t *iter, size_t *len, const char **str);
  +
  +int  get_int_field (lua_State *L, const char* field);
  +void set_int_field (lua_State *L, const char* field, int val);
  +int  get_flags (lua_State *L, const flag_pair **arr);
  +const char *get_flag_key (const flag_pair *fp, int val);
  +void *Lmalloc (lua_State *L, size_t size);
  +
  +#endif
  @@ .
  patch -p0 <<'@@ .'
  Index: lua/local/lrexlib_lpcre.c
  ============================================================================
  $ cvs diff -u -r0 -r1.1 lrexlib_lpcre.c
  --- /dev/null	2008-01-12 13:33:00 +0100
  +++ lrexlib_lpcre.c	2008-01-12 13:33:34 +0100
  @@ -0,0 +1,446 @@
  +/* lpcre.c - Lua binding of PCRE library */
  +/* See Copyright Notice in the file LICENSE */
  +
  +#ifndef REX_LIBNAME
  +#  define REX_LIBNAME "rex_pcre"
  +#endif
  +#ifndef REX_OPENLIB
  +#  define REX_OPENLIB luaopen_rex_pcre
  +#endif
  +
  +#include <stdlib.h>
  +#include <string.h>
  +#include <locale.h>
  +#include <ctype.h>
  +
  +#include "lua.h"
  +#include "lauxlib.h"
  +#include "lrexlib.h"
  +#include "lrexlib_common.h"
  +
  +#if defined(LUA_USE_PCRE)
  +
  +#include <pcre.h>
  +
  +extern int Lpcre_get_flags (lua_State *L);
  +extern int Lpcre_config (lua_State *L);
  +extern flag_pair pcre_error_flags[];
  +
  +#define ALG_CFLAGS_DFLT 0
  +#define ALG_EFLAGS_DFLT 0
  +
  +static int getcflags (lua_State *L, int pos);
  +#define ALG_GETCFLAGS(L,pos)  getcflags(L, pos)
  +
  +static void optlocale (TArgComp *argC, lua_State *L, int pos);
  +#define ALG_OPTLOCALE(a,b,c)  optlocale(a,b,c)
  +
  +#define ALG_NOMATCH        PCRE_ERROR_NOMATCH
  +#define ALG_ISMATCH(res)   ((res) >= 0)
  +#define ALG_SUBBEG(ud,n)   ud->match[n+n]
  +#define ALG_SUBEND(ud,n)   ud->match[n+n+1]
  +#define ALG_SUBLEN(ud,n)   (ALG_SUBEND(ud,n) - ALG_SUBBEG(ud,n))
  +#define ALG_SUBVALID(ud,n) (ALG_SUBBEG(ud,n) >= 0)
  +#define ALG_NSUB(ud)       ((int)ud->ncapt)
  +
  +#define ALG_PUSHSUB(L,ud,text,n) \
  +  lua_pushlstring (L, (text) + ALG_SUBBEG(ud,n), ALG_SUBLEN(ud,n))
  +
  +#define ALG_PUSHSUB_OR_FALSE(L,ud,text,n) \
  +  (ALG_SUBVALID(ud,n) ? ALG_PUSHSUB (L,ud,text,n) : lua_pushboolean (L,0))
  +
  +#define ALG_PUSHSTART(L,ud,offs,n)   lua_pushinteger(L, (offs) + ALG_SUBBEG(ud,n) + 1)
  +#define ALG_PUSHEND(L,ud,offs,n)     lua_pushinteger(L, (offs) + ALG_SUBEND(ud,n))
  +#define ALG_PUSHOFFSETS(L,ud,offs,n) \
  +  (ALG_PUSHSTART(L,ud,offs,n), ALG_PUSHEND(L,ud,offs,n))
  +
  +#define ALG_BASE(st)  0
  +#define ALG_PULL
  +#define ALG_USERETRY
  +
  +typedef struct {
  +  pcre       * pr;
  +  pcre_extra * extra;
  +  int        * match;
  +  int          ncapt;
  +  const unsigned char * tables;
  +  int          freed;
  +} TPcre;
  +
  +#define TUserdata TPcre
  +
  +#if PCRE_MAJOR >= 4
  +static void do_named_subpatterns (lua_State *L, TPcre *ud, const char *text);
  +#  define DO_NAMED_SUBPATTERNS do_named_subpatterns
  +#endif
  +
  +const char pcre_typename[] = REX_LIBNAME"_regex";
  +
  +#include "lrexlib_algo.h"
  +
  +/* Locations of the 2 permanent tables in the function environment */
  +#define INDEX_CHARTABLES_META  1      /* chartables type's metatable */
  +#define INDEX_CHARTABLES_LINK  2      /* link chartables to compiled regex */
  +
  +const char chartables_typename[] = "chartables";
  +
  +/*  Functions
  + ******************************************************************************
  + */
  +
  +static void push_chartables_meta (lua_State *L) {
  +  lua_pushinteger (L, INDEX_CHARTABLES_META);
  +  lua_rawget (L, LUA_ENVIRONINDEX);
  +}
  +
  +static int getcflags (lua_State *L, int pos) {
  +  switch (lua_type (L, pos)) {
  +    case LUA_TNONE:
  +    case LUA_TNIL:
  +      return ALG_CFLAGS_DFLT;
  +    case LUA_TNUMBER:
  +      return lua_tointeger (L, pos);
  +    case LUA_TSTRING: {
  +      const char *s = lua_tostring (L, pos);
  +      int res = 0, ch;
  +      while ((ch = *s++) != '\0') {
  +        if (ch == 'i') res |= PCRE_CASELESS;
  +        else if (ch == 'm') res |= PCRE_MULTILINE;
  +        else if (ch == 's') res |= PCRE_DOTALL;
  +        else if (ch == 'x') res |= PCRE_EXTENDED;
  +        else if (ch == 'U') res |= PCRE_UNGREEDY;
  +        else if (ch == 'X') res |= PCRE_EXTRA;
  +      }
  +      return res;
  +    }
  +    default:
  +      return luaL_argerror (L, pos, "number or string expected");
  +  }
  +}
  +
  +static int generate_error (lua_State *L, const TPcre *ud, int errcode) {
  +  const char *key = get_flag_key (pcre_error_flags, errcode);
  +  (void) ud;
  +  if (key)
  +    return luaL_error (L, "error PCRE_%s", key);
  +  else
  +    return luaL_error (L, "PCRE error code %d", errcode);
  +}
  +
  +#if PCRE_MAJOR >= 6
  +/* method r:dfa_exec (s, [st], [ef], [ovecsize], [wscount]) */
  +static void checkarg_dfa_exec (lua_State *L, TArgExec *argE, TPcre **ud) {
  +  *ud = check_ud (L);
  +  argE->text = luaL_checklstring (L, 2, &argE->textlen);
  +  argE->startoffset = get_startoffset (L, 3, argE->textlen);
  +  argE->eflags = luaL_optint (L, 4, ALG_EFLAGS_DFLT);
  +  argE->ovecsize = luaL_optint (L, 5, 100);
  +  argE->wscount = luaL_optint (L, 6, 50);
  +}
  +#endif
  +
  +static int Lpcre_maketables (lua_State *L) {
  +  *(const void**)lua_newuserdata (L, sizeof(void**)) = pcre_maketables();
  +  push_chartables_meta (L);
  +  lua_setmetatable (L, -2);
  +  return 1;
  +}
  +
  +static void **check_chartables (lua_State *L, int pos) {
  +  void **q;
  +  /* Compare the metatable against the C function environment. */
  +  if (lua_getmetatable(L, pos)) {
  +    push_chartables_meta (L);
  +    if (lua_rawequal(L, -1, -2) &&
  +        (q = (void **)lua_touserdata(L, pos)) != NULL) {
  +      lua_pop(L, 2);
  +      return q;
  +    }
  +  }
  +  luaL_argerror(L, pos, lua_pushfstring (L, "not a %s", chartables_typename));
  +  return NULL;
  +}
  +
  +static int chartables_gc (lua_State *L) {
  +  void **ud = check_chartables (L, 1);
  +  if (*ud) {
  +    pcre_free (*ud);
  +    *ud = NULL;
  +  }
  +  return 0;
  +}
  +
  +static void optlocale (TArgComp *argC, lua_State *L, int pos) {
  +  argC->locale = NULL;
  +  argC->tables = NULL;
  +  if (!lua_isnoneornil (L, pos)) {
  +    if (lua_isstring (L, pos))
  +      argC->locale = lua_tostring (L, pos);
  +    else {
  +      argC->tablespos = pos;
  +      argC->tables = *check_chartables (L, pos);
  +    }
  +  }
  +}
  +
  +static int compile_regex (lua_State *L, const TArgComp *argC, TPcre **pud) {
  +  const char *error;
  +  int erroffset;
  +  TPcre *ud;
  +  const unsigned char *tables = NULL;
  +
  +  ud = (TPcre*)lua_newuserdata (L, sizeof (TPcre));
  +  memset (ud, 0, sizeof (TPcre));           /* initialize all members to 0 */
  +  lua_pushvalue (L, LUA_ENVIRONINDEX);
  +  lua_setmetatable (L, -2);
  +
  +  if (argC->locale) {
  +    char old_locale[256];
  +    strcpy (old_locale, setlocale (LC_CTYPE, NULL));  /* store the locale */
  +    if (NULL == setlocale (LC_CTYPE, argC->locale))   /* set new locale */
  +      return luaL_error (L, "cannot set locale");
  +    ud->tables = tables = pcre_maketables ();  /* make tables with new locale */
  +    setlocale (LC_CTYPE, old_locale);          /* restore the old locale */
  +  }
  +  else if (argC->tables) {
  +    tables = argC->tables;
  +    lua_pushinteger (L, INDEX_CHARTABLES_LINK);
  +    lua_rawget (L, LUA_ENVIRONINDEX);
  +    lua_pushvalue (L, -2);
  +    lua_pushvalue (L, argC->tablespos);
  +    lua_rawset (L, -3);
  +    lua_pop (L, 1);
  +  }
  +
  +  ud->pr = pcre_compile (argC->pattern, argC->cflags, &error, &erroffset, tables);
  +  if (!ud->pr)
  +    return luaL_error (L, "%s (pattern offset: %d)", error, erroffset + 1);
  +
  +  ud->extra = pcre_study (ud->pr, 0, &error);
  +  if (error) return luaL_error (L, "%s", error);
  +
  +  pcre_fullinfo (ud->pr, ud->extra, PCRE_INFO_CAPTURECOUNT, &ud->ncapt);
  +  /* need (2 ints per capture, plus one for substring match) * 3/2 */
  +  ud->match = (int *) Lmalloc (L, (ALG_NSUB(ud) + 1) * 3 * sizeof (int));
  +
  +  if (pud) *pud = ud;
  +  return 1;
  +}
  +
  +#if PCRE_MAJOR >= 4
  +/* the target table must be on lua stack top */
  +static void do_named_subpatterns (lua_State *L, TPcre *ud, const char *text) {
  +  int i, namecount, name_entry_size;
  +  unsigned char *name_table, *tabptr;
  +
  +  /* do named subpatterns - NJG */
  +  pcre_fullinfo (ud->pr, ud->extra, PCRE_INFO_NAMECOUNT, &namecount);
  +  if (namecount <= 0)
  +    return;
  +  pcre_fullinfo (ud->pr, ud->extra, PCRE_INFO_NAMETABLE, &name_table);
  +  pcre_fullinfo (ud->pr, ud->extra, PCRE_INFO_NAMEENTRYSIZE, &name_entry_size);
  +  tabptr = name_table;
  +  for (i = 0; i < namecount; i++) {
  +    int n = (tabptr[0] << 8) | tabptr[1]; /* number of the capturing parenthesis */
  +    if (n > 0 && n <= ALG_NSUB(ud)) {   /* check range */
  +      lua_pushstring (L, (char *)tabptr + 2); /* name of the capture, zero terminated */
  +      ALG_PUSHSUB_OR_FALSE (L, ud, text, n);
  +      lua_rawset (L, -3);
  +    }
  +    tabptr += name_entry_size;
  +  }
  +}
  +#endif /* #if PCRE_MAJOR >= 4 */
  +
  +static int tfind_exec (TPcre *ud, TArgExec *argE) {
  +  return pcre_exec (ud->pr, ud->extra, argE->text, (int)argE->textlen,
  +              argE->startoffset, argE->eflags, ud->match, (ud->ncapt + 1) * 3);
  +}
  +
  +#if PCRE_MAJOR >= 6
  +static int Lpcre_dfa_exec (lua_State *L)
  +{
  +  TArgExec argE;
  +  TPcre *ud;
  +  int res;
  +  int *buf, *ovector, *wspace;
  +
  +  checkarg_dfa_exec (L, &argE, &ud);
  +  buf = (int*) Lmalloc (L, (argE.ovecsize + argE.wscount) * sizeof(int));
  +  ovector = buf;
  +  wspace = buf + argE.ovecsize;
  +
  +  res = pcre_dfa_exec (ud->pr, ud->extra, argE.text, (int)argE.textlen,
  +    argE.startoffset, argE.eflags, ovector, argE.ovecsize, wspace, argE.wscount);
  +
  +  if (ALG_ISMATCH (res) || res == PCRE_ERROR_PARTIAL) {
  +    int i;
  +    int max = (res>0) ? res : (res==0) ? (int)argE.ovecsize/2 : 1;
  +    lua_pushinteger (L, ovector[0] + 1);         /* 1-st return value */
  +    lua_newtable (L);                            /* 2-nd return value */
  +    for (i=0; i<max; i++) {
  +      lua_pushinteger (L, ovector[i+i+1]);
  +      lua_rawseti (L, -2, i+1);
  +    }
  +    lua_pushinteger (L, res);                    /* 3-rd return value */
  +    free (buf);
  +    return 3;
  +  }
  +  else {
  +    free (buf);
  +    if (res == ALG_NOMATCH)
  +      return lua_pushnil (L), 1;
  +    else
  +      return generate_error (L, ud, res);
  +  }
  +}
  +#endif /* #if PCRE_MAJOR >= 6 */
  +
  +#ifdef ALG_USERETRY
  +  static int gmatch_exec (TUserdata *ud, TArgExec *argE, int retry) {
  +    int eflags = retry ? (argE->eflags|PCRE_NOTEMPTY|PCRE_ANCHORED) : argE->eflags;
  +    return pcre_exec (ud->pr, ud->extra, argE->text, argE->textlen,
  +      argE->startoffset, eflags, ud->match, (ALG_NSUB(ud) + 1) * 3);
  +  }
  +#else
  +  static int gmatch_exec (TUserdata *ud, TArgExec *argE) {
  +    return pcre_exec (ud->pr, ud->extra, argE->text, argE->textlen,
  +      argE->startoffset, argE->eflags, ud->match, (ALG_NSUB(ud) + 1) * 3);
  +  }
  +#endif
  +
  +static void gmatch_pushsubject (lua_State *L, TArgExec *argE) {
  +  lua_pushlstring (L, argE->text, argE->textlen);
  +}
  +
  +static int findmatch_exec (TPcre *ud, TArgExec *argE) {
  +  return pcre_exec (ud->pr, ud->extra, argE->text, argE->textlen,
  +    argE->startoffset, argE->eflags, ud->match, (ALG_NSUB(ud) + 1) * 3);
  +}
  +
  +#ifdef ALG_USERETRY
  +  static int gsub_exec (TPcre *ud, TArgExec *argE, int st, int retry) {
  +    int eflags = retry ? (argE->eflags|PCRE_NOTEMPTY|PCRE_ANCHORED) : argE->eflags;
  +    return pcre_exec (ud->pr, ud->extra, argE->text, (int)argE->textlen,
  +      st, eflags, ud->match, (ALG_NSUB(ud) + 1) * 3);
  +  }
  +#else
  +  static int gsub_exec (TPcre *ud, TArgExec *argE, int st) {
  +    return pcre_exec (ud->pr, ud->extra, argE->text, (int)argE->textlen,
  +      st, argE->eflags, ud->match, (ALG_NSUB(ud) + 1) * 3);
  +  }
  +#endif
  +
  +static int split_exec (TPcre *ud, TArgExec *argE, int offset) {
  +  return pcre_exec (ud->pr, ud->extra, argE->text, argE->textlen, offset,
  +                    argE->eflags, ud->match, (ALG_NSUB(ud) + 1) * 3);
  +}
  +
  +static int Lpcre_gc (lua_State *L) {
  +  TPcre *ud = check_ud (L);
  +  if (ud->freed == 0) {           /* precaution against "manual" __gc calling */
  +    ud->freed = 1;
  +    if (ud->pr)      pcre_free (ud->pr);
  +    if (ud->extra)   pcre_free (ud->extra);
  +    if (ud->tables)  pcre_free ((void *)ud->tables);
  +    if (ud->match)   free (ud->match);
  +  }
  +  return 0;
  +}
  +
  +static int Lpcre_tostring (lua_State *L) {
  +  TPcre *ud = check_ud (L);
  +  if (ud->freed == 0)
  +    lua_pushfstring (L, "%s (%p)", pcre_typename, (void*)ud);
  +  else
  +    lua_pushfstring (L, "%s (deleted)", pcre_typename);
  +  return 1;
  +}
  +
  +static int chartables_tostring (lua_State *L) {
  +  void **ud = check_chartables (L, 1);
  +  lua_pushfstring (L, "%s (%p)", chartables_typename, ud);
  +  return 1;
  +}
  +
  +static int Lpcre_version (lua_State *L) {
  +  lua_pushstring (L, pcre_version ());
  +  return 1;
  +}
  +
  +static const luaL_reg chartables_meta[] = {
  +  { "__gc",        chartables_gc },
  +  { "__tostring",  chartables_tostring },
  +  { NULL, NULL }
  +};
  +
  +static const luaL_reg regex_meta[] = {
  +  { "exec",        ud_exec },
  +  { "tfind",       ud_tfind },    /* old name: match */
  +#if PCRE_MAJOR >= 6
  +  { "dfa_exec",    Lpcre_dfa_exec },
  +#endif
  +  { "__gc",        Lpcre_gc },
  +  { "__tostring",  Lpcre_tostring },
  +  { NULL, NULL }
  +};
  +
  +static const luaL_reg rexlib[] = {
  +  { "match",       match },
  +  { "find",        find },
  +  { "gmatch",      gmatch },
  +  { "gsub",        gsub },
  +  { "split",       split },
  +  { "new",         ud_new },
  +  { "plainfind",   plainfind_func },
  +  { "flags",       Lpcre_get_flags },
  +  { "version",     Lpcre_version },
  +  { "maketables",  Lpcre_maketables },
  +#if PCRE_MAJOR >= 4
  +  { "config",      Lpcre_config },
  +#endif
  +  { NULL, NULL }
  +};
  +
  +#endif
  +
  +/* Open the library */
  +REX_API int REX_OPENLIB (lua_State *L) {
  +#if defined(LUA_USE_PCRE)
  +  if (PCRE_MAJOR > atoi (pcre_version ())) {
  +    return luaL_error (L, "%s requires at least version %d of PCRE library",
  +      REX_LIBNAME, (int)PCRE_MAJOR);
  +  }
  +  /* create a new function environment to serve as a metatable for methods */
  +  lua_newtable (L);
  +  lua_pushvalue (L, -1);
  +  lua_replace (L, LUA_ENVIRONINDEX);
  +  lua_pushvalue(L, -1); /* mt.__index = mt */
  +  lua_setfield(L, -2, "__index");
  +  luaL_register (L, NULL, regex_meta);
  +
  +  /* register functions */
  +  luaL_register (L, REX_LIBNAME, rexlib);
  +  lua_pushliteral (L, REX_VERSION" (for PCRE)");
  +  lua_setfield (L, -2, "_VERSION");
  +
  +  /* create a table and register it as a metatable for "chartables" userdata */
  +  lua_pushinteger (L, INDEX_CHARTABLES_META);
  +  lua_newtable (L);
  +  lua_pushliteral (L, "access denied");
  +  lua_setfield (L, -2, "__metatable");
  +  luaL_register (L, NULL, chartables_meta);
  +  lua_rawset (L, LUA_ENVIRONINDEX);
  +
  +  /* create a table for connecting "chartables" userdata to "regex" userdata */
  +  lua_pushinteger (L, INDEX_CHARTABLES_LINK);
  +  lua_newtable (L);
  +  lua_pushliteral (L, "k");         /* weak keys */
  +  lua_setfield (L, -2, "__mode");
  +  lua_pushvalue (L, -1);            /* setmetatable (tb, tb) */
  +  lua_setmetatable (L, -2);
  +  lua_rawset (L, LUA_ENVIRONINDEX);
  +#endif
  +  return 1;
  +}
  +
  @@ .
  patch -p0 <<'@@ .'
  Index: lua/local/lrexlib_lpcre_f.c
  ============================================================================
  $ cvs diff -u -r0 -r1.1 lrexlib_lpcre_f.c
  --- /dev/null	2008-01-12 13:33:00 +0100
  +++ lrexlib_lpcre_f.c	2008-01-12 13:33:34 +0100
  @@ -0,0 +1,194 @@
  +/* lpcre.c - PCRE regular expression library */
  +/* See Copyright Notice in the file LICENSE */
  +
  +#if defined(LUA_USE_PCRE)
  +
  +#include <pcre.h>
  +#include "lua.h"
  +#include "lauxlib.h"
  +#include "lrexlib_common.h"
  +
  +int Lpcre_get_flags (lua_State *L);
  +int Lpcre_config (lua_State *L);
  +flag_pair pcre_error_flags[];
  +
  +#define VERSION_PCRE (PCRE_MAJOR*100 + PCRE_MINOR)
  +
  +static flag_pair pcre_flags[] = {
  +  { "MAJOR",                         PCRE_MAJOR },
  +  { "MINOR",                         PCRE_MINOR },
  +/*---------------------------------------------------------------------------*/
  +  { "CASELESS",                      PCRE_CASELESS },
  +  { "MULTILINE",                     PCRE_MULTILINE },
  +  { "DOTALL",                        PCRE_DOTALL },
  +  { "EXTENDED",                      PCRE_EXTENDED },
  +  { "ANCHORED",                      PCRE_ANCHORED },
  +  { "DOLLAR_ENDONLY",                PCRE_DOLLAR_ENDONLY },
  +  { "EXTRA",                         PCRE_EXTRA },
  +  { "NOTBOL",                        PCRE_NOTBOL },
  +  { "NOTEOL",                        PCRE_NOTEOL },
  +  { "UNGREEDY",                      PCRE_UNGREEDY },
  +  { "NOTEMPTY",                      PCRE_NOTEMPTY },
  +  { "UTF8",                          PCRE_UTF8 },
  +#if VERSION_PCRE >= 400
  +  { "NO_AUTO_CAPTURE",               PCRE_NO_AUTO_CAPTURE },
  +  { "NO_UTF8_CHECK",                 PCRE_NO_UTF8_CHECK },
  +#endif
  +#if VERSION_PCRE >= 500
  +  { "AUTO_CALLOUT",                  PCRE_AUTO_CALLOUT },
  +  { "PARTIAL",                       PCRE_PARTIAL },
  +#endif
  +#if VERSION_PCRE >= 600
  +  { "DFA_SHORTEST",                  PCRE_DFA_SHORTEST },
  +  { "DFA_RESTART",                   PCRE_DFA_RESTART },
  +  { "FIRSTLINE",                     PCRE_FIRSTLINE },
  +#endif
  +#if VERSION_PCRE >= 607
  +  { "DUPNAMES",                      PCRE_DUPNAMES },
  +  { "NEWLINE_CR",                    PCRE_NEWLINE_CR },
  +  { "NEWLINE_LF",                    PCRE_NEWLINE_LF },
  +  { "NEWLINE_CRLF",                  PCRE_NEWLINE_CRLF },
  +#endif
  +#if VERSION_PCRE >= 700
  +  { "NEWLINE_ANY",                   PCRE_NEWLINE_ANY },
  +#endif
  +#if VERSION_PCRE >= 701
  +  { "NEWLINE_ANYCRLF",               PCRE_NEWLINE_ANYCRLF },
  +#endif
  +#if VERSION_PCRE >= 704
  +  { "BSR_ANYCRLF",                   PCRE_BSR_ANYCRLF },
  +  { "BSR_UNICODE",                   PCRE_BSR_UNICODE },
  +#endif
  +/*---------------------------------------------------------------------------*/
  +  { "INFO_OPTIONS",                  PCRE_INFO_OPTIONS },
  +  { "INFO_SIZE",                     PCRE_INFO_SIZE },
  +  { "INFO_CAPTURECOUNT",             PCRE_INFO_CAPTURECOUNT },
  +  { "INFO_BACKREFMAX",               PCRE_INFO_BACKREFMAX },
  +#if VERSION_PCRE >= 400
  +  { "INFO_FIRSTBYTE",                PCRE_INFO_FIRSTBYTE },
  +#endif
  +  { "INFO_FIRSTCHAR",                PCRE_INFO_FIRSTCHAR },
  +  { "INFO_FIRSTTABLE",               PCRE_INFO_FIRSTTABLE },
  +  { "INFO_LASTLITERAL",              PCRE_INFO_LASTLITERAL },
  +#if VERSION_PCRE >= 400
  +  { "INFO_NAMEENTRYSIZE",            PCRE_INFO_NAMEENTRYSIZE },
  +  { "INFO_NAMECOUNT",                PCRE_INFO_NAMECOUNT },
  +  { "INFO_NAMETABLE",                PCRE_INFO_NAMETABLE },
  +  { "INFO_STUDYSIZE",                PCRE_INFO_STUDYSIZE },
  +#endif
  +#if VERSION_PCRE >= 500
  +  { "INFO_DEFAULT_TABLES",           PCRE_INFO_DEFAULT_TABLES },
  +#endif
  +#ifdef PCRE_INFO_OKPARTIAL
  +  { "INFO_OKPARTIAL",                PCRE_INFO_OKPARTIAL },
  +#endif
  +#ifdef PCRE_INFO_JCHANGED
  +  { "INFO_JCHANGED",                 PCRE_INFO_JCHANGED },
  +#endif
  +#ifdef PCRE_INFO_HASCRORLF
  +  { "INFO_HASCRORLF",                PCRE_INFO_HASCRORLF },
  +#endif
  +/*---------------------------------------------------------------------------*/
  +#if VERSION_PCRE >= 400
  +  { "EXTRA_STUDY_DATA",              PCRE_EXTRA_STUDY_DATA },
  +  { "EXTRA_MATCH_LIMIT",             PCRE_EXTRA_MATCH_LIMIT },
  +  { "EXTRA_CALLOUT_DATA",            PCRE_EXTRA_CALLOUT_DATA },
  +#endif
  +#if VERSION_PCRE >= 500
  +  { "EXTRA_TABLES",                  PCRE_EXTRA_TABLES },
  +#endif
  +#ifdef PCRE_EXTRA_MATCH_LIMIT_RECURSION
  +  { "EXTRA_MATCH_LIMIT_RECURSION",   PCRE_EXTRA_MATCH_LIMIT_RECURSION },
  +#endif
  +/*---------------------------------------------------------------------------*/
  +  { NULL, 0 }
  +};
  +
  +flag_pair pcre_error_flags[] = {
  +  { "ERROR_NOMATCH",                 PCRE_ERROR_NOMATCH },
  +  { "ERROR_NULL",                    PCRE_ERROR_NULL },
  +  { "ERROR_BADOPTION",               PCRE_ERROR_BADOPTION },
  +  { "ERROR_BADMAGIC",                PCRE_ERROR_BADMAGIC },
  +#if VERSION_PCRE >= 700
  +  { "ERROR_UNKNOWN_OPCODE",          PCRE_ERROR_UNKNOWN_OPCODE },
  +#endif
  +  { "ERROR_UNKNOWN_NODE",            PCRE_ERROR_UNKNOWN_NODE },
  +  { "ERROR_NOMEMORY",                PCRE_ERROR_NOMEMORY },
  +  { "ERROR_NOSUBSTRING",             PCRE_ERROR_NOSUBSTRING },
  +#if VERSION_PCRE >= 400
  +  { "ERROR_MATCHLIMIT",              PCRE_ERROR_MATCHLIMIT },
  +  { "ERROR_CALLOUT",                 PCRE_ERROR_CALLOUT },
  +  { "ERROR_BADUTF8",                 PCRE_ERROR_BADUTF8 },
  +  { "ERROR_BADUTF8_OFFSET",          PCRE_ERROR_BADUTF8_OFFSET },
  +#endif
  +#if VERSION_PCRE >= 500
  +  { "ERROR_PARTIAL",                 PCRE_ERROR_PARTIAL },
  +  { "ERROR_BADPARTIAL",              PCRE_ERROR_BADPARTIAL },
  +  { "ERROR_INTERNAL",                PCRE_ERROR_INTERNAL },
  +  { "ERROR_BADCOUNT",                PCRE_ERROR_BADCOUNT },
  +#endif
  +#if VERSION_PCRE >= 600
  +  { "ERROR_DFA_UITEM",               PCRE_ERROR_DFA_UITEM },
  +  { "ERROR_DFA_UCOND",               PCRE_ERROR_DFA_UCOND },
  +  { "ERROR_DFA_UMLIMIT",             PCRE_ERROR_DFA_UMLIMIT },
  +  { "ERROR_DFA_WSSIZE",              PCRE_ERROR_DFA_WSSIZE },
  +  { "ERROR_DFA_RECURSE",             PCRE_ERROR_DFA_RECURSE },
  +#endif
  +#if VERSION_PCRE >= 607
  +  { "ERROR_RECURSIONLIMIT",          PCRE_ERROR_RECURSIONLIMIT },
  +#endif
  +#if VERSION_PCRE >= 700
  +  { "ERROR_BADNEWLINE",              PCRE_ERROR_BADNEWLINE },
  +#endif
  +#ifdef PCRE_ERROR_NULLWSLIMIT
  +  { "ERROR_NULLWSLIMIT",             PCRE_ERROR_NULLWSLIMIT },
  +#endif
  +/*---------------------------------------------------------------------------*/
  +  { NULL, 0 }
  +};
  +
  +#if VERSION_PCRE >= 400
  +static flag_pair pcre_config_flags[] = {
  +  { "CONFIG_UTF8",                   PCRE_CONFIG_UTF8 },
  +  { "CONFIG_NEWLINE",                PCRE_CONFIG_NEWLINE },
  +  { "CONFIG_LINK_SIZE",              PCRE_CONFIG_LINK_SIZE },
  +  { "CONFIG_POSIX_MALLOC_THRESHOLD", PCRE_CONFIG_POSIX_MALLOC_THRESHOLD },
  +  { "CONFIG_MATCH_LIMIT",            PCRE_CONFIG_MATCH_LIMIT },
  +  { "CONFIG_STACKRECURSE",           PCRE_CONFIG_STACKRECURSE },
  +#if VERSION_PCRE >= 500
  +  { "CONFIG_UNICODE_PROPERTIES",     PCRE_CONFIG_UNICODE_PROPERTIES },
  +#endif
  +#if VERSION_PCRE >= 650
  +  { "CONFIG_MATCH_LIMIT_RECURSION",  PCRE_CONFIG_MATCH_LIMIT_RECURSION },
  +#endif
  +#if VERSION_PCRE >= 704
  +  { "CONFIG_BSR",                    PCRE_CONFIG_BSR },
  +#endif
  +/*---------------------------------------------------------------------------*/
  +  { NULL, 0 }
  +};
  +
  +int Lpcre_config (lua_State *L) {
  +  int val;
  +  flag_pair *fp;
  +  if (lua_istable (L, 1))
  +    lua_settop (L, 1);
  +  else
  +    lua_newtable (L);
  +  for (fp = pcre_config_flags; fp->key; ++fp) {
  +    if (0 == pcre_config (fp->val, &val)) {
  +      lua_pushinteger (L, val);
  +      lua_setfield (L, -2, fp->key);
  +    }
  +  }
  +  return 1;
  +}
  +#endif /* #if VERSION_PCRE >= 400 */
  +
  +int Lpcre_get_flags (lua_State *L) {
  +  const flag_pair* fps[] = { pcre_flags, pcre_error_flags, NULL };
  +  return get_flags (L, fps);
  +}
  +
  +#endif
  +
  @@ .
  patch -p0 <<'@@ .'
  Index: lua/local/lrexlib_lposix.c
  ============================================================================
  $ cvs diff -u -r0 -r1.1 lrexlib_lposix.c
  --- /dev/null	2008-01-12 13:33:00 +0100
  +++ lrexlib_lposix.c	2008-01-12 13:33:34 +0100
  @@ -0,0 +1,308 @@
  +/* lposix.c - Lua binding of POSIX regular expressions library */
  +/* See Copyright Notice in the file LICENSE */
  +
  +#ifndef REX_LIBNAME
  +#  define REX_LIBNAME "rex_posix"
  +#endif
  +#ifndef REX_OPENLIB
  +#  define REX_OPENLIB luaopen_rex_posix
  +#endif
  +
  +#include <stdlib.h>
  +#include <string.h>
  +#include <ctype.h>
  +
  +#include "lua.h"
  +#include "lauxlib.h"
  +#include "lrexlib.h"
  +#include "lrexlib_common.h"
  +
  +#if defined(LUA_USE_POSIX)
  +
  +#ifndef REX_POSIX_INCLUDE
  +#  include <regex.h>
  +#else
  +#  include REX_POSIX_INCLUDE
  +#endif
  +
  +/* Test if regex.h corresponds to the extended POSIX library, i.e. H.Spencer's.
  +   This test may not work as intended if regex.h introduced REG_BASIC, etc.
  +   via enum rather than #define.
  +   If that's the case, add -DREX_POSIX_EXT in the makefile/command line.
  +*/
  +#ifndef REX_POSIX_EXT
  +#  if defined(REG_BASIC) && defined(REG_STARTEND)
  +#    define REX_POSIX_EXT
  +#  endif
  +#endif
  +
  +#define ALG_CFLAGS_DFLT REG_EXTENDED
  +#ifdef REX_POSIX_EXT
  +#  define ALG_EFLAGS_DFLT REG_STARTEND
  +#else
  +#  define ALG_EFLAGS_DFLT 0
  +#endif
  +
  +#define ALG_NOMATCH        REG_NOMATCH
  +#define ALG_ISMATCH(res)   ((res) == 0)
  +#define ALG_SUBBEG(ud,n)   ud->match[n].rm_so
  +#define ALG_SUBEND(ud,n)   ud->match[n].rm_eo
  +#define ALG_SUBLEN(ud,n)   (ALG_SUBEND(ud,n) - ALG_SUBBEG(ud,n))
  +#define ALG_SUBVALID(ud,n) (ALG_SUBBEG(ud,n) >= 0)
  +#ifdef REX_NSUB_BASE1
  +#  define ALG_NSUB(ud)     ((int)ud->r.re_nsub - 1)
  +#else
  +#  define ALG_NSUB(ud)     ((int)ud->r.re_nsub)
  +#endif
  +
  +#define ALG_PUSHSUB(L,ud,text,n) \
  +  lua_pushlstring (L, (text) + ALG_SUBBEG(ud,n), ALG_SUBLEN(ud,n))
  +
  +#define ALG_PUSHSUB_OR_FALSE(L,ud,text,n) \
  +  (ALG_SUBVALID(ud,n) ? ALG_PUSHSUB (L,ud,text,n) : lua_pushboolean (L,0))
  +
  +#define ALG_PUSHSTART(L,ud,offs,n)   lua_pushinteger(L, (offs) + ALG_SUBBEG(ud,n) + 1)
  +#define ALG_PUSHEND(L,ud,offs,n)     lua_pushinteger(L, (offs) + ALG_SUBEND(ud,n))
  +#define ALG_PUSHOFFSETS(L,ud,offs,n) \
  +  (ALG_PUSHSTART(L,ud,offs,n), ALG_PUSHEND(L,ud,offs,n))
  +
  +#define ALG_BASE(st)                  (st)
  +#define ALG_GETCFLAGS(L,pos)          luaL_optint(L, pos, ALG_CFLAGS_DFLT)
  +
  +typedef struct {
  +  regex_t      r;
  +  regmatch_t * match;
  +  int          freed;
  +} TPosix;
  +
  +#define TUserdata TPosix
  +
  +const char posix_typename[] = REX_LIBNAME"_regex";
  +
  +#include "lrexlib_algo.h"
  +
  +/*  Functions
  + ******************************************************************************
  + */
  +
  +static int generate_error (lua_State *L, const TPosix *ud, int errcode) {
  +  char errbuf[80];
  +  regerror (errcode, &ud->r, errbuf, sizeof (errbuf));
  +  return luaL_error (L, "%s", errbuf);
  +}
  +
  +static int compile_regex (lua_State *L, const TArgComp *argC, TPosix **pud) {
  +  int res;
  +  TPosix *ud;
  +
  +  ud = (TPosix *)lua_newuserdata (L, sizeof (TPosix));
  +  memset (ud, 0, sizeof (TPosix));          /* initialize all members to 0 */
  +
  +#ifdef REX_POSIX_EXT
  +  if (argC->cflags & REG_PEND)
  +    ud->r.re_endp = argC->pattern + argC->patlen;
  +#endif
  +
  +  res = regcomp (&ud->r, argC->pattern, argC->cflags);
  +  if (res != 0)
  +    return generate_error (L, ud, res);
  +
  +  if (argC->cflags & REG_NOSUB)
  +    ud->r.re_nsub = 0;
  +  ud->match = (regmatch_t *) Lmalloc (L, (ALG_NSUB(ud) + 1) * sizeof (regmatch_t));
  +  lua_pushvalue (L, LUA_ENVIRONINDEX);
  +  lua_setmetatable (L, -2);
  +
  +  if (pud) *pud = ud;
  +  return 1;
  +}
  +
  +#ifdef REX_POSIX_EXT
  +static void CheckStartEnd (TArgExec *argE, TPosix *ud) {
  +  if (argE->eflags & REG_STARTEND) {
  +    ud->match[0].rm_so = argE->startoffset;
  +    ud->match[0].rm_eo = argE->textlen;
  +    argE->startoffset = 0;
  +  }
  +  else
  +    argE->text += argE->startoffset;
  +}
  +#endif
  +
  +static int tfind_exec (TPosix *ud, TArgExec *argE) {
  +#ifdef REX_POSIX_EXT
  +  CheckStartEnd (argE, ud);
  +#else
  +  argE->text += argE->startoffset;
  +#endif
  +  return regexec (&ud->r, argE->text, ALG_NSUB(ud) + 1, ud->match, argE->eflags);
  +}
  +
  +static int gmatch_exec (TUserdata *ud, TArgExec *argE) {
  +  if (argE->startoffset > 0)
  +    argE->eflags |= REG_NOTBOL;
  +
  +#ifdef REX_POSIX_EXT
  +  if (argE->eflags & REG_STARTEND) {
  +    ALG_SUBBEG(ud,0) = 0;
  +    ALG_SUBEND(ud,0) = argE->textlen - argE->startoffset;
  +  }
  +#endif
  +
  +  argE->text += argE->startoffset;
  +  return regexec (&ud->r, argE->text, ALG_NSUB(ud) + 1, ud->match, argE->eflags);
  +}
  +
  +static void gmatch_pushsubject (lua_State *L, TArgExec *argE) {
  +#ifdef REX_POSIX_EXT
  +  if (argE->eflags & REG_STARTEND)
  +    lua_pushlstring (L, argE->text, argE->textlen);
  +  else
  +    lua_pushlstring (L, argE->text, strlen (argE->text));
  +#else
  +    lua_pushlstring (L, argE->text, strlen (argE->text));
  +#endif
  +}
  +
  +static int findmatch_exec (TPosix *ud, TArgExec *argE) {
  +#ifdef REX_POSIX_EXT
  +  CheckStartEnd (argE, ud);
  +#else
  +  argE->text += argE->startoffset;
  +#endif
  +  return regexec (&ud->r, argE->text, ALG_NSUB(ud) + 1, ud->match, argE->eflags);
  +}
  +
  +static int gsub_exec (TPosix *ud, TArgExec *argE, int st) {
  +#ifdef REX_POSIX_EXT
  +  if(argE->eflags & REG_STARTEND) {
  +    ALG_SUBBEG(ud,0) = 0;
  +    ALG_SUBEND(ud,0) = argE->textlen - st;
  +  }
  +#endif
  +  if (st > 0)
  +    argE->eflags |= REG_NOTBOL;
  +  return regexec (&ud->r, argE->text+st, ALG_NSUB(ud)+1, ud->match, argE->eflags);
  +}
  +
  +static int split_exec (TPosix *ud, TArgExec *argE, int offset) {
  +#ifdef REX_POSIX_EXT
  +  if (argE->eflags & REG_STARTEND) {
  +    ALG_SUBBEG(ud,0) = 0;
  +    ALG_SUBEND(ud,0) = argE->textlen - offset;
  +  }
  +#endif
  +  if (offset > 0)
  +    argE->eflags |= REG_NOTBOL;
  +
  +  return regexec (&ud->r, argE->text + offset, ALG_NSUB(ud) + 1, ud->match, argE->eflags);
  +}
  +
  +static int Posix_gc (lua_State *L) {
  +  TPosix *ud = check_ud (L);
  +  if (ud->freed == 0) {           /* precaution against "manual" __gc calling */
  +    ud->freed = 1;
  +    regfree (&ud->r);
  +    if (ud->match)
  +      free (ud->match);
  +  }
  +  return 0;
  +}
  +
  +static int Posix_tostring (lua_State *L) {
  +  TPosix *ud = check_ud (L);
  +  if (ud->freed == 0)
  +    lua_pushfstring (L, "%s (%p)", posix_typename, (void*)ud);
  +  else
  +    lua_pushfstring (L, "%s (deleted)", posix_typename);
  +  return 1;
  +}
  +
  +static flag_pair posix_flags[] =
  +{
  +#ifdef REX_POSIX_EXT
  +  { "BASIC",    REG_BASIC },
  +  { "NOSPEC",   REG_NOSPEC },
  +  { "PEND",     REG_PEND },
  +  { "STARTEND", REG_STARTEND },
  +#endif
  +  { "EXTENDED", REG_EXTENDED },
  +  { "ICASE",    REG_ICASE },
  +  { "NOSUB",    REG_NOSUB },
  +  { "NEWLINE",  REG_NEWLINE },
  +  { "NOTBOL",   REG_NOTBOL },
  +  { "NOTEOL",   REG_NOTEOL },
  +/*---------------------------------------------------------------------------*/
  +  { NULL, 0 }
  +};
  +
  +static flag_pair posix_error_flags[] = {
  +  { "NOMATCH",  REG_NOMATCH },
  +  { "BADPAT",   REG_BADPAT },
  +  { "ECOLLATE", REG_ECOLLATE },
  +  { "ECTYPE",   REG_ECTYPE },
  +  { "EESCAPE",  REG_EESCAPE },
  +  { "ESUBREG",  REG_ESUBREG },
  +  { "EBRACK",   REG_EBRACK },
  +  { "EPAREN",   REG_EPAREN },
  +  { "EBRACE",   REG_EBRACE },
  +  { "BADBR",    REG_BADBR },
  +  { "ERANGE",   REG_ERANGE },
  +  { "ESPACE",   REG_ESPACE },
  +  { "BADRPT",   REG_BADRPT },
  +#ifdef REX_POSIX_EXT
  +  { "EMPTY",    REG_EMPTY },
  +  { "ASSERT",   REG_ASSERT },
  +  { "INVARG",   REG_INVARG },
  +#endif
  +/*---------------------------------------------------------------------------*/
  +  { NULL, 0 }
  +};
  +
  +static int Posix_get_flags (lua_State *L) {
  +  const flag_pair* fps[] = { posix_flags, posix_error_flags, NULL };
  +  return get_flags (L, fps);
  +}
  +
  +static const luaL_reg posixmeta[] = {
  +  { "exec",       ud_exec },
  +  { "tfind",      ud_tfind },    /* old match */
  +  { "__gc",       Posix_gc },
  +  { "__tostring", Posix_tostring },
  +  { NULL, NULL}
  +};
  +
  +static const luaL_reg rexlib[] = {
  +  { "match",      match },
  +  { "find",       find },
  +  { "gmatch",     gmatch },
  +  { "gsub",       gsub },
  +  { "split",      split },
  +  { "new",        ud_new },
  +  { "flags",      Posix_get_flags },
  +  { "plainfind",  plainfind_func },
  +  { NULL, NULL }
  +};
  +
  +#endif
  +
  +/* Open the library */
  +REX_API int REX_OPENLIB (lua_State *L)
  +{
  +#if defined(LUA_USE_POSIX)
  +  /* create a new function environment to serve as a metatable for methods */
  +  lua_newtable (L);
  +  lua_pushvalue (L, -1);
  +  lua_replace (L, LUA_ENVIRONINDEX);
  +  lua_pushvalue(L, -1); /* mt.__index = mt */
  +  lua_setfield(L, -2, "__index");
  +  luaL_register (L, NULL, posixmeta);
  +
  +  /* register functions */
  +  luaL_register (L, REX_LIBNAME, rexlib);
  +  lua_pushliteral (L, REX_VERSION" (for POSIX regexes)");
  +  lua_setfield (L, -2, "_VERSION");
  +#endif
  +  return 1;
  +}
  +
  @@ .
  patch -p0 <<'@@ .'
  Index: rpm/rpmio/rpmlua.c
  ============================================================================
  $ cvs diff -u -r2.39 -r2.40 rpmlua.c
  --- rpm/rpmio/rpmlua.c	11 Jan 2008 12:57:49 -0000	2.39
  +++ rpm/rpmio/rpmlua.c	12 Jan 2008 12:33:34 -0000	2.40
  @@ -79,7 +79,8 @@
   	/* local LUA libraries (RPM only) */
   #ifdef WITH_LUA_INTERNAL
   	{"posix", luaopen_posix},
  -	{"rex", luaopen_rex},
  +	{"rex_posix", luaopen_rex_posix},
  +	{"rex_pcre", luaopen_rex_pcre},
   	{"uuid", luaopen_uuid},
   	{"local", luaopen_local},
   #endif
  @@ .
Received on Sat Jan 12 13:33:34 2008
Driven by Jeff Johnson and the RPM project team.
Hosted by OpenPKG and Ralf S. Engelschall.
Powered by FreeBSD and OpenPKG.