Commit 8d36b253 by Vicent Martí

Merge pull request #565 from carlosmn/multimap

Add config multivar support
parents 20ed8983 b13dbb91
......@@ -28,6 +28,10 @@ FILE(GLOB SRC_HTTP deps/http-parser/*.c)
IF (NOT WIN32)
FIND_PACKAGE(ZLIB)
ELSE()
# Windows doesn't understand POSIX regex on its own
INCLUDE_DIRECTORIES(deps/regex)
SET(SRC_REGEX deps/regex/regex.c)
ENDIF()
IF (ZLIB_FOUND)
......@@ -99,7 +103,7 @@ ELSE()
ENDIF ()
# Compile and link libgit2
ADD_LIBRARY(git2 ${SRC} ${SRC_ZLIB} ${SRC_HTTP} ${WIN_RC})
ADD_LIBRARY(git2 ${SRC} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${WIN_RC})
IF (WIN32)
TARGET_LINK_LIBRARIES(git2 ws2_32)
......@@ -130,7 +134,7 @@ IF (BUILD_TESTS)
INCLUDE_DIRECTORIES(tests)
FILE(GLOB SRC_TEST tests/t??-*.c)
ADD_EXECUTABLE(libgit2_test tests/test_main.c tests/test_lib.c tests/test_helpers.c ${SRC} ${SRC_TEST} ${SRC_ZLIB} ${SRC_HTTP})
ADD_EXECUTABLE(libgit2_test tests/test_main.c tests/test_lib.c tests/test_helpers.c ${SRC} ${SRC_TEST} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX})
TARGET_LINK_LIBRARIES(libgit2_test ${CMAKE_THREAD_LIBS_INIT})
IF (WIN32)
TARGET_LINK_LIBRARIES(libgit2_test ws2_32)
......@@ -158,7 +162,7 @@ IF (BUILD_CLAR)
DEPENDS ${CLAR_PATH}/clar ${SRC_TEST}
WORKING_DIRECTORY ${CLAR_PATH}
)
ADD_EXECUTABLE(libgit2_clar ${SRC} ${CLAR_PATH}/clar_main.c ${SRC_TEST} ${SRC_ZLIB} ${SRC_HTTP})
ADD_EXECUTABLE(libgit2_clar ${SRC} ${CLAR_PATH}/clar_main.c ${SRC_TEST} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX})
TARGET_LINK_LIBRARIES(libgit2_clar ${CMAKE_THREAD_LIBS_INIT})
IF (WIN32)
TARGET_LINK_LIBRARIES(libgit2_clar ws2_32)
......
......@@ -422,4 +422,509 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
----------------------------------------------------------------------
The regex library (deps/regex/) is licensed under the GNU LGPL
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!
#ifndef _REGEX_CONFIG_H_
#define _REGEX_CONFIG_H_
# define GAWK
# define NO_MBSUPPORT
#endif
This source diff could not be displayed because it is too large. You can view the blob instead.
/* Extended regular expression matching and search library.
Copyright (C) 2002, 2003, 2005 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, write to the Free
Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA. */
#include "config.h"
/* Make sure noone compiles this code with a C++ compiler. */
#ifdef __cplusplus
# error "This is C code, use a C compiler"
#endif
#ifdef _LIBC
/* We have to keep the namespace clean. */
# define regfree(preg) __regfree (preg)
# define regexec(pr, st, nm, pm, ef) __regexec (pr, st, nm, pm, ef)
# define regcomp(preg, pattern, cflags) __regcomp (preg, pattern, cflags)
# define regerror(errcode, preg, errbuf, errbuf_size) \
__regerror(errcode, preg, errbuf, errbuf_size)
# define re_set_registers(bu, re, nu, st, en) \
__re_set_registers (bu, re, nu, st, en)
# define re_match_2(bufp, string1, size1, string2, size2, pos, regs, stop) \
__re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop)
# define re_match(bufp, string, size, pos, regs) \
__re_match (bufp, string, size, pos, regs)
# define re_search(bufp, string, size, startpos, range, regs) \
__re_search (bufp, string, size, startpos, range, regs)
# define re_compile_pattern(pattern, length, bufp) \
__re_compile_pattern (pattern, length, bufp)
# define re_set_syntax(syntax) __re_set_syntax (syntax)
# define re_search_2(bufp, st1, s1, st2, s2, startpos, range, regs, stop) \
__re_search_2 (bufp, st1, s1, st2, s2, startpos, range, regs, stop)
# define re_compile_fastmap(bufp) __re_compile_fastmap (bufp)
# include "../locale/localeinfo.h"
#endif
#if defined (_MSC_VER)
#include <stdio.h> /* for size_t */
#endif
/* On some systems, limits.h sets RE_DUP_MAX to a lower value than
GNU regex allows. Include it before <regex.h>, which correctly
#undefs RE_DUP_MAX and sets it to the right value. */
#include <limits.h>
#ifdef GAWK
#undef alloca
#define alloca alloca_is_bad_you_should_never_use_it
#endif
#include <regex.h>
#include "regex_internal.h"
#include "regex_internal.c"
#ifdef GAWK
#define bool int
#define true (1)
#define false (0)
#endif
#include "regcomp.c"
#include "regexec.c"
/* Binary backward compatibility. */
#if _LIBC
# include <shlib-compat.h>
# if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_3)
link_warning (re_max_failures, "the 're_max_failures' variable is obsolete and will go away.")
int re_max_failures = 2000;
# endif
#endif
#include <stdio.h>
#include <stddef.h>
/* Definitions for data structures and routines for the regular
expression library.
Copyright (C) 1985,1989-93,1995-98,2000,2001,2002,2003,2005,2006,2008
Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, write to the Free
Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA. */
#ifndef _REGEX_H
#define _REGEX_H 1
#ifdef HAVE_STDDEF_H
#include <stddef.h>
#endif
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifndef _LIBC
#define __USE_GNU 1
#endif
/* Allow the use in C++ code. */
#ifdef __cplusplus
extern "C" {
#endif
/* The following two types have to be signed and unsigned integer type
wide enough to hold a value of a pointer. For most ANSI compilers
ptrdiff_t and size_t should be likely OK. Still size of these two
types is 2 for Microsoft C. Ugh... */
typedef long int s_reg_t;
typedef unsigned long int active_reg_t;
/* The following bits are used to determine the regexp syntax we
recognize. The set/not-set meanings are chosen so that Emacs syntax
remains the value 0. The bits are given in alphabetical order, and
the definitions shifted by one from the previous bit; thus, when we
add or remove a bit, only one other definition need change. */
typedef unsigned long int reg_syntax_t;
#ifdef __USE_GNU
/* If this bit is not set, then \ inside a bracket expression is literal.
If set, then such a \ quotes the following character. */
# define RE_BACKSLASH_ESCAPE_IN_LISTS ((unsigned long int) 1)
/* If this bit is not set, then + and ? are operators, and \+ and \? are
literals.
If set, then \+ and \? are operators and + and ? are literals. */
# define RE_BK_PLUS_QM (RE_BACKSLASH_ESCAPE_IN_LISTS << 1)
/* If this bit is set, then character classes are supported. They are:
[:alpha:], [:upper:], [:lower:], [:digit:], [:alnum:], [:xdigit:],
[:space:], [:print:], [:punct:], [:graph:], and [:cntrl:].
If not set, then character classes are not supported. */
# define RE_CHAR_CLASSES (RE_BK_PLUS_QM << 1)
/* If this bit is set, then ^ and $ are always anchors (outside bracket
expressions, of course).
If this bit is not set, then it depends:
^ is an anchor if it is at the beginning of a regular
expression or after an open-group or an alternation operator;
$ is an anchor if it is at the end of a regular expression, or
before a close-group or an alternation operator.
This bit could be (re)combined with RE_CONTEXT_INDEP_OPS, because
POSIX draft 11.2 says that * etc. in leading positions is undefined.
We already implemented a previous draft which made those constructs
invalid, though, so we haven't changed the code back. */
# define RE_CONTEXT_INDEP_ANCHORS (RE_CHAR_CLASSES << 1)
/* If this bit is set, then special characters are always special
regardless of where they are in the pattern.
If this bit is not set, then special characters are special only in
some contexts; otherwise they are ordinary. Specifically,
* + ? and intervals are only special when not after the beginning,
open-group, or alternation operator. */
# define RE_CONTEXT_INDEP_OPS (RE_CONTEXT_INDEP_ANCHORS << 1)
/* If this bit is set, then *, +, ?, and { cannot be first in an re or
immediately after an alternation or begin-group operator. */
# define RE_CONTEXT_INVALID_OPS (RE_CONTEXT_INDEP_OPS << 1)
/* If this bit is set, then . matches newline.
If not set, then it doesn't. */
# define RE_DOT_NEWLINE (RE_CONTEXT_INVALID_OPS << 1)
/* If this bit is set, then . doesn't match NUL.
If not set, then it does. */
# define RE_DOT_NOT_NULL (RE_DOT_NEWLINE << 1)
/* If this bit is set, nonmatching lists [^...] do not match newline.
If not set, they do. */
# define RE_HAT_LISTS_NOT_NEWLINE (RE_DOT_NOT_NULL << 1)
/* If this bit is set, either \{...\} or {...} defines an
interval, depending on RE_NO_BK_BRACES.
If not set, \{, \}, {, and } are literals. */
# define RE_INTERVALS (RE_HAT_LISTS_NOT_NEWLINE << 1)
/* If this bit is set, +, ? and | aren't recognized as operators.
If not set, they are. */
# define RE_LIMITED_OPS (RE_INTERVALS << 1)
/* If this bit is set, newline is an alternation operator.
If not set, newline is literal. */
# define RE_NEWLINE_ALT (RE_LIMITED_OPS << 1)
/* If this bit is set, then `{...}' defines an interval, and \{ and \}
are literals.
If not set, then `\{...\}' defines an interval. */
# define RE_NO_BK_BRACES (RE_NEWLINE_ALT << 1)
/* If this bit is set, (...) defines a group, and \( and \) are literals.
If not set, \(...\) defines a group, and ( and ) are literals. */
# define RE_NO_BK_PARENS (RE_NO_BK_BRACES << 1)
/* If this bit is set, then \<digit> matches <digit>.
If not set, then \<digit> is a back-reference. */
# define RE_NO_BK_REFS (RE_NO_BK_PARENS << 1)
/* If this bit is set, then | is an alternation operator, and \| is literal.
If not set, then \| is an alternation operator, and | is literal. */
# define RE_NO_BK_VBAR (RE_NO_BK_REFS << 1)
/* If this bit is set, then an ending range point collating higher
than the starting range point, as in [z-a], is invalid.
If not set, then when ending range point collates higher than the
starting range point, the range is ignored. */
# define RE_NO_EMPTY_RANGES (RE_NO_BK_VBAR << 1)
/* If this bit is set, then an unmatched ) is ordinary.
If not set, then an unmatched ) is invalid. */
# define RE_UNMATCHED_RIGHT_PAREN_ORD (RE_NO_EMPTY_RANGES << 1)
/* If this bit is set, succeed as soon as we match the whole pattern,
without further backtracking. */
# define RE_NO_POSIX_BACKTRACKING (RE_UNMATCHED_RIGHT_PAREN_ORD << 1)
/* If this bit is set, do not process the GNU regex operators.
If not set, then the GNU regex operators are recognized. */
# define RE_NO_GNU_OPS (RE_NO_POSIX_BACKTRACKING << 1)
/* If this bit is set, a syntactically invalid interval is treated as
a string of ordinary characters. For example, the ERE 'a{1' is
treated as 'a\{1'. */
# define RE_INVALID_INTERVAL_ORD (RE_NO_GNU_OPS << 1)
/* If this bit is set, then ignore case when matching.
If not set, then case is significant. */
# define RE_ICASE (RE_INVALID_INTERVAL_ORD << 1)
/* This bit is used internally like RE_CONTEXT_INDEP_ANCHORS but only
for ^, because it is difficult to scan the regex backwards to find
whether ^ should be special. */
# define RE_CARET_ANCHORS_HERE (RE_ICASE << 1)
/* If this bit is set, then \{ cannot be first in an bre or
immediately after an alternation or begin-group operator. */
# define RE_CONTEXT_INVALID_DUP (RE_CARET_ANCHORS_HERE << 1)
/* If this bit is set, then no_sub will be set to 1 during
re_compile_pattern. */
#define RE_NO_SUB (RE_CONTEXT_INVALID_DUP << 1)
#endif
/* This global variable defines the particular regexp syntax to use (for
some interfaces). When a regexp is compiled, the syntax used is
stored in the pattern buffer, so changing this does not affect
already-compiled regexps. */
extern reg_syntax_t re_syntax_options;
#ifdef __USE_GNU
/* Define combinations of the above bits for the standard possibilities.
(The [[[ comments delimit what gets put into the Texinfo file, so
don't delete them!) */
/* [[[begin syntaxes]]] */
#define RE_SYNTAX_EMACS 0
#define RE_SYNTAX_AWK \
(RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DOT_NOT_NULL \
| RE_NO_BK_PARENS | RE_NO_BK_REFS \
| RE_NO_BK_VBAR | RE_NO_EMPTY_RANGES \
| RE_DOT_NEWLINE | RE_CONTEXT_INDEP_ANCHORS \
| RE_UNMATCHED_RIGHT_PAREN_ORD | RE_NO_GNU_OPS)
#define RE_SYNTAX_GNU_AWK \
((RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS \
| RE_INVALID_INTERVAL_ORD) \
& ~(RE_DOT_NOT_NULL | RE_CONTEXT_INDEP_OPS \
| RE_CONTEXT_INVALID_OPS ))
#define RE_SYNTAX_POSIX_AWK \
(RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS \
| RE_INTERVALS | RE_NO_GNU_OPS \
| RE_INVALID_INTERVAL_ORD)
#define RE_SYNTAX_GREP \
(RE_BK_PLUS_QM | RE_CHAR_CLASSES \
| RE_HAT_LISTS_NOT_NEWLINE | RE_INTERVALS \
| RE_NEWLINE_ALT)
#define RE_SYNTAX_EGREP \
(RE_CHAR_CLASSES | RE_CONTEXT_INDEP_ANCHORS \
| RE_CONTEXT_INDEP_OPS | RE_HAT_LISTS_NOT_NEWLINE \
| RE_NEWLINE_ALT | RE_NO_BK_PARENS \
| RE_NO_BK_VBAR)
#define RE_SYNTAX_POSIX_EGREP \
(RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES \
| RE_INVALID_INTERVAL_ORD)
/* P1003.2/D11.2, section 4.20.7.1, lines 5078ff. */
#define RE_SYNTAX_ED RE_SYNTAX_POSIX_BASIC
#define RE_SYNTAX_SED RE_SYNTAX_POSIX_BASIC
/* Syntax bits common to both basic and extended POSIX regex syntax. */
#define _RE_SYNTAX_POSIX_COMMON \
(RE_CHAR_CLASSES | RE_DOT_NEWLINE | RE_DOT_NOT_NULL \
| RE_INTERVALS | RE_NO_EMPTY_RANGES)
#define RE_SYNTAX_POSIX_BASIC \
(_RE_SYNTAX_POSIX_COMMON | RE_BK_PLUS_QM | RE_CONTEXT_INVALID_DUP)
/* Differs from ..._POSIX_BASIC only in that RE_BK_PLUS_QM becomes
RE_LIMITED_OPS, i.e., \? \+ \| are not recognized. Actually, this
isn't minimal, since other operators, such as \`, aren't disabled. */
#define RE_SYNTAX_POSIX_MINIMAL_BASIC \
(_RE_SYNTAX_POSIX_COMMON | RE_LIMITED_OPS)
#define RE_SYNTAX_POSIX_EXTENDED \
(_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \
| RE_CONTEXT_INDEP_OPS | RE_NO_BK_BRACES \
| RE_NO_BK_PARENS | RE_NO_BK_VBAR \
| RE_CONTEXT_INVALID_OPS | RE_UNMATCHED_RIGHT_PAREN_ORD)
/* Differs from ..._POSIX_EXTENDED in that RE_CONTEXT_INDEP_OPS is
removed and RE_NO_BK_REFS is added. */
#define RE_SYNTAX_POSIX_MINIMAL_EXTENDED \
(_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \
| RE_CONTEXT_INVALID_OPS | RE_NO_BK_BRACES \
| RE_NO_BK_PARENS | RE_NO_BK_REFS \
| RE_NO_BK_VBAR | RE_UNMATCHED_RIGHT_PAREN_ORD)
/* [[[end syntaxes]]] */
/* Maximum number of duplicates an interval can allow. Some systems
(erroneously) define this in other header files, but we want our
value, so remove any previous define. */
# ifdef RE_DUP_MAX
# undef RE_DUP_MAX
# endif
/* If sizeof(int) == 2, then ((1 << 15) - 1) overflows. */
# define RE_DUP_MAX (0x7fff)
#endif
/* POSIX `cflags' bits (i.e., information for `regcomp'). */
/* If this bit is set, then use extended regular expression syntax.
If not set, then use basic regular expression syntax. */
#define REG_EXTENDED 1
/* If this bit is set, then ignore case when matching.
If not set, then case is significant. */
#define REG_ICASE (REG_EXTENDED << 1)
/* If this bit is set, then anchors do not match at newline
characters in the string.
If not set, then anchors do match at newlines. */
#define REG_NEWLINE (REG_ICASE << 1)
/* If this bit is set, then report only success or fail in regexec.
If not set, then returns differ between not matching and errors. */
#define REG_NOSUB (REG_NEWLINE << 1)
/* POSIX `eflags' bits (i.e., information for regexec). */
/* If this bit is set, then the beginning-of-line operator doesn't match
the beginning of the string (presumably because it's not the
beginning of a line).
If not set, then the beginning-of-line operator does match the
beginning of the string. */
#define REG_NOTBOL 1
/* Like REG_NOTBOL, except for the end-of-line. */
#define REG_NOTEOL (1 << 1)
/* Use PMATCH[0] to delimit the start and end of the search in the
buffer. */
#define REG_STARTEND (1 << 2)
/* If any error codes are removed, changed, or added, update the
`re_error_msg' table in regex.c. */
typedef enum
{
#if defined _XOPEN_SOURCE || defined __USE_XOPEN2K
REG_ENOSYS = -1, /* This will never happen for this implementation. */
#endif
REG_NOERROR = 0, /* Success. */
REG_NOMATCH, /* Didn't find a match (for regexec). */
/* POSIX regcomp return error codes. (In the order listed in the
standard.) */
REG_BADPAT, /* Invalid pattern. */
REG_ECOLLATE, /* Inalid collating element. */
REG_ECTYPE, /* Invalid character class name. */
REG_EESCAPE, /* Trailing backslash. */
REG_ESUBREG, /* Invalid back reference. */
REG_EBRACK, /* Unmatched left bracket. */
REG_EPAREN, /* Parenthesis imbalance. */
REG_EBRACE, /* Unmatched \{. */
REG_BADBR, /* Invalid contents of \{\}. */
REG_ERANGE, /* Invalid range end. */
REG_ESPACE, /* Ran out of memory. */
REG_BADRPT, /* No preceding re for repetition op. */
/* Error codes we've added. */
REG_EEND, /* Premature end. */
REG_ESIZE, /* Compiled pattern bigger than 2^16 bytes. */
REG_ERPAREN /* Unmatched ) or \); not returned from regcomp. */
} reg_errcode_t;
/* This data structure represents a compiled pattern. Before calling
the pattern compiler, the fields `buffer', `allocated', `fastmap',
`translate', and `no_sub' can be set. After the pattern has been
compiled, the `re_nsub' field is available. All other fields are
private to the regex routines. */
#ifndef RE_TRANSLATE_TYPE
# define __RE_TRANSLATE_TYPE unsigned char *
# ifdef __USE_GNU
# define RE_TRANSLATE_TYPE __RE_TRANSLATE_TYPE
# endif
#endif
#ifdef __USE_GNU
# define __REPB_PREFIX(name) name
#else
# define __REPB_PREFIX(name) __##name
#endif
struct re_pattern_buffer
{
/* Space that holds the compiled pattern. It is declared as
`unsigned char *' because its elements are sometimes used as
array indexes. */
unsigned char *__REPB_PREFIX(buffer);
/* Number of bytes to which `buffer' points. */
unsigned long int __REPB_PREFIX(allocated);
/* Number of bytes actually used in `buffer'. */
unsigned long int __REPB_PREFIX(used);
/* Syntax setting with which the pattern was compiled. */
reg_syntax_t __REPB_PREFIX(syntax);
/* Pointer to a fastmap, if any, otherwise zero. re_search uses the
fastmap, if there is one, to skip over impossible starting points
for matches. */
char *__REPB_PREFIX(fastmap);
/* Either a translate table to apply to all characters before
comparing them, or zero for no translation. The translation is
applied to a pattern when it is compiled and to a string when it
is matched. */
__RE_TRANSLATE_TYPE __REPB_PREFIX(translate);
/* Number of subexpressions found by the compiler. */
size_t re_nsub;
/* Zero if this pattern cannot match the empty string, one else.
Well, in truth it's used only in `re_search_2', to see whether or
not we should use the fastmap, so we don't set this absolutely
perfectly; see `re_compile_fastmap' (the `duplicate' case). */
unsigned __REPB_PREFIX(can_be_null) : 1;
/* If REGS_UNALLOCATED, allocate space in the `regs' structure
for `max (RE_NREGS, re_nsub + 1)' groups.
If REGS_REALLOCATE, reallocate space if necessary.
If REGS_FIXED, use what's there. */
#ifdef __USE_GNU
# define REGS_UNALLOCATED 0
# define REGS_REALLOCATE 1
# define REGS_FIXED 2
#endif
unsigned __REPB_PREFIX(regs_allocated) : 2;
/* Set to zero when `regex_compile' compiles a pattern; set to one
by `re_compile_fastmap' if it updates the fastmap. */
unsigned __REPB_PREFIX(fastmap_accurate) : 1;
/* If set, `re_match_2' does not return information about
subexpressions. */
unsigned __REPB_PREFIX(no_sub) : 1;
/* If set, a beginning-of-line anchor doesn't match at the beginning
of the string. */
unsigned __REPB_PREFIX(not_bol) : 1;
/* Similarly for an end-of-line anchor. */
unsigned __REPB_PREFIX(not_eol) : 1;
/* If true, an anchor at a newline matches. */
unsigned __REPB_PREFIX(newline_anchor) : 1;
};
typedef struct re_pattern_buffer regex_t;
/* Type for byte offsets within the string. POSIX mandates this. */
typedef int regoff_t;
#ifdef __USE_GNU
/* This is the structure we store register match data in. See
regex.texinfo for a full description of what registers match. */
struct re_registers
{
unsigned num_regs;
regoff_t *start;
regoff_t *end;
};
/* If `regs_allocated' is REGS_UNALLOCATED in the pattern buffer,
`re_match_2' returns information about at least this many registers
the first time a `regs' structure is passed. */
# ifndef RE_NREGS
# define RE_NREGS 30
# endif
#endif
/* POSIX specification for registers. Aside from the different names than
`re_registers', POSIX uses an array of structures, instead of a
structure of arrays. */
typedef struct
{
regoff_t rm_so; /* Byte offset from string's start to substring's start. */
regoff_t rm_eo; /* Byte offset from string's start to substring's end. */
} regmatch_t;
/* Declarations for routines. */
#ifdef __USE_GNU
/* Sets the current default syntax to SYNTAX, and return the old syntax.
You can also simply assign to the `re_syntax_options' variable. */
extern reg_syntax_t re_set_syntax (reg_syntax_t __syntax);
/* Compile the regular expression PATTERN, with length LENGTH
and syntax given by the global `re_syntax_options', into the buffer
BUFFER. Return NULL if successful, and an error string if not. */
extern const char *re_compile_pattern (const char *__pattern, size_t __length,
struct re_pattern_buffer *__buffer);
/* Compile a fastmap for the compiled pattern in BUFFER; used to
accelerate searches. Return 0 if successful and -2 if was an
internal error. */
extern int re_compile_fastmap (struct re_pattern_buffer *__buffer);
/* Search in the string STRING (with length LENGTH) for the pattern
compiled into BUFFER. Start searching at position START, for RANGE
characters. Return the starting position of the match, -1 for no
match, or -2 for an internal error. Also return register
information in REGS (if REGS and BUFFER->no_sub are nonzero). */
extern int re_search (struct re_pattern_buffer *__buffer, const char *__cstring,
int __length, int __start, int __range,
struct re_registers *__regs);
/* Like `re_search', but search in the concatenation of STRING1 and
STRING2. Also, stop searching at index START + STOP. */
extern int re_search_2 (struct re_pattern_buffer *__buffer,
const char *__string1, int __length1,
const char *__string2, int __length2, int __start,
int __range, struct re_registers *__regs, int __stop);
/* Like `re_search', but return how many characters in STRING the regexp
in BUFFER matched, starting at position START. */
extern int re_match (struct re_pattern_buffer *__buffer, const char *__cstring,
int __length, int __start, struct re_registers *__regs);
/* Relates to `re_match' as `re_search_2' relates to `re_search'. */
extern int re_match_2 (struct re_pattern_buffer *__buffer,
const char *__string1, int __length1,
const char *__string2, int __length2, int __start,
struct re_registers *__regs, int __stop);
/* Set REGS to hold NUM_REGS registers, storing them in STARTS and
ENDS. Subsequent matches using BUFFER and REGS will use this memory
for recording register information. STARTS and ENDS must be
allocated with malloc, and must each be at least `NUM_REGS * sizeof
(regoff_t)' bytes long.
If NUM_REGS == 0, then subsequent matches should allocate their own
register data.
Unless this function is called, the first search or match using
PATTERN_BUFFER will allocate its own register data, without
freeing the old data. */
extern void re_set_registers (struct re_pattern_buffer *__buffer,
struct re_registers *__regs,
unsigned int __num_regs,
regoff_t *__starts, regoff_t *__ends);
#endif /* Use GNU */
#if defined _REGEX_RE_COMP || (defined _LIBC && defined __USE_BSD)
# ifndef _CRAY
/* 4.2 bsd compatibility. */
extern char *re_comp (const char *);
extern int re_exec (const char *);
# endif
#endif
/* GCC 2.95 and later have "__restrict"; C99 compilers have
"restrict", and "configure" may have defined "restrict". */
#ifndef __restrict
# if ! (2 < __GNUC__ || (2 == __GNUC__ && 95 <= __GNUC_MINOR__))
# if defined restrict || 199901L <= __STDC_VERSION__
# define __restrict restrict
# else
# define __restrict
# endif
# endif
#endif
/* gcc 3.1 and up support the [restrict] syntax. */
#ifndef __restrict_arr
# if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) \
&& !defined __GNUG__
# define __restrict_arr __restrict
# else
# define __restrict_arr
# endif
#endif
/* POSIX compatibility. */
extern int regcomp (regex_t *__restrict __preg,
const char *__restrict __pattern,
int __cflags);
extern int regexec (const regex_t *__restrict __preg,
const char *__restrict __cstring, size_t __nmatch,
regmatch_t __pmatch[__restrict_arr],
int __eflags);
extern size_t regerror (int __errcode, const regex_t *__restrict __preg,
char *__restrict __errbuf, size_t __errbuf_size);
extern void regfree (regex_t *__preg);
#ifdef __cplusplus
}
#endif /* C++ */
#endif /* regex.h */
/* Extended regular expression matching and search library.
Copyright (C) 2002-2006, 2010 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, write to the Free
Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA. */
static void re_string_construct_common (const char *str, int len,
re_string_t *pstr,
RE_TRANSLATE_TYPE trans, int icase,
const re_dfa_t *dfa) internal_function;
static re_dfastate_t *create_ci_newstate (const re_dfa_t *dfa,
const re_node_set *nodes,
unsigned int hash) internal_function;
static re_dfastate_t *create_cd_newstate (const re_dfa_t *dfa,
const re_node_set *nodes,
unsigned int context,
unsigned int hash) internal_function;
#ifdef GAWK
#undef MAX /* safety */
static int
MAX(size_t a, size_t b)
{
return (a > b ? a : b);
}
#endif
/* Functions for string operation. */
/* This function allocate the buffers. It is necessary to call
re_string_reconstruct before using the object. */
static reg_errcode_t
internal_function
re_string_allocate (re_string_t *pstr, const char *str, int len, int init_len,
RE_TRANSLATE_TYPE trans, int icase, const re_dfa_t *dfa)
{
reg_errcode_t ret;
int init_buf_len;
/* Ensure at least one character fits into the buffers. */
if (init_len < dfa->mb_cur_max)
init_len = dfa->mb_cur_max;
init_buf_len = (len + 1 < init_len) ? len + 1: init_len;
re_string_construct_common (str, len, pstr, trans, icase, dfa);
ret = re_string_realloc_buffers (pstr, init_buf_len);
if (BE (ret != REG_NOERROR, 0))
return ret;
pstr->word_char = dfa->word_char;
pstr->word_ops_used = dfa->word_ops_used;
pstr->mbs = pstr->mbs_allocated ? pstr->mbs : (unsigned char *) str;
pstr->valid_len = (pstr->mbs_allocated || dfa->mb_cur_max > 1) ? 0 : len;
pstr->valid_raw_len = pstr->valid_len;
return REG_NOERROR;
}
/* This function allocate the buffers, and initialize them. */
static reg_errcode_t
internal_function
re_string_construct (re_string_t *pstr, const char *str, int len,
RE_TRANSLATE_TYPE trans, int icase, const re_dfa_t *dfa)
{
reg_errcode_t ret;
memset (pstr, '\0', sizeof (re_string_t));
re_string_construct_common (str, len, pstr, trans, icase, dfa);
if (len > 0)
{
ret = re_string_realloc_buffers (pstr, len + 1);
if (BE (ret != REG_NOERROR, 0))
return ret;
}
pstr->mbs = pstr->mbs_allocated ? pstr->mbs : (unsigned char *) str;
if (icase)
{
#ifdef RE_ENABLE_I18N
if (dfa->mb_cur_max > 1)
{
while (1)
{
ret = build_wcs_upper_buffer (pstr);
if (BE (ret != REG_NOERROR, 0))
return ret;
if (pstr->valid_raw_len >= len)
break;
if (pstr->bufs_len > pstr->valid_len + dfa->mb_cur_max)
break;
ret = re_string_realloc_buffers (pstr, pstr->bufs_len * 2);
if (BE (ret != REG_NOERROR, 0))
return ret;
}
}
else
#endif /* RE_ENABLE_I18N */
build_upper_buffer (pstr);
}
else
{
#ifdef RE_ENABLE_I18N
if (dfa->mb_cur_max > 1)
build_wcs_buffer (pstr);
else
#endif /* RE_ENABLE_I18N */
{
if (trans != NULL)
re_string_translate_buffer (pstr);
else
{
pstr->valid_len = pstr->bufs_len;
pstr->valid_raw_len = pstr->bufs_len;
}
}
}
return REG_NOERROR;
}
/* Helper functions for re_string_allocate, and re_string_construct. */
static reg_errcode_t
internal_function
re_string_realloc_buffers (re_string_t *pstr, int new_buf_len)
{
#ifdef RE_ENABLE_I18N
if (pstr->mb_cur_max > 1)
{
wint_t *new_wcs;
/* Avoid overflow in realloc. */
const size_t max_object_size = MAX (sizeof (wint_t), sizeof (int));
if (BE (SIZE_MAX / max_object_size < new_buf_len, 0))
return REG_ESPACE;
new_wcs = re_realloc (pstr->wcs, wint_t, new_buf_len);
if (BE (new_wcs == NULL, 0))
return REG_ESPACE;
pstr->wcs = new_wcs;
if (pstr->offsets != NULL)
{
int *new_offsets = re_realloc (pstr->offsets, int, new_buf_len);
if (BE (new_offsets == NULL, 0))
return REG_ESPACE;
pstr->offsets = new_offsets;
}
}
#endif /* RE_ENABLE_I18N */
if (pstr->mbs_allocated)
{
unsigned char *new_mbs = re_realloc (pstr->mbs, unsigned char,
new_buf_len);
if (BE (new_mbs == NULL, 0))
return REG_ESPACE;
pstr->mbs = new_mbs;
}
pstr->bufs_len = new_buf_len;
return REG_NOERROR;
}
static void
internal_function
re_string_construct_common (const char *str, int len, re_string_t *pstr,
RE_TRANSLATE_TYPE trans, int icase,
const re_dfa_t *dfa)
{
pstr->raw_mbs = (const unsigned char *) str;
pstr->len = len;
pstr->raw_len = len;
pstr->trans = trans;
pstr->icase = icase ? 1 : 0;
pstr->mbs_allocated = (trans != NULL || icase);
pstr->mb_cur_max = dfa->mb_cur_max;
pstr->is_utf8 = dfa->is_utf8;
pstr->map_notascii = dfa->map_notascii;
pstr->stop = pstr->len;
pstr->raw_stop = pstr->stop;
}
#ifdef RE_ENABLE_I18N
/* Build wide character buffer PSTR->WCS.
If the byte sequence of the string are:
<mb1>(0), <mb1>(1), <mb2>(0), <mb2>(1), <sb3>
Then wide character buffer will be:
<wc1> , WEOF , <wc2> , WEOF , <wc3>
We use WEOF for padding, they indicate that the position isn't
a first byte of a multibyte character.
Note that this function assumes PSTR->VALID_LEN elements are already
built and starts from PSTR->VALID_LEN. */
static void
internal_function
build_wcs_buffer (re_string_t *pstr)
{
#ifdef _LIBC
unsigned char buf[MB_LEN_MAX];
assert (MB_LEN_MAX >= pstr->mb_cur_max);
#else
unsigned char buf[64];
#endif
mbstate_t prev_st;
int byte_idx, end_idx, remain_len;
size_t mbclen;
/* Build the buffers from pstr->valid_len to either pstr->len or
pstr->bufs_len. */
end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len;
for (byte_idx = pstr->valid_len; byte_idx < end_idx;)
{
wchar_t wc;
const char *p;
remain_len = end_idx - byte_idx;
prev_st = pstr->cur_state;
/* Apply the translation if we need. */
if (BE (pstr->trans != NULL, 0))
{
int i, ch;
for (i = 0; i < pstr->mb_cur_max && i < remain_len; ++i)
{
ch = pstr->raw_mbs [pstr->raw_mbs_idx + byte_idx + i];
buf[i] = pstr->mbs[byte_idx + i] = pstr->trans[ch];
}
p = (const char *) buf;
}
else
p = (const char *) pstr->raw_mbs + pstr->raw_mbs_idx + byte_idx;
mbclen = __mbrtowc (&wc, p, remain_len, &pstr->cur_state);
if (BE (mbclen == (size_t) -2, 0))
{
/* The buffer doesn't have enough space, finish to build. */
pstr->cur_state = prev_st;
break;
}
else if (BE (mbclen == (size_t) -1 || mbclen == 0, 0))
{
/* We treat these cases as a singlebyte character. */
mbclen = 1;
wc = (wchar_t) pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx];
if (BE (pstr->trans != NULL, 0))
wc = pstr->trans[wc];
pstr->cur_state = prev_st;
}
/* Write wide character and padding. */
pstr->wcs[byte_idx++] = wc;
/* Write paddings. */
for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;)
pstr->wcs[byte_idx++] = WEOF;
}
pstr->valid_len = byte_idx;
pstr->valid_raw_len = byte_idx;
}
/* Build wide character buffer PSTR->WCS like build_wcs_buffer,
but for REG_ICASE. */
static reg_errcode_t
internal_function
build_wcs_upper_buffer (re_string_t *pstr)
{
mbstate_t prev_st;
int src_idx, byte_idx, end_idx, remain_len;
size_t mbclen;
#ifdef _LIBC
char buf[MB_LEN_MAX];
assert (MB_LEN_MAX >= pstr->mb_cur_max);
#else
char buf[64];
#endif
byte_idx = pstr->valid_len;
end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len;
/* The following optimization assumes that ASCII characters can be
mapped to wide characters with a simple cast. */
if (! pstr->map_notascii && pstr->trans == NULL && !pstr->offsets_needed)
{
while (byte_idx < end_idx)
{
wchar_t wc;
if (isascii (pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx])
&& mbsinit (&pstr->cur_state))
{
/* In case of a singlebyte character. */
pstr->mbs[byte_idx]
= toupper (pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx]);
/* The next step uses the assumption that wchar_t is encoded
ASCII-safe: all ASCII values can be converted like this. */
pstr->wcs[byte_idx] = (wchar_t) pstr->mbs[byte_idx];
++byte_idx;
continue;
}
remain_len = end_idx - byte_idx;
prev_st = pstr->cur_state;
mbclen = __mbrtowc (&wc,
((const char *) pstr->raw_mbs + pstr->raw_mbs_idx
+ byte_idx), remain_len, &pstr->cur_state);
if (BE (mbclen + 2 > 2, 1))
{
wchar_t wcu = wc;
if (iswlower (wc))
{
size_t mbcdlen;
wcu = towupper (wc);
mbcdlen = wcrtomb (buf, wcu, &prev_st);
if (BE (mbclen == mbcdlen, 1))
memcpy (pstr->mbs + byte_idx, buf, mbclen);
else
{
src_idx = byte_idx;
goto offsets_needed;
}
}
else
memcpy (pstr->mbs + byte_idx,
pstr->raw_mbs + pstr->raw_mbs_idx + byte_idx, mbclen);
pstr->wcs[byte_idx++] = wcu;
/* Write paddings. */
for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;)
pstr->wcs[byte_idx++] = WEOF;
}
else if (mbclen == (size_t) -1 || mbclen == 0)
{
/* It is an invalid character or '\0'. Just use the byte. */
int ch = pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx];
pstr->mbs[byte_idx] = ch;
/* And also cast it to wide char. */
pstr->wcs[byte_idx++] = (wchar_t) ch;
if (BE (mbclen == (size_t) -1, 0))
pstr->cur_state = prev_st;
}
else
{
/* The buffer doesn't have enough space, finish to build. */
pstr->cur_state = prev_st;
break;
}
}
pstr->valid_len = byte_idx;
pstr->valid_raw_len = byte_idx;
return REG_NOERROR;
}
else
for (src_idx = pstr->valid_raw_len; byte_idx < end_idx;)
{
wchar_t wc;
const char *p;
offsets_needed:
remain_len = end_idx - byte_idx;
prev_st = pstr->cur_state;
if (BE (pstr->trans != NULL, 0))
{
int i, ch;
for (i = 0; i < pstr->mb_cur_max && i < remain_len; ++i)
{
ch = pstr->raw_mbs [pstr->raw_mbs_idx + src_idx + i];
buf[i] = pstr->trans[ch];
}
p = (const char *) buf;
}
else
p = (const char *) pstr->raw_mbs + pstr->raw_mbs_idx + src_idx;
mbclen = __mbrtowc (&wc, p, remain_len, &pstr->cur_state);
if (BE (mbclen + 2 > 2, 1))
{
wchar_t wcu = wc;
if (iswlower (wc))
{
size_t mbcdlen;
wcu = towupper (wc);
mbcdlen = wcrtomb ((char *) buf, wcu, &prev_st);
if (BE (mbclen == mbcdlen, 1))
memcpy (pstr->mbs + byte_idx, buf, mbclen);
else if (mbcdlen != (size_t) -1)
{
size_t i;
if (byte_idx + mbcdlen > pstr->bufs_len)
{
pstr->cur_state = prev_st;
break;
}
if (pstr->offsets == NULL)
{
pstr->offsets = re_malloc (int, pstr->bufs_len);
if (pstr->offsets == NULL)
return REG_ESPACE;
}
if (!pstr->offsets_needed)
{
for (i = 0; i < (size_t) byte_idx; ++i)
pstr->offsets[i] = i;
pstr->offsets_needed = 1;
}
memcpy (pstr->mbs + byte_idx, buf, mbcdlen);
pstr->wcs[byte_idx] = wcu;
pstr->offsets[byte_idx] = src_idx;
for (i = 1; i < mbcdlen; ++i)
{
pstr->offsets[byte_idx + i]
= src_idx + (i < mbclen ? i : mbclen - 1);
pstr->wcs[byte_idx + i] = WEOF;
}
pstr->len += mbcdlen - mbclen;
if (pstr->raw_stop > src_idx)
pstr->stop += mbcdlen - mbclen;
end_idx = (pstr->bufs_len > pstr->len)
? pstr->len : pstr->bufs_len;
byte_idx += mbcdlen;
src_idx += mbclen;
continue;
}
else
memcpy (pstr->mbs + byte_idx, p, mbclen);
}
else
memcpy (pstr->mbs + byte_idx, p, mbclen);
if (BE (pstr->offsets_needed != 0, 0))
{
size_t i;
for (i = 0; i < mbclen; ++i)
pstr->offsets[byte_idx + i] = src_idx + i;
}
src_idx += mbclen;
pstr->wcs[byte_idx++] = wcu;
/* Write paddings. */
for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;)
pstr->wcs[byte_idx++] = WEOF;
}
else if (mbclen == (size_t) -1 || mbclen == 0)
{
/* It is an invalid character or '\0'. Just use the byte. */
int ch = pstr->raw_mbs[pstr->raw_mbs_idx + src_idx];
if (BE (pstr->trans != NULL, 0))
ch = pstr->trans [ch];
pstr->mbs[byte_idx] = ch;
if (BE (pstr->offsets_needed != 0, 0))
pstr->offsets[byte_idx] = src_idx;
++src_idx;
/* And also cast it to wide char. */
pstr->wcs[byte_idx++] = (wchar_t) ch;
if (BE (mbclen == (size_t) -1, 0))
pstr->cur_state = prev_st;
}
else
{
/* The buffer doesn't have enough space, finish to build. */
pstr->cur_state = prev_st;
break;
}
}
pstr->valid_len = byte_idx;
pstr->valid_raw_len = src_idx;
return REG_NOERROR;
}
/* Skip characters until the index becomes greater than NEW_RAW_IDX.
Return the index. */
static int
internal_function
re_string_skip_chars (re_string_t *pstr, int new_raw_idx, wint_t *last_wc)
{
mbstate_t prev_st;
int rawbuf_idx;
size_t mbclen;
wint_t wc = WEOF;
/* Skip the characters which are not necessary to check. */
for (rawbuf_idx = pstr->raw_mbs_idx + pstr->valid_raw_len;
rawbuf_idx < new_raw_idx;)
{
wchar_t wc2;
int remain_len = pstr->len - rawbuf_idx;
prev_st = pstr->cur_state;
mbclen = __mbrtowc (&wc2, (const char *) pstr->raw_mbs + rawbuf_idx,
remain_len, &pstr->cur_state);
if (BE (mbclen == (size_t) -2 || mbclen == (size_t) -1 || mbclen == 0, 0))
{
/* We treat these cases as a single byte character. */
if (mbclen == 0 || remain_len == 0)
wc = L'\0';
else
wc = *(unsigned char *) (pstr->raw_mbs + rawbuf_idx);
mbclen = 1;
pstr->cur_state = prev_st;
}
else
wc = (wint_t) wc2;
/* Then proceed the next character. */
rawbuf_idx += mbclen;
}
*last_wc = (wint_t) wc;
return rawbuf_idx;
}
#endif /* RE_ENABLE_I18N */
/* Build the buffer PSTR->MBS, and apply the translation if we need.
This function is used in case of REG_ICASE. */
static void
internal_function
build_upper_buffer (re_string_t *pstr)
{
int char_idx, end_idx;
end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len;
for (char_idx = pstr->valid_len; char_idx < end_idx; ++char_idx)
{
int ch = pstr->raw_mbs[pstr->raw_mbs_idx + char_idx];
if (BE (pstr->trans != NULL, 0))
ch = pstr->trans[ch];
if (islower (ch))
pstr->mbs[char_idx] = toupper (ch);
else
pstr->mbs[char_idx] = ch;
}
pstr->valid_len = char_idx;
pstr->valid_raw_len = char_idx;
}
/* Apply TRANS to the buffer in PSTR. */
static void
internal_function
re_string_translate_buffer (re_string_t *pstr)
{
int buf_idx, end_idx;
end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len;
for (buf_idx = pstr->valid_len; buf_idx < end_idx; ++buf_idx)
{
int ch = pstr->raw_mbs[pstr->raw_mbs_idx + buf_idx];
pstr->mbs[buf_idx] = pstr->trans[ch];
}
pstr->valid_len = buf_idx;
pstr->valid_raw_len = buf_idx;
}
/* This function re-construct the buffers.
Concretely, convert to wide character in case of pstr->mb_cur_max > 1,
convert to upper case in case of REG_ICASE, apply translation. */
static reg_errcode_t
internal_function
re_string_reconstruct (re_string_t *pstr, int idx, int eflags)
{
int offset = idx - pstr->raw_mbs_idx;
if (BE (offset < 0, 0))
{
/* Reset buffer. */
#ifdef RE_ENABLE_I18N
if (pstr->mb_cur_max > 1)
memset (&pstr->cur_state, '\0', sizeof (mbstate_t));
#endif /* RE_ENABLE_I18N */
pstr->len = pstr->raw_len;
pstr->stop = pstr->raw_stop;
pstr->valid_len = 0;
pstr->raw_mbs_idx = 0;
pstr->valid_raw_len = 0;
pstr->offsets_needed = 0;
pstr->tip_context = ((eflags & REG_NOTBOL) ? CONTEXT_BEGBUF
: CONTEXT_NEWLINE | CONTEXT_BEGBUF);
if (!pstr->mbs_allocated)
pstr->mbs = (unsigned char *) pstr->raw_mbs;
offset = idx;
}
if (BE (offset != 0, 1))
{
/* Should the already checked characters be kept? */
if (BE (offset < pstr->valid_raw_len, 1))
{
/* Yes, move them to the front of the buffer. */
#ifdef RE_ENABLE_I18N
if (BE (pstr->offsets_needed, 0))
{
int low = 0, high = pstr->valid_len, mid;
do
{
mid = (high + low) / 2;
if (pstr->offsets[mid] > offset)
high = mid;
else if (pstr->offsets[mid] < offset)
low = mid + 1;
else
break;
}
while (low < high);
if (pstr->offsets[mid] < offset)
++mid;
pstr->tip_context = re_string_context_at (pstr, mid - 1,
eflags);
/* This can be quite complicated, so handle specially
only the common and easy case where the character with
different length representation of lower and upper
case is present at or after offset. */
if (pstr->valid_len > offset
&& mid == offset && pstr->offsets[mid] == offset)
{
memmove (pstr->wcs, pstr->wcs + offset,
(pstr->valid_len - offset) * sizeof (wint_t));
memmove (pstr->mbs, pstr->mbs + offset, pstr->valid_len - offset);
pstr->valid_len -= offset;
pstr->valid_raw_len -= offset;
for (low = 0; low < pstr->valid_len; low++)
pstr->offsets[low] = pstr->offsets[low + offset] - offset;
}
else
{
/* Otherwise, just find out how long the partial multibyte
character at offset is and fill it with WEOF/255. */
pstr->len = pstr->raw_len - idx + offset;
pstr->stop = pstr->raw_stop - idx + offset;
pstr->offsets_needed = 0;
while (mid > 0 && pstr->offsets[mid - 1] == offset)
--mid;
while (mid < pstr->valid_len)
if (pstr->wcs[mid] != WEOF)
break;
else
++mid;
if (mid == pstr->valid_len)
pstr->valid_len = 0;
else
{
pstr->valid_len = pstr->offsets[mid] - offset;
if (pstr->valid_len)
{
for (low = 0; low < pstr->valid_len; ++low)
pstr->wcs[low] = WEOF;
memset (pstr->mbs, 255, pstr->valid_len);
}
}
pstr->valid_raw_len = pstr->valid_len;
}
}
else
#endif
{
pstr->tip_context = re_string_context_at (pstr, offset - 1,
eflags);
#ifdef RE_ENABLE_I18N
if (pstr->mb_cur_max > 1)
memmove (pstr->wcs, pstr->wcs + offset,
(pstr->valid_len - offset) * sizeof (wint_t));
#endif /* RE_ENABLE_I18N */
if (BE (pstr->mbs_allocated, 0))
memmove (pstr->mbs, pstr->mbs + offset,
pstr->valid_len - offset);
pstr->valid_len -= offset;
pstr->valid_raw_len -= offset;
#if DEBUG
assert (pstr->valid_len > 0);
#endif
}
}
else
{
#ifdef RE_ENABLE_I18N
/* No, skip all characters until IDX. */
int prev_valid_len = pstr->valid_len;
if (BE (pstr->offsets_needed, 0))
{
pstr->len = pstr->raw_len - idx + offset;
pstr->stop = pstr->raw_stop - idx + offset;
pstr->offsets_needed = 0;
}
#endif
pstr->valid_len = 0;
#ifdef RE_ENABLE_I18N
if (pstr->mb_cur_max > 1)
{
int wcs_idx;
wint_t wc = WEOF;
if (pstr->is_utf8)
{
const unsigned char *raw, *p, *end;
/* Special case UTF-8. Multi-byte chars start with any
byte other than 0x80 - 0xbf. */
raw = pstr->raw_mbs + pstr->raw_mbs_idx;
end = raw + (offset - pstr->mb_cur_max);
if (end < pstr->raw_mbs)
end = pstr->raw_mbs;
p = raw + offset - 1;
#ifdef _LIBC
/* We know the wchar_t encoding is UCS4, so for the simple
case, ASCII characters, skip the conversion step. */
if (isascii (*p) && BE (pstr->trans == NULL, 1))
{
memset (&pstr->cur_state, '\0', sizeof (mbstate_t));
/* pstr->valid_len = 0; */
wc = (wchar_t) *p;
}
else
#endif
for (; p >= end; --p)
if ((*p & 0xc0) != 0x80)
{
mbstate_t cur_state;
wchar_t wc2;
int mlen = raw + pstr->len - p;
unsigned char buf[6];
size_t mbclen;
if (BE (pstr->trans != NULL, 0))
{
int i = mlen < 6 ? mlen : 6;
while (--i >= 0)
buf[i] = pstr->trans[p[i]];
}
/* XXX Don't use mbrtowc, we know which conversion
to use (UTF-8 -> UCS4). */
memset (&cur_state, 0, sizeof (cur_state));
mbclen = __mbrtowc (&wc2, (const char *) p, mlen,
&cur_state);
if (raw + offset - p <= mbclen
&& mbclen < (size_t) -2)
{
memset (&pstr->cur_state, '\0',
sizeof (mbstate_t));
pstr->valid_len = mbclen - (raw + offset - p);
wc = wc2;
}
break;
}
}
if (wc == WEOF)
pstr->valid_len = re_string_skip_chars (pstr, idx, &wc) - idx;
if (wc == WEOF)
pstr->tip_context
= re_string_context_at (pstr, prev_valid_len - 1, eflags);
else
pstr->tip_context = ((BE (pstr->word_ops_used != 0, 0)
&& IS_WIDE_WORD_CHAR (wc))
? CONTEXT_WORD
: ((IS_WIDE_NEWLINE (wc)
&& pstr->newline_anchor)
? CONTEXT_NEWLINE : 0));
if (BE (pstr->valid_len, 0))
{
for (wcs_idx = 0; wcs_idx < pstr->valid_len; ++wcs_idx)
pstr->wcs[wcs_idx] = WEOF;
if (pstr->mbs_allocated)
memset (pstr->mbs, 255, pstr->valid_len);
}
pstr->valid_raw_len = pstr->valid_len;
}
else
#endif /* RE_ENABLE_I18N */
{
int c = pstr->raw_mbs[pstr->raw_mbs_idx + offset - 1];
pstr->valid_raw_len = 0;
if (pstr->trans)
c = pstr->trans[c];
pstr->tip_context = (bitset_contain (pstr->word_char, c)
? CONTEXT_WORD
: ((IS_NEWLINE (c) && pstr->newline_anchor)
? CONTEXT_NEWLINE : 0));
}
}
if (!BE (pstr->mbs_allocated, 0))
pstr->mbs += offset;
}
pstr->raw_mbs_idx = idx;
pstr->len -= offset;
pstr->stop -= offset;
/* Then build the buffers. */
#ifdef RE_ENABLE_I18N
if (pstr->mb_cur_max > 1)
{
if (pstr->icase)
{
reg_errcode_t ret = build_wcs_upper_buffer (pstr);
if (BE (ret != REG_NOERROR, 0))
return ret;
}
else
build_wcs_buffer (pstr);
}
else
#endif /* RE_ENABLE_I18N */
if (BE (pstr->mbs_allocated, 0))
{
if (pstr->icase)
build_upper_buffer (pstr);
else if (pstr->trans != NULL)
re_string_translate_buffer (pstr);
}
else
pstr->valid_len = pstr->len;
pstr->cur_idx = 0;
return REG_NOERROR;
}
static unsigned char
internal_function __attribute ((pure))
re_string_peek_byte_case (const re_string_t *pstr, int idx)
{
int ch, off;
/* Handle the common (easiest) cases first. */
if (BE (!pstr->mbs_allocated, 1))
return re_string_peek_byte (pstr, idx);
#ifdef RE_ENABLE_I18N
if (pstr->mb_cur_max > 1
&& ! re_string_is_single_byte_char (pstr, pstr->cur_idx + idx))
return re_string_peek_byte (pstr, idx);
#endif
off = pstr->cur_idx + idx;
#ifdef RE_ENABLE_I18N
if (pstr->offsets_needed)
off = pstr->offsets[off];
#endif
ch = pstr->raw_mbs[pstr->raw_mbs_idx + off];
#ifdef RE_ENABLE_I18N
/* Ensure that e.g. for tr_TR.UTF-8 BACKSLASH DOTLESS SMALL LETTER I
this function returns CAPITAL LETTER I instead of first byte of
DOTLESS SMALL LETTER I. The latter would confuse the parser,
since peek_byte_case doesn't advance cur_idx in any way. */
if (pstr->offsets_needed && !isascii (ch))
return re_string_peek_byte (pstr, idx);
#endif
return ch;
}
static unsigned char
internal_function __attribute ((pure))
re_string_fetch_byte_case (re_string_t *pstr)
{
if (BE (!pstr->mbs_allocated, 1))
return re_string_fetch_byte (pstr);
#ifdef RE_ENABLE_I18N
if (pstr->offsets_needed)
{
int off, ch;
/* For tr_TR.UTF-8 [[:islower:]] there is
[[: CAPITAL LETTER I WITH DOT lower:]] in mbs. Skip
in that case the whole multi-byte character and return
the original letter. On the other side, with
[[: DOTLESS SMALL LETTER I return [[:I, as doing
anything else would complicate things too much. */
if (!re_string_first_byte (pstr, pstr->cur_idx))
return re_string_fetch_byte (pstr);
off = pstr->offsets[pstr->cur_idx];
ch = pstr->raw_mbs[pstr->raw_mbs_idx + off];
if (! isascii (ch))
return re_string_fetch_byte (pstr);
re_string_skip_bytes (pstr,
re_string_char_size_at (pstr, pstr->cur_idx));
return ch;
}
#endif
return pstr->raw_mbs[pstr->raw_mbs_idx + pstr->cur_idx++];
}
static void
internal_function
re_string_destruct (re_string_t *pstr)
{
#ifdef RE_ENABLE_I18N
re_free (pstr->wcs);
re_free (pstr->offsets);
#endif /* RE_ENABLE_I18N */
if (pstr->mbs_allocated)
re_free (pstr->mbs);
}
/* Return the context at IDX in INPUT. */
static unsigned int
internal_function
re_string_context_at (const re_string_t *input, int idx, int eflags)
{
int c;
if (BE (idx < 0, 0))
/* In this case, we use the value stored in input->tip_context,
since we can't know the character in input->mbs[-1] here. */
return input->tip_context;
if (BE (idx == input->len, 0))
return ((eflags & REG_NOTEOL) ? CONTEXT_ENDBUF
: CONTEXT_NEWLINE | CONTEXT_ENDBUF);
#ifdef RE_ENABLE_I18N
if (input->mb_cur_max > 1)
{
wint_t wc;
int wc_idx = idx;
while(input->wcs[wc_idx] == WEOF)
{
#ifdef DEBUG
/* It must not happen. */
assert (wc_idx >= 0);
#endif
--wc_idx;
if (wc_idx < 0)
return input->tip_context;
}
wc = input->wcs[wc_idx];
if (BE (input->word_ops_used != 0, 0) && IS_WIDE_WORD_CHAR (wc))
return CONTEXT_WORD;
return (IS_WIDE_NEWLINE (wc) && input->newline_anchor
? CONTEXT_NEWLINE : 0);
}
else
#endif
{
c = re_string_byte_at (input, idx);
if (bitset_contain (input->word_char, c))
return CONTEXT_WORD;
return IS_NEWLINE (c) && input->newline_anchor ? CONTEXT_NEWLINE : 0;
}
}
/* Functions for set operation. */
static reg_errcode_t
internal_function
re_node_set_alloc (re_node_set *set, int size)
{
/*
* ADR: valgrind says size can be 0, which then doesn't
* free the block of size 0. Harumph. This seems
* to work ok, though.
*/
if (size == 0)
{
memset(set, 0, sizeof(*set));
return REG_NOERROR;
}
set->alloc = size;
set->nelem = 0;
set->elems = re_malloc (int, size);
if (BE (set->elems == NULL, 0))
return REG_ESPACE;
return REG_NOERROR;
}
static reg_errcode_t
internal_function
re_node_set_init_1 (re_node_set *set, int elem)
{
set->alloc = 1;
set->nelem = 1;
set->elems = re_malloc (int, 1);
if (BE (set->elems == NULL, 0))
{
set->alloc = set->nelem = 0;
return REG_ESPACE;
}
set->elems[0] = elem;
return REG_NOERROR;
}
static reg_errcode_t
internal_function
re_node_set_init_2 (re_node_set *set, int elem1, int elem2)
{
set->alloc = 2;
set->elems = re_malloc (int, 2);
if (BE (set->elems == NULL, 0))
return REG_ESPACE;
if (elem1 == elem2)
{
set->nelem = 1;
set->elems[0] = elem1;
}
else
{
set->nelem = 2;
if (elem1 < elem2)
{
set->elems[0] = elem1;
set->elems[1] = elem2;
}
else
{
set->elems[0] = elem2;
set->elems[1] = elem1;
}
}
return REG_NOERROR;
}
static reg_errcode_t
internal_function
re_node_set_init_copy (re_node_set *dest, const re_node_set *src)
{
dest->nelem = src->nelem;
if (src->nelem > 0)
{
dest->alloc = dest->nelem;
dest->elems = re_malloc (int, dest->alloc);
if (BE (dest->elems == NULL, 0))
{
dest->alloc = dest->nelem = 0;
return REG_ESPACE;
}
memcpy (dest->elems, src->elems, src->nelem * sizeof (int));
}
else
re_node_set_init_empty (dest);
return REG_NOERROR;
}
/* Calculate the intersection of the sets SRC1 and SRC2. And merge it to
DEST. Return value indicate the error code or REG_NOERROR if succeeded.
Note: We assume dest->elems is NULL, when dest->alloc is 0. */
static reg_errcode_t
internal_function
re_node_set_add_intersect (re_node_set *dest, const re_node_set *src1,
const re_node_set *src2)
{
int i1, i2, is, id, delta, sbase;
if (src1->nelem == 0 || src2->nelem == 0)
return REG_NOERROR;
/* We need dest->nelem + 2 * elems_in_intersection; this is a
conservative estimate. */
if (src1->nelem + src2->nelem + dest->nelem > dest->alloc)
{
int new_alloc = src1->nelem + src2->nelem + dest->alloc;
int *new_elems = re_realloc (dest->elems, int, new_alloc);
if (BE (new_elems == NULL, 0))
return REG_ESPACE;
dest->elems = new_elems;
dest->alloc = new_alloc;
}
/* Find the items in the intersection of SRC1 and SRC2, and copy
into the top of DEST those that are not already in DEST itself. */
sbase = dest->nelem + src1->nelem + src2->nelem;
i1 = src1->nelem - 1;
i2 = src2->nelem - 1;
id = dest->nelem - 1;
for (;;)
{
if (src1->elems[i1] == src2->elems[i2])
{
/* Try to find the item in DEST. Maybe we could binary search? */
while (id >= 0 && dest->elems[id] > src1->elems[i1])
--id;
if (id < 0 || dest->elems[id] != src1->elems[i1])
dest->elems[--sbase] = src1->elems[i1];
if (--i1 < 0 || --i2 < 0)
break;
}
/* Lower the highest of the two items. */
else if (src1->elems[i1] < src2->elems[i2])
{
if (--i2 < 0)
break;
}
else
{
if (--i1 < 0)
break;
}
}
id = dest->nelem - 1;
is = dest->nelem + src1->nelem + src2->nelem - 1;
delta = is - sbase + 1;
/* Now copy. When DELTA becomes zero, the remaining
DEST elements are already in place; this is more or
less the same loop that is in re_node_set_merge. */
dest->nelem += delta;
if (delta > 0 && id >= 0)
for (;;)
{
if (dest->elems[is] > dest->elems[id])
{
/* Copy from the top. */
dest->elems[id + delta--] = dest->elems[is--];
if (delta == 0)
break;
}
else
{
/* Slide from the bottom. */
dest->elems[id + delta] = dest->elems[id];
if (--id < 0)
break;
}
}
/* Copy remaining SRC elements. */
memcpy (dest->elems, dest->elems + sbase, delta * sizeof (int));
return REG_NOERROR;
}
/* Calculate the union set of the sets SRC1 and SRC2. And store it to
DEST. Return value indicate the error code or REG_NOERROR if succeeded. */
static reg_errcode_t
internal_function
re_node_set_init_union (re_node_set *dest, const re_node_set *src1,
const re_node_set *src2)
{
int i1, i2, id;
if (src1 != NULL && src1->nelem > 0 && src2 != NULL && src2->nelem > 0)
{
dest->alloc = src1->nelem + src2->nelem;
dest->elems = re_malloc (int, dest->alloc);
if (BE (dest->elems == NULL, 0))
return REG_ESPACE;
}
else
{
if (src1 != NULL && src1->nelem > 0)
return re_node_set_init_copy (dest, src1);
else if (src2 != NULL && src2->nelem > 0)
return re_node_set_init_copy (dest, src2);
else
re_node_set_init_empty (dest);
return REG_NOERROR;
}
for (i1 = i2 = id = 0 ; i1 < src1->nelem && i2 < src2->nelem ;)
{
if (src1->elems[i1] > src2->elems[i2])
{
dest->elems[id++] = src2->elems[i2++];
continue;
}
if (src1->elems[i1] == src2->elems[i2])
++i2;
dest->elems[id++] = src1->elems[i1++];
}
if (i1 < src1->nelem)
{
memcpy (dest->elems + id, src1->elems + i1,
(src1->nelem - i1) * sizeof (int));
id += src1->nelem - i1;
}
else if (i2 < src2->nelem)
{
memcpy (dest->elems + id, src2->elems + i2,
(src2->nelem - i2) * sizeof (int));
id += src2->nelem - i2;
}
dest->nelem = id;
return REG_NOERROR;
}
/* Calculate the union set of the sets DEST and SRC. And store it to
DEST. Return value indicate the error code or REG_NOERROR if succeeded. */
static reg_errcode_t
internal_function
re_node_set_merge (re_node_set *dest, const re_node_set *src)
{
int is, id, sbase, delta;
if (src == NULL || src->nelem == 0)
return REG_NOERROR;
if (dest->alloc < 2 * src->nelem + dest->nelem)
{
int new_alloc = 2 * (src->nelem + dest->alloc);
int *new_buffer = re_realloc (dest->elems, int, new_alloc);
if (BE (new_buffer == NULL, 0))
return REG_ESPACE;
dest->elems = new_buffer;
dest->alloc = new_alloc;
}
if (BE (dest->nelem == 0, 0))
{
dest->nelem = src->nelem;
memcpy (dest->elems, src->elems, src->nelem * sizeof (int));
return REG_NOERROR;
}
/* Copy into the top of DEST the items of SRC that are not
found in DEST. Maybe we could binary search in DEST? */
for (sbase = dest->nelem + 2 * src->nelem,
is = src->nelem - 1, id = dest->nelem - 1; is >= 0 && id >= 0; )
{
if (dest->elems[id] == src->elems[is])
is--, id--;
else if (dest->elems[id] < src->elems[is])
dest->elems[--sbase] = src->elems[is--];
else /* if (dest->elems[id] > src->elems[is]) */
--id;
}
if (is >= 0)
{
/* If DEST is exhausted, the remaining items of SRC must be unique. */
sbase -= is + 1;
memcpy (dest->elems + sbase, src->elems, (is + 1) * sizeof (int));
}
id = dest->nelem - 1;
is = dest->nelem + 2 * src->nelem - 1;
delta = is - sbase + 1;
if (delta == 0)
return REG_NOERROR;
/* Now copy. When DELTA becomes zero, the remaining
DEST elements are already in place. */
dest->nelem += delta;
for (;;)
{
if (dest->elems[is] > dest->elems[id])
{
/* Copy from the top. */
dest->elems[id + delta--] = dest->elems[is--];
if (delta == 0)
break;
}
else
{
/* Slide from the bottom. */
dest->elems[id + delta] = dest->elems[id];
if (--id < 0)
{
/* Copy remaining SRC elements. */
memcpy (dest->elems, dest->elems + sbase,
delta * sizeof (int));
break;
}
}
}
return REG_NOERROR;
}
/* Insert the new element ELEM to the re_node_set* SET.
SET should not already have ELEM.
return -1 if an error is occured, return 1 otherwise. */
static int
internal_function
re_node_set_insert (re_node_set *set, int elem)
{
int idx;
/* In case the set is empty. */
if (set->alloc == 0)
{
if (BE (re_node_set_init_1 (set, elem) == REG_NOERROR, 1))
return 1;
else
return -1;
}
if (BE (set->nelem, 0) == 0)
{
/* We already guaranteed above that set->alloc != 0. */
set->elems[0] = elem;
++set->nelem;
return 1;
}
/* Realloc if we need. */
if (set->alloc == set->nelem)
{
int *new_elems;
set->alloc = set->alloc * 2;
new_elems = re_realloc (set->elems, int, set->alloc);
if (BE (new_elems == NULL, 0))
return -1;
set->elems = new_elems;
}
/* Move the elements which follows the new element. Test the
first element separately to skip a check in the inner loop. */
if (elem < set->elems[0])
{
idx = 0;
for (idx = set->nelem; idx > 0; idx--)
set->elems[idx] = set->elems[idx - 1];
}
else
{
for (idx = set->nelem; set->elems[idx - 1] > elem; idx--)
set->elems[idx] = set->elems[idx - 1];
}
/* Insert the new element. */
set->elems[idx] = elem;
++set->nelem;
return 1;
}
/* Insert the new element ELEM to the re_node_set* SET.
SET should not already have any element greater than or equal to ELEM.
Return -1 if an error is occured, return 1 otherwise. */
static int
internal_function
re_node_set_insert_last (re_node_set *set, int elem)
{
/* Realloc if we need. */
if (set->alloc == set->nelem)
{
int *new_elems;
set->alloc = (set->alloc + 1) * 2;
new_elems = re_realloc (set->elems, int, set->alloc);
if (BE (new_elems == NULL, 0))
return -1;
set->elems = new_elems;
}
/* Insert the new element. */
set->elems[set->nelem++] = elem;
return 1;
}
/* Compare two node sets SET1 and SET2.
return 1 if SET1 and SET2 are equivalent, return 0 otherwise. */
static int
internal_function __attribute ((pure))
re_node_set_compare (const re_node_set *set1, const re_node_set *set2)
{
int i;
if (set1 == NULL || set2 == NULL || set1->nelem != set2->nelem)
return 0;
for (i = set1->nelem ; --i >= 0 ; )
if (set1->elems[i] != set2->elems[i])
return 0;
return 1;
}
/* Return (idx + 1) if SET contains the element ELEM, return 0 otherwise. */
static int
internal_function __attribute ((pure))
re_node_set_contains (const re_node_set *set, int elem)
{
unsigned int idx, right, mid;
if (set->nelem <= 0)
return 0;
/* Binary search the element. */
idx = 0;
right = set->nelem - 1;
while (idx < right)
{
mid = (idx + right) / 2;
if (set->elems[mid] < elem)
idx = mid + 1;
else
right = mid;
}
return set->elems[idx] == elem ? idx + 1 : 0;
}
static void
internal_function
re_node_set_remove_at (re_node_set *set, int idx)
{
if (idx < 0 || idx >= set->nelem)
return;
--set->nelem;
for (; idx < set->nelem; idx++)
set->elems[idx] = set->elems[idx + 1];
}
/* Add the token TOKEN to dfa->nodes, and return the index of the token.
Or return -1, if an error will be occured. */
static int
internal_function
re_dfa_add_node (re_dfa_t *dfa, re_token_t token)
{
if (BE (dfa->nodes_len >= dfa->nodes_alloc, 0))
{
size_t new_nodes_alloc = dfa->nodes_alloc * 2;
int *new_nexts, *new_indices;
re_node_set *new_edests, *new_eclosures;
re_token_t *new_nodes;
/* Avoid overflows in realloc. */
const size_t max_object_size = MAX (sizeof (re_token_t),
MAX (sizeof (re_node_set),
sizeof (int)));
if (BE (SIZE_MAX / max_object_size < new_nodes_alloc, 0))
return -1;
new_nodes = re_realloc (dfa->nodes, re_token_t, new_nodes_alloc);
if (BE (new_nodes == NULL, 0))
return -1;
dfa->nodes = new_nodes;
new_nexts = re_realloc (dfa->nexts, int, new_nodes_alloc);
new_indices = re_realloc (dfa->org_indices, int, new_nodes_alloc);
new_edests = re_realloc (dfa->edests, re_node_set, new_nodes_alloc);
new_eclosures = re_realloc (dfa->eclosures, re_node_set, new_nodes_alloc);
if (BE (new_nexts == NULL || new_indices == NULL
|| new_edests == NULL || new_eclosures == NULL, 0))
return -1;
dfa->nexts = new_nexts;
dfa->org_indices = new_indices;
dfa->edests = new_edests;
dfa->eclosures = new_eclosures;
dfa->nodes_alloc = new_nodes_alloc;
}
dfa->nodes[dfa->nodes_len] = token;
dfa->nodes[dfa->nodes_len].constraint = 0;
#ifdef RE_ENABLE_I18N
dfa->nodes[dfa->nodes_len].accept_mb =
(token.type == OP_PERIOD && dfa->mb_cur_max > 1) || token.type == COMPLEX_BRACKET;
#endif
dfa->nexts[dfa->nodes_len] = -1;
re_node_set_init_empty (dfa->edests + dfa->nodes_len);
re_node_set_init_empty (dfa->eclosures + dfa->nodes_len);
return dfa->nodes_len++;
}
static inline unsigned int
internal_function
calc_state_hash (const re_node_set *nodes, unsigned int context)
{
unsigned int hash = nodes->nelem + context;
int i;
for (i = 0 ; i < nodes->nelem ; i++)
hash += nodes->elems[i];
return hash;
}
/* Search for the state whose node_set is equivalent to NODES.
Return the pointer to the state, if we found it in the DFA.
Otherwise create the new one and return it. In case of an error
return NULL and set the error code in ERR.
Note: - We assume NULL as the invalid state, then it is possible that
return value is NULL and ERR is REG_NOERROR.
- We never return non-NULL value in case of any errors, it is for
optimization. */
static re_dfastate_t *
internal_function
re_acquire_state (reg_errcode_t *err, const re_dfa_t *dfa,
const re_node_set *nodes)
{
unsigned int hash;
re_dfastate_t *new_state;
struct re_state_table_entry *spot;
int i;
if (BE (nodes->nelem == 0, 0))
{
*err = REG_NOERROR;
return NULL;
}
hash = calc_state_hash (nodes, 0);
spot = dfa->state_table + (hash & dfa->state_hash_mask);
for (i = 0 ; i < spot->num ; i++)
{
re_dfastate_t *state = spot->array[i];
if (hash != state->hash)
continue;
if (re_node_set_compare (&state->nodes, nodes))
return state;
}
/* There are no appropriate state in the dfa, create the new one. */
new_state = create_ci_newstate (dfa, nodes, hash);
if (BE (new_state == NULL, 0))
*err = REG_ESPACE;
return new_state;
}
/* Search for the state whose node_set is equivalent to NODES and
whose context is equivalent to CONTEXT.
Return the pointer to the state, if we found it in the DFA.
Otherwise create the new one and return it. In case of an error
return NULL and set the error code in ERR.
Note: - We assume NULL as the invalid state, then it is possible that
return value is NULL and ERR is REG_NOERROR.
- We never return non-NULL value in case of any errors, it is for
optimization. */
static re_dfastate_t *
internal_function
re_acquire_state_context (reg_errcode_t *err, const re_dfa_t *dfa,
const re_node_set *nodes, unsigned int context)
{
unsigned int hash;
re_dfastate_t *new_state;
struct re_state_table_entry *spot;
int i;
if (nodes->nelem == 0)
{
*err = REG_NOERROR;
return NULL;
}
hash = calc_state_hash (nodes, context);
spot = dfa->state_table + (hash & dfa->state_hash_mask);
for (i = 0 ; i < spot->num ; i++)
{
re_dfastate_t *state = spot->array[i];
if (state->hash == hash
&& state->context == context
&& re_node_set_compare (state->entrance_nodes, nodes))
return state;
}
/* There are no appropriate state in `dfa', create the new one. */
new_state = create_cd_newstate (dfa, nodes, context, hash);
if (BE (new_state == NULL, 0))
*err = REG_ESPACE;
return new_state;
}
/* Finish initialization of the new state NEWSTATE, and using its hash value
HASH put in the appropriate bucket of DFA's state table. Return value
indicates the error code if failed. */
static reg_errcode_t
register_state (const re_dfa_t *dfa, re_dfastate_t *newstate,
unsigned int hash)
{
struct re_state_table_entry *spot;
reg_errcode_t err;
int i;
newstate->hash = hash;
err = re_node_set_alloc (&newstate->non_eps_nodes, newstate->nodes.nelem);
if (BE (err != REG_NOERROR, 0))
return REG_ESPACE;
for (i = 0; i < newstate->nodes.nelem; i++)
{
int elem = newstate->nodes.elems[i];
if (!IS_EPSILON_NODE (dfa->nodes[elem].type))
if (re_node_set_insert_last (&newstate->non_eps_nodes, elem) < 0)
return REG_ESPACE;
}
spot = dfa->state_table + (hash & dfa->state_hash_mask);
if (BE (spot->alloc <= spot->num, 0))
{
int new_alloc = 2 * spot->num + 2;
re_dfastate_t **new_array = re_realloc (spot->array, re_dfastate_t *,
new_alloc);
if (BE (new_array == NULL, 0))
return REG_ESPACE;
spot->array = new_array;
spot->alloc = new_alloc;
}
spot->array[spot->num++] = newstate;
return REG_NOERROR;
}
static void
free_state (re_dfastate_t *state)
{
re_node_set_free (&state->non_eps_nodes);
re_node_set_free (&state->inveclosure);
if (state->entrance_nodes != &state->nodes)
{
re_node_set_free (state->entrance_nodes);
re_free (state->entrance_nodes);
}
re_node_set_free (&state->nodes);
re_free (state->word_trtable);
re_free (state->trtable);
re_free (state);
}
/* Create the new state which is independ of contexts.
Return the new state if succeeded, otherwise return NULL. */
static re_dfastate_t *
internal_function
create_ci_newstate (const re_dfa_t *dfa, const re_node_set *nodes,
unsigned int hash)
{
int i;
reg_errcode_t err;
re_dfastate_t *newstate;
newstate = (re_dfastate_t *) calloc (sizeof (re_dfastate_t), 1);
if (BE (newstate == NULL, 0))
return NULL;
err = re_node_set_init_copy (&newstate->nodes, nodes);
if (BE (err != REG_NOERROR, 0))
{
re_free (newstate);
return NULL;
}
newstate->entrance_nodes = &newstate->nodes;
for (i = 0 ; i < nodes->nelem ; i++)
{
re_token_t *node = dfa->nodes + nodes->elems[i];
re_token_type_t type = node->type;
if (type == CHARACTER && !node->constraint)
continue;
#ifdef RE_ENABLE_I18N
newstate->accept_mb |= node->accept_mb;
#endif /* RE_ENABLE_I18N */
/* If the state has the halt node, the state is a halt state. */
if (type == END_OF_RE)
newstate->halt = 1;
else if (type == OP_BACK_REF)
newstate->has_backref = 1;
else if (type == ANCHOR || node->constraint)
newstate->has_constraint = 1;
}
err = register_state (dfa, newstate, hash);
if (BE (err != REG_NOERROR, 0))
{
free_state (newstate);
newstate = NULL;
}
return newstate;
}
/* Create the new state which is depend on the context CONTEXT.
Return the new state if succeeded, otherwise return NULL. */
static re_dfastate_t *
internal_function
create_cd_newstate (const re_dfa_t *dfa, const re_node_set *nodes,
unsigned int context, unsigned int hash)
{
int i, nctx_nodes = 0;
reg_errcode_t err;
re_dfastate_t *newstate;
newstate = (re_dfastate_t *) calloc (sizeof (re_dfastate_t), 1);
if (BE (newstate == NULL, 0))
return NULL;
err = re_node_set_init_copy (&newstate->nodes, nodes);
if (BE (err != REG_NOERROR, 0))
{
re_free (newstate);
return NULL;
}
newstate->context = context;
newstate->entrance_nodes = &newstate->nodes;
for (i = 0 ; i < nodes->nelem ; i++)
{
re_token_t *node = dfa->nodes + nodes->elems[i];
re_token_type_t type = node->type;
unsigned int constraint = node->constraint;
if (type == CHARACTER && !constraint)
continue;
#ifdef RE_ENABLE_I18N
newstate->accept_mb |= node->accept_mb;
#endif /* RE_ENABLE_I18N */
/* If the state has the halt node, the state is a halt state. */
if (type == END_OF_RE)
newstate->halt = 1;
else if (type == OP_BACK_REF)
newstate->has_backref = 1;
if (constraint)
{
if (newstate->entrance_nodes == &newstate->nodes)
{
newstate->entrance_nodes = re_malloc (re_node_set, 1);
if (BE (newstate->entrance_nodes == NULL, 0))
{
free_state (newstate);
return NULL;
}
if (re_node_set_init_copy (newstate->entrance_nodes, nodes)
!= REG_NOERROR)
return NULL;
nctx_nodes = 0;
newstate->has_constraint = 1;
}
if (NOT_SATISFY_PREV_CONSTRAINT (constraint,context))
{
re_node_set_remove_at (&newstate->nodes, i - nctx_nodes);
++nctx_nodes;
}
}
}
err = register_state (dfa, newstate, hash);
if (BE (err != REG_NOERROR, 0))
{
free_state (newstate);
newstate = NULL;
}
return newstate;
}
/* Extended regular expression matching and search library.
Copyright (C) 2002-2005, 2007, 2008, 2010 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */
#ifndef _REGEX_INTERNAL_H
#define _REGEX_INTERNAL_H 1
#include <assert.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined HAVE_LANGINFO_H || defined HAVE_LANGINFO_CODESET || defined _LIBC
# include <langinfo.h>
#endif
#if defined HAVE_LOCALE_H || defined _LIBC
# include <locale.h>
#endif
#if defined HAVE_WCHAR_H || defined _LIBC
# include <wchar.h>
#endif /* HAVE_WCHAR_H || _LIBC */
#if defined HAVE_WCTYPE_H || defined _LIBC
# include <wctype.h>
#endif /* HAVE_WCTYPE_H || _LIBC */
#if defined HAVE_STDBOOL_H || defined _LIBC
# include <stdbool.h>
#endif /* HAVE_STDBOOL_H || _LIBC */
#if !defined(ZOS_USS)
#if defined HAVE_STDINT_H || defined _LIBC
# include <stdint.h>
#endif /* HAVE_STDINT_H || _LIBC */
#endif /* !ZOS_USS */
#if defined _LIBC
# include <bits/libc-lock.h>
#else
# define __libc_lock_define(CLASS,NAME)
# define __libc_lock_init(NAME) do { } while (0)
# define __libc_lock_lock(NAME) do { } while (0)
# define __libc_lock_unlock(NAME) do { } while (0)
#endif
#ifndef GAWK
/* In case that the system doesn't have isblank(). */
#if !defined _LIBC && !defined HAVE_ISBLANK && !defined isblank
# define isblank(ch) ((ch) == ' ' || (ch) == '\t')
#endif
#else /* GAWK */
/*
* This is a freaking mess. On glibc systems you have to define
* a magic constant to get isblank() out of <ctype.h>, since it's
* a C99 function. To heck with all that and borrow a page from
* dfa.c's book.
*/
static int
is_blank (int c)
{
return (c == ' ' || c == '\t');
}
#endif /* GAWK */
#ifdef _LIBC
# ifndef _RE_DEFINE_LOCALE_FUNCTIONS
# define _RE_DEFINE_LOCALE_FUNCTIONS 1
# include <locale/localeinfo.h>
# include <locale/elem-hash.h>
# include <locale/coll-lookup.h>
# endif
#endif
/* This is for other GNU distributions with internationalized messages. */
#if (HAVE_LIBINTL_H && ENABLE_NLS) || defined _LIBC
# include <libintl.h>
# ifdef _LIBC
# undef gettext
# define gettext(msgid) \
INTUSE(__dcgettext) (_libc_intl_domainname, msgid, LC_MESSAGES)
# endif
#else
# define gettext(msgid) (msgid)
#endif
#ifndef gettext_noop
/* This define is so xgettext can find the internationalizable
strings. */
# define gettext_noop(String) String
#endif
/* For loser systems without the definition. */
#ifndef SIZE_MAX
# define SIZE_MAX ((size_t) -1)
#endif
#ifndef NO_MBSUPPORT
#include "mbsupport.h" /* gawk */
#endif
#ifndef MB_CUR_MAX
#define MB_CUR_MAX 1
#endif
#if (defined MBS_SUPPORT) || _LIBC
# define RE_ENABLE_I18N
#endif
#if __GNUC__ >= 3
# define BE(expr, val) __builtin_expect (expr, val)
#else
# define BE(expr, val) (expr)
# ifdef inline
# undef inline
# endif
# define inline
#endif
/* Number of single byte character. */
#define SBC_MAX 256
#define COLL_ELEM_LEN_MAX 8
/* The character which represents newline. */
#define NEWLINE_CHAR '\n'
#define WIDE_NEWLINE_CHAR L'\n'
/* Rename to standard API for using out of glibc. */
#ifndef _LIBC
# ifdef __wctype
# undef __wctype
# endif
# define __wctype wctype
# ifdef __iswctype
# undef __iswctype
# endif
# define __iswctype iswctype
# define __btowc btowc
# define __mbrtowc mbrtowc
#undef __mempcpy /* GAWK */
# define __mempcpy mempcpy
# define __wcrtomb wcrtomb
# define __regfree regfree
# define attribute_hidden
#endif /* not _LIBC */
#ifdef __GNUC__
# define __attribute(arg) __attribute__ (arg)
#else
# define __attribute(arg)
#endif
extern const char __re_error_msgid[] attribute_hidden;
extern const size_t __re_error_msgid_idx[] attribute_hidden;
/* An integer used to represent a set of bits. It must be unsigned,
and must be at least as wide as unsigned int. */
typedef unsigned long int bitset_word_t;
/* All bits set in a bitset_word_t. */
#define BITSET_WORD_MAX ULONG_MAX
/* Number of bits in a bitset_word_t. */
#define BITSET_WORD_BITS (sizeof (bitset_word_t) * CHAR_BIT)
/* Number of bitset_word_t in a bit_set. */
#define BITSET_WORDS (SBC_MAX / BITSET_WORD_BITS)
typedef bitset_word_t bitset_t[BITSET_WORDS];
typedef bitset_word_t *re_bitset_ptr_t;
typedef const bitset_word_t *re_const_bitset_ptr_t;
#define bitset_set(set,i) \
(set[i / BITSET_WORD_BITS] |= (bitset_word_t) 1 << i % BITSET_WORD_BITS)
#define bitset_clear(set,i) \
(set[i / BITSET_WORD_BITS] &= ~((bitset_word_t) 1 << i % BITSET_WORD_BITS))
#define bitset_contain(set,i) \
(set[i / BITSET_WORD_BITS] & ((bitset_word_t) 1 << i % BITSET_WORD_BITS))
#define bitset_empty(set) memset (set, '\0', sizeof (bitset_t))
#define bitset_set_all(set) memset (set, '\xff', sizeof (bitset_t))
#define bitset_copy(dest,src) memcpy (dest, src, sizeof (bitset_t))
#define PREV_WORD_CONSTRAINT 0x0001
#define PREV_NOTWORD_CONSTRAINT 0x0002
#define NEXT_WORD_CONSTRAINT 0x0004
#define NEXT_NOTWORD_CONSTRAINT 0x0008
#define PREV_NEWLINE_CONSTRAINT 0x0010
#define NEXT_NEWLINE_CONSTRAINT 0x0020
#define PREV_BEGBUF_CONSTRAINT 0x0040
#define NEXT_ENDBUF_CONSTRAINT 0x0080
#define WORD_DELIM_CONSTRAINT 0x0100
#define NOT_WORD_DELIM_CONSTRAINT 0x0200
typedef enum
{
INSIDE_WORD = PREV_WORD_CONSTRAINT | NEXT_WORD_CONSTRAINT,
WORD_FIRST = PREV_NOTWORD_CONSTRAINT | NEXT_WORD_CONSTRAINT,
WORD_LAST = PREV_WORD_CONSTRAINT | NEXT_NOTWORD_CONSTRAINT,
INSIDE_NOTWORD = PREV_NOTWORD_CONSTRAINT | NEXT_NOTWORD_CONSTRAINT,
LINE_FIRST = PREV_NEWLINE_CONSTRAINT,
LINE_LAST = NEXT_NEWLINE_CONSTRAINT,
BUF_FIRST = PREV_BEGBUF_CONSTRAINT,
BUF_LAST = NEXT_ENDBUF_CONSTRAINT,
WORD_DELIM = WORD_DELIM_CONSTRAINT,
NOT_WORD_DELIM = NOT_WORD_DELIM_CONSTRAINT
} re_context_type;
typedef struct
{
int alloc;
int nelem;
int *elems;
} re_node_set;
typedef enum
{
NON_TYPE = 0,
/* Node type, These are used by token, node, tree. */
CHARACTER = 1,
END_OF_RE = 2,
SIMPLE_BRACKET = 3,
OP_BACK_REF = 4,
OP_PERIOD = 5,
#ifdef RE_ENABLE_I18N
COMPLEX_BRACKET = 6,
OP_UTF8_PERIOD = 7,
#endif /* RE_ENABLE_I18N */
/* We define EPSILON_BIT as a macro so that OP_OPEN_SUBEXP is used
when the debugger shows values of this enum type. */
#define EPSILON_BIT 8
OP_OPEN_SUBEXP = EPSILON_BIT | 0,
OP_CLOSE_SUBEXP = EPSILON_BIT | 1,
OP_ALT = EPSILON_BIT | 2,
OP_DUP_ASTERISK = EPSILON_BIT | 3,
ANCHOR = EPSILON_BIT | 4,
/* Tree type, these are used only by tree. */
CONCAT = 16,
SUBEXP = 17,
/* Token type, these are used only by token. */
OP_DUP_PLUS = 18,
OP_DUP_QUESTION,
OP_OPEN_BRACKET,
OP_CLOSE_BRACKET,
OP_CHARSET_RANGE,
OP_OPEN_DUP_NUM,
OP_CLOSE_DUP_NUM,
OP_NON_MATCH_LIST,
OP_OPEN_COLL_ELEM,
OP_CLOSE_COLL_ELEM,
OP_OPEN_EQUIV_CLASS,
OP_CLOSE_EQUIV_CLASS,
OP_OPEN_CHAR_CLASS,
OP_CLOSE_CHAR_CLASS,
OP_WORD,
OP_NOTWORD,
OP_SPACE,
OP_NOTSPACE,
BACK_SLASH
} re_token_type_t;
#ifdef RE_ENABLE_I18N
typedef struct
{
/* Multibyte characters. */
wchar_t *mbchars;
/* Collating symbols. */
# ifdef _LIBC
int32_t *coll_syms;
# endif
/* Equivalence classes. */
# ifdef _LIBC
int32_t *equiv_classes;
# endif
/* Range expressions. */
# ifdef _LIBC
uint32_t *range_starts;
uint32_t *range_ends;
# else /* not _LIBC */
wchar_t *range_starts;
wchar_t *range_ends;
# endif /* not _LIBC */
/* Character classes. */
wctype_t *char_classes;
/* If this character set is the non-matching list. */
unsigned int non_match : 1;
/* # of multibyte characters. */
int nmbchars;
/* # of collating symbols. */
int ncoll_syms;
/* # of equivalence classes. */
int nequiv_classes;
/* # of range expressions. */
int nranges;
/* # of character classes. */
int nchar_classes;
} re_charset_t;
#endif /* RE_ENABLE_I18N */
typedef struct
{
union
{
unsigned char c; /* for CHARACTER */
re_bitset_ptr_t sbcset; /* for SIMPLE_BRACKET */
#ifdef RE_ENABLE_I18N
re_charset_t *mbcset; /* for COMPLEX_BRACKET */
#endif /* RE_ENABLE_I18N */
int idx; /* for BACK_REF */
re_context_type ctx_type; /* for ANCHOR */
} opr;
#if __GNUC__ >= 2
re_token_type_t type : 8;
#else
re_token_type_t type;
#endif
unsigned int constraint : 10; /* context constraint */
unsigned int duplicated : 1;
unsigned int opt_subexp : 1;
#ifdef RE_ENABLE_I18N
unsigned int accept_mb : 1;
/* These 2 bits can be moved into the union if needed (e.g. if running out
of bits; move opr.c to opr.c.c and move the flags to opr.c.flags). */
unsigned int mb_partial : 1;
#endif
unsigned int word_char : 1;
} re_token_t;
#define IS_EPSILON_NODE(type) ((type) & EPSILON_BIT)
struct re_string_t
{
/* Indicate the raw buffer which is the original string passed as an
argument of regexec(), re_search(), etc.. */
const unsigned char *raw_mbs;
/* Store the multibyte string. In case of "case insensitive mode" like
REG_ICASE, upper cases of the string are stored, otherwise MBS points
the same address that RAW_MBS points. */
unsigned char *mbs;
#ifdef RE_ENABLE_I18N
/* Store the wide character string which is corresponding to MBS. */
wint_t *wcs;
int *offsets;
mbstate_t cur_state;
#endif
/* Index in RAW_MBS. Each character mbs[i] corresponds to
raw_mbs[raw_mbs_idx + i]. */
int raw_mbs_idx;
/* The length of the valid characters in the buffers. */
int valid_len;
/* The corresponding number of bytes in raw_mbs array. */
int valid_raw_len;
/* The length of the buffers MBS and WCS. */
int bufs_len;
/* The index in MBS, which is updated by re_string_fetch_byte. */
int cur_idx;
/* length of RAW_MBS array. */
int raw_len;
/* This is RAW_LEN - RAW_MBS_IDX + VALID_LEN - VALID_RAW_LEN. */
int len;
/* End of the buffer may be shorter than its length in the cases such
as re_match_2, re_search_2. Then, we use STOP for end of the buffer
instead of LEN. */
int raw_stop;
/* This is RAW_STOP - RAW_MBS_IDX adjusted through OFFSETS. */
int stop;
/* The context of mbs[0]. We store the context independently, since
the context of mbs[0] may be different from raw_mbs[0], which is
the beginning of the input string. */
unsigned int tip_context;
/* The translation passed as a part of an argument of re_compile_pattern. */
RE_TRANSLATE_TYPE trans;
/* Copy of re_dfa_t's word_char. */
re_const_bitset_ptr_t word_char;
/* 1 if REG_ICASE. */
unsigned char icase;
unsigned char is_utf8;
unsigned char map_notascii;
unsigned char mbs_allocated;
unsigned char offsets_needed;
unsigned char newline_anchor;
unsigned char word_ops_used;
int mb_cur_max;
};
typedef struct re_string_t re_string_t;
struct re_dfa_t;
typedef struct re_dfa_t re_dfa_t;
#ifndef _LIBC
# ifdef __i386__
# define internal_function __attribute ((regparm (3), stdcall))
# else
# define internal_function
# endif
#endif
#ifndef NOT_IN_libc
static reg_errcode_t re_string_realloc_buffers (re_string_t *pstr,
int new_buf_len)
internal_function;
# ifdef RE_ENABLE_I18N
static void build_wcs_buffer (re_string_t *pstr) internal_function;
static reg_errcode_t build_wcs_upper_buffer (re_string_t *pstr)
internal_function;
# endif /* RE_ENABLE_I18N */
static void build_upper_buffer (re_string_t *pstr) internal_function;
static void re_string_translate_buffer (re_string_t *pstr) internal_function;
static unsigned int re_string_context_at (const re_string_t *input, int idx,
int eflags)
internal_function __attribute ((pure));
#endif
#define re_string_peek_byte(pstr, offset) \
((pstr)->mbs[(pstr)->cur_idx + offset])
#define re_string_fetch_byte(pstr) \
((pstr)->mbs[(pstr)->cur_idx++])
#define re_string_first_byte(pstr, idx) \
((idx) == (pstr)->valid_len || (pstr)->wcs[idx] != WEOF)
#define re_string_is_single_byte_char(pstr, idx) \
((pstr)->wcs[idx] != WEOF && ((pstr)->valid_len == (idx) + 1 \
|| (pstr)->wcs[(idx) + 1] != WEOF))
#define re_string_eoi(pstr) ((pstr)->stop <= (pstr)->cur_idx)
#define re_string_cur_idx(pstr) ((pstr)->cur_idx)
#define re_string_get_buffer(pstr) ((pstr)->mbs)
#define re_string_length(pstr) ((pstr)->len)
#define re_string_byte_at(pstr,idx) ((pstr)->mbs[idx])
#define re_string_skip_bytes(pstr,idx) ((pstr)->cur_idx += (idx))
#define re_string_set_index(pstr,idx) ((pstr)->cur_idx = (idx))
#ifndef _LIBC
# if HAVE_ALLOCA
# if (_MSC_VER)
# include <malloc.h>
# define __libc_use_alloca(n) 0
# else
# include <alloca.h>
/* The OS usually guarantees only one guard page at the bottom of the stack,
and a page size can be as small as 4096 bytes. So we cannot safely
allocate anything larger than 4096 bytes. Also care for the possibility
of a few compiler-allocated temporary stack slots. */
# define __libc_use_alloca(n) ((n) < 4032)
# endif
# else
/* alloca is implemented with malloc, so just use malloc. */
# define __libc_use_alloca(n) 0
# endif
#endif
#define re_malloc(t,n) ((t *) malloc ((n) * sizeof (t)))
/* SunOS 4.1.x realloc doesn't accept null pointers: pre-Standard C. Sigh. */
#define re_realloc(p,t,n) ((p != NULL) ? (t *) realloc (p,(n)*sizeof(t)) : (t *) calloc(n,sizeof(t)))
#define re_free(p) free (p)
struct bin_tree_t
{
struct bin_tree_t *parent;
struct bin_tree_t *left;
struct bin_tree_t *right;
struct bin_tree_t *first;
struct bin_tree_t *next;
re_token_t token;
/* `node_idx' is the index in dfa->nodes, if `type' == 0.
Otherwise `type' indicate the type of this node. */
int node_idx;
};
typedef struct bin_tree_t bin_tree_t;
#define BIN_TREE_STORAGE_SIZE \
((1024 - sizeof (void *)) / sizeof (bin_tree_t))
struct bin_tree_storage_t
{
struct bin_tree_storage_t *next;
bin_tree_t data[BIN_TREE_STORAGE_SIZE];
};
typedef struct bin_tree_storage_t bin_tree_storage_t;
#define CONTEXT_WORD 1
#define CONTEXT_NEWLINE (CONTEXT_WORD << 1)
#define CONTEXT_BEGBUF (CONTEXT_NEWLINE << 1)
#define CONTEXT_ENDBUF (CONTEXT_BEGBUF << 1)
#define IS_WORD_CONTEXT(c) ((c) & CONTEXT_WORD)
#define IS_NEWLINE_CONTEXT(c) ((c) & CONTEXT_NEWLINE)
#define IS_BEGBUF_CONTEXT(c) ((c) & CONTEXT_BEGBUF)
#define IS_ENDBUF_CONTEXT(c) ((c) & CONTEXT_ENDBUF)
#define IS_ORDINARY_CONTEXT(c) ((c) == 0)
#define IS_WORD_CHAR(ch) (isalnum (ch) || (ch) == '_')
#define IS_NEWLINE(ch) ((ch) == NEWLINE_CHAR)
#define IS_WIDE_WORD_CHAR(ch) (iswalnum (ch) || (ch) == L'_')
#define IS_WIDE_NEWLINE(ch) ((ch) == WIDE_NEWLINE_CHAR)
#define NOT_SATISFY_PREV_CONSTRAINT(constraint,context) \
((((constraint) & PREV_WORD_CONSTRAINT) && !IS_WORD_CONTEXT (context)) \
|| ((constraint & PREV_NOTWORD_CONSTRAINT) && IS_WORD_CONTEXT (context)) \
|| ((constraint & PREV_NEWLINE_CONSTRAINT) && !IS_NEWLINE_CONTEXT (context))\
|| ((constraint & PREV_BEGBUF_CONSTRAINT) && !IS_BEGBUF_CONTEXT (context)))
#define NOT_SATISFY_NEXT_CONSTRAINT(constraint,context) \
((((constraint) & NEXT_WORD_CONSTRAINT) && !IS_WORD_CONTEXT (context)) \
|| (((constraint) & NEXT_NOTWORD_CONSTRAINT) && IS_WORD_CONTEXT (context)) \
|| (((constraint) & NEXT_NEWLINE_CONSTRAINT) && !IS_NEWLINE_CONTEXT (context)) \
|| (((constraint) & NEXT_ENDBUF_CONSTRAINT) && !IS_ENDBUF_CONTEXT (context)))
struct re_dfastate_t
{
unsigned int hash;
re_node_set nodes;
re_node_set non_eps_nodes;
re_node_set inveclosure;
re_node_set *entrance_nodes;
struct re_dfastate_t **trtable, **word_trtable;
unsigned int context : 4;
unsigned int halt : 1;
/* If this state can accept `multi byte'.
Note that we refer to multibyte characters, and multi character
collating elements as `multi byte'. */
unsigned int accept_mb : 1;
/* If this state has backreference node(s). */
unsigned int has_backref : 1;
unsigned int has_constraint : 1;
};
typedef struct re_dfastate_t re_dfastate_t;
struct re_state_table_entry
{
int num;
int alloc;
re_dfastate_t **array;
};
/* Array type used in re_sub_match_last_t and re_sub_match_top_t. */
typedef struct
{
int next_idx;
int alloc;
re_dfastate_t **array;
} state_array_t;
/* Store information about the node NODE whose type is OP_CLOSE_SUBEXP. */
typedef struct
{
int node;
int str_idx; /* The position NODE match at. */
state_array_t path;
} re_sub_match_last_t;
/* Store information about the node NODE whose type is OP_OPEN_SUBEXP.
And information about the node, whose type is OP_CLOSE_SUBEXP,
corresponding to NODE is stored in LASTS. */
typedef struct
{
int str_idx;
int node;
state_array_t *path;
int alasts; /* Allocation size of LASTS. */
int nlasts; /* The number of LASTS. */
re_sub_match_last_t **lasts;
} re_sub_match_top_t;
struct re_backref_cache_entry
{
int node;
int str_idx;
int subexp_from;
int subexp_to;
char more;
char unused;
unsigned short int eps_reachable_subexps_map;
};
typedef struct
{
/* The string object corresponding to the input string. */
re_string_t input;
#if defined _LIBC || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L)
const re_dfa_t *const dfa;
#else
const re_dfa_t *dfa;
#endif
/* EFLAGS of the argument of regexec. */
int eflags;
/* Where the matching ends. */
int match_last;
int last_node;
/* The state log used by the matcher. */
re_dfastate_t **state_log;
int state_log_top;
/* Back reference cache. */
int nbkref_ents;
int abkref_ents;
struct re_backref_cache_entry *bkref_ents;
int max_mb_elem_len;
int nsub_tops;
int asub_tops;
re_sub_match_top_t **sub_tops;
} re_match_context_t;
typedef struct
{
re_dfastate_t **sifted_states;
re_dfastate_t **limited_states;
int last_node;
int last_str_idx;
re_node_set limits;
} re_sift_context_t;
struct re_fail_stack_ent_t
{
int idx;
int node;
regmatch_t *regs;
re_node_set eps_via_nodes;
};
struct re_fail_stack_t
{
int num;
int alloc;
struct re_fail_stack_ent_t *stack;
};
struct re_dfa_t
{
re_token_t *nodes;
size_t nodes_alloc;
size_t nodes_len;
int *nexts;
int *org_indices;
re_node_set *edests;
re_node_set *eclosures;
re_node_set *inveclosures;
struct re_state_table_entry *state_table;
re_dfastate_t *init_state;
re_dfastate_t *init_state_word;
re_dfastate_t *init_state_nl;
re_dfastate_t *init_state_begbuf;
bin_tree_t *str_tree;
bin_tree_storage_t *str_tree_storage;
re_bitset_ptr_t sb_char;
int str_tree_storage_idx;
/* number of subexpressions `re_nsub' is in regex_t. */
unsigned int state_hash_mask;
int init_node;
int nbackref; /* The number of backreference in this dfa. */
/* Bitmap expressing which backreference is used. */
bitset_word_t used_bkref_map;
bitset_word_t completed_bkref_map;
unsigned int has_plural_match : 1;
/* If this dfa has "multibyte node", which is a backreference or
a node which can accept multibyte character or multi character
collating element. */
unsigned int has_mb_node : 1;
unsigned int is_utf8 : 1;
unsigned int map_notascii : 1;
unsigned int word_ops_used : 1;
int mb_cur_max;
bitset_t word_char;
reg_syntax_t syntax;
int *subexp_map;
#ifdef DEBUG
char* re_str;
#endif
#if defined _LIBC
__libc_lock_define (, lock)
#endif
};
#define re_node_set_init_empty(set) memset (set, '\0', sizeof (re_node_set))
#define re_node_set_remove(set,id) \
(re_node_set_remove_at (set, re_node_set_contains (set, id) - 1))
#define re_node_set_empty(p) ((p)->nelem = 0)
#define re_node_set_free(set) re_free ((set)->elems)
typedef enum
{
SB_CHAR,
MB_CHAR,
EQUIV_CLASS,
COLL_SYM,
CHAR_CLASS
} bracket_elem_type;
typedef struct
{
bracket_elem_type type;
union
{
unsigned char ch;
unsigned char *name;
wchar_t wch;
} opr;
} bracket_elem_t;
/* Inline functions for bitset operation. */
static inline void
bitset_not (bitset_t set)
{
int bitset_i;
for (bitset_i = 0; bitset_i < BITSET_WORDS; ++bitset_i)
set[bitset_i] = ~set[bitset_i];
}
static inline void
bitset_merge (bitset_t dest, const bitset_t src)
{
int bitset_i;
for (bitset_i = 0; bitset_i < BITSET_WORDS; ++bitset_i)
dest[bitset_i] |= src[bitset_i];
}
static inline void
bitset_mask (bitset_t dest, const bitset_t src)
{
int bitset_i;
for (bitset_i = 0; bitset_i < BITSET_WORDS; ++bitset_i)
dest[bitset_i] &= src[bitset_i];
}
#ifdef RE_ENABLE_I18N
/* Inline functions for re_string. */
static inline int
internal_function __attribute ((pure))
re_string_char_size_at (const re_string_t *pstr, int idx)
{
int byte_idx;
if (pstr->mb_cur_max == 1)
return 1;
for (byte_idx = 1; idx + byte_idx < pstr->valid_len; ++byte_idx)
if (pstr->wcs[idx + byte_idx] != WEOF)
break;
return byte_idx;
}
static inline wint_t
internal_function __attribute ((pure))
re_string_wchar_at (const re_string_t *pstr, int idx)
{
if (pstr->mb_cur_max == 1)
return (wint_t) pstr->mbs[idx];
return (wint_t) pstr->wcs[idx];
}
# ifndef NOT_IN_libc
static int
internal_function __attribute ((pure))
re_string_elem_size_at (const re_string_t *pstr, int idx)
{
# ifdef _LIBC
const unsigned char *p, *extra;
const int32_t *table, *indirect;
int32_t tmp;
# include <locale/weight.h>
uint_fast32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
if (nrules != 0)
{
table = (const int32_t *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB);
extra = (const unsigned char *)
_NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAMB);
indirect = (const int32_t *) _NL_CURRENT (LC_COLLATE,
_NL_COLLATE_INDIRECTMB);
p = pstr->mbs + idx;
tmp = findidx (&p);
return p - pstr->mbs - idx;
}
else
# endif /* _LIBC */
return 1;
}
# endif
#endif /* RE_ENABLE_I18N */
#endif /* _REGEX_INTERNAL_H */
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -29,7 +29,9 @@ struct git_config_file {
/* Open means open the file/database and parse if necessary */
int (*open)(struct git_config_file *);
int (*get)(struct git_config_file *, const char *key, const char **value);
int (*get_multivar)(struct git_config_file *, const char *key, const char *regexp, int (*fn)(const char *, void *), void *data);
int (*set)(struct git_config_file *, const char *key, const char *value);
int (*set_multivar)(git_config_file *cfg, const char *name, const char *regexp, const char *value);
int (*del)(struct git_config_file *, const char *key);
int (*foreach)(struct git_config_file *, int (*fn)(const char *, const char *, void *), void *data);
void (*free)(struct git_config_file *);
......@@ -206,6 +208,20 @@ GIT_EXTERN(int) git_config_get_bool(git_config *cfg, const char *name, int *out)
GIT_EXTERN(int) git_config_get_string(git_config *cfg, const char *name, const char **out);
/**
* Get each value of a multivar.
*
* The callback will be called on each variable found
*
* @param cfg where to look for the variable
* @param name the variable's name
* @param regexp regular expression to filter which variables we're
* interested in. Use NULL to indicate all
* @param fn the function to be called on each value of the variable
* @param data opaque pointer to pass to the callback
*/
GIT_EXTERN(int) git_config_get_multivar(git_config *cfg, const char *name, const char *regexp, int (*fn)(const char *, void *), void *data);
/**
* Set the value of an integer config variable.
*
* @param cfg where to look for the variable
......@@ -248,6 +264,17 @@ GIT_EXTERN(int) git_config_set_bool(git_config *cfg, const char *name, int value
*/
GIT_EXTERN(int) git_config_set_string(git_config *cfg, const char *name, const char *value);
/**
* Set a multivar
*
* @param cfg where to look for the variable
* @param name the variable's name
* @param regexp a regular expression to indicate which values to replace
* @param value the new value.
*/
GIT_EXTERN(int) git_config_set_multivar(git_config *cfg, const char *name, const char *regexp, const char *value);
/**
* Delete a config variable
*
......
......@@ -337,6 +337,51 @@ int git_config_get_string(git_config *cfg, const char *name, const char **out)
return git__throw(error, "Config value '%s' not found", name);
}
int git_config_get_multivar(git_config *cfg, const char *name, const char *regexp,
int (*fn)(const char *value, void *data), void *data)
{
file_internal *internal;
git_config_file *file;
int error = GIT_ENOTFOUND;
unsigned int i;
if (cfg->files.length == 0)
return git__throw(GIT_EINVALIDARGS, "Cannot get variable value; no files open in the `git_config` instance");
/*
* This loop runs the "wrong" way 'round because we need to
* look at every value from the most general to most specific
*/
for (i = cfg->files.length; i > 0; --i) {
internal = git_vector_get(&cfg->files, i - 1);
file = internal->file;
error = file->get_multivar(file, name, regexp, fn, data);
if (error < GIT_SUCCESS && error != GIT_ENOTFOUND)
git__rethrow(error, "Failed to get multivar");
}
return GIT_SUCCESS;
}
int git_config_set_multivar(git_config *cfg, const char *name, const char *regexp, const char *value)
{
file_internal *internal;
git_config_file *file;
int error = GIT_ENOTFOUND;
unsigned int i;
for (i = cfg->files.length; i > 0; --i) {
internal = git_vector_get(&cfg->files, i - 1);
file = internal->file;
error = file->set_multivar(file, name, regexp, value);
if (error < GIT_SUCCESS && error != GIT_ENOTFOUND)
git__rethrow(error, "Failed to replace multivar");
}
return GIT_SUCCESS;
}
int git_config_find_global_r(git_buf *path)
{
return git_futils_find_global_file(path, GIT_CONFIG_FILENAME);
......
......@@ -15,11 +15,12 @@
#include <ctype.h>
#include <sys/types.h>
#include <regex.h>
typedef struct cvar_t {
struct cvar_t *next;
char *section;
char *name;
char *key; /* TODO: we might be able to get rid of this */
char *value;
} cvar_t;
......@@ -69,7 +70,7 @@ typedef struct {
typedef struct {
git_config_file parent;
cvar_t_list var_list;
git_hashtable *values;
struct {
git_fbuffer buffer;
......@@ -83,161 +84,62 @@ typedef struct {
static int config_parse(diskfile_backend *cfg_file);
static int parse_variable(diskfile_backend *cfg, char **var_name, char **var_value);
static int config_write(diskfile_backend *cfg, cvar_t *var);
static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char *value);
static void cvar_free(cvar_t *var)
{
if (var == NULL)
return;
git__free(var->section);
git__free(var->name);
git__free(var->key);
git__free(var->value);
git__free(var);
}
static void cvar_list_free(cvar_t_list *list)
/* Take something the user gave us and make it nice for our hash function */
static int normalize_name(const char *in, char **out)
{
cvar_t *cur;
char *name, *fdot, *ldot;
while (!CVAR_LIST_EMPTY(list)) {
cur = CVAR_LIST_HEAD(list);
CVAR_LIST_REMOVE_HEAD(list);
cvar_free(cur);
}
}
/*
* Compare according to the git rules. Section contains the section as
* it's stored internally. query is the full name as would be given to
* 'git config'.
*/
static int cvar_match_section(const char *section, const char *query)
{
const char *sdot, *qdot, *qsub;
size_t section_len;
sdot = strchr(section, '.');
/* If the section doesn't have any dots, it's easy */
if (sdot == NULL)
return !strncasecmp(section, query, strlen(section));
/*
* If it does have dots, compare the sections
* case-insensitively. The comparison includes the dots.
*/
section_len = sdot - section + 1;
if (strncasecmp(section, query, sdot - section))
return 0;
qsub = query + section_len;
qdot = strchr(qsub, '.');
/* Make sure the subsections are the same length */
if (strlen(sdot + 1) != (size_t) (qdot - qsub))
return 0;
/* The subsection is case-sensitive */
return !strncmp(sdot + 1, qsub, strlen(sdot + 1));
}
static int cvar_match_name(const cvar_t *var, const char *str)
{
const char *name_start;
if (!cvar_match_section(var->section, str)) {
return 0;
}
/* Early exit if the lengths are different */
name_start = strrchr(str, '.') + 1;
if (strlen(var->name) != strlen(name_start))
return 0;
return !strcasecmp(var->name, name_start);
}
static cvar_t *cvar_list_find(cvar_t_list *list, const char *name)
{
cvar_t *iter;
CVAR_LIST_FOREACH (list, iter) {
if (cvar_match_name(iter, name))
return iter;
}
assert(in && out);
return NULL;
}
static int cvar_normalize_name(cvar_t *var, char **output)
{
char *section_sp = strchr(var->section, ' ');
char *quote, *name;
size_t len;
int ret;
/*
* The final string is going to be at most one char longer than
* the input
*/
len = strlen(var->section) + strlen(var->name) + 1;
name = git__malloc(len + 1);
name = git__strdup(in);
if (name == NULL)
return GIT_ENOMEM;
/* If there aren't any spaces in the section, it's easy */
if (section_sp == NULL) {
ret = p_snprintf(name, len + 1, "%s.%s", var->section, var->name);
if (ret < 0) {
fdot = strchr(name, '.');
ldot = strrchr(name, '.');
if (fdot == NULL || ldot == NULL) {
git__free(name);
return git__throw(GIT_EOSERR, "Failed to normalize name. OS err: %s", strerror(errno));
return git__throw(GIT_EINVALIDARGS, "Bad format. No dot in '%s'", in);
}
*output = name;
return GIT_SUCCESS;
}
/* Downcase up to the first dot and after the last one */
git__strntolower(name, fdot - name);
git__strtolower(ldot);
/*
* If there are spaces, we replace the space by a dot, move
* section name so it overwrites the first quotation mark and
* replace the last quotation mark by a dot. We then append the
* variable name.
*/
strcpy(name, var->section);
section_sp = strchr(name, ' ');
*section_sp = '.';
/* Remove first quote */
quote = strchr(name, '"');
memmove(quote, quote+1, strlen(quote+1));
/* Remove second quote */
quote = strchr(name, '"');
*quote = '.';
strcpy(quote+1, var->name);
*output = name;
*out = name;
return GIT_SUCCESS;
}
static char *interiorize_section(const char *orig)
static void free_vars(git_hashtable *values)
{
char *dot, *last_dot, *section, *ret;
size_t len;
dot = strchr(orig, '.');
last_dot = strrchr(orig, '.');
len = last_dot - orig;
const char *GIT_UNUSED(_unused) = NULL;
cvar_t *var = NULL;
/* No subsection, this is easy */
if (last_dot == dot)
return git__strndup(orig, dot - orig);
if (values == NULL)
return;
section = git__strndup(orig, len);
if (section == NULL)
return NULL;
GIT_HASHTABLE_FOREACH(values, _unused, var,
do {
cvar_t *next = CVAR_LIST_NEXT(var);
cvar_free(var);
var = next;
} while (var != NULL);
)
ret = section;
len = dot - orig;
git__strntolower(section, len);
return ret;
git_hashtable_free(values);
}
static int config_open(git_config_file *cfg)
......@@ -245,6 +147,10 @@ static int config_open(git_config_file *cfg)
int error;
diskfile_backend *b = (diskfile_backend *)cfg;
b->values = git_hashtable_alloc (20, git_hash__strhash_cb, git_hash__strcmp_cb);
if (b->values == NULL)
return GIT_ENOMEM;
error = git_futils_readbuffer(&b->reader.buffer, b->file_path);
/* It's fine if the file doesn't exist */
......@@ -263,7 +169,8 @@ static int config_open(git_config_file *cfg)
return GIT_SUCCESS;
cleanup:
cvar_list_free(&b->var_list);
free_vars(b->values);
b->values = NULL;
git_futils_freebuffer(&b->reader.buffer);
return git__rethrow(error, "Failed to open config");
......@@ -277,7 +184,8 @@ static void backend_free(git_config_file *_backend)
return;
git__free(backend->file_path);
cvar_list_free(&backend->var_list);
free_vars(backend->values);
git__free(backend);
}
......@@ -287,19 +195,17 @@ static int file_foreach(git_config_file *backend, int (*fn)(const char *, const
int ret = GIT_SUCCESS;
cvar_t *var;
diskfile_backend *b = (diskfile_backend *)backend;
const char *key;
CVAR_LIST_FOREACH(&b->var_list, var) {
char *normalized = NULL;
ret = cvar_normalize_name(var, &normalized);
if (ret < GIT_SUCCESS)
return ret;
ret = fn(normalized, var->value, data);
git__free(normalized);
GIT_HASHTABLE_FOREACH(b->values, key, var,
do {
ret = fn(key, var->value, data);
if (ret)
break;
}
var = CVAR_LIST_NEXT(var);
} while (var != NULL);
)
return ret;
}
......@@ -307,38 +213,34 @@ static int file_foreach(git_config_file *backend, int (*fn)(const char *, const
static int config_set(git_config_file *cfg, const char *name, const char *value)
{
cvar_t *var = NULL;
cvar_t *existing = NULL;
cvar_t *existing = NULL, *old_value = NULL;
int error = GIT_SUCCESS;
const char *last_dot;
diskfile_backend *b = (diskfile_backend *)cfg;
char *key;
if ((error = normalize_name(name, &key)) < GIT_SUCCESS)
return git__rethrow(error, "Failed to normalize variable name '%s'", name);
/*
* If it already exists, we just need to update its value.
* Try to find it in the existing values and update it if it
* only has one value.
*/
existing = cvar_list_find(&b->var_list, name);
existing = git_hashtable_lookup(b->values, key);
if (existing != NULL) {
char *tmp = value ? git__strdup(value) : NULL;
char *tmp;
git__free(key);
if (existing->next != NULL)
return git__throw(GIT_EINVALIDARGS, "Multivar incompatible with simple set");
tmp = value ? git__strdup(value) : NULL;
if (tmp == NULL && value != NULL)
return GIT_ENOMEM;
git__free(existing->value);
existing->value = tmp;
return config_write(b, existing);
}
/*
* Otherwise, create it and stick it at the end of the queue. If
* value is NULL, we return an error, because you can't delete a
* variable that doesn't exist.
*/
if (value == NULL)
return git__throw(GIT_ENOTFOUND, "Can't delete non-exitent variable");
last_dot = strrchr(name, '.');
if (last_dot == NULL) {
return git__throw(GIT_EINVALIDTYPE, "Variables without section aren't allowed");
return config_write(b, existing->key, NULL, value);
}
var = git__malloc(sizeof(cvar_t));
......@@ -347,17 +249,7 @@ static int config_set(git_config_file *cfg, const char *name, const char *value)
memset(var, 0x0, sizeof(cvar_t));
var->section = interiorize_section(name);
if (var->section == NULL) {
error = GIT_ENOMEM;
goto out;
}
var->name = git__strdup(last_dot + 1);
if (var->name == NULL) {
error = GIT_ENOMEM;
goto out;
}
var->key = key;
var->value = value ? git__strdup(value) : NULL;
if (var->value == NULL && value != NULL) {
......@@ -365,8 +257,13 @@ static int config_set(git_config_file *cfg, const char *name, const char *value)
goto out;
}
CVAR_LIST_APPEND(&b->var_list, var);
error = config_write(b, var);
error = git_hashtable_insert2(b->values, key, var, (void **)&old_value);
if (error < GIT_SUCCESS)
goto out;
cvar_free(old_value);
error = config_write(b, key, NULL, value);
out:
if (error < GIT_SUCCESS)
......@@ -383,8 +280,13 @@ static int config_get(git_config_file *cfg, const char *name, const char **out)
cvar_t *var;
int error = GIT_SUCCESS;
diskfile_backend *b = (diskfile_backend *)cfg;
char *key;
var = cvar_list_find(&b->var_list, name);
if ((error = normalize_name(name, &key)) < GIT_SUCCESS)
return error;
var = git_hashtable_lookup(b->values, key);
git__free(key);
if (var == NULL)
return git__throw(GIT_ENOTFOUND, "Variable '%s' not found", name);
......@@ -394,33 +296,155 @@ static int config_get(git_config_file *cfg, const char *name, const char **out)
return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to get config value for %s", name);
}
static int config_delete(git_config_file *cfg, const char *name)
static int config_get_multivar(git_config_file *cfg, const char *name, const char *regexp, int (*fn)(const char *, void *), void *data)
{
cvar_t *var;
int error = GIT_SUCCESS;
diskfile_backend *b = (diskfile_backend *)cfg;
char *key;
regex_t preg;
if ((error = normalize_name(name, &key)) < GIT_SUCCESS)
return error;
var = git_hashtable_lookup(b->values, key);
git__free(key);
if (var == NULL)
return git__throw(GIT_ENOTFOUND, "Variable '%s' not found", name);
if (regexp != NULL) {
error = regcomp(&preg, regexp, REG_EXTENDED);
if (error < 0)
return git__throw(GIT_EINVALIDARGS, "Failed to compile regex");
}
do {
if (regexp == NULL || !regexec(&preg, var->value, 0, NULL, 0)) {
error = fn(var->value, data);
if (error < GIT_SUCCESS)
goto exit;
}
var = var->next;
} while (var != NULL);
exit:
if (regexp != NULL)
regfree(&preg);
return error;
}
static int config_set_multivar(git_config_file *cfg, const char *name, const char *regexp, const char *value)
{
int error;
cvar_t *iter, *prev = NULL;
cvar_t *var;
diskfile_backend *b = (diskfile_backend *)cfg;
char *key;
regex_t preg;
CVAR_LIST_FOREACH (&b->var_list, iter) {
/* This is a bit hacky because we use a singly-linked list */
if (cvar_match_name(iter, name)) {
if (CVAR_LIST_HEAD(&b->var_list) == iter)
CVAR_LIST_HEAD(&b->var_list) = CVAR_LIST_NEXT(iter);
else
CVAR_LIST_REMOVE_AFTER(prev);
git__free(iter->value);
iter->value = NULL;
error = config_write(b, iter);
cvar_free(iter);
return error == GIT_SUCCESS ?
GIT_SUCCESS :
git__rethrow(error, "Failed to update config file");
if (regexp == NULL)
return git__throw(GIT_EINVALIDARGS, "No regex supplied");
if ((error = normalize_name(name, &key)) < GIT_SUCCESS)
return error;
var = git_hashtable_lookup(b->values, key);
free(key);
if (var == NULL)
return git__throw(GIT_ENOTFOUND, "Variable '%s' not found", name);
error = regcomp(&preg, regexp, REG_EXTENDED);
if (error < 0)
return git__throw(GIT_EINVALIDARGS, "Failed to compile regex");
/* "^$" means we need to addd */
if (!regexec(&preg, "", 0, NULL, 0)) {
cvar_t *newvar = git__malloc(sizeof(cvar_t));
if (newvar == NULL) {
error = GIT_ENOMEM;
goto exit;
}
memset(newvar, 0x0, sizeof(cvar_t));
newvar->key = git__strdup(var->key);
if (newvar->key == NULL) {
error = GIT_ENOMEM;
goto exit;
}
newvar->value = git__strdup(value);
if (newvar->value == NULL) {
error = GIT_ENOMEM;
goto exit;
}
while (var->next != NULL) {
var = var->next;
}
var->next = newvar;
error = config_write(b, var->key, &preg, value);
if (error < GIT_SUCCESS) {
error = git__rethrow(error, "Failed to update value in file");
goto exit;
}
}
do {
if (!regexec(&preg, var->value, 0, NULL, 0)) {
char *tmp = git__strdup(value);
if (tmp == NULL) {
error = GIT_ENOMEM;
goto exit;
}
free(var->value);
var->value = tmp;
error = config_write(b, var->key, &preg, var->value);
if (error < GIT_SUCCESS) {
error = git__rethrow(error, "Failed to update value in file");
goto exit;
}
/* Store it for the next round */
prev = iter;
}
var = var->next;
} while (var != NULL);
exit:
regfree(&preg);
return error;
}
static int config_delete(git_config_file *cfg, const char *name)
{
int error;
const cvar_t *var;
cvar_t *old_value;
diskfile_backend *b = (diskfile_backend *)cfg;
char *key;
if ((error = normalize_name(name, &key)) < GIT_SUCCESS)
return error;
var = git_hashtable_lookup(b->values, key);
free(key);
if (var == NULL)
return git__throw(GIT_ENOTFOUND, "Variable '%s' not found", name);
if (var->next != NULL)
return git__throw(GIT_EINVALIDARGS, "Multivar incompatible with simple delete");
if ((error = git_hashtable_remove2(b->values, var->key, (void **)&old_value)) < GIT_SUCCESS)
return git__rethrow(error, "Failed to remove %s from hashtable", key);
error = config_write(b, var->key, NULL, NULL);
cvar_free(old_value);
return error;
}
int git_config_file__ondisk(git_config_file **out, const char *path)
......@@ -441,7 +465,9 @@ int git_config_file__ondisk(git_config_file **out, const char *path)
backend->parent.open = config_open;
backend->parent.get = config_get;
backend->parent.get_multivar = config_get_multivar;
backend->parent.set = config_set;
backend->parent.set_multivar = config_set_multivar;
backend->parent.del = config_delete;
backend->parent.foreach = file_foreach;
backend->parent.free = backend_free;
......@@ -631,6 +657,7 @@ static int parse_section_header_ext(const char *line, const char *base_name, cha
*/
do {
if (quote_marks == 2) {
puts("too many marks");
error = git__throw(GIT_EOBJCORRUPTED, "Falied to parse ext header. Text after closing quote");
goto out;
......@@ -818,7 +845,8 @@ static int config_parse(diskfile_backend *cfg_file)
char *current_section = NULL;
char *var_name;
char *var_value;
cvar_t *var;
cvar_t *var, *existing;
git_buf buf = GIT_BUF_INIT;
/* Initialize the reading position */
cfg_file->reader.read_ptr = cfg_file->reader.buffer.data;
......@@ -864,18 +892,28 @@ static int config_parse(diskfile_backend *cfg_file)
memset(var, 0x0, sizeof(cvar_t));
var->section = git__strdup(current_section);
if (var->section == NULL) {
git__strtolower(var_name);
git_buf_printf(&buf, "%s.%s", current_section, var_name);
git__free(var_name);
if (git_buf_oom(&buf)) {
error = GIT_ENOMEM;
git__free(var);
break;
}
var->name = var_name;
var->key = git_buf_detach(&buf);
var->value = var_value;
git__strtolower(var->name);
CVAR_LIST_APPEND(&cfg_file->var_list, var);
/* Add or append the new config option */
existing = git_hashtable_lookup(cfg_file->values, var->key);
if (existing == NULL) {
error = git_hashtable_insert(cfg_file->values, var->key, var);
} else {
while (existing->next != NULL) {
existing = existing->next;
}
existing->next = var;
}
break;
}
......@@ -886,26 +924,44 @@ static int config_parse(diskfile_backend *cfg_file)
return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to parse config");
}
static int write_section(git_filebuf *file, cvar_t *var)
static int write_section(git_filebuf *file, const char *key)
{
int error;
const char *fdot, *ldot;
git_buf buf = GIT_BUF_INIT;
error = git_filebuf_printf(file, "[%s]\n", var->section);
if (error < GIT_SUCCESS)
return error;
/* All of this just for [section "subsection"] */
fdot = strchr(key, '.');
git_buf_putc(&buf, '[');
if (fdot == NULL)
git_buf_puts(&buf, key);
else
git_buf_put(&buf, key, fdot - key);
ldot = strrchr(key, '.');
if (fdot != ldot && fdot != NULL) {
git_buf_putc(&buf, '"');
/* TODO: escape */
git_buf_put(&buf, fdot + 1, ldot - fdot - 1);
git_buf_putc(&buf, '"');
}
git_buf_puts(&buf, "]\n");
if (git_buf_oom(&buf))
return GIT_ENOMEM;
error = git_filebuf_write(file, git_buf_cstr(&buf), buf.size);
git_buf_free(&buf);
error = git_filebuf_printf(file, " %s = %s\n", var->name, var->value);
return error;
}
/*
* This is pretty much the parsing, except we write out anything we don't have
*/
static int config_write(diskfile_backend *cfg, cvar_t *var)
static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char* value)
{
int error = GIT_SUCCESS, c;
int section_matches = 0, last_section_matched = 0;
char *current_section = NULL;
char *current_section = NULL, *section, *name, *ldot;
char *var_name, *var_value, *data_start;
git_filebuf file = GIT_FILEBUF_INIT;
const char *pre_end = NULL, *post_start = NULL;
......@@ -936,6 +992,9 @@ static int config_write(diskfile_backend *cfg, cvar_t *var)
return git__rethrow(error, "Failed to lock config file");
skip_bom(cfg);
ldot = strrchr(key, '.');
name = ldot + 1;
section = git__strndup(key, ldot - key);
while (error == GIT_SUCCESS && !cfg->reader.eof) {
c = cfg_peek(cfg, SKIP_WHITESPACE);
......@@ -961,7 +1020,7 @@ static int config_write(diskfile_backend *cfg, cvar_t *var)
/* Keep track of when it stops matching */
last_section_matched = section_matches;
section_matches = !strcmp(current_section, var->section);
section_matches = !strcmp(current_section, section);
break;
case ';':
......@@ -984,13 +1043,20 @@ static int config_write(diskfile_backend *cfg, cvar_t *var)
if (!last_section_matched) {
cfg_consume_line(cfg);
break;
} else {
/* As a last attempt, if we were given "^$", we should add it */
if (preg != NULL && regexec(preg, "", 0, NULL, 0))
break;
}
} else {
int cmp = -1;
pre_end = cfg->reader.read_ptr;
if ((error = parse_variable(cfg, &var_name, &var_value)) == GIT_SUCCESS)
cmp = strcasecmp(var->name, var_name);
cmp = strcasecmp(name, var_name);
if (preg != NULL)
cmp = regexec(preg, var_value, 0, NULL, 0);
git__free(var_name);
git__free(var_value);
......@@ -1016,10 +1082,10 @@ static int config_write(diskfile_backend *cfg, cvar_t *var)
* means we want to delete it, so pretend everything went
* fine
*/
if (var->value == NULL)
if (value == NULL)
error = GIT_SUCCESS;
else
error = git_filebuf_printf(&file, "\t%s = %s\n", var->name, var->value);
error = git_filebuf_printf(&file, "\t%s = %s\n", name, value);
if (error < GIT_SUCCESS) {
git__rethrow(error, "Failed to overwrite the variable");
break;
......@@ -1058,16 +1124,21 @@ static int config_write(diskfile_backend *cfg, cvar_t *var)
/* And now if we just need to add a variable */
if (section_matches) {
error = git_filebuf_printf(&file, "\t%s = %s\n", var->name, var->value);
if (preg == NULL || !regexec(preg, "", 0, NULL, 0)) {
error = git_filebuf_printf(&file, "\t%s = %s\n", name, value);
goto cleanup;
}
}
/* Or maybe we need to write out a whole section */
error = write_section(&file, var);
error = write_section(&file, section);
if (error < GIT_SUCCESS)
git__rethrow(error, "Failed to write new section");
if (preg == NULL || !regexec(preg, "", 0, NULL, 0))
error = git_filebuf_printf(&file, "\t%s = %s\n", name, value);
cleanup:
git__free(section);
git__free(current_section);
if (error < GIT_SUCCESS)
......
#include "clar_libgit2.h"
static int mv_read_cb(const char *name, const char *GIT_UNUSED(value), void *data)
{
int *n = (int *) data;
if (!strcmp(name, "remote.fancy.url"))
(*n)++;
return 0;
}
void test_config_multivar__foreach(void)
{
git_config *cfg;
int n = 0;
cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config11")));
cl_git_pass(git_config_foreach(cfg, mv_read_cb, &n));
cl_assert(n == 2);
git_config_free(cfg);
}
static int cb(const char *GIT_UNUSED(val), void *data)
{
int *n = (int *) data;
(*n)++;
return GIT_SUCCESS;
}
void test_config_multivar__get(void)
{
git_config *cfg;
const char *name = "remote.fancy.url";
int n;
cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config11")));
n = 0;
cl_git_pass(git_config_get_multivar(cfg, name, NULL, cb, &n));
cl_assert(n == 2);
n = 0;
cl_git_pass(git_config_get_multivar(cfg, name, "example", cb, &n));
cl_assert(n == 1);
git_config_free(cfg);
}
void test_config_multivar__add(void)
{
git_config *cfg;
const char *name = "remote.fancy.url";
int n;
cl_fixture_sandbox("config");
cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
cl_git_pass(git_config_set_multivar(cfg, name, "^$", "git://git.otherplace.org/libgit2"));
n = 0;
cl_git_pass(git_config_get_multivar(cfg, name, NULL, cb, &n));
cl_assert(n == 3);
n = 0;
cl_git_pass(git_config_get_multivar(cfg, name, "otherplace", cb, &n));
cl_assert(n == 1);
git_config_free(cfg);
/* We know it works in memory, let's see if the file is written correctly */
cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
n = 0;
cl_git_pass(git_config_get_multivar(cfg, name, NULL, cb, &n));
cl_assert(n == 3);
n = 0;
cl_git_pass(git_config_get_multivar(cfg, name, "otherplace", cb, &n));
cl_assert(n == 1);
git_config_free(cfg);
}
[remote "fancy"]
url = git://github.com/libgit2/libgit2
url = git://git.example.com/libgit2
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment