summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDongHun Kwak <dh0128.kwak@samsung.com>2019-09-10 15:38:54 +0900
committerDongHun Kwak <dh0128.kwak@samsung.com>2019-09-10 15:38:54 +0900
commit18ebbaf4f619e79231f5ad18a2ab8c135d22ef56 (patch)
treec486aa0883b10e1633d04530150251f19a56db9a
parentf078975d65d0cace665590f3cf60025e6f2c7a0a (diff)
downloadlibsolv-18ebbaf4f619e79231f5ad18a2ab8c135d22ef56.tar.gz
libsolv-18ebbaf4f619e79231f5ad18a2ab8c135d22ef56.tar.bz2
libsolv-18ebbaf4f619e79231f5ad18a2ab8c135d22ef56.zip
Imported Upstream version 0.7.5upstream/0.7.5
-rw-r--r--.appveyor.yml34
-rw-r--r--.travis.yml26
-rw-r--r--CMakeLists.txt50
-rw-r--r--CREDITS3
-rw-r--r--NEWS11
-rw-r--r--README2
-rw-r--r--TODO_1.04
-rw-r--r--VERSION.cmake2
-rw-r--r--bindings/solv.i10
-rw-r--r--doc/CMakeLists.txt4
-rw-r--r--examples/solv/CMakeLists.txt2
-rw-r--r--ext/CMakeLists.txt28
-rw-r--r--ext/libsolvext.ver1
-rw-r--r--ext/repo_conda.c180
-rw-r--r--ext/repo_conda.h8
-rw-r--r--ext/repo_deb.c8
-rw-r--r--ext/repo_rpmdb_bdb.h13
-rw-r--r--ext/repo_susetags.c4
-rw-r--r--ext/repo_testcase.c716
-rw-r--r--ext/solv_jsonparser.c35
-rw-r--r--ext/solv_jsonparser.h6
-rw-r--r--ext/solv_xfopen.c56
-rw-r--r--ext/testcase.c826
-rw-r--r--ext/testcase.h1
-rw-r--r--libsolv.pc.in4
-rw-r--r--libsolvext.pc.in4
-rw-r--r--package/libsolv.changes17
-rw-r--r--src/CMakeLists.txt18
-rw-r--r--src/chksum.c4
-rw-r--r--src/cleandeps.c2
-rw-r--r--src/conda.c242
-rw-r--r--src/conda.h2
-rw-r--r--src/knownid.h15
-rw-r--r--src/libsolv.ver2
-rw-r--r--src/policy.c30
-rw-r--r--src/pool.c18
-rw-r--r--src/qsort_r.c2
-rw-r--r--src/repo.c13
-rw-r--r--src/repo_solv.c2
-rw-r--r--src/repodata.c32
-rw-r--r--src/repopage.c19
-rw-r--r--src/rules.c166
-rw-r--r--src/rules.h2
-rw-r--r--src/selection.c21
-rw-r--r--src/solvable.c74
-rw-r--r--src/solver.c78
-rw-r--r--src/solver.h1
-rw-r--r--src/util.c22
-rw-r--r--src/util.h1
-rw-r--r--test/CMakeLists.txt24
-rw-r--r--test/runtestcases.bat24
-rwxr-xr-xtest/runtestcases.sh (renamed from test/runtestcases)0
-rw-r--r--test/testcases/focus/best.t8
-rw-r--r--test/testcases/focus/installed.t6
-rw-r--r--test/testcases/focus/normal.t7
-rw-r--r--test/testcases/lock/best.t27
-rw-r--r--test/testcases/lock/dup.t25
-rw-r--r--test/testcases/lockstep/lockstep_install.t3
-rw-r--r--test/testcases/lockstep/lockstep_update.t3
-rw-r--r--test/testcases/weakdeps/supplements_implicitobsoleteusescolors.t9
-rw-r--r--tools/CMakeLists.txt25
-rw-r--r--tools/conda2solv.c71
-rw-r--r--tools/deb2solv.c2
-rw-r--r--tools/diskusagexml2solv.c60
-rw-r--r--tools/dumpsolv.c1
-rw-r--r--tools/installcheck.c3
-rw-r--r--tools/mergesolv.c1
-rw-r--r--tools/testsolv.c1
-rw-r--r--win32/CMakeLists.txt9
-rw-r--r--win32/LICENSE70
-rw-r--r--win32/fmemopen.c41
-rw-r--r--win32/fnmatch.c320
-rw-r--r--win32/fnmatch.h24
-rw-r--r--win32/getopt.c97
-rw-r--r--win32/getopt.h30
-rw-r--r--win32/regcomp.c2953
-rw-r--r--win32/regerror.c37
-rw-r--r--win32/regex.h65
-rw-r--r--win32/regexec.c1028
-rw-r--r--win32/strfncs.c35
-rw-r--r--win32/strfncs.h8
-rw-r--r--win32/tre-mem.c158
-rw-r--r--win32/tre.h230
-rw-r--r--win32/unistd.h4
84 files changed, 7238 insertions, 992 deletions
diff --git a/.appveyor.yml b/.appveyor.yml
new file mode 100644
index 0000000..2b91665
--- /dev/null
+++ b/.appveyor.yml
@@ -0,0 +1,34 @@
+build: false
+
+platform:
+ - x64
+
+image:
+ - Visual Studio 2017
+
+environment:
+ matrix:
+ - MINICONDA: C:\libsolv-conda
+
+init:
+ - "ECHO %MINICONDA%"
+ - if "%APPVEYOR_BUILD_WORKER_IMAGE%" == "Visual Studio 2015" set VCVARPATH="C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"
+ - if "%APPVEYOR_BUILD_WORKER_IMAGE%" == "Visual Studio 2015" set VCARGUMENT=%PLATFORM%
+ - if "%APPVEYOR_BUILD_WORKER_IMAGE%" == "Visual Studio 2017" set VCVARPATH="C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat"
+ - echo "%VCVARPATH% %VCARGUMENT%"
+ - "%VCVARPATH% %VCARGUMENT%"
+ - ps: if($env:Platform -eq "x64"){Start-FileDownload 'http://repo.continuum.io/miniconda/Miniconda3-latest-Windows-x86_64.exe' C:\Miniconda.exe; echo "Done"}
+ - ps: if($env:Platform -eq "x86"){Start-FileDownload 'http://repo.continuum.io/miniconda/Miniconda3-latest-Windows-x86.exe' C:\Miniconda.exe; echo "Done"}
+ - cmd: C:\Miniconda.exe /S /D=C:\libsolv-conda
+ - "set PATH=%MINICONDA%;%MINICONDA%\\Scripts;%MINICONDA%\\Library\\bin;%PATH%"
+
+install:
+ - conda config --set always_yes yes --set changeps1 no
+ - conda update -q conda
+ - conda info -a
+ - conda install cmake zlib xz -c conda-forge
+ - cmake -G "NMake Makefiles" -D CMAKE_INSTALL_PREFIX=%MINICONDA%\\LIBRARY -DDISABLE_SHARED=1 -DWITHOUT_COOKIEOPEN=1 -DMULTI_SEMANTICS=1 -DENABLE_COMPLEX_DEPS=1 -DENABLE_EXAMPLES=0 .
+ - nmake
+
+build_script:
+ - ctest
diff --git a/.travis.yml b/.travis.yml
index 8d7dda5..a115095 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,5 +1,23 @@
language: C
-before_script: sudo apt-get install cmake
-script: mkdir build && cd build && cmake -DDEBIAN=1 -DMULTI_SEMANTICS=1 .. && make && make test
-
-
+matrix:
+ fast_finish: true
+ include:
+ - os: linux
+ addons:
+ apt:
+ packages:
+ - cmake
+ - os: osx
+ osx_image: xcode9.4
+ compiler: clang
+ addons:
+ homebrew:
+ packages:
+ - cmake
+ - zlib
+script:
+- mkdir build
+- cd build
+- cmake -DDEBIAN=1 -DMULTI_SEMANTICS=1 ..
+- make
+- make test
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ab385f1..e331318 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,6 +1,6 @@
PROJECT (libsolv)
-CMAKE_MINIMUM_REQUIRED (VERSION 2.4)
+CMAKE_MINIMUM_REQUIRED (VERSION 2.8.5)
OPTION (ENABLE_STATIC "Build a static version of the libraries?" OFF)
OPTION (DISABLE_SHARED "Do not build a shared version of the libraries?" OFF)
@@ -38,35 +38,16 @@ OPTION (ENABLE_ZSTD_COMPRESSION "Build with zstd compression support?" OFF)
OPTION (ENABLE_ZCHUNK_COMPRESSION "Build with zchunk compression support?" OFF)
OPTION (WITH_SYSTEM_ZCHUNK "Use system zchunk library?" OFF)
OPTION (WITH_LIBXML2 "Build with libxml2 instead of libexpat?" OFF)
+OPTION (WITHOUT_COOKIEOPEN "Disable the use of stdio cookie opens?" OFF)
-# Library
-IF (DEFINED LIB)
- SET (LIB_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${LIB}")
-ELSE (DEFINED LIB)
- IF (CMAKE_SIZEOF_VOID_P MATCHES "8")
- SET (LIB_SUFFIX "64")
- ENDIF (CMAKE_SIZEOF_VOID_P MATCHES "8")
- SET (LIB_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}")
-ENDIF (DEFINED LIB)
-MESSAGE (STATUS "Libraries will be installed in ${LIB_INSTALL_DIR}")
-# Library
-IF (DEFINED INCLUDE)
- SET (INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${INCLUDE}")
-else (DEFINED INCLUDE)
- SET (INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/include")
-ENDIF (DEFINED INCLUDE)
-MESSAGE (STATUS "Header files will be installed in ${INCLUDE_INSTALL_DIR}")
-SET (BIN_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/bin")
-IF (NOT MAN_INSTALL_DIR)
-SET (MAN_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/share/man")
-IF (IS_DIRECTORY "${CMAKE_INSTALL_PREFIX}/man" AND NOT IS_DIRECTORY "${CMAKE_INSTALL_PREFIX}/share/man")
- SET (MAN_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/man")
-ENDIF (IS_DIRECTORY "${CMAKE_INSTALL_PREFIX}/man" AND NOT IS_DIRECTORY "${CMAKE_INSTALL_PREFIX}/share/man")
-ENDIF (NOT MAN_INSTALL_DIR)
-MESSAGE(STATUS "Man pages will be installed in ${MAN_INSTALL_DIR}")
+include (GNUInstallDirs)
+message (STATUS "Libraries will be installed in ${CMAKE_INSTALL_FULL_LIBDIR}")
+message (STATUS "Header files will be installed in ${CMAKE_INSTALL_FULL_INCLUDEDIR}")
+message (STATUS "Binaries will be installed in ${CMAKE_INSTALL_FULL_BINDIR}")
+message (STATUS "Man pages will be installed in ${CMAKE_INSTALL_FULL_MANDIR}")
IF (NOT PKGCONFIG_INSTALL_DIR)
- SET (PKGCONFIG_INSTALL_DIR ${LIB_INSTALL_DIR}/pkgconfig)
+ SET (PKGCONFIG_INSTALL_DIR ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
ENDIF (NOT PKGCONFIG_INSTALL_DIR)
####################################################################
# CONFIGURATION #
@@ -287,7 +268,7 @@ ENDIF (${CMAKE_MAJOR_VERSION} GREATER 2)
# should create config.h with #cmakedefine instead...
FOREACH (VAR HAVE_STRCHRNUL HAVE_FOPENCOOKIE HAVE_FUNOPEN WORDS_BIGENDIAN
- HAVE_RPM_DB_H HAVE_PGPDIGGETPARAMS WITH_LIBXML2 )
+ HAVE_RPM_DB_H HAVE_PGPDIGGETPARAMS WITH_LIBXML2 WITHOUT_COOKIEOPEN)
IF(${VAR})
ADD_DEFINITIONS (-D${VAR}=1)
SET (SWIG_FLAGS ${SWIG_FLAGS} -D${VAR})
@@ -389,10 +370,15 @@ INCLUDE_DIRECTORIES (${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_DIR} ${CMAKE_SOU
MESSAGE (STATUS "Looking for modules in ${CMAKE_MODULE_PATH}")
-set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
-set (CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS} -O3")
-set (CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS} -g -O3")
-set (CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS} -g3 -O0")
+IF (MSVC)
+ set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /wd4244 /wd4267")
+ add_definitions(-D_CRT_SECURE_NO_WARNINGS)
+ELSE ()
+ set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
+ set (CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS} -O3")
+ set (CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS} -g -O3")
+ set (CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS} -g3 -O0")
+ENDIF ()
# set system libraries
SET (SYSTEM_LIBRARIES "")
diff --git a/CREDITS b/CREDITS
index 049bcae..38f89f9 100644
--- a/CREDITS
+++ b/CREDITS
@@ -23,3 +23,6 @@ Michael Schroeder
Ingo Weinhold
- haiku support
+Wolf Vollprecht
+ - win32 port
+
diff --git a/NEWS b/NEWS
index 198fca3..ede4a0e 100644
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,17 @@
This file contains the major changes between
libsolv versions:
+Version 0.7.5
+- selected bug fixes:
+ * fix favorq leaking between solver runs if the solver is reused
+ * fix SOLVER_FLAG_FOCUS_BEST updateing packages without reason
+ * be more correct with multiversion packages that obsolete their
+ own name
+ * allow building with swig-4.0.0
+ * lock jobs now take precedence over dup and forcebest jobs
+- new features
+ * MSVC compilation support
+
Version 0.7.4
- selected bug fixes:
* repo_add_rpmdb: do not copy bad solvables from the old solv file
diff --git a/README b/README
index 0242459..59ac1a1 100644
--- a/README
+++ b/README
@@ -37,7 +37,7 @@ Supported repository formats:
Build instructions
==================
-Requires: cmake 2.4.x
+Requires: cmake 2.8.5 or later
mkdir build
cd build
diff --git a/TODO_1.0 b/TODO_1.0
index d77df67..3e243d3 100644
--- a/TODO_1.0
+++ b/TODO_1.0
@@ -7,10 +7,6 @@
- deal with DIRSTR entries having dirid 0 (for source rpms)
-- drop patchcheck
-
-- make FAVOR handling deal with versions
-
- write more manpages
IDEAS:
diff --git a/VERSION.cmake b/VERSION.cmake
index 80d0e03..5c228a3 100644
--- a/VERSION.cmake
+++ b/VERSION.cmake
@@ -49,5 +49,5 @@ SET(LIBSOLVEXT_SOVERSION "1")
SET(LIBSOLV_MAJOR "0")
SET(LIBSOLV_MINOR "7")
-SET(LIBSOLV_PATCH "4")
+SET(LIBSOLV_PATCH "5")
diff --git a/bindings/solv.i b/bindings/solv.i
index ad265a5..107192f 100644
--- a/bindings/solv.i
+++ b/bindings/solv.i
@@ -629,8 +629,10 @@ SWIG_AsValDepId(void *obj, int *val) {
%typemap(out) disown_helper {
#if defined(SWIGRUBY)
SWIG_ConvertPtr(self, &argp1,SWIGTYPE_p_Pool, SWIG_POINTER_DISOWN | 0 );
-#elif defined(SWIGPYTHON)
+#elif defined(SWIGPYTHON) && SWIG_VERSION < 0x040000
SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_Pool, SWIG_POINTER_DISOWN | 0 );
+#elif defined(SWIGPYTHON)
+ SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_Pool, SWIG_POINTER_DISOWN | 0 );
#elif defined(SWIGPERL)
SWIG_ConvertPtr(ST(0), &argp1,SWIGTYPE_p_Pool, SWIG_POINTER_DISOWN | 0 );
#elif defined(SWIGTCL)
@@ -1121,7 +1123,7 @@ SolvFp *solvfp_xfopen_fd(const char *fn, int fd, const char *mode = 0);
fd = dup(fd);
if (fd == -1)
return 0;
- fcntl(fd, F_SETFD, FD_CLOEXEC);
+ solv_setcloexec(fd, 1);
fp = solv_xfopen_fd(fn, fd, mode);
if (!fp) {
close(fd);
@@ -1138,7 +1140,7 @@ SolvFp *solvfp_xfopen_fd(const char *fn, int fd, const char *mode = 0);
if (!fp)
return 0;
if (fileno(fp) != -1)
- fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
+ solv_setcloexec(fileno(fp), 1);
sfp = solv_calloc(1, sizeof(SolvFp));
sfp->fp = fp;
return sfp;
@@ -1220,7 +1222,7 @@ typedef struct {
void cloexec(bool state) {
if (!$self->fp || fileno($self->fp) == -1)
return;
- fcntl(fileno($self->fp), F_SETFD, state ? FD_CLOEXEC : 0);
+ solv_setcloexec(fileno($self->fp), state);
}
}
diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt
index 66011b4..ed38274 100644
--- a/doc/CMakeLists.txt
+++ b/doc/CMakeLists.txt
@@ -48,8 +48,8 @@ STRING(REGEX REPLACE "([^;]+)" "gen/\\1" libsolv_MANPAGES3 "${libsolv_MANPAGES3}
INSTALL(FILES
${libsolv_MANPAGES3}
- DESTINATION "${MAN_INSTALL_DIR}/man3")
+ DESTINATION "${CMAKE_INSTALL_MANDIR}/man3")
INSTALL(FILES
${libsolv_MANPAGES1}
- DESTINATION "${MAN_INSTALL_DIR}/man1")
+ DESTINATION "${CMAKE_INSTALL_MANDIR}/man1")
diff --git a/examples/solv/CMakeLists.txt b/examples/solv/CMakeLists.txt
index 41f45f7..0f3bd47 100644
--- a/examples/solv/CMakeLists.txt
+++ b/examples/solv/CMakeLists.txt
@@ -25,5 +25,5 @@ TARGET_LINK_LIBRARIES (solv libsolvext libsolv ${SYSTEM_LIBRARIES})
INSTALL(TARGETS
solv
- DESTINATION ${BIN_INSTALL_DIR})
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
diff --git a/ext/CMakeLists.txt b/ext/CMakeLists.txt
index edc2b9f..7c25535 100644
--- a/ext/CMakeLists.txt
+++ b/ext/CMakeLists.txt
@@ -1,5 +1,5 @@
SET (libsolvext_SRCS
- solv_xfopen.c testcase.c)
+ solv_xfopen.c testcase.c repo_testcase.c)
SET (libsolvext_HEADERS
tools_util.h solv_xfopen.h testcase.h)
@@ -116,6 +116,13 @@ IF (ENABLE_APPDATA)
repo_appdata.h)
ENDIF (ENABLE_APPDATA)
+IF (ENABLE_CONDA)
+ SET (libsolvext_SRCS ${libsolvext_SRCS}
+ repo_conda.c)
+ SET (libsolvext_HEADERS ${libsolvext_HEADERS}
+ repo_conda.h)
+ENDIF (ENABLE_CONDA)
+
IF (ENABLE_RPMMD OR ENABLE_SUSEREPO)
SET (libsolvext_SRCS ${libsolvext_SRCS}
repodata_diskusage.c)
@@ -126,12 +133,19 @@ IF (ENABLE_RPMMD OR ENABLE_SUSEREPO OR ENABLE_APPDATA OR ENABLE_COMPS OR ENABLE_
solv_xmlparser.c)
ENDIF (ENABLE_RPMMD OR ENABLE_SUSEREPO OR ENABLE_APPDATA OR ENABLE_COMPS OR ENABLE_HELIXREPO OR ENABLE_MDKREPO)
+IF (ENABLE_CONDA)
+ SET (libsolvext_SRCS ${libsolvext_SRCS}
+ solv_jsonparser.c)
+ENDIF (ENABLE_CONDA)
+
IF (ENABLE_ZCHUNK_COMPRESSION)
SET (libsolvext_SRCS ${libsolvext_SRCS}
solv_zchunk.c)
ENDIF (ENABLE_ZCHUNK_COMPRESSION)
+IF (NOT MSVC)
SET (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC")
+ENDIF (NOT MSVC)
IF (HAVE_LINKER_VERSION_SCRIPT)
SET (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${LINK_FLAGS} -Wl,--version-script=${CMAKE_SOURCE_DIR}/ext/libsolvext.ver")
ENDIF (HAVE_LINKER_VERSION_SCRIPT)
@@ -143,16 +157,20 @@ ADD_LIBRARY (libsolvext SHARED ${libsolvext_SRCS})
TARGET_LINK_LIBRARIES(libsolvext libsolv ${SYSTEM_LIBRARIES})
ENDIF (DISABLE_SHARED)
+IF (WIN32)
+ INCLUDE_DIRECTORIES (${PROJECT_SOURCE_DIR}/win32/)
+ENDIF ()
+
SET_TARGET_PROPERTIES(libsolvext PROPERTIES OUTPUT_NAME "solvext")
SET_TARGET_PROPERTIES(libsolvext PROPERTIES SOVERSION ${LIBSOLVEXT_SOVERSION})
-SET_TARGET_PROPERTIES(libsolvext PROPERTIES INSTALL_NAME_DIR ${LIB_INSTALL_DIR})
+SET_TARGET_PROPERTIES(libsolvext PROPERTIES INSTALL_NAME_DIR ${CMAKE_INSTALL_LIBDIR})
-INSTALL (FILES ${libsolvext_HEADERS} DESTINATION "${INCLUDE_INSTALL_DIR}/solv")
-INSTALL (TARGETS libsolvext LIBRARY DESTINATION ${LIB_INSTALL_DIR} ARCHIVE DESTINATION ${LIB_INSTALL_DIR} RUNTIME DESTINATION bin)
+INSTALL (FILES ${libsolvext_HEADERS} DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/solv")
+INSTALL (TARGETS libsolvext LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
IF (ENABLE_STATIC AND NOT DISABLE_SHARED)
ADD_LIBRARY (libsolvext_static STATIC ${libsolvext_SRCS})
SET_TARGET_PROPERTIES(libsolvext_static PROPERTIES OUTPUT_NAME "solvext")
SET_TARGET_PROPERTIES(libsolvext_static PROPERTIES SOVERSION ${LIBSOLVEXT_SOVERSION})
-INSTALL (TARGETS libsolvext_static LIBRARY DESTINATION ${LIB_INSTALL_DIR} ARCHIVE DESTINATION ${LIB_INSTALL_DIR})
+INSTALL (TARGETS libsolvext_static LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
ENDIF (ENABLE_STATIC AND NOT DISABLE_SHARED)
diff --git a/ext/libsolvext.ver b/ext/libsolvext.ver
index 4896e85..ef028f0 100644
--- a/ext/libsolvext.ver
+++ b/ext/libsolvext.ver
@@ -10,6 +10,7 @@ SOLV_1.0 {
repo_add_arch_repo;
repo_add_autopattern;
repo_add_code11_products;
+ repo_add_conda;
repo_add_content;
repo_add_comps;
repo_add_cudf;
diff --git a/ext/repo_conda.c b/ext/repo_conda.c
new file mode 100644
index 0000000..0828aff
--- /dev/null
+++ b/ext/repo_conda.c
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2019, SUSE LLC.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#define _GNU_SOURCE
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "chksum.h"
+#include "solv_jsonparser.h"
+#include "conda.h"
+#include "repo_conda.h"
+
+struct parsedata {
+ Pool *pool;
+ Repo *repo;
+ Repodata *data;
+};
+
+static int
+parse_deps(struct parsedata *pd, struct solv_jsonparser *jp, Offset *depp)
+{
+ int type = JP_ARRAY;
+ while (type > 0 && (type = jsonparser_parse(jp)) > 0 && type != JP_ARRAY_END)
+ {
+ if (type == JP_STRING)
+ {
+ Id id = pool_conda_matchspec(pd->pool, jp->value);
+ if (id)
+ *depp = repo_addid_dep(pd->repo, *depp, id, 0);
+ }
+ else
+ type = jsonparser_skip(jp, type);
+ }
+ return type;
+}
+
+static int
+parse_package(struct parsedata *pd, struct solv_jsonparser *jp, char *kfn)
+{
+ int type = JP_OBJECT;
+ Pool *pool= pd->pool;
+ Repodata *data = pd->data;
+ Solvable *s;
+ Id handle = repo_add_solvable(pd->repo);
+ s = pool_id2solvable(pool, handle);
+ char *fn = 0;
+ char *subdir = 0;
+
+ while (type > 0 && (type = jsonparser_parse(jp)) > 0 && type != JP_OBJECT_END)
+ {
+ if (type == JP_STRING && !strcmp(jp->key, "build"))
+ repodata_add_poolstr_array(data, handle, SOLVABLE_BUILDFLAVOR, jp->value);
+ else if (type == JP_NUMBER && !strcmp(jp->key, "build_number"))
+ repodata_set_str(data, handle, SOLVABLE_BUILDVERSION, jp->value);
+ else if (type == JP_ARRAY && !strcmp(jp->key, "depends"))
+ type = parse_deps(pd, jp, &s->requires);
+ else if (type == JP_ARRAY && !strcmp(jp->key, "requires"))
+ type = parse_deps(pd, jp, &s->requires);
+ else if (type == JP_STRING && !strcmp(jp->key, "license"))
+ repodata_add_poolstr_array(data, handle, SOLVABLE_LICENSE, jp->value);
+ else if (type == JP_STRING && !strcmp(jp->key, "md5"))
+ repodata_set_checksum(data, handle, SOLVABLE_PKGID, REPOKEY_TYPE_MD5, jp->value);
+ else if (type == JP_STRING && !strcmp(jp->key, "sha256"))
+ repodata_set_checksum(data, handle, SOLVABLE_CHECKSUM, REPOKEY_TYPE_SHA256, jp->value);
+ else if (type == JP_STRING && !strcmp(jp->key, "name"))
+ s->name = pool_str2id(pool, jp->value, 1);
+ else if (type == JP_STRING && !strcmp(jp->key, "version"))
+ s->evr= pool_str2id(pool, jp->value, 1);
+ else if (type == JP_STRING && !strcmp(jp->key, "fn") && !fn)
+ fn = solv_strdup(jp->value);
+ else if (type == JP_STRING && !strcmp(jp->key, "subdir") && !subdir)
+ subdir = solv_strdup(jp->value);
+ else if (type == JP_NUMBER && !strcmp(jp->key, "size"))
+ repodata_set_num(data, handle, SOLVABLE_DOWNLOADSIZE, strtoull(jp->value, 0, 10));
+ else if (type == JP_NUMBER && !strcmp(jp->key, "timestamp"))
+ {
+ unsigned long long ts = strtoull(jp->value, 0, 10);
+ if (ts > 253402300799ULL)
+ ts /= 1000;
+ repodata_set_num(data, handle, SOLVABLE_BUILDTIME, ts);
+ }
+ else
+ type = jsonparser_skip(jp, type);
+ }
+ if (fn || kfn)
+ repodata_set_location(data, handle, 0, subdir, fn ? fn : kfn);
+ solv_free(fn);
+ solv_free(subdir);
+ if (!s->evr)
+ s->evr = 1;
+ if (s->name)
+ s->provides = repo_addid_dep(pd->repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
+ return type;
+}
+
+static int
+parse_packages(struct parsedata *pd, struct solv_jsonparser *jp)
+{
+ int type = JP_OBJECT;
+ while (type > 0 && (type = jsonparser_parse(jp)) > 0 && type != JP_OBJECT_END)
+ {
+ if (type == JP_OBJECT)
+ {
+ char *fn = solv_strdup(jp->key);
+ type = parse_package(pd, jp, fn);
+ solv_free(fn);
+ }
+ else
+ type = jsonparser_skip(jp, type);
+ }
+ return type;
+}
+
+static int
+parse_packages2(struct parsedata *pd, struct solv_jsonparser *jp)
+{
+ int type = JP_ARRAY;
+ while (type > 0 && (type = jsonparser_parse(jp)) > 0 && type != JP_ARRAY_END)
+ {
+ if (type == JP_OBJECT)
+ type = parse_package(pd, jp, 0);
+ else
+ type = jsonparser_skip(jp, type);
+ }
+ return type;
+}
+
+static int
+parse_main(struct parsedata *pd, struct solv_jsonparser *jp)
+{
+ int type = JP_OBJECT;
+ while (type > 0 && (type = jsonparser_parse(jp)) > 0 && type != JP_OBJECT_END)
+ {
+ if (type == JP_OBJECT && !strcmp("packages", jp->key))
+ type = parse_packages(pd, jp);
+ if (type == JP_ARRAY && !strcmp("packages", jp->key))
+ type = parse_packages2(pd, jp);
+ else
+ type = jsonparser_skip(jp, type);
+ }
+ return type;
+}
+
+int
+repo_add_conda(Repo *repo, FILE *fp, int flags)
+{
+ Pool *pool = repo->pool;
+ struct solv_jsonparser jp;
+ struct parsedata pd;
+ Repodata *data;
+ int type, ret = 0;
+
+ data = repo_add_repodata(repo, flags);
+
+ memset(&pd, 0, sizeof(pd));
+ pd.pool = pool;
+ pd.repo = repo;
+ pd.data = data;
+
+ jsonparser_init(&jp, fp);
+ if ((type = jsonparser_parse(&jp)) != JP_OBJECT)
+ ret = pool_error(pool, -1, "repository does not start with an object");
+ else if ((type = parse_main(&pd, &jp)) != JP_OBJECT_END)
+ ret = pool_error(pool, -1, "parse error line %d", jp.line);
+ jsonparser_free(&jp);
+
+ if (!(flags & REPO_NO_INTERNALIZE))
+ repodata_internalize(data);
+
+ return ret;
+}
+
diff --git a/ext/repo_conda.h b/ext/repo_conda.h
new file mode 100644
index 0000000..7e90a3d
--- /dev/null
+++ b/ext/repo_conda.h
@@ -0,0 +1,8 @@
+/*
+ * Copyright (c) 2019, SUSE LLC
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+extern int repo_add_conda(Repo *repo, FILE *fp, int flags);
diff --git a/ext/repo_deb.c b/ext/repo_deb.c
index 5dd79f4..8f63756 100644
--- a/ext/repo_deb.c
+++ b/ext/repo_deb.c
@@ -22,6 +22,14 @@
#include "chksum.h"
#include "repo_deb.h"
+#ifdef _WIN32
+ #include "strfncs.h"
+#endif
+
+#if !defined(S_ISREG) && defined(S_IFMT) && defined(S_IFREG)
+#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
+#endif
+
static unsigned char *
decompress_gz(unsigned char *in, int inl, int *outlp, int maxoutl)
{
diff --git a/ext/repo_rpmdb_bdb.h b/ext/repo_rpmdb_bdb.h
index ed82a69..ae477f7 100644
--- a/ext/repo_rpmdb_bdb.h
+++ b/ext/repo_rpmdb_bdb.h
@@ -22,7 +22,13 @@
#endif
#ifdef RPM5
-# define RPM_INDEX_SIZE 4 /* just the rpmdbid */
+# include <rpm/rpmversion.h>
+# if RPMLIB_VERSION < RPMLIB_VERSION_ENCODE(5,3,_,0,0,_)
+# define RPM_INDEX_SIZE 8 /* rpmdbid + array index */
+# else
+# define RPM_INDEX_SIZE 4 /* just the rpmdbid */
+# define RPM5_BIG_ENDIAN_ID
+#endif
#else
# define RPM_INDEX_SIZE 8 /* rpmdbid + array index */
#endif
@@ -66,11 +72,10 @@ stat_database(struct rpmdbstate *state, char *dbname, struct stat *statbuf, int
return 0;
}
-
static inline Id
db2rpmdbid(unsigned char *db, int byteswapped)
{
-#ifdef RPM5
+#ifdef RPM5_BIG_ENDIAN_ID
return db[0] << 24 | db[1] << 16 | db[2] << 8 | db[3];
#else
# if defined(WORDS_BIGENDIAN)
@@ -87,7 +92,7 @@ db2rpmdbid(unsigned char *db, int byteswapped)
static inline void
rpmdbid2db(unsigned char *db, Id id, int byteswapped)
{
-#ifdef RPM5
+#ifdef RPM5_BIG_ENDIAN_ID
db[0] = id >> 24, db[1] = id >> 16, db[2] = id >> 8, db[3] = id;
#else
# if defined(WORDS_BIGENDIAN)
diff --git a/ext/repo_susetags.c b/ext/repo_susetags.c
index 561c789..dc60aa4 100644
--- a/ext/repo_susetags.c
+++ b/ext/repo_susetags.c
@@ -21,6 +21,10 @@
#endif
#include "repodata_diskusage.h"
+#ifdef _WIN32
+#include "strfncs.h"
+#endif
+
struct datashare {
Id name;
Id evr;
diff --git a/ext/repo_testcase.c b/ext/repo_testcase.c
new file mode 100644
index 0000000..48d8a0e
--- /dev/null
+++ b/ext/repo_testcase.c
@@ -0,0 +1,716 @@
+/*
+ * Copyright (c) 2019, SUSE LLC
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "testcase.h"
+
+#define DISABLE_JOIN2
+#include "tools_util.h"
+
+static const char *
+testcase_id2str(Pool *pool, Id id, int isname)
+{
+ const char *s = pool_id2str(pool, id);
+ const char *ss;
+ char *s2, *s2p;
+ int bad = 0, paren = 0, parenbad = 0;
+
+ if (id == 0)
+ return "<NULL>";
+ if (id == 1)
+ return "\\00";
+ if (strchr("[(<=>!", *s))
+ bad++;
+ if (!strncmp(s, "namespace:", 10))
+ bad++;
+ for (ss = s + bad; *ss; ss++)
+ {
+ if (*ss == ' ' || *ss == '\\' || *(unsigned char *)ss < 32 || *ss == '(' || *ss == ')')
+ bad++;
+ if (*ss == '(')
+ paren = paren == 0 ? 1 : -1;
+ else if (*ss == ')')
+ {
+ paren = paren == 1 ? 0 : -1;
+ if (!paren)
+ parenbad += 2;
+ }
+ }
+ if (isname && ss - s > 4 && !strcmp(ss - 4, ":any"))
+ bad++;
+ if (!paren && !(bad - parenbad))
+ return s;
+
+ /* we need escaping! */
+ s2 = s2p = pool_alloctmpspace(pool, strlen(s) + bad * 2 + 1);
+ if (!strncmp(s, "namespace:", 10))
+ {
+ strcpy(s2p, "namespace\\3a");
+ s2p += 12;
+ s += 10;
+ }
+ ss = s;
+ for (; *ss; ss++)
+ {
+ *s2p++ = *ss;
+ if ((ss == s && strchr("[(<=>!", *s)) || *ss == ' ' || *ss == '\\' || *(unsigned char *)ss < 32 || *ss == '(' || *ss == ')')
+ {
+ s2p[-1] = '\\';
+ solv_bin2hex((unsigned char *)ss, 1, s2p);
+ s2p += 2;
+ }
+ }
+ *s2p = 0;
+ if (isname && s2p - s2 > 4 && !strcmp(s2p - 4, ":any"))
+ strcpy(s2p - 4, "\\3aany");
+ return s2;
+}
+
+struct oplist {
+ Id flags;
+ const char *opname;
+} oplist[] = {
+ { REL_EQ, "=" },
+ { REL_GT | REL_LT | REL_EQ, "<=>" },
+ { REL_LT | REL_EQ, "<=" },
+ { REL_GT | REL_EQ, ">=" },
+ { REL_GT, ">" },
+ { REL_GT | REL_LT, "<>" },
+ { REL_AND, "&" },
+ { REL_OR , "|" },
+ { REL_WITH , "+" },
+ { REL_WITHOUT , "-" },
+ { REL_NAMESPACE , "<NAMESPACE>" },
+ { REL_ARCH, "." },
+ { REL_MULTIARCH, "<MULTIARCH>" },
+ { REL_FILECONFLICT, "<FILECONFLICT>" },
+ { REL_COND, "<IF>" },
+ { REL_COMPAT, "compat >=" },
+ { REL_KIND, "<KIND>" },
+ { REL_ELSE, "<ELSE>" },
+ { REL_ERROR, "<ERROR>" },
+ { REL_UNLESS, "<UNLESS>" },
+ { REL_CONDA, "<CONDA>" },
+ { REL_LT, "<" },
+ { 0, 0 }
+};
+
+static char *
+testcase_dep2str_complex(Pool *pool, char *s, Id id, int addparens)
+{
+ Reldep *rd;
+ const char *s2;
+ int needparens;
+ struct oplist *op;
+
+ if (!ISRELDEP(id))
+ {
+ s2 = testcase_id2str(pool, id, 1);
+ s = pool_tmpappend(pool, s, s2, 0);
+ pool_freetmpspace(pool, s2);
+ return s;
+ }
+ rd = GETRELDEP(pool, id);
+
+ /* check for special shortcuts */
+ if (rd->flags == REL_NAMESPACE && !ISRELDEP(rd->name) && !strncmp(pool_id2str(pool, rd->name), "namespace:", 10))
+ {
+ s = pool_tmpappend(pool, s, pool_id2str(pool, rd->name), "(");
+ s = testcase_dep2str_complex(pool, s, rd->evr, 0);
+ return pool_tmpappend(pool, s, ")", 0);
+ }
+ if (rd->flags == REL_MULTIARCH && !ISRELDEP(rd->name) && rd->evr == ARCH_ANY)
+ {
+ /* append special :any suffix */
+ s2 = testcase_id2str(pool, rd->name, 1);
+ s = pool_tmpappend(pool, s, s2, ":any");
+ pool_freetmpspace(pool, s2);
+ return s;
+ }
+
+ needparens = 0;
+ if (ISRELDEP(rd->name))
+ {
+ Reldep *rd2 = GETRELDEP(pool, rd->name);
+ needparens = 1;
+ if (rd->flags > 7 && rd->flags != REL_COMPAT && rd2->flags && rd2->flags <= 7)
+ needparens = 0;
+ }
+
+ if (addparens)
+ s = pool_tmpappend(pool, s, "(", 0);
+ s = testcase_dep2str_complex(pool, s, rd->name, needparens);
+
+ for (op = oplist; op->flags; op++)
+ if (rd->flags == op->flags)
+ break;
+ if (op->flags)
+ {
+ s = pool_tmpappend(pool, s, " ", op->opname);
+ s = pool_tmpappend(pool, s, " ", 0);
+ }
+ else
+ {
+ char buf[64];
+ sprintf(buf, " <%u> ", rd->flags);
+ s = pool_tmpappend(pool, s, buf, 0);
+ }
+
+ needparens = 0;
+ if (ISRELDEP(rd->evr))
+ {
+ Reldep *rd2 = GETRELDEP(pool, rd->evr);
+ needparens = 1;
+ if (rd->flags > 7 && rd2->flags && rd2->flags <= 7)
+ needparens = 0;
+ if (rd->flags == REL_AND && rd2->flags == REL_AND)
+ needparens = 0; /* chain */
+ if (rd->flags == REL_OR && rd2->flags == REL_OR)
+ needparens = 0; /* chain */
+ if (rd->flags > 0 && rd->flags < 8 && rd2->flags == REL_COMPAT)
+ needparens = 0; /* chain */
+ }
+ if (!ISRELDEP(rd->evr))
+ {
+ s2 = testcase_id2str(pool, rd->evr, 0);
+ s = pool_tmpappend(pool, s, s2, 0);
+ pool_freetmpspace(pool, s2);
+ }
+ else
+ s = (char *)testcase_dep2str_complex(pool, s, rd->evr, needparens);
+ if (addparens)
+ s = pool_tmpappend(pool, s, ")", 0);
+ return s;
+}
+
+const char *
+testcase_dep2str(Pool *pool, Id id)
+{
+ char *s;
+ if (!ISRELDEP(id))
+ return testcase_id2str(pool, id, 1);
+ s = pool_alloctmpspace(pool, 1);
+ *s = 0;
+ return testcase_dep2str_complex(pool, s, id, 0);
+}
+
+
+/* Convert a simple string. Also handle the :any suffix */
+static Id
+testcase_str2dep_simple(Pool *pool, const char **sp, int isname)
+{
+ int haveesc = 0;
+ int paren = 0;
+ int isany = 0;
+ Id id;
+ const char *s;
+ for (s = *sp; *s; s++)
+ {
+ if (*s == '\\')
+ haveesc++;
+ if (*s == ' ' || *(unsigned char *)s < 32)
+ break;
+ if (*s == '(')
+ paren++;
+ if (*s == ')' && paren-- <= 0)
+ break;
+ }
+ if (isname && s - *sp > 4 && !strncmp(s - 4, ":any", 4))
+ {
+ isany = 1;
+ s -= 4;
+ }
+ if (!haveesc)
+ {
+ if (s - *sp == 6 && !strncmp(*sp, "<NULL>", 6))
+ id = 0;
+ else
+ id = pool_strn2id(pool, *sp, s - *sp, 1);
+ }
+ else if (s - *sp == 3 && !strncmp(*sp, "\\00", 3))
+ id = 1;
+ else
+ {
+ char buf[128], *bp, *bp2;
+ const char *sp2;
+ bp = s - *sp >= 128 ? solv_malloc(s - *sp + 1) : buf;
+ for (bp2 = bp, sp2 = *sp; sp2 < s;)
+ {
+ *bp2++ = *sp2++;
+ if (bp2[-1] == '\\')
+ solv_hex2bin(&sp2, (unsigned char *)bp2 - 1, 1);
+ }
+ *bp2 = 0;
+ id = pool_str2id(pool, bp, 1);
+ if (bp != buf)
+ solv_free(bp);
+ }
+ if (isany)
+ {
+ id = pool_rel2id(pool, id, ARCH_ANY, REL_MULTIARCH, 1);
+ s += 4;
+ }
+ *sp = s;
+ return id;
+}
+
+
+static Id
+testcase_str2dep_complex(Pool *pool, const char **sp, int relop)
+{
+ const char *s = *sp;
+ Id flags, id, id2, namespaceid = 0;
+ struct oplist *op;
+
+ if (!s)
+ return 0;
+ while (*s == ' ' || *s == '\t')
+ s++;
+ if (!strncmp(s, "namespace:", 10))
+ {
+ /* special namespace hack */
+ const char *s2;
+ for (s2 = s + 10; *s2 && *s2 != '('; s2++)
+ ;
+ if (*s2 == '(')
+ {
+ namespaceid = pool_strn2id(pool, s, s2 - s, 1);
+ s = s2;
+ }
+ }
+ if (*s == '(')
+ {
+ s++;
+ id = testcase_str2dep_complex(pool, &s, 0);
+ if (!s || *s != ')')
+ {
+ *sp = 0;
+ return 0;
+ }
+ s++;
+ }
+ else
+ id = testcase_str2dep_simple(pool, &s, relop ? 0 : 1);
+ if (namespaceid)
+ id = pool_rel2id(pool, namespaceid, id, REL_NAMESPACE, 1);
+
+ for (;;)
+ {
+ while (*s == ' ' || *s == '\t')
+ s++;
+ if (!*s || *s == ')' || (relop && strncmp(s, "compat >= ", 10) != 0))
+ {
+ *sp = s;
+ return id;
+ }
+
+ /* we have an op! Find the end */
+ flags = -1;
+ if (s[0] == '<' && (s[1] >= '0' && s[1] <= '9'))
+ {
+ const char *s2;
+ for (s2 = s + 1; *s2 >= '0' && *s2 <= '9'; s2++)
+ ;
+ if (*s2 == '>')
+ {
+ flags = strtoul(s + 1, 0, 10);
+ s = s2 + 1;
+ }
+ }
+ if (flags == -1)
+ {
+ for (op = oplist; op->flags; op++)
+ if (!strncmp(s, op->opname, strlen(op->opname)))
+ break;
+ if (!op->flags)
+ {
+ *sp = 0;
+ return 0;
+ }
+ flags = op->flags;
+ s += strlen(op->opname);
+ }
+ id2 = testcase_str2dep_complex(pool, &s, flags > 0 && flags < 8);
+ if (!s)
+ {
+ *sp = 0;
+ return 0;
+ }
+ id = pool_rel2id(pool, id, id2, flags, 1);
+ }
+}
+
+Id
+testcase_str2dep(Pool *pool, const char *s)
+{
+ Id id = testcase_str2dep_complex(pool, &s, 0);
+ return s && !*s ? id : 0;
+}
+
+static void
+writedeps(Repo *repo, FILE *fp, const char *tag, Id key, Solvable *s, Offset off)
+{
+ Pool *pool = repo->pool;
+ Id id, *dp;
+ int tagwritten = 0;
+ const char *idstr;
+
+ if (!off)
+ return;
+ dp = repo->idarraydata + off;
+ while ((id = *dp++) != 0)
+ {
+ if (key == SOLVABLE_REQUIRES && id == SOLVABLE_PREREQMARKER)
+ {
+ if (tagwritten)
+ fprintf(fp, "-%s\n", tag);
+ tagwritten = 0;
+ tag = "Prq:";
+ continue;
+ }
+ if (key == SOLVABLE_PROVIDES && id == SOLVABLE_FILEMARKER)
+ continue;
+ idstr = testcase_dep2str(pool, id);
+ if (!tagwritten)
+ {
+ fprintf(fp, "+%s\n", tag);
+ tagwritten = 1;
+ }
+ fprintf(fp, "%s\n", idstr);
+ }
+ if (tagwritten)
+ fprintf(fp, "-%s\n", tag);
+}
+
+static void
+writefilelist(Repo *repo, FILE *fp, const char *tag, Solvable *s)
+{
+ Pool *pool = repo->pool;
+ int tagwritten = 0;
+ Dataiterator di;
+
+ dataiterator_init(&di, pool, repo, s - pool->solvables, SOLVABLE_FILELIST, 0, 0);
+ while (dataiterator_step(&di))
+ {
+ const char *s = repodata_dir2str(di.data, di.kv.id, di.kv.str);
+ if (!tagwritten)
+ {
+ fprintf(fp, "+%s\n", tag);
+ tagwritten = 1;
+ }
+ fprintf(fp, "%s\n", s);
+ }
+ if (tagwritten)
+ fprintf(fp, "-%s\n", tag);
+ dataiterator_free(&di);
+}
+
+int
+testcase_write_testtags(Repo *repo, FILE *fp)
+{
+ Pool *pool = repo->pool;
+ Solvable *s;
+ Id p;
+ const char *name;
+ const char *evr;
+ const char *arch;
+ const char *release;
+ const char *tmp;
+ unsigned int ti;
+ Queue q;
+
+ fprintf(fp, "=Ver: 3.0\n");
+ queue_init(&q);
+ FOR_REPO_SOLVABLES(repo, p, s)
+ {
+ name = pool_id2str(pool, s->name);
+ evr = pool_id2str(pool, s->evr);
+ arch = s->arch ? pool_id2str(pool, s->arch) : "-";
+ release = strrchr(evr, '-');
+ if (!release)
+ release = evr + strlen(evr);
+ fprintf(fp, "=Pkg: %s %.*s %s %s\n", name, (int)(release - evr), evr, *release && release[1] ? release + 1 : "-", arch);
+ tmp = solvable_lookup_str(s, SOLVABLE_SUMMARY);
+ if (tmp)
+ fprintf(fp, "=Sum: %s\n", tmp);
+ writedeps(repo, fp, "Req:", SOLVABLE_REQUIRES, s, s->requires);
+ writedeps(repo, fp, "Prv:", SOLVABLE_PROVIDES, s, s->provides);
+ writedeps(repo, fp, "Obs:", SOLVABLE_OBSOLETES, s, s->obsoletes);
+ writedeps(repo, fp, "Con:", SOLVABLE_CONFLICTS, s, s->conflicts);
+ writedeps(repo, fp, "Rec:", SOLVABLE_RECOMMENDS, s, s->recommends);
+ writedeps(repo, fp, "Sup:", SOLVABLE_SUPPLEMENTS, s, s->supplements);
+ writedeps(repo, fp, "Sug:", SOLVABLE_SUGGESTS, s, s->suggests);
+ writedeps(repo, fp, "Enh:", SOLVABLE_ENHANCES, s, s->enhances);
+ if (solvable_lookup_idarray(s, SOLVABLE_PREREQ_IGNOREINST, &q))
+ {
+ int i;
+ fprintf(fp, "+Ipr:\n");
+ for (i = 0; i < q.count; i++)
+ fprintf(fp, "%s\n", testcase_dep2str(pool, q.elements[i]));
+ fprintf(fp, "-Ipr:\n");
+ }
+ if (s->vendor)
+ fprintf(fp, "=Vnd: %s\n", pool_id2str(pool, s->vendor));
+ if (solvable_lookup_idarray(s, SOLVABLE_BUILDFLAVOR, &q))
+ {
+ int i;
+ for (i = 0; i < q.count; i++)
+ fprintf(fp, "=Flv: %s\n", pool_id2str(pool, q.elements[i]));
+ }
+ tmp = solvable_lookup_str(s, SOLVABLE_BUILDVERSION);
+ if (tmp)
+ fprintf(fp, "=Bvr: %s\n", tmp);
+ ti = solvable_lookup_num(s, SOLVABLE_BUILDTIME, 0);
+ if (ti)
+ fprintf(fp, "=Tim: %u\n", ti);
+ writefilelist(repo, fp, "Fls:", s);
+ }
+ queue_free(&q);
+ return 0;
+}
+
+static inline Offset
+adddep(Repo *repo, Offset olddeps, char *str, Id marker)
+{
+ Id id = *str == '/' ? pool_str2id(repo->pool, str, 1) : testcase_str2dep(repo->pool, str);
+ return repo_addid_dep(repo, olddeps, id, marker);
+}
+
+static void
+finish_v2_solvable(Pool *pool, Repodata *data, Solvable *s, char *filelist, int nfilelist)
+{
+ if (nfilelist)
+ {
+ int l;
+ Id did;
+ for (l = 0; l < nfilelist; l += strlen(filelist + l) + 1)
+ {
+ char *p = strrchr(filelist + l, '/');
+ if (!p)
+ continue;
+ *p++ = 0;
+ did = repodata_str2dir(data, filelist + l, 1);
+ p[-1] = '/';
+ if (!did)
+ did = repodata_str2dir(data, "/", 1);
+ repodata_add_dirstr(data, s - pool->solvables, SOLVABLE_FILELIST, did, p);
+ }
+ }
+ repo_rewrite_suse_deps(s, 0);
+}
+
+/* stripped down version of susetags parser used for testcases */
+int
+testcase_add_testtags(Repo *repo, FILE *fp, int flags)
+{
+ Pool *pool = repo->pool;
+ char *line, *linep;
+ int aline;
+ int tag;
+ Repodata *data;
+ Solvable *s;
+ char *sp[5];
+ unsigned int t;
+ int intag;
+ char *filelist = 0;
+ int afilelist = 0;
+ int nfilelist = 0;
+ int tagsversion = 0;
+ int addselfprovides = 1; /* for compat reasons */
+
+ data = repo_add_repodata(repo, flags);
+ s = 0;
+ intag = 0;
+
+ aline = 1024;
+ line = solv_malloc(aline);
+ linep = line;
+ for (;;)
+ {
+ if (linep - line + 16 > aline)
+ {
+ aline = linep - line;
+ line = solv_realloc(line, aline + 512);
+ linep = line + aline;
+ aline += 512;
+ }
+ if (!fgets(linep, aline - (linep - line), fp))
+ break;
+ linep += strlen(linep);
+ if (linep == line || linep[-1] != '\n')
+ continue;
+ linep[-1] = 0;
+ linep = line + intag;
+ if (intag)
+ {
+ if (line[intag] == '-' && !strncmp(line + 1, line + intag + 1, intag - 2))
+ {
+ intag = 0;
+ linep = line;
+ continue;
+ }
+ }
+ else if (line[0] == '+' && line[1] && line[1] != ':')
+ {
+ char *tagend = strchr(line, ':');
+ if (!tagend)
+ continue;
+ line[0] = '=';
+ tagend[1] = ' ';
+ intag = tagend + 2 - line;
+ linep = line + intag;
+ continue;
+ }
+ if (*line != '=' || !line[1] || !line[2] || !line[3] || line[4] != ':')
+ continue;
+ tag = line[1] << 16 | line[2] << 8 | line[3];
+ /* tags that do not need a solvable */
+ switch(tag)
+ {
+ case 'V' << 16 | 'e' << 8 | 'r':
+ tagsversion = atoi(line + 6);
+ addselfprovides = tagsversion < 3 || strstr(line + 6, "addselfprovides") != 0;
+ continue;
+ case 'P' << 16 | 'k' << 8 | 'g':
+ if (s)
+ {
+ if (tagsversion == 2)
+ finish_v2_solvable(pool, data, s, filelist, nfilelist);
+ if (addselfprovides && s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
+ s->provides = repo_addid_dep(s->repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
+ }
+ nfilelist = 0;
+ if (split(line + 5, sp, 5) != 4)
+ break;
+ s = pool_id2solvable(pool, repo_add_solvable(repo));
+ s->name = pool_str2id(pool, sp[0], 1);
+ /* join back version and release */
+ if (sp[2] && !(sp[2][0] == '-' && !sp[2][1]))
+ sp[2][-1] = '-';
+ s->evr = makeevr(pool, sp[1]);
+ s->arch = strcmp(sp[3], "-") ? pool_str2id(pool, sp[3], 1) : 0;
+ continue;
+ default:
+ break;
+ }
+ if (!s)
+ continue;
+ /* tags that need a solvable */
+ switch(tag)
+ {
+ case 'S' << 16 | 'u' << 8 | 'm':
+ repodata_set_str(data, s - pool->solvables, SOLVABLE_SUMMARY, line + 6);
+ break;
+ case 'V' << 16 | 'n' << 8 | 'd':
+ s->vendor = pool_str2id(pool, line + 6, 1);
+ break;
+ case 'T' << 16 | 'i' << 8 | 'm':
+ t = atoi(line + 6);
+ if (t)
+ repodata_set_num(data, s - pool->solvables, SOLVABLE_BUILDTIME, t);
+ break;
+ case 'R' << 16 | 'e' << 8 | 'q':
+ s->requires = adddep(repo, s->requires, line + 6, -SOLVABLE_PREREQMARKER);
+ break;
+ case 'P' << 16 | 'r' << 8 | 'q':
+ s->requires = adddep(repo, s->requires, line + 6, SOLVABLE_PREREQMARKER);
+ break;
+ case 'P' << 16 | 'r' << 8 | 'v':
+ /* version 2 had the file list at the end of the provides */
+ if (tagsversion == 2)
+ {
+ if (line[6] == '/')
+ {
+ int l = strlen(line + 6) + 1;
+ if (nfilelist + l > afilelist)
+ {
+ afilelist = nfilelist + l + 512;
+ filelist = solv_realloc(filelist, afilelist);
+ }
+ memcpy(filelist + nfilelist, line + 6, l);
+ nfilelist += l;
+ break;
+ }
+ if (nfilelist)
+ {
+ int l;
+ for (l = 0; l < nfilelist; l += strlen(filelist + l) + 1)
+ s->provides = repo_addid_dep(repo, s->provides, pool_str2id(pool, filelist + l, 1), 0);
+ nfilelist = 0;
+ }
+ }
+ s->provides = adddep(repo, s->provides, line + 6, 0);
+ break;
+ case 'F' << 16 | 'l' << 8 | 's':
+ {
+ char *p = strrchr(line + 6, '/');
+ Id did;
+ if (!p)
+ break;
+ *p++ = 0;
+ did = repodata_str2dir(data, line + 6, 1);
+ if (!did)
+ did = repodata_str2dir(data, "/", 1);
+ repodata_add_dirstr(data, s - pool->solvables, SOLVABLE_FILELIST, did, p);
+ break;
+ }
+ case 'O' << 16 | 'b' << 8 | 's':
+ s->obsoletes = adddep(repo, s->obsoletes, line + 6, 0);
+ break;
+ case 'C' << 16 | 'o' << 8 | 'n':
+ s->conflicts = adddep(repo, s->conflicts, line + 6, 0);
+ break;
+ case 'R' << 16 | 'e' << 8 | 'c':
+ s->recommends = adddep(repo, s->recommends, line + 6, 0);
+ break;
+ case 'S' << 16 | 'u' << 8 | 'p':
+ s->supplements = adddep(repo, s->supplements, line + 6, 0);
+ break;
+ case 'S' << 16 | 'u' << 8 | 'g':
+ s->suggests = adddep(repo, s->suggests, line + 6, 0);
+ break;
+ case 'E' << 16 | 'n' << 8 | 'h':
+ s->enhances = adddep(repo, s->enhances, line + 6, 0);
+ break;
+ case 'I' << 16 | 'p' << 8 | 'r':
+ {
+ Id id = line[6] == '/' ? pool_str2id(pool, line + 6, 1) : testcase_str2dep(pool, line + 6);
+ repodata_add_idarray(data, s - pool->solvables, SOLVABLE_PREREQ_IGNOREINST, id);
+ break;
+ }
+ case 'F' << 16 | 'l' << 8 | 'v':
+ repodata_add_poolstr_array(data, s - pool->solvables, SOLVABLE_BUILDFLAVOR, line + 6);
+ break;
+ case 'B' << 16 | 'v' << 8 | 'r':
+ repodata_set_str(data, s - pool->solvables, SOLVABLE_BUILDVERSION, line + 6);
+ break;
+ default:
+ break;
+ }
+ }
+ if (s)
+ {
+ if (tagsversion == 2)
+ finish_v2_solvable(pool, data, s, filelist, nfilelist);
+ if (addselfprovides && s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
+ s->provides = repo_addid_dep(s->repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
+ }
+ solv_free(line);
+ solv_free(filelist);
+ repodata_free_dircache(data);
+ if (!(flags & REPO_NO_INTERNALIZE))
+ repodata_internalize(data);
+ return 0;
+}
diff --git a/ext/solv_jsonparser.c b/ext/solv_jsonparser.c
index 053ee6f..ea31923 100644
--- a/ext/solv_jsonparser.c
+++ b/ext/solv_jsonparser.c
@@ -15,7 +15,7 @@
#include "util.h"
#include "solv_jsonparser.h"
-struct solv_jsonparser *
+void
jsonparser_init(struct solv_jsonparser *jp, FILE *fp)
{
memset(jp, 0, sizeof(*jp));
@@ -24,14 +24,13 @@ jsonparser_init(struct solv_jsonparser *jp, FILE *fp)
jp->line = jp->nextline = 1;
jp->nextc = ' ';
queue_init(&jp->stateq);
- return jp;
}
-struct solv_jsonparser *
+void
jsonparser_free(struct solv_jsonparser *jp)
{
+ solv_free(jp->space);
queue_free(&jp->stateq);
- return 0;
}
static void
@@ -73,18 +72,18 @@ static int
skipspace(struct solv_jsonparser *jp)
{
int c = jp->nextc;
+ jp->nextc = ' ';
while (c == ' ' || c == '\t' || c == '\r' || c == '\n')
c = nextc(jp);
jp->line = jp->nextline;
- return (jp->nextc = c);
+ return c;
}
static int
-parseliteral(struct solv_jsonparser *jp)
+parseliteral(struct solv_jsonparser *jp, int c)
{
size_t nspace = jp->nspace;
- int c;
- savec(jp, jp->nextc);
+ savec(jp, c);
for (;;)
{
c = nextc(jp);
@@ -104,10 +103,9 @@ parseliteral(struct solv_jsonparser *jp)
}
static int
-parsenumber(struct solv_jsonparser *jp)
+parsenumber(struct solv_jsonparser *jp, int c)
{
- int c;
- savec(jp, jp->nextc);
+ savec(jp, c);
for (;;)
{
c = nextc(jp);
@@ -194,7 +192,6 @@ parsestring(struct solv_jsonparser *jp)
}
savec(jp, c);
}
- jp->nextc = ' ';
savec(jp, 0);
return JP_STRING;
}
@@ -206,10 +203,9 @@ parsevalue(struct solv_jsonparser *jp)
if (c == '"')
return parsestring(jp);
if ((c >= '0' && c <= '9') || c == '+' || c == '-' || c == '.')
- return parsenumber(jp);
+ return parsenumber(jp, c);
if ((c >= 'a' && c <= 'z'))
- return parseliteral(jp);
- jp->nextc = ' ';
+ return parseliteral(jp, c);
if (c == '[')
return JP_ARRAY;
if (c == '{')
@@ -252,7 +248,6 @@ jsonparser_parse(struct solv_jsonparser *jp)
return JP_ERROR;
if (skipspace(jp) != ':')
return JP_ERROR;
- jp->nextc = ' ';
type = parsevalue(jp);
if (type == JP_OBJECT_END || type == JP_ARRAY_END)
return JP_ERROR;
@@ -264,7 +259,7 @@ jsonparser_parse(struct solv_jsonparser *jp)
jp->value = jp->space + nspace;
jp->valuelen = jp->nspace - nspace - 1;
}
- if (type == JP_ARRAY || type == JP_OBJECT)
+ if (type == JP_OBJECT || type == JP_ARRAY)
{
queue_push(&jp->stateq, jp->state);
jp->state = type;
@@ -272,9 +267,9 @@ jsonparser_parse(struct solv_jsonparser *jp)
else if (jp->state == JP_OBJECT || jp->state == JP_ARRAY)
{
int c = skipspace(jp);
- if (c == ',')
- jp->nextc = ' ';
- else if (c != (jp->state == JP_OBJECT ? '}' : ']'))
+ if (c == (jp->state == JP_OBJECT ? '}' : ']'))
+ jp->nextc = c;
+ else if (c != ',')
return JP_ERROR;
}
return type;
diff --git a/ext/solv_jsonparser.h b/ext/solv_jsonparser.h
index d58d9e3..f728fb6 100644
--- a/ext/solv_jsonparser.h
+++ b/ext/solv_jsonparser.h
@@ -20,7 +20,7 @@ struct solv_jsonparser {
char *value;
size_t valuelen;
- int state;
+ int state; /* START, END, OBJECT, ARRAY */
Queue stateq;
int nextc;
int nextline;
@@ -41,8 +41,8 @@ struct solv_jsonparser {
#define JP_ARRAY 8
#define JP_ARRAY_END 9
-struct solv_jsonparser *jsonparser_init(struct solv_jsonparser *jp, FILE *fp);
-struct solv_jsonparser *jsonparser_free(struct solv_jsonparser *jp);
+void jsonparser_init(struct solv_jsonparser *jp, FILE *fp);
+void jsonparser_free(struct solv_jsonparser *jp);
int jsonparser_parse(struct solv_jsonparser *jp);
int jsonparser_skip(struct solv_jsonparser *jp, int type);
diff --git a/ext/solv_xfopen.c b/ext/solv_xfopen.c
index 343aed8..9aab68b 100644
--- a/ext/solv_xfopen.c
+++ b/ext/solv_xfopen.c
@@ -12,9 +12,14 @@
#include <string.h>
#include <fcntl.h>
+#ifdef _WIN32
+ #include "fmemopen.c"
+#endif
+
#include "solv_xfopen.h"
#include "util.h"
+#ifndef WITHOUT_COOKIEOPEN
static FILE *cookieopen(void *cookie, const char *mode,
ssize_t (*cread)(void *, char *, size_t),
@@ -602,6 +607,16 @@ static inline FILE *myzchunkfdopen(int fd, const char *mode)
#endif /* ENABLE_ZCHUNK_COMPRESSION */
+#else
+/* no cookies no compression */
+#undef ENABLE_ZLIB_COMPRESSION
+#undef ENABLE_LZMA_COMPRESSION
+#undef ENABLE_BZIP2_COMPRESSION
+#undef ENABLE_ZSTD_COMPRESSION
+#undef ENABLE_ZCHUNK_COMPRESSION
+#endif
+
+
FILE *
solv_xfopen(const char *fn, const char *mode)
@@ -664,7 +679,15 @@ solv_xfopen_fd(const char *fn, int fd, const char *mode)
suf = fn ? strrchr(fn, '.') : 0;
if (!mode)
{
+ #ifndef _WIN32
int fl = fcntl(fd, F_GETFL, 0);
+ #else
+ HANDLE handle = (HANDLE) _get_osfhandle(fd);
+ BY_HANDLE_FILE_INFORMATION file_info;
+ if (!GetFileInformationByHandle(handle, &file_info))
+ return 0;
+ int fl = file_info.dwFileAttributes;
+ #endif
if (fl == -1)
return 0;
fl &= O_RDONLY|O_WRONLY|O_RDWR;
@@ -759,6 +782,9 @@ solv_xfopen_iscompressed(const char *fn)
return 0;
}
+
+#ifndef WITHOUT_COOKIEOPEN
+
struct bufcookie {
char **bufp;
size_t *buflp;
@@ -835,3 +861,33 @@ solv_xfopen_buf(const char *fn, char **bufp, size_t *buflp, const char *mode)
}
return fp;
}
+
+#else
+
+FILE *
+solv_xfopen_buf(const char *fn, char **bufp, size_t *buflp, const char *mode)
+{
+ FILE *fp;
+ size_t l;
+ if (*mode != 'r')
+ return 0;
+ l = buflp ? *buflp : strlen(*bufp);
+ if (!strcmp(mode, "rf"))
+ {
+ if (!(fp = fmemopen(0, l, "r+")))
+ return 0;
+ if (l && fwrite(*bufp, l, 1, fp) != 1)
+ {
+ fclose(fp);
+ return 0;
+ }
+ solv_free(*bufp);
+ rewind(fp);
+ }
+ else
+ fp = fmemopen(*bufp, l, "r");
+ return fp;
+}
+
+#endif
+
diff --git a/ext/testcase.c b/ext/testcase.c
index 8edf58f..fcebcfa 100644
--- a/ext/testcase.c
+++ b/ext/testcase.c
@@ -28,8 +28,17 @@
#include "ext/repo_helix.h"
#endif
-#define DISABLE_JOIN2
-#include "tools_util.h"
+#ifdef _WIN32
+ #include <direct.h>
+#endif
+
+/* see repo_testcase.c */
+struct oplist {
+ Id flags;
+ const char *opname;
+};
+extern struct oplist oplist[];
+
static struct job2str {
Id job;
@@ -88,6 +97,7 @@ static struct resultflags2str {
{ TESTCASE_RESULT_REASON, "reason" },
{ TESTCASE_RESULT_CLEANDEPS, "cleandeps" },
{ TESTCASE_RESULT_JOBS, "jobs" },
+ { TESTCASE_RESULT_USERINSTALLED, "userinstalled" },
{ 0, 0 }
};
@@ -322,347 +332,6 @@ strqueue_diff(Strqueue *sq1, Strqueue *sq2, Strqueue *osq)
strqueue_pushjoin(osq, "+", sq2->str[j++], 0);
}
-
-static const char *
-testcase_id2str(Pool *pool, Id id, int isname)
-{
- const char *s = pool_id2str(pool, id);
- const char *ss;
- char *s2, *s2p;
- int bad = 0, paren = 0, parenbad = 0;
-
- if (id == 0)
- return "<NULL>";
- if (id == 1)
- return "\\00";
- if (strchr("[(<=>!", *s))
- bad++;
- if (!strncmp(s, "namespace:", 10))
- bad++;
- for (ss = s + bad; *ss; ss++)
- {
- if (*ss == ' ' || *ss == '\\' || *(unsigned char *)ss < 32 || *ss == '(' || *ss == ')')
- bad++;
- if (*ss == '(')
- paren = paren == 0 ? 1 : -1;
- else if (*ss == ')')
- {
- paren = paren == 1 ? 0 : -1;
- if (!paren)
- parenbad += 2;
- }
- }
- if (isname && ss - s > 4 && !strcmp(ss - 4, ":any"))
- bad++;
- if (!paren && !(bad - parenbad))
- return s;
-
- /* we need escaping! */
- s2 = s2p = pool_alloctmpspace(pool, strlen(s) + bad * 2 + 1);
- if (!strncmp(s, "namespace:", 10))
- {
- strcpy(s2p, "namespace\\3a");
- s2p += 12;
- s += 10;
- }
- ss = s;
- for (; *ss; ss++)
- {
- *s2p++ = *ss;
- if ((ss == s && strchr("[(<=>!", *s)) || *ss == ' ' || *ss == '\\' || *(unsigned char *)ss < 32 || *ss == '(' || *ss == ')')
- {
- s2p[-1] = '\\';
- solv_bin2hex((unsigned char *)ss, 1, s2p);
- s2p += 2;
- }
- }
- *s2p = 0;
- if (isname && s2p - s2 > 4 && !strcmp(s2p - 4, ":any"))
- strcpy(s2p - 4, "\\3aany");
- return s2;
-}
-
-struct oplist {
- Id flags;
- const char *opname;
-} oplist[] = {
- { REL_EQ, "=" },
- { REL_GT | REL_LT | REL_EQ, "<=>" },
- { REL_LT | REL_EQ, "<=" },
- { REL_GT | REL_EQ, ">=" },
- { REL_GT, ">" },
- { REL_GT | REL_LT, "<>" },
- { REL_AND, "&" },
- { REL_OR , "|" },
- { REL_WITH , "+" },
- { REL_WITHOUT , "-" },
- { REL_NAMESPACE , "<NAMESPACE>" },
- { REL_ARCH, "." },
- { REL_MULTIARCH, "<MULTIARCH>" },
- { REL_FILECONFLICT, "<FILECONFLICT>" },
- { REL_COND, "<IF>" },
- { REL_COMPAT, "compat >=" },
- { REL_KIND, "<KIND>" },
- { REL_ELSE, "<ELSE>" },
- { REL_ERROR, "<ERROR>" },
- { REL_UNLESS, "<UNLESS>" },
- { REL_CONDA, "<CONDA>" },
- { REL_LT, "<" },
- { 0, 0 }
-};
-
-static char *
-testcase_dep2str_complex(Pool *pool, char *s, Id id, int addparens)
-{
- Reldep *rd;
- const char *s2;
- int needparens;
- struct oplist *op;
-
- if (!ISRELDEP(id))
- {
- s2 = testcase_id2str(pool, id, 1);
- s = pool_tmpappend(pool, s, s2, 0);
- pool_freetmpspace(pool, s2);
- return s;
- }
- rd = GETRELDEP(pool, id);
-
- /* check for special shortcuts */
- if (rd->flags == REL_NAMESPACE && !ISRELDEP(rd->name) && !strncmp(pool_id2str(pool, rd->name), "namespace:", 10))
- {
- s = pool_tmpappend(pool, s, pool_id2str(pool, rd->name), "(");
- s = testcase_dep2str_complex(pool, s, rd->evr, 0);
- return pool_tmpappend(pool, s, ")", 0);
- }
- if (rd->flags == REL_MULTIARCH && !ISRELDEP(rd->name) && rd->evr == ARCH_ANY)
- {
- /* append special :any suffix */
- s2 = testcase_id2str(pool, rd->name, 1);
- s = pool_tmpappend(pool, s, s2, ":any");
- pool_freetmpspace(pool, s2);
- return s;
- }
-
- needparens = 0;
- if (ISRELDEP(rd->name))
- {
- Reldep *rd2 = GETRELDEP(pool, rd->name);
- needparens = 1;
- if (rd->flags > 7 && rd->flags != REL_COMPAT && rd2->flags && rd2->flags <= 7)
- needparens = 0;
- }
-
- if (addparens)
- s = pool_tmpappend(pool, s, "(", 0);
- s = testcase_dep2str_complex(pool, s, rd->name, needparens);
-
- for (op = oplist; op->flags; op++)
- if (rd->flags == op->flags)
- break;
- if (op->flags)
- {
- s = pool_tmpappend(pool, s, " ", op->opname);
- s = pool_tmpappend(pool, s, " ", 0);
- }
- else
- {
- char buf[64];
- sprintf(buf, " <%u> ", rd->flags);
- s = pool_tmpappend(pool, s, buf, 0);
- }
-
- needparens = 0;
- if (ISRELDEP(rd->evr))
- {
- Reldep *rd2 = GETRELDEP(pool, rd->evr);
- needparens = 1;
- if (rd->flags > 7 && rd2->flags && rd2->flags <= 7)
- needparens = 0;
- if (rd->flags == REL_AND && rd2->flags == REL_AND)
- needparens = 0; /* chain */
- if (rd->flags == REL_OR && rd2->flags == REL_OR)
- needparens = 0; /* chain */
- if (rd->flags > 0 && rd->flags < 8 && rd2->flags == REL_COMPAT)
- needparens = 0; /* chain */
- }
- if (!ISRELDEP(rd->evr))
- {
- s2 = testcase_id2str(pool, rd->evr, 0);
- s = pool_tmpappend(pool, s, s2, 0);
- pool_freetmpspace(pool, s2);
- }
- else
- s = (char *)testcase_dep2str_complex(pool, s, rd->evr, needparens);
- if (addparens)
- s = pool_tmpappend(pool, s, ")", 0);
- return s;
-}
-
-const char *
-testcase_dep2str(Pool *pool, Id id)
-{
- char *s;
- if (!ISRELDEP(id))
- return testcase_id2str(pool, id, 1);
- s = pool_alloctmpspace(pool, 1);
- *s = 0;
- return testcase_dep2str_complex(pool, s, id, 0);
-}
-
-
-/* Convert a simple string. Also handle the :any suffix */
-static Id
-testcase_str2dep_simple(Pool *pool, const char **sp, int isname)
-{
- int haveesc = 0;
- int paren = 0;
- int isany = 0;
- Id id;
- const char *s;
- for (s = *sp; *s; s++)
- {
- if (*s == '\\')
- haveesc++;
- if (*s == ' ' || *(unsigned char *)s < 32)
- break;
- if (*s == '(')
- paren++;
- if (*s == ')' && paren-- <= 0)
- break;
- }
- if (isname && s - *sp > 4 && !strncmp(s - 4, ":any", 4))
- {
- isany = 1;
- s -= 4;
- }
- if (!haveesc)
- {
- if (s - *sp == 6 && !strncmp(*sp, "<NULL>", 6))
- id = 0;
- else
- id = pool_strn2id(pool, *sp, s - *sp, 1);
- }
- else if (s - *sp == 3 && !strncmp(*sp, "\\00", 3))
- id = 1;
- else
- {
- char buf[128], *bp, *bp2;
- const char *sp2;
- bp = s - *sp >= 128 ? solv_malloc(s - *sp + 1) : buf;
- for (bp2 = bp, sp2 = *sp; sp2 < s;)
- {
- *bp2++ = *sp2++;
- if (bp2[-1] == '\\')
- solv_hex2bin(&sp2, (unsigned char *)bp2 - 1, 1);
- }
- *bp2 = 0;
- id = pool_str2id(pool, bp, 1);
- if (bp != buf)
- solv_free(bp);
- }
- if (isany)
- {
- id = pool_rel2id(pool, id, ARCH_ANY, REL_MULTIARCH, 1);
- s += 4;
- }
- *sp = s;
- return id;
-}
-
-
-static Id
-testcase_str2dep_complex(Pool *pool, const char **sp, int relop)
-{
- const char *s = *sp;
- Id flags, id, id2, namespaceid = 0;
- struct oplist *op;
-
- if (!s)
- return 0;
- while (*s == ' ' || *s == '\t')
- s++;
- if (!strncmp(s, "namespace:", 10))
- {
- /* special namespace hack */
- const char *s2;
- for (s2 = s + 10; *s2 && *s2 != '('; s2++)
- ;
- if (*s2 == '(')
- {
- namespaceid = pool_strn2id(pool, s, s2 - s, 1);
- s = s2;
- }
- }
- if (*s == '(')
- {
- s++;
- id = testcase_str2dep_complex(pool, &s, 0);
- if (!s || *s != ')')
- {
- *sp = 0;
- return 0;
- }
- s++;
- }
- else
- id = testcase_str2dep_simple(pool, &s, relop ? 0 : 1);
- if (namespaceid)
- id = pool_rel2id(pool, namespaceid, id, REL_NAMESPACE, 1);
-
- for (;;)
- {
- while (*s == ' ' || *s == '\t')
- s++;
- if (!*s || *s == ')' || (relop && strncmp(s, "compat >= ", 10) != 0))
- {
- *sp = s;
- return id;
- }
-
- /* we have an op! Find the end */
- flags = -1;
- if (s[0] == '<' && (s[1] >= '0' && s[1] <= '9'))
- {
- const char *s2;
- for (s2 = s + 1; *s2 >= '0' && *s2 <= '9'; s2++)
- ;
- if (*s2 == '>')
- {
- flags = strtoul(s + 1, 0, 10);
- s = s2 + 1;
- }
- }
- if (flags == -1)
- {
- for (op = oplist; op->flags; op++)
- if (!strncmp(s, op->opname, strlen(op->opname)))
- break;
- if (!op->flags)
- {
- *sp = 0;
- return 0;
- }
- flags = op->flags;
- s += strlen(op->opname);
- }
- id2 = testcase_str2dep_complex(pool, &s, flags > 0 && flags < 8);
- if (!s)
- {
- *sp = 0;
- return 0;
- }
- id = pool_rel2id(pool, id, id2, flags, 1);
- }
-}
-
-Id
-testcase_str2dep(Pool *pool, const char *s)
-{
- Id id = testcase_str2dep_complex(pool, &s, 0);
- return s && !*s ? id : 0;
-}
-
/**********************************************************/
const char *
@@ -699,17 +368,30 @@ testcase_solvid2str(Pool *pool, Id p)
e = pool_id2str(pool, s->evr);
a = pool_id2str(pool, s->arch);
str = pool_alloctmpspace(pool, strlen(n) + strlen(e) + strlen(a) + 3);
- sprintf(str, "%s-%s.%s", n, e, a);
+ sprintf(str, "%s-%s", n, e);
+ if (solvable_lookup_type(s, SOLVABLE_BUILDFLAVOR))
+ {
+ Queue flavorq;
+ int i;
+
+ queue_init(&flavorq);
+ solvable_lookup_idarray(s, SOLVABLE_BUILDFLAVOR, &flavorq);
+ for (i = 0; i < flavorq.count; i++)
+ str = pool_tmpappend(pool, str, "-", pool_id2str(pool, flavorq.elements[i]));
+ queue_free(&flavorq);
+ }
+ if (s->arch)
+ str = pool_tmpappend(pool, str, ".", a);
if (!s->repo)
return pool_tmpappend(pool, str, "@", 0);
if (s->repo->name)
{
int l = strlen(str);
- char *str2 = pool_tmpappend(pool, str, "@", s->repo->name);
- for (; str2[l]; l++)
- if (str2[l] == ' ' || str2[l] == '\t')
- str2[l] = '_';
- return str2;
+ str = pool_tmpappend(pool, str, "@", s->repo->name);
+ for (; str[l]; l++)
+ if (str[l] == ' ' || str[l] == '\t')
+ str[l] = '_';
+ return str;
}
sprintf(buf, "@#%d", s->repo->repoid);
return pool_tmpappend(pool, str, buf, 0);
@@ -754,6 +436,47 @@ testcase_str2repo(Pool *pool, const char *str)
return repo;
}
+/* check evr and buildflavors */
+static int
+str2solvid_check(Pool *pool, Solvable *s, const char *start, const char *end, Id evrid)
+{
+ if (!solvable_lookup_type(s, SOLVABLE_BUILDFLAVOR))
+ {
+ /* just check the evr */
+ return evrid && s->evr == evrid;
+ }
+ else
+ {
+ Queue flavorq;
+ int i;
+
+ queue_init(&flavorq);
+ solvable_lookup_idarray(s, SOLVABLE_BUILDFLAVOR, &flavorq);
+ queue_unshift(&flavorq, s->evr);
+ for (i = 0; i < flavorq.count; i++)
+ {
+ const char *part = pool_id2str(pool, flavorq.elements[i]);
+ size_t partl = strlen(part);
+ if (start + partl > end || strncmp(start, part, partl) != 0)
+ break;
+ start += partl;
+ if (i + 1 < flavorq.count)
+ {
+ if (start >= end || *start != '-')
+ break;
+ start++;
+ }
+ }
+ if (i < flavorq.count)
+ {
+ queue_free(&flavorq);
+ return 0;
+ }
+ queue_free(&flavorq);
+ return start == end;
+ }
+}
+
Id
testcase_str2solvid(Pool *pool, const char *str)
{
@@ -793,19 +516,18 @@ testcase_str2solvid(Pool *pool, const char *str)
if (!nid)
continue;
evrid = pool_strn2id(pool, str + i + 1, repostart - (i + 1), 0);
- if (!evrid)
- continue;
/* first check whatprovides */
FOR_PROVIDES(p, pp, nid)
{
Solvable *s = pool->solvables + p;
- if (s->name != nid || s->evr != evrid)
+ if (s->name != nid)
continue;
if (repo && s->repo != repo)
continue;
if (arch && s->arch != arch)
continue;
- return p;
+ if (str2solvid_check(pool, s, str + i + 1, str + repostart, evrid))
+ return p;
}
/* maybe it's not installable and thus not in whatprovides. do a slow search */
if (repo)
@@ -813,11 +535,12 @@ testcase_str2solvid(Pool *pool, const char *str)
Solvable *s;
FOR_REPO_SOLVABLES(repo, p, s)
{
- if (s->name != nid || s->evr != evrid)
+ if (s->name != nid)
continue;
if (arch && s->arch != arch)
continue;
- return p;
+ if (str2solvid_check(pool, s, str + i + 1, str + repostart, evrid))
+ return p;
}
}
else
@@ -825,11 +548,12 @@ testcase_str2solvid(Pool *pool, const char *str)
FOR_POOL_SOLVABLES(p)
{
Solvable *s = pool->solvables + p;
- if (s->name != nid || s->evr != evrid)
+ if (s->name != nid)
continue;
if (arch && s->arch != arch)
continue;
- return p;
+ if (str2solvid_check(pool, s, str + i + 1, str + repostart, evrid))
+ return p;
}
}
}
@@ -1180,347 +904,6 @@ addselectionjob(Pool *pool, char **pieces, int npieces, Queue *jobqueue, int typ
return r;
}
-static void
-writedeps(Repo *repo, FILE *fp, const char *tag, Id key, Solvable *s, Offset off)
-{
- Pool *pool = repo->pool;
- Id id, *dp;
- int tagwritten = 0;
- const char *idstr;
-
- if (!off)
- return;
- dp = repo->idarraydata + off;
- while ((id = *dp++) != 0)
- {
- if (key == SOLVABLE_REQUIRES && id == SOLVABLE_PREREQMARKER)
- {
- if (tagwritten)
- fprintf(fp, "-%s\n", tag);
- tagwritten = 0;
- tag = "Prq:";
- continue;
- }
- if (key == SOLVABLE_PROVIDES && id == SOLVABLE_FILEMARKER)
- continue;
- idstr = testcase_dep2str(pool, id);
- if (!tagwritten)
- {
- fprintf(fp, "+%s\n", tag);
- tagwritten = 1;
- }
- fprintf(fp, "%s\n", idstr);
- }
- if (tagwritten)
- fprintf(fp, "-%s\n", tag);
-}
-
-static void
-writefilelist(Repo *repo, FILE *fp, const char *tag, Solvable *s)
-{
- Pool *pool = repo->pool;
- int tagwritten = 0;
- Dataiterator di;
-
- dataiterator_init(&di, pool, repo, s - pool->solvables, SOLVABLE_FILELIST, 0, 0);
- while (dataiterator_step(&di))
- {
- const char *s = repodata_dir2str(di.data, di.kv.id, di.kv.str);
- if (!tagwritten)
- {
- fprintf(fp, "+%s\n", tag);
- tagwritten = 1;
- }
- fprintf(fp, "%s\n", s);
- }
- if (tagwritten)
- fprintf(fp, "-%s\n", tag);
- dataiterator_free(&di);
-}
-
-int
-testcase_write_testtags(Repo *repo, FILE *fp)
-{
- Pool *pool = repo->pool;
- Solvable *s;
- Id p;
- const char *name;
- const char *evr;
- const char *arch;
- const char *release;
- const char *tmp;
- unsigned int ti;
- Queue q;
-
- fprintf(fp, "=Ver: 3.0\n");
- queue_init(&q);
- FOR_REPO_SOLVABLES(repo, p, s)
- {
- name = pool_id2str(pool, s->name);
- evr = pool_id2str(pool, s->evr);
- arch = pool_id2str(pool, s->arch);
- release = strrchr(evr, '-');
- if (!release)
- release = evr + strlen(evr);
- fprintf(fp, "=Pkg: %s %.*s %s %s\n", name, (int)(release - evr), evr, *release && release[1] ? release + 1 : "-", arch);
- tmp = solvable_lookup_str(s, SOLVABLE_SUMMARY);
- if (tmp)
- fprintf(fp, "=Sum: %s\n", tmp);
- writedeps(repo, fp, "Req:", SOLVABLE_REQUIRES, s, s->requires);
- writedeps(repo, fp, "Prv:", SOLVABLE_PROVIDES, s, s->provides);
- writedeps(repo, fp, "Obs:", SOLVABLE_OBSOLETES, s, s->obsoletes);
- writedeps(repo, fp, "Con:", SOLVABLE_CONFLICTS, s, s->conflicts);
- writedeps(repo, fp, "Rec:", SOLVABLE_RECOMMENDS, s, s->recommends);
- writedeps(repo, fp, "Sup:", SOLVABLE_SUPPLEMENTS, s, s->supplements);
- writedeps(repo, fp, "Sug:", SOLVABLE_SUGGESTS, s, s->suggests);
- writedeps(repo, fp, "Enh:", SOLVABLE_ENHANCES, s, s->enhances);
- if (solvable_lookup_idarray(s, SOLVABLE_PREREQ_IGNOREINST, &q))
- {
- int i;
- fprintf(fp, "+Ipr:\n");
- for (i = 0; i < q.count; i++)
- fprintf(fp, "%s\n", testcase_dep2str(pool, q.elements[i]));
- fprintf(fp, "-Ipr:\n");
- }
- if (s->vendor)
- fprintf(fp, "=Vnd: %s\n", pool_id2str(pool, s->vendor));
- ti = solvable_lookup_num(s, SOLVABLE_BUILDTIME, 0);
- if (ti)
- fprintf(fp, "=Tim: %u\n", ti);
- writefilelist(repo, fp, "Fls:", s);
- }
- queue_free(&q);
- return 0;
-}
-
-static inline Offset
-adddep(Repo *repo, Offset olddeps, char *str, Id marker)
-{
- Id id = *str == '/' ? pool_str2id(repo->pool, str, 1) : testcase_str2dep(repo->pool, str);
- return repo_addid_dep(repo, olddeps, id, marker);
-}
-
-static void
-finish_v2_solvable(Pool *pool, Repodata *data, Solvable *s, char *filelist, int nfilelist)
-{
- if (nfilelist)
- {
- int l;
- Id did;
- for (l = 0; l < nfilelist; l += strlen(filelist + l) + 1)
- {
- char *p = strrchr(filelist + l, '/');
- if (!p)
- continue;
- *p++ = 0;
- did = repodata_str2dir(data, filelist + l, 1);
- p[-1] = '/';
- if (!did)
- did = repodata_str2dir(data, "/", 1);
- repodata_add_dirstr(data, s - pool->solvables, SOLVABLE_FILELIST, did, p);
- }
- }
- repo_rewrite_suse_deps(s, 0);
-}
-
-/* stripped down version of susetags parser used for testcases */
-int
-testcase_add_testtags(Repo *repo, FILE *fp, int flags)
-{
- Pool *pool = repo->pool;
- char *line, *linep;
- int aline;
- int tag;
- Repodata *data;
- Solvable *s;
- char *sp[5];
- unsigned int t;
- int intag;
- char *filelist = 0;
- int afilelist = 0;
- int nfilelist = 0;
- int tagsversion = 0;
- int addselfprovides = 1; /* for compat reasons */
-
- data = repo_add_repodata(repo, flags);
- s = 0;
- intag = 0;
-
- aline = 1024;
- line = solv_malloc(aline);
- linep = line;
- for (;;)
- {
- if (linep - line + 16 > aline)
- {
- aline = linep - line;
- line = solv_realloc(line, aline + 512);
- linep = line + aline;
- aline += 512;
- }
- if (!fgets(linep, aline - (linep - line), fp))
- break;
- linep += strlen(linep);
- if (linep == line || linep[-1] != '\n')
- continue;
- linep[-1] = 0;
- linep = line + intag;
- if (intag)
- {
- if (line[intag] == '-' && !strncmp(line + 1, line + intag + 1, intag - 2))
- {
- intag = 0;
- linep = line;
- continue;
- }
- }
- else if (line[0] == '+' && line[1] && line[1] != ':')
- {
- char *tagend = strchr(line, ':');
- if (!tagend)
- continue;
- line[0] = '=';
- tagend[1] = ' ';
- intag = tagend + 2 - line;
- linep = line + intag;
- continue;
- }
- if (*line != '=' || !line[1] || !line[2] || !line[3] || line[4] != ':')
- continue;
- tag = line[1] << 16 | line[2] << 8 | line[3];
- /* tags that do not need a solvable */
- switch(tag)
- {
- case 'V' << 16 | 'e' << 8 | 'r':
- tagsversion = atoi(line + 6);
- addselfprovides = tagsversion < 3 || strstr(line + 6, "addselfprovides") != 0;
- continue;
- case 'P' << 16 | 'k' << 8 | 'g':
- if (s)
- {
- if (tagsversion == 2)
- finish_v2_solvable(pool, data, s, filelist, nfilelist);
- if (addselfprovides && s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
- s->provides = repo_addid_dep(s->repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
- }
- nfilelist = 0;
- if (split(line + 5, sp, 5) != 4)
- break;
- s = pool_id2solvable(pool, repo_add_solvable(repo));
- s->name = pool_str2id(pool, sp[0], 1);
- /* join back version and release */
- if (sp[2] && !(sp[2][0] == '-' && !sp[2][1]))
- sp[2][-1] = '-';
- s->evr = makeevr(pool, sp[1]);
- s->arch = pool_str2id(pool, sp[3], 1);
- continue;
- default:
- break;
- }
- if (!s)
- continue;
- /* tags that need a solvable */
- switch(tag)
- {
- case 'S' << 16 | 'u' << 8 | 'm':
- repodata_set_str(data, s - pool->solvables, SOLVABLE_SUMMARY, line + 6);
- break;
- case 'V' << 16 | 'n' << 8 | 'd':
- s->vendor = pool_str2id(pool, line + 6, 1);
- break;
- case 'T' << 16 | 'i' << 8 | 'm':
- t = atoi(line + 6);
- if (t)
- repodata_set_num(data, s - pool->solvables, SOLVABLE_BUILDTIME, t);
- break;
- case 'R' << 16 | 'e' << 8 | 'q':
- s->requires = adddep(repo, s->requires, line + 6, -SOLVABLE_PREREQMARKER);
- break;
- case 'P' << 16 | 'r' << 8 | 'q':
- s->requires = adddep(repo, s->requires, line + 6, SOLVABLE_PREREQMARKER);
- break;
- case 'P' << 16 | 'r' << 8 | 'v':
- /* version 2 had the file list at the end of the provides */
- if (tagsversion == 2)
- {
- if (line[6] == '/')
- {
- int l = strlen(line + 6) + 1;
- if (nfilelist + l > afilelist)
- {
- afilelist = nfilelist + l + 512;
- filelist = solv_realloc(filelist, afilelist);
- }
- memcpy(filelist + nfilelist, line + 6, l);
- nfilelist += l;
- break;
- }
- if (nfilelist)
- {
- int l;
- for (l = 0; l < nfilelist; l += strlen(filelist + l) + 1)
- s->provides = repo_addid_dep(repo, s->provides, pool_str2id(pool, filelist + l, 1), 0);
- nfilelist = 0;
- }
- }
- s->provides = adddep(repo, s->provides, line + 6, 0);
- break;
- case 'F' << 16 | 'l' << 8 | 's':
- {
- char *p = strrchr(line + 6, '/');
- Id did;
- if (!p)
- break;
- *p++ = 0;
- did = repodata_str2dir(data, line + 6, 1);
- if (!did)
- did = repodata_str2dir(data, "/", 1);
- repodata_add_dirstr(data, s - pool->solvables, SOLVABLE_FILELIST, did, p);
- break;
- }
- case 'O' << 16 | 'b' << 8 | 's':
- s->obsoletes = adddep(repo, s->obsoletes, line + 6, 0);
- break;
- case 'C' << 16 | 'o' << 8 | 'n':
- s->conflicts = adddep(repo, s->conflicts, line + 6, 0);
- break;
- case 'R' << 16 | 'e' << 8 | 'c':
- s->recommends = adddep(repo, s->recommends, line + 6, 0);
- break;
- case 'S' << 16 | 'u' << 8 | 'p':
- s->supplements = adddep(repo, s->supplements, line + 6, 0);
- break;
- case 'S' << 16 | 'u' << 8 | 'g':
- s->suggests = adddep(repo, s->suggests, line + 6, 0);
- break;
- case 'E' << 16 | 'n' << 8 | 'h':
- s->enhances = adddep(repo, s->enhances, line + 6, 0);
- break;
- case 'I' << 16 | 'p' << 8 | 'r':
- {
- Id id = line[6] == '/' ? pool_str2id(pool, line + 6, 1) : testcase_str2dep(pool, line + 6);
- repodata_add_idarray(data, s - pool->solvables, SOLVABLE_PREREQ_IGNOREINST, id);
- break;
- }
- default:
- break;
- }
- }
- if (s)
- {
- if (tagsversion == 2)
- finish_v2_solvable(pool, data, s, filelist, nfilelist);
- if (addselfprovides && s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
- s->provides = repo_addid_dep(s->repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
- }
- solv_free(line);
- solv_free(filelist);
- repodata_free_dircache(data);
- if (!(flags & REPO_NO_INTERNALIZE))
- repodata_internalize(data);
- return 0;
-}
-
const char *
testcase_getpoolflags(Pool *pool)
{
@@ -1987,6 +1370,24 @@ testcase_solverresult(Solver *solv, int resultflags)
queue_free(&q);
queue_free(&qf);
}
+ if ((resultflags & TESTCASE_RESULT_USERINSTALLED) != 0)
+ {
+ Queue q;
+ solver_get_userinstalled(solv, &q, 0);
+ for (i = 0; i < q.count; i++)
+ {
+ s = pool_tmpjoin(pool, "userinstalled pkg ", testcase_solvid2str(pool, q.elements[i]), 0);
+ strqueue_push(&sq, s);
+ }
+ queue_empty(&q);
+ solver_get_userinstalled(solv, &q, GET_USERINSTALLED_NAMES | GET_USERINSTALLED_INVERTED);
+ for (i = 0; i < q.count; i++)
+ {
+ s = pool_tmpjoin(pool, "autoinst name ", pool_id2str(pool, q.elements[i]), 0);
+ strqueue_push(&sq, s);
+ }
+ queue_free(&q);
+ }
if ((resultflags & TESTCASE_RESULT_ALTERNATIVES) != 0)
{
char *altprefix;
@@ -2023,7 +1424,7 @@ testcase_solverresult(Solver *solv, int resultflags)
if ((rtype & SOLVER_RULE_TYPEMASK) == SOLVER_RULE_JOB)
{
const char *js = testcase_job2str(pool, rq.elements[i + 2], rq.elements[i + 3]);
- char *s = pool_tmpjoin(pool, altprefix, num, " job ");
+ char *s = pool_tmpjoin(pool, altprefix, num, "job ");
s = pool_tmpappend(pool, s, js, 0);
strqueue_push(&sq, s);
}
@@ -2033,6 +1434,13 @@ testcase_solverresult(Solver *solv, int resultflags)
s = pool_tmpappend(pool, s, " requires ", testcase_dep2str(pool, rq.elements[i + 3]));
strqueue_push(&sq, s);
}
+ else if (rtype == SOLVER_RULE_UPDATE || rtype == SOLVER_RULE_FEATURE)
+ {
+ const char *js = testcase_solvid2str(pool, rq.elements[i + 1]);
+ char *s = pool_tmpjoin(pool, altprefix, num, "update ");
+ s = pool_tmpappend(pool, s, js, 0);
+ strqueue_push(&sq, s);
+ }
}
}
for (i = 0; i < q.count; i++)
@@ -2188,7 +1596,11 @@ testcase_write_mangled(Solver *solv, const char *dir, int resultflags, const cha
if (!resultname)
resultname = "solver.result";
+#ifdef _WIN32
+ if (mkdir(dir) && errno != EEXIST)
+#else
if (mkdir(dir, 0777) && errno != EEXIST)
+#endif
return pool_error(solv->pool, 0, "testcase_write: could not create directory '%s'", dir);
strqueue_init(&sq);
FOR_REPOS(repoid, repo)
@@ -2199,7 +1611,11 @@ testcase_write_mangled(Solver *solv, const char *dir, int resultflags, const cha
sprintf(priobuf, "%d.%d", repo->priority, repo->subpriority);
else
sprintf(priobuf, "%d", repo->priority);
+#if !defined(WITHOUT_COOKIEOPEN) && defined(ENABLE_ZLIB_COMPRESSION)
out = pool_tmpjoin(pool, name, ".repo", ".gz");
+#else
+ out = pool_tmpjoin(pool, name, ".repo", 0);
+#endif
for (i = 0; out[i]; i++)
if (out[i] == '/')
out[i] = '_';
@@ -2235,7 +1651,7 @@ testcase_write_mangled(Solver *solv, const char *dir, int resultflags, const cha
lowscore = pool->id2arch[i];
}
}
- cmd = pool_tmpjoin(pool, "system ", pool->lastarch ? pool_id2str(pool, arch) : "unset", 0);
+ cmd = pool_tmpjoin(pool, "system ", pool->lastarch ? pool_id2str(pool, arch) : "-", 0);
for (i = 0; disttype2str[i].str != 0; i++)
if (pool->disttype == disttype2str[i].type)
break;
@@ -2569,7 +1985,13 @@ testcase_read(Pool *pool, FILE *fp, const char *testcase, Queue *job, char **res
return 0;
}
testcasedir = solv_strdup(testcase);
- if ((s = strrchr(testcasedir, '/')) != 0)
+ s = strrchr(testcasedir, '/');
+#ifdef _WIN32
+ buf = strrchr(testcasedir, '\\');
+ if (!s || (buf && buf > s))
+ s = buf;
+#endif
+ if (s)
s[1] = 0;
else
*testcasedir = 0;
@@ -2717,7 +2139,7 @@ testcase_read(Pool *pool, FILE *fp, const char *testcase, Queue *job, char **res
missing_features = 1;
}
}
- if (strcmp(pieces[1], "unset") == 0)
+ if (strcmp(pieces[1], "unset") == 0 || strcmp(pieces[1], "-") == 0)
pool_setarch(pool, 0);
else if (pieces[1][0] == ':')
pool_setarchpolicy(pool, pieces[1] + 1);
diff --git a/ext/testcase.h b/ext/testcase.h
index 387a506..997feaf 100644
--- a/ext/testcase.h
+++ b/ext/testcase.h
@@ -20,6 +20,7 @@
#define TESTCASE_RESULT_REASON (1 << 8)
#define TESTCASE_RESULT_CLEANDEPS (1 << 9)
#define TESTCASE_RESULT_JOBS (1 << 10)
+#define TESTCASE_RESULT_USERINSTALLED (1 << 11)
/* reuse solver hack, testsolv use only */
#define TESTCASE_RESULT_REUSE_SOLVER (1 << 31)
diff --git a/libsolv.pc.in b/libsolv.pc.in
index 40a8623..766146c 100644
--- a/libsolv.pc.in
+++ b/libsolv.pc.in
@@ -1,5 +1,5 @@
-libdir=@LIB_INSTALL_DIR@
-includedir=@INCLUDE_INSTALL_DIR@
+libdir=@CMAKE_INSTALL_FULL_LIBDIR@
+includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
Name: libsolv
Description: Library for solving packages
diff --git a/libsolvext.pc.in b/libsolvext.pc.in
index d48b6fa..d007846 100644
--- a/libsolvext.pc.in
+++ b/libsolvext.pc.in
@@ -1,5 +1,5 @@
-libdir=@LIB_INSTALL_DIR@
-includedir=@INCLUDE_INSTALL_DIR@
+libdir=@CMAKE_INSTALL_FULL_LIBDIR@
+includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
Name: libsolvext
Description: Library for reading repositories
diff --git a/package/libsolv.changes b/package/libsolv.changes
index 897cc04..6393734 100644
--- a/package/libsolv.changes
+++ b/package/libsolv.changes
@@ -1,4 +1,20 @@
-------------------------------------------------------------------
+Wed Jun 12 13:22:40 CEST 2019 - mls@suse.de
+
+- fix favorq leaking between solver runs if the solver is reused
+- fix SOLVER_FLAG_FOCUS_BEST updateing packages without reason
+- be more correct with multiversion packages that obsolete their
+ own name [bnc#1127155]
+- allow building with swig-4.0.0 [bnc#1135749]
+- bump version to 0.7.5
+
+-------------------------------------------------------------------
+Wed Apr 24 15:34:44 CEST 2019 - mls@suse.de
+
+- always prefer to stay with the same package name if there are
+ multiple alternatives [bnc#1131823]
+
+-------------------------------------------------------------------
Fri Mar 29 15:58:54 CET 2019 - mls@suse.de
- repo_add_rpmdb: do not copy bad solvables from the old solv file
@@ -11,6 +27,7 @@ Wed Jan 30 15:51:36 CET 2019 - mls@suse.de
- fixed a couple of null pointer derefs
[bnc#1120629] [bnc#1120630] [bnc#1120631]
+ [CVE-2018-20532] [CVE-2018-20533] [CVE-2018-20534]
- do favor evaluation before pruning allowing to (dis)favor
specific package versions
- no longer disable infarch rules when they don't conflict with
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index be487a7..f91c9c0 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -31,10 +31,18 @@ SET (libsolv_HEADERS
IF (ENABLE_CONDA)
SET (libsolv_SRCS ${libsolv_SRCS} conda.c)
+ SET (libsolv_HEADERS ${libsolv_HEADERS} conda.h)
ENDIF (ENABLE_CONDA)
+IF (NOT MSVC)
+ SET (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC")
+ENDIF (NOT MSVC)
+
+IF (WIN32)
+ INCLUDE (${PROJECT_SOURCE_DIR}/win32/CMakeLists.txt)
+ LIST (APPEND libsolv_SRCS ${WIN32_COMPAT_SOURCES})
+ENDIF (WIN32)
-SET (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC")
IF (HAVE_LINKER_VERSION_SCRIPT)
SET (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${LINK_FLAGS} -Wl,--version-script=${CMAKE_SOURCE_DIR}/src/libsolv.ver")
ENDIF (HAVE_LINKER_VERSION_SCRIPT)
@@ -47,14 +55,14 @@ ENDIF (DISABLE_SHARED)
SET_TARGET_PROPERTIES(libsolv PROPERTIES OUTPUT_NAME "solv")
SET_TARGET_PROPERTIES(libsolv PROPERTIES SOVERSION ${LIBSOLV_SOVERSION})
-SET_TARGET_PROPERTIES(libsolv PROPERTIES INSTALL_NAME_DIR ${LIB_INSTALL_DIR})
+SET_TARGET_PROPERTIES(libsolv PROPERTIES INSTALL_NAME_DIR ${CMAKE_INSTALL_LIBDIR})
-INSTALL (FILES ${libsolv_HEADERS} DESTINATION "${INCLUDE_INSTALL_DIR}/solv")
-INSTALL (TARGETS libsolv LIBRARY DESTINATION ${LIB_INSTALL_DIR} ARCHIVE DESTINATION ${LIB_INSTALL_DIR} RUNTIME DESTINATION bin)
+INSTALL (FILES ${libsolv_HEADERS} DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/solv")
+INSTALL (TARGETS libsolv LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
IF (ENABLE_STATIC AND NOT DISABLE_SHARED)
ADD_LIBRARY (libsolv_static STATIC ${libsolv_SRCS})
SET_TARGET_PROPERTIES(libsolv_static PROPERTIES OUTPUT_NAME "solv")
SET_TARGET_PROPERTIES(libsolv_static PROPERTIES SOVERSION ${LIBSOLV_SOVERSION})
-INSTALL (TARGETS libsolv_static LIBRARY DESTINATION ${LIB_INSTALL_DIR} ARCHIVE DESTINATION ${LIB_INSTALL_DIR})
+INSTALL (TARGETS libsolv_static LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
ENDIF (ENABLE_STATIC AND NOT DISABLE_SHARED)
diff --git a/src/chksum.c b/src/chksum.c
index df46145..1f8ab47 100644
--- a/src/chksum.c
+++ b/src/chksum.c
@@ -19,6 +19,10 @@
#include "sha1.h"
#include "sha2.h"
+#ifdef _WIN32
+ #include "strfncs.h"
+#endif
+
struct s_Chksum {
Id type;
int done;
diff --git a/src/cleandeps.c b/src/cleandeps.c
index 8ef6fd3..aa83c10 100644
--- a/src/cleandeps.c
+++ b/src/cleandeps.c
@@ -836,7 +836,7 @@ solver_createcleandepsmap(Solver *solv, Map *cleandepsmap, int unneeded)
if (MAPTST(&solv->multiversion, p))
break;
if (p)
- continue;
+ continue; /* found a multiversion package that will not obsolate anything */
}
om.size = 0;
diff --git a/src/conda.c b/src/conda.c
index 5b134dd..7f2538a 100644
--- a/src/conda.c
+++ b/src/conda.c
@@ -24,6 +24,10 @@
#include "util.h"
#include "conda.h"
+#ifdef _WIN32
+#include "strfncs.h"
+#endif
+
static const char *
endseg(const char *seg, const char *end)
{
@@ -226,7 +230,7 @@ pool_evrcmp_conda(const Pool *pool, const char *evr1, const char *evr2, int mode
}
static int
-regexmatch(const char *evr, const char *version, size_t versionlen)
+regexmatch(const char *evr, const char *version, size_t versionlen, int icase)
{
regex_t reg;
char *buf = solv_malloc(versionlen + 1);
@@ -234,15 +238,19 @@ regexmatch(const char *evr, const char *version, size_t versionlen)
memcpy(buf, version, versionlen);
buf[versionlen] = 0;
- if (regcomp(&reg, buf, REG_EXTENDED | REG_NOSUB))
- return 0;
+ if (regcomp(&reg, buf, REG_EXTENDED | REG_NOSUB | (icase ? REG_ICASE : 0)))
+ {
+ solv_free(buf);
+ return 0;
+ }
r = regexec(&reg, evr, 0, NULL, 0);
regfree(&reg);
+ solv_free(buf);
return r == 0;
}
static int
-globmatch(const char *evr, const char *version, size_t versionlen)
+globmatch(const char *evr, const char *version, size_t versionlen, int icase)
{
regex_t reg;
char *buf = solv_malloc(2 * versionlen + 3);
@@ -259,10 +267,14 @@ globmatch(const char *evr, const char *version, size_t versionlen)
}
buf[j++] = '$';
buf[j] = 0;
- if (regcomp(&reg, buf, REG_EXTENDED | REG_NOSUB))
- return 0;
+ if (regcomp(&reg, buf, REG_EXTENDED | REG_NOSUB | (icase ? REG_ICASE : 0)))
+ {
+ solv_free(buf);
+ return 0;
+ }
r = regexec(&reg, evr, 0, NULL, 0);
regfree(&reg);
+ solv_free(buf);
return r == 0;
}
@@ -279,7 +291,7 @@ solvable_conda_matchversion_single(Solvable *s, const char *version, size_t vers
return 1; /* matches every version */
evr = pool_id2str(s->repo->pool, s->evr);
if (versionlen >= 2 && version[0] == '^' && version[versionlen - 1] == '$')
- return regexmatch(evr, version, versionlen);
+ return regexmatch(evr, version, versionlen, 0);
if (version[0] == '=' || version[0] == '<' || version[0] == '>' || version[0] == '!' || version[0] == '~')
{
int flags = 0;
@@ -361,7 +373,7 @@ solvable_conda_matchversion_single(Solvable *s, const char *version, size_t vers
if (version[i] != '*')
break;
if (i < versionlen)
- return globmatch(evr, version, versionlen);
+ return globmatch(evr, version, versionlen, 1);
}
if (versionlen > 1 && version[versionlen - 1] == '*')
@@ -430,6 +442,26 @@ solvable_conda_matchversion_rec(Solvable *s, const char **versionp, const char *
}
}
+static int
+solvable_conda_matchbuild(Solvable *s, const char *build, const char *buildend)
+{
+ const char *bp;
+ const char *flavor = solvable_lookup_str(s, SOLVABLE_BUILDFLAVOR);
+
+ if (!flavor)
+ flavor = "";
+ if (build == buildend)
+ return *flavor ? 0 : 1;
+ if (build + 1 == buildend && *build == '*')
+ return 1;
+ if (*build == '^' && buildend[-1] == '$')
+ return regexmatch(flavor, build, buildend - build, 0);
+ for (bp = build; bp < buildend; bp++)
+ if (*bp == '*')
+ return globmatch(flavor, build, buildend - build, 0);
+ return strncmp(flavor, build, buildend - build) == 0 && flavor[buildend - build] == 0 ? 1 : 0;
+}
+
/* return true if solvable s matches the version */
/* see conda/models/match_spec.py */
int
@@ -449,6 +481,200 @@ solvable_conda_matchversion(Solvable *s, const char *version)
r = solvable_conda_matchversion_rec(s, &version, versionend);
if (r != 1 || version != versionend)
return 0;
+ if (build && !solvable_conda_matchbuild(s, build, build + strlen(build)))
+ return 0;
return r;
}
+static Id
+pool_addrelproviders_conda_slow(Pool *pool, const char *namestr, Id evr, Queue *plist, int mode)
+{
+ size_t namestrlen = strlen(namestr);
+ const char *evrstr = evr == 0 || evr == 1 ? 0 : pool_id2str(pool, evr);
+ Id p;
+
+ FOR_POOL_SOLVABLES(p)
+ {
+ Solvable *s = pool->solvables + p;
+ if (!pool_installable(pool, s))
+ continue;
+ if (mode == 1 && !globmatch(pool_id2str(pool, s->name), namestr, namestrlen, 1))
+ continue;
+ if (mode == 2 && !regexmatch(pool_id2str(pool, s->name), namestr, namestrlen, 1))
+ continue;
+ if (!evrstr || solvable_conda_matchversion(s, evrstr))
+ queue_push(plist, p);
+ }
+ return 0;
+}
+
+/* called from pool_addrelproviders, plist is an empty queue
+ * it can either return an offset into the whatprovides array
+ * or fill the plist queue and return zero */
+Id
+pool_addrelproviders_conda(Pool *pool, Id name, Id evr, Queue *plist)
+{
+ const char *namestr = pool_id2str(pool, name), *np;
+ size_t l, nuc = 0;
+ Id wp, p, *pp;
+
+ /* is this a regex? */
+ if (*namestr && *namestr == '^')
+ {
+ l = strlen(namestr);
+ if (namestr[l - 1] == '$')
+ return pool_addrelproviders_conda_slow(pool, namestr, evr, plist, 2);
+ }
+ /* is this '*'? */
+ if (*namestr && *namestr == '*' && namestr[1] == 0)
+ return pool_addrelproviders_conda_slow(pool, namestr, evr, plist, 0);
+ /* does the string contain '*' or uppercase? */
+ for (np = namestr; *np; np++)
+ {
+ if (*np == '*')
+ return pool_addrelproviders_conda_slow(pool, namestr, evr, plist, 1);
+ else if (*np >= 'A' && *np <= 'Z')
+ nuc++;
+ }
+ if (nuc)
+ {
+ char *nbuf = solv_strdup(namestr), *nbufp;
+ for (nbufp = nbuf; *nbufp; nbufp++)
+ *nbufp = *nbufp >= 'A' && *nbufp <= 'Z' ? *nbufp + ('a' - 'A') : *nbufp;
+ name = pool_str2id(pool, nbuf, 0);
+ wp = name ? pool_whatprovides(pool, name) : 0;
+ solv_free(nbuf);
+ }
+ else
+ wp = pool_whatprovides(pool, name);
+ if (wp && evr && evr != 1)
+ {
+ const char *evrstr = pool_id2str(pool, evr);
+ pp = pool->whatprovidesdata + wp;
+ while ((p = *pp++) != 0)
+ {
+ if (solvable_conda_matchversion(pool->solvables + p, evrstr))
+ queue_push(plist, p);
+ else
+ wp = 0;
+ }
+ }
+ return wp;
+}
+
+/* create a CONDA_REL relation from a matchspec */
+Id
+pool_conda_matchspec(Pool *pool, const char *name)
+{
+ const char *p2;
+ char *name2;
+ char *p, *pp;
+ char *build, *buildend, *version, *versionend;
+ Id nameid, evrid;
+ int haveglob = 0;
+
+ /* ignore channel and namespace for now */
+ if ((p2 = strrchr(name, ':')))
+ name = p2 + 1;
+ name2 = solv_strdup(name);
+ /* find end of name */
+ for (p = name2; *p && *p != ' ' && *p != '=' && *p != '<' && *p != '>' && *p != '!' && *p != '~'; p++)
+ {
+ if (*p >= 'A' && *p <= 'Z')
+ *(char *)p += 'a' - 'A'; /* lower case the name */
+ else if (*p == '*')
+ haveglob = 1;
+ }
+ if (p == name2)
+ {
+ solv_free(name2);
+ return 0; /* error: empty package name */
+ }
+ nameid = pool_strn2id(pool, name2, p - name2, 1);
+ while (*p == ' ')
+ p++;
+ if (!*p)
+ {
+ if (*name2 != '^' && !haveglob)
+ {
+ solv_free(name2);
+ return nameid; /* return a simple dependency if no glob/regex */
+ }
+ evrid = pool_str2id(pool, "*", 1);
+ solv_free(name2);
+ return pool_rel2id(pool, nameid, evrid, REL_CONDA, 1);
+ }
+ /* have version */
+ version = p;
+ versionend = p + strlen(p);
+ while (versionend > version && versionend[-1] == ' ')
+ versionend--;
+ build = buildend = 0;
+ /* split of build */
+ p = versionend;
+ for (;;)
+ {
+ while (p > version && p[-1] != ' ' && p[-1] != '-' && p[-1] != '=' && p[-1] != ',' && p[-1] != '|' && p[-1] != '<' && p[-1] != '>' && p[-1] != '~')
+ p--;
+ if (p <= version + 1 || (p[-1] != ' ' && p[-1] != '='))
+ break; /* no build */
+ /* check char before delimiter */
+ if (p[-2] == '=' || p[-2] == '!' || p[-2] == '|' || p[-2] == ',' || p[-2] == '<' || p[-2] == '>' || p[-2] == '~')
+ {
+ /* illegal combination */
+ if (p[-1] == ' ')
+ {
+ p--;
+ continue; /* special case space: it may be in the build */
+ }
+ break; /* no build */
+ }
+ if (p < versionend)
+ {
+ build = p;
+ buildend = versionend;
+ versionend = p - 1;
+ }
+ break;
+ }
+ /* do weird version translation */
+ if (versionend > version && version[0] == '=')
+ {
+ if (versionend - version >= 2 && version[1] == '=')
+ {
+ if (!build)
+ version += 2;
+ }
+ else if (build)
+ version += 1;
+ else
+ {
+ for (p = version + 1; p < versionend; p++)
+ if (*p == '=' || *p == ',' || *p == '|')
+ break;
+ if (p == versionend)
+ {
+ memmove(version, version + 1, versionend - version - 1);
+ versionend[-1] = '*';
+ }
+ }
+ }
+#if 0
+ printf("version: >%.*s<\n", (int)(versionend - version), version);
+ if (build) printf("build: >%.*s<\n", (int)(buildend - build), build);
+#endif
+ /* strip spaces from version */
+ for (p = pp = version; pp < versionend; pp++)
+ if (*pp != ' ')
+ *p++ = *pp;
+ if (build)
+ {
+ *p++ = ' ';
+ memcpy(p, build, buildend - build);
+ p += buildend - build;
+ }
+ evrid = pool_strn2id(pool, version, p - version, 1);
+ solv_free(name2);
+ return pool_rel2id(pool, nameid, evrid, REL_CONDA, 1);
+}
+
diff --git a/src/conda.h b/src/conda.h
index 7233f17..3bcfa2d 100644
--- a/src/conda.h
+++ b/src/conda.h
@@ -15,6 +15,8 @@
int pool_evrcmp_conda(const Pool *pool, const char *evr1, const char *evr2, int mode);
int solvable_conda_matchversion(Solvable *s, const char *version);
+Id pool_addrelproviders_conda(Pool *pool, Id name, Id evr, Queue *plist);
+Id pool_conda_matchspec(Pool *pool, const char *name);
#endif /* LIBSOLV_CONDA_H */
diff --git a/src/knownid.h b/src/knownid.h
index b5b41d6..9d7f157 100644
--- a/src/knownid.h
+++ b/src/knownid.h
@@ -251,12 +251,15 @@ KNOWNID(SIGNATURE_EXPIRES, "signature:expires"),
KNOWNID(SIGNATURE_DATA, "signature:data"),
/* 'content' of patch, usually list of modules */
-KNOWNID(UPDATE_MODULE, "update:module"), /* "name stream version context arch" */
-KNOWNID(UPDATE_MODULE_NAME, "update:module:name"), /* name */
-KNOWNID(UPDATE_MODULE_STREAM, "update:module:stream"), /* stream */
-KNOWNID(UPDATE_MODULE_VERSION, "update:module:version"), /* version */
-KNOWNID(UPDATE_MODULE_CONTEXT, "update:module:context"), /* context */
-KNOWNID(UPDATE_MODULE_ARCH, "update:module:arch"), /* architecture */
+KNOWNID(UPDATE_MODULE, "update:module"), /* "name stream version context arch" */
+KNOWNID(UPDATE_MODULE_NAME, "update:module:name"), /* name */
+KNOWNID(UPDATE_MODULE_STREAM, "update:module:stream"), /* stream */
+KNOWNID(UPDATE_MODULE_VERSION, "update:module:version"), /* version */
+KNOWNID(UPDATE_MODULE_CONTEXT, "update:module:context"), /* context */
+KNOWNID(UPDATE_MODULE_ARCH, "update:module:arch"), /* architecture */
+
+KNOWNID(SOLVABLE_BUILDVERSION, "solvable:buildversion"), /* conda */
+KNOWNID(SOLVABLE_BUILDFLAVOR, "solvable:buildflavor"), /* conda */
KNOWNID(ID_NUM_INTERNAL, 0)
diff --git a/src/libsolv.ver b/src/libsolv.ver
index f0e86ff..eafe3e6 100644
--- a/src/libsolv.ver
+++ b/src/libsolv.ver
@@ -448,4 +448,6 @@ SOLV_1.2 {
SOLV_1.3 {
repodata_set_kv;
+ solv_setcloexec;
+ pool_conda_matchspec;
} SOLV_1.2;
diff --git a/src/policy.c b/src/policy.c
index 5f61115..823a008 100644
--- a/src/policy.c
+++ b/src/policy.c
@@ -798,6 +798,8 @@ move_installed_to_front(Pool *pool, Queue *plist)
Solvable *s;
Id p, pp;
+ if (!pool->installed)
+ return;
for (i = j = 0; i < plist->count; i++)
{
s = pool->solvables + plist->elements[i];
@@ -831,6 +833,26 @@ move_installed_to_front(Pool *pool, Queue *plist)
}
}
+static int
+pool_buildversioncmp(Pool *pool, Solvable *s1, Solvable *s2)
+{
+ const char *bv1 = solvable_lookup_str(s1, SOLVABLE_BUILDVERSION);
+ const char *bv2 = solvable_lookup_str(s2, SOLVABLE_BUILDVERSION);
+ if (!bv1 && !bv2)
+ return 0;
+ return pool_evrcmp_str(pool, bv1 ? bv1 : "" , bv2 ? bv2 : "", EVRCMP_COMPARE);
+}
+
+static int
+pool_buildflavorcmp(Pool *pool, Solvable *s1, Solvable *s2)
+{
+ const char *f1 = solvable_lookup_str(s1, SOLVABLE_BUILDFLAVOR);
+ const char *f2 = solvable_lookup_str(s2, SOLVABLE_BUILDFLAVOR);
+ if (!f1 && !f2)
+ return 0;
+ return pool_evrcmp_str(pool, f1 ? f1 : "" , f2 ? f2 : "", EVRCMP_COMPARE);
+}
+
/*
* prune_to_best_version
*
@@ -878,6 +900,10 @@ prune_to_best_version(Pool *pool, Queue *plist)
if (r == 0 && has_package_link(pool, s))
r = pool_link_evrcmp(pool, best, s);
#endif
+ if (r == 0 && pool->disttype == DISTTYPE_CONDA)
+ r = pool_buildversioncmp(pool, best, s);
+ if (r == 0 && pool->disttype == DISTTYPE_CONDA)
+ r = pool_buildflavorcmp(pool, best, s);
if (r < 0)
best = s;
}
@@ -893,8 +919,6 @@ prune_to_best_version(Pool *pool, Queue *plist)
else
prune_obsoleted(pool, plist);
}
- if (plist->count > 1 && pool->installed)
- move_installed_to_front(pool, plist);
}
@@ -1321,6 +1345,7 @@ policy_filter_unwanted(Solver *solv, Queue *plist, int mode)
#endif
dislike_old_versions(pool, plist);
sort_by_common_dep(pool, plist);
+ move_installed_to_front(pool, plist);
if (solv->urpmreorder)
urpm_reorder(solv, plist);
prefer_suggested(solv, plist);
@@ -1342,6 +1367,7 @@ pool_best_solvables(Pool *pool, Queue *plist, int flags)
{
dislike_old_versions(pool, plist);
sort_by_common_dep(pool, plist);
+ move_installed_to_front(pool, plist);
}
}
diff --git a/src/pool.c b/src/pool.c
index 09354ef..a554453 100644
--- a/src/pool.c
+++ b/src/pool.c
@@ -1289,19 +1289,7 @@ pool_addrelproviders(Pool *pool, Id d)
break;
#ifdef ENABLE_CONDA
case REL_CONDA:
- wp = pool_whatprovides(pool, name);
- if (evr)
- {
- const char *evrstr = pool_id2str(pool, evr);
- pp = pool->whatprovidesdata + wp;
- while ((p = *pp++) != 0)
- {
- if (solvable_conda_matchversion(pool->solvables + p, evrstr))
- queue_push(&plist, p);
- else
- wp = 0;
- }
- }
+ wp = pool_addrelproviders_conda(pool, name, evr, &plist);
break;
#endif
default:
@@ -1324,7 +1312,7 @@ pool_addrelproviders(Pool *pool, Id d)
POOL_DEBUG(SOLV_DEBUG_STATS, "addrelproviders: what provides %s?\n", pool_dep2str(pool, name));
#endif
pp = pool_whatprovides_ptr(pool, name);
- if (!ISRELDEP(name) && name < pool->whatprovidesauxoff)
+ if (!ISRELDEP(name) && (Offset)name < pool->whatprovidesauxoff)
ppaux = pool->whatprovidesaux[name] ? pool->whatprovidesauxdata + pool->whatprovidesaux[name] : 0;
while (ISRELDEP(name))
{
@@ -1938,7 +1926,7 @@ pool_set_whatprovides(Pool *pool, Id id, Id providers)
else
{
pool->whatprovides[id] = providers;
- if (id < pool->whatprovidesauxoff)
+ if ((Offset)id < pool->whatprovidesauxoff)
pool->whatprovidesaux[id] = 0; /* sorry */
d = 1;
}
diff --git a/src/qsort_r.c b/src/qsort_r.c
index d49049a..ffc09dc 100644
--- a/src/qsort_r.c
+++ b/src/qsort_r.c
@@ -37,7 +37,9 @@
#if defined(LIBC_SCCS) && !defined(lint)
static char sccsid[] = "@(#)qsort.c 8.1 (Berkeley) 6/4/93";
#endif /* LIBC_SCCS and not lint */
+#ifndef _WIN32
#include <sys/cdefs.h>
+#endif
/* $FreeBSD: src/lib/libc/stdlib/qsort.c,v 1.13.2.1.8.1 2010/12/21 17:10:29 kensmith Exp $ */
diff --git a/src/repo.c b/src/repo.c
index d163f13..da40219 100644
--- a/src/repo.c
+++ b/src/repo.c
@@ -391,7 +391,7 @@ repo_addid_dep_hash(Repo *repo, Offset olddeps, Id id, Id marker, int size)
if (repo->lastidhash_idarraysize != repo->idarraysize || (Hashval)size * 2 > repo->lastidhash_mask || repo->lastmarker != marker)
{
repo->lastmarkerpos = 0;
- if (size * 2 > (Hashval)repo->lastidhash_mask)
+ if ((Hashval)size * 2 > repo->lastidhash_mask)
{
repo->lastidhash_mask = mkmask(size < REPO_ADDID_DEP_HASHMIN ? REPO_ADDID_DEP_HASHMIN : size);
repo->lastidhash = solv_realloc2(repo->lastidhash, repo->lastidhash_mask + 1, sizeof(Id));
@@ -844,7 +844,7 @@ repo_search_md(Repo *repo, Id p, Id keyname, struct matchdata *md)
case RPM_RPMDBID:
if (repo->rpmdbid)
{
- kv.num = repo->rpmdbid[p - repo->start];
+ kv.num = (unsigned int)repo->rpmdbid[p - repo->start];
kv.num2 = 0;
repo_matchvalue(md, s, 0, repo_solvablekeys + (RPM_RPMDBID - SOLVABLE_NAME), &kv);
}
@@ -1097,7 +1097,7 @@ repo_lookup_num(Repo *repo, Id entry, Id keyname, unsigned long long notfound)
if (keyname == RPM_RPMDBID)
{
if (repo->rpmdbid && entry >= repo->start && entry < repo->end)
- return repo->rpmdbid[entry - repo->start];
+ return (unsigned int)repo->rpmdbid[entry - repo->start];
return notfound;
}
}
@@ -1250,7 +1250,10 @@ Id
repo_lookup_type(Repo *repo, Id entry, Id keyname)
{
Id type;
- Repodata *data = repo_lookup_repodata_opt(repo, entry, keyname);
+ Repodata *data;
+ if (keyname >= SOLVABLE_NAME && keyname <= RPM_RPMDBID)
+ return repo_solvablekeys[keyname - SOLVABLE_NAME].type;
+ data = repo_lookup_repodata_opt(repo, entry, keyname);
if (data && (type = repodata_lookup_type(data, entry, keyname)) != 0 && type != REPOKEY_TYPE_DELETED)
return type;
return 0;
@@ -1363,7 +1366,7 @@ repo_set_num(Repo *repo, Id p, Id keyname, unsigned long long num)
{
if (!repo->rpmdbid)
repo->rpmdbid = repo_sidedata_create(repo, sizeof(Id));
- repo->rpmdbid[p - repo->start] = num;
+ repo->rpmdbid[p - repo->start] = (Id)num;
return;
}
}
diff --git a/src/repo_solv.c b/src/repo_solv.c
index be33967..761d06e 100644
--- a/src/repo_solv.c
+++ b/src/repo_solv.c
@@ -1220,7 +1220,7 @@ printf("=> %s %s %p\n", pool_id2str(pool, keys[key].name), pool_id2str(pool, key
default:
if (id == RPM_RPMDBID && s && keys[key].type == REPOKEY_TYPE_NUM)
{
- dp = data_read_id_max(dp, &id, 0, 0, &data);
+ dp = data_read_id(dp, &id);
if (!repo->rpmdbid)
repo->rpmdbid = repo_sidedata_create(repo, sizeof(Id));
repo->rpmdbid[(s - pool->solvables) - repo->start] = id;
diff --git a/src/repodata.c b/src/repodata.c
index ad5aaea..0c7a51f 100644
--- a/src/repodata.c
+++ b/src/repodata.c
@@ -34,6 +34,10 @@
#include "repopack.h"
#include "repopage.h"
+#ifdef _WIN32
+ #include "strfncs.h"
+#endif
+
#define REPODATA_BLOCK 255
static unsigned char *data_skip_key(Repodata *data, unsigned char *dp, Repokey *key);
@@ -791,14 +795,28 @@ repodata_lookup_idarray(Repodata *data, Id solvid, Id keyname, Queue *q)
queue_empty(q);
dp = find_key_data(data, solvid, keyname, &key);
- if (!dp || key->type != REPOKEY_TYPE_IDARRAY)
+ if (!dp)
return 0;
- for (;;)
+ switch (key->type)
{
- dp = data_read_ideof(dp, &id, &eof);
+ case REPOKEY_TYPE_CONSTANTID:
+ queue_push(q, key->size);
+ break;
+ case REPOKEY_TYPE_ID:
+ dp = data_read_id(dp, &id);
queue_push(q, id);
- if (eof)
- break;
+ break;
+ case REPOKEY_TYPE_IDARRAY:
+ for (;;)
+ {
+ dp = data_read_ideof(dp, &id, &eof);
+ queue_push(q, id);
+ if (eof)
+ break;
+ }
+ break;
+ default:
+ return 0;
}
return 1;
}
@@ -1124,7 +1142,7 @@ repodata_search_array(Repodata *data, Id solvid, Id keyname, int flags, Repokey
if (!dp || kv->entry != -1)
return 0;
- while (++kv->entry < kv->num)
+ while (++kv->entry < (int)kv->num)
{
if (kv->entry)
dp = data_skip_schema(data, dp, schema);
@@ -3142,7 +3160,7 @@ compact_attrdata(Repodata *data, int entry, int nentry)
break;
case REPOKEY_TYPE_DIRSTRARRAY:
for (v = attrs[1]; data->attriddata[v] ; v += 2)
- if (data->attriddata[v + 1] < attrdatastart)
+ if ((unsigned int)data->attriddata[v + 1] < attrdatastart)
attrdatastart = data->attriddata[v + 1];
/* FALLTHROUGH */
case REPOKEY_TYPE_IDARRAY:
diff --git a/src/repopage.c b/src/repopage.c
index 85d53eb..9e9694f 100644
--- a/src/repopage.c
+++ b/src/repopage.c
@@ -30,6 +30,12 @@
#include <fcntl.h>
#include <time.h>
+#ifdef _WIN32
+ #include <windows.h>
+ #include <fileapi.h>
+ #include <io.h>
+#endif
+
#include "repo.h"
#include "repopage.h"
@@ -704,11 +710,22 @@ repopagestore_load_page_range(Repopagestore *store, unsigned int pstart, unsigne
#ifdef DEBUG_PAGING
fprintf(stderr, "PAGEIN: %d to %d", pnum, i);
#endif
+#ifndef _WIN32
if (pread(store->pagefd, compressed ? buf : dest, in_len, store->file_offset + p->page_offset) != in_len)
{
perror("mapping pread");
return 0;
}
+#else
+ DWORD read_len;
+ OVERLAPPED ovlp = {0};
+ ovlp.Offset = store->file_offset + p->page_offset;
+ if (!ReadFile((HANDLE) _get_osfhandle(store->pagefd), compressed ? buf : dest, in_len, &read_len, &ovlp) || read_len != in_len)
+ {
+ perror("mapping ReadFile");
+ return 0;
+ }
+#endif
if (compressed)
{
unsigned int out_len;
@@ -785,7 +802,7 @@ repopagestore_read_or_setup_pages(Repopagestore *store, FILE *fp, unsigned int p
if (store->pagefd == -1)
can_seek = 0;
else
- fcntl(store->pagefd, F_SETFD, FD_CLOEXEC);
+ solv_setcloexec(store->pagefd, 1);
#ifdef DEBUG_PAGING
fprintf(stderr, "can %sseek\n", can_seek ? "" : "NOT ");
diff --git a/src/rules.c b/src/rules.c
index b6cd582..3aef6ee 100644
--- a/src/rules.c
+++ b/src/rules.c
@@ -1328,6 +1328,31 @@ solver_addfeaturerule(Solver *solv, Solvable *s)
}
}
+/* check if multiversion solvable s2 has an obsoletes for installed solvable s */
+static int
+is_multiversion_obsoleteed(Pool *pool, Solvable *s, Solvable *s2)
+{
+ Id *wp, obs, *obsp;
+
+ if (pool->obsoleteusescolors && !pool_colormatch(pool, s, s2))
+ return 0;
+ obsp = s2->repo->idarraydata + s2->obsoletes;
+ if (!pool->obsoleteusesprovides)
+ {
+ while ((obs = *obsp++) != 0)
+ if (pool_match_nevr(pool, s, obs))
+ return 1;
+ }
+ else
+ {
+ while ((obs = *obsp++) != 0)
+ for (wp = pool_whatprovides_ptr(pool, obs); *wp; wp++)
+ if (pool->solvables + *wp == s)
+ return 1;
+ }
+ return 0;
+}
+
/*-------------------------------------------------------------------
*
* add rule for update
@@ -1389,9 +1414,8 @@ solver_addupdaterule(Solver *solv, Solvable *s)
if (MAPTST(&solv->multiversion, qs.elements[i]))
{
Solvable *ps = pool->solvables + qs.elements[i];
- /* if keepexplicitobsoletes is set and the name is different,
- * we assume that there is an obsoletes. XXX: not 100% correct */
- if (solv->keepexplicitobsoletes && ps->name != s->name)
+ /* check if there is an explicit obsoletes */
+ if (solv->keepexplicitobsoletes && ps->obsoletes && is_multiversion_obsoleteed(pool, s, ps))
{
qs.elements[j++] = qs.elements[i];
continue;
@@ -2189,7 +2213,7 @@ jobtodisablelist(Solver *solv, Id how, Id what, Queue *q)
if (pool->solvables[p].repo == installed)
return;
if (solv->multiversion.size && MAPTST(&solv->multiversion, p) && !solv->keepexplicitobsoletes)
- return;
+ return; /* will not obsolete anything, so just return */
}
omap.size = 0;
qstart = q->count;
@@ -2273,6 +2297,34 @@ jobtodisablelist(Solver *solv, Id how, Id what, Queue *q)
#endif
}
return;
+
+ case SOLVER_LOCK:
+ if (!installed)
+ break;
+ qstart = q->count;
+ if (select == SOLVER_SOLVABLE_ALL || (select == SOLVER_SOLVABLE_REPO && what == installed->repoid))
+ {
+ FOR_REPO_SOLVABLES(installed, p, s)
+ {
+ for (i = qstart; i < q->count; i += 2)
+ if (q->elements[i] == DISABLE_DUP && q->elements[i + 1] == pool->solvables[p].name)
+ break;
+ if (i == q->count)
+ queue_push2(q, DISABLE_DUP, pool->solvables[p].name);
+ }
+ }
+ FOR_JOB_SELECT(p, pp, select, what)
+ {
+ if (pool->solvables[p].repo != installed)
+ continue;
+ for (i = qstart; i < q->count; i += 2)
+ if (q->elements[i] == DISABLE_DUP && q->elements[i + 1] == pool->solvables[p].name)
+ break;
+ if (i == q->count)
+ queue_push2(q, DISABLE_DUP, pool->solvables[p].name);
+ }
+ break;
+
default:
return;
}
@@ -3260,8 +3312,19 @@ prune_to_dup_packages(Solver *solv, Id p, Queue *q)
queue_truncate(q, j);
}
+static void
+prune_best_update(Solver *solv, Id p, Queue *q)
+{
+ if (solv->update_targets && solv->update_targets->elements[p - solv->installed->start])
+ prune_to_update_targets(solv, solv->update_targets->elements + solv->update_targets->elements[p - solv->installed->start], q);
+ if (solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p))
+ prune_to_dup_packages(solv, p, q);
+ /* select best packages, just look at prio and version */
+ policy_filter_unwanted(solv, q, POLICY_MODE_RECOMMEND);
+}
+
void
-solver_addbestrules(Solver *solv, int havebestinstalljobs)
+solver_addbestrules(Solver *solv, int havebestinstalljobs, int haslockjob)
{
Pool *pool = solv->pool;
Id p;
@@ -3271,12 +3334,26 @@ solver_addbestrules(Solver *solv, int havebestinstalljobs)
Rule *r;
Queue infoq;
int i, oldcnt;
+ Map *lockedmap = 0;
solv->bestrules = solv->nrules;
queue_init(&q);
queue_init(&q2);
queue_init(&infoq);
+ if (haslockjob)
+ {
+ int i;
+ lockedmap = solv_calloc(1, sizeof(Map));
+ map_init(lockedmap, pool->nsolvables);
+ for (i = 0, r = solv->rules + solv->jobrules; i < solv->ruletojob.count; i++, r++)
+ {
+ if (r->w2 || (solv->job.elements[solv->ruletojob.elements[i]] & SOLVER_JOBMASK) != SOLVER_LOCK)
+ continue;
+ p = r->p > 0 ? r->p : -r->p;
+ MAPSET(lockedmap, p);
+ }
+ }
if (havebestinstalljobs)
{
for (i = 0; i < solv->job.count; i += 2)
@@ -3284,7 +3361,7 @@ solver_addbestrules(Solver *solv, int havebestinstalljobs)
Id how = solv->job.elements[i];
if ((how & (SOLVER_JOBMASK | SOLVER_FORCEBEST)) == (SOLVER_INSTALL | SOLVER_FORCEBEST))
{
- int j;
+ int j, k;
Id p2, pp2;
for (j = 0; j < solv->ruletojob.count; j++)
{
@@ -3307,6 +3384,25 @@ solver_addbestrules(Solver *solv, int havebestinstalljobs)
policy_filter_unwanted(solv, &q, POLICY_MODE_RECOMMEND);
if (q.count == oldcnt)
continue; /* nothing filtered */
+ if (lockedmap)
+ {
+ FOR_RULELITERALS(p2, pp2, r)
+ {
+ if (p2 <= 0)
+ continue;
+ if (installed && pool->solvables[p2].repo == installed)
+ {
+ if (MAPTST(lockedmap, p2))
+ queue_pushunique(&q, p2); /* we always want that package */
+ }
+ else if (MAPTST(lockedmap, p2))
+ continue;
+ queue_push(&q2, p2);
+ }
+ policy_filter_unwanted(solv, &q2, POLICY_MODE_RECOMMEND);
+ for (k = 0; k < q2.count; k++)
+ queue_pushunique(&q, q2.elements[k]);
+ }
if (q2.count)
queue_insertn(&q, 0, q2.count, q2.elements);
p2 = queue_shift(&q);
@@ -3361,14 +3457,38 @@ solver_addbestrules(Solver *solv, int havebestinstalljobs)
if (p2 > 0)
queue_push(&q, p2);
}
- if (solv->update_targets && solv->update_targets->elements[p - installed->start])
- prune_to_update_targets(solv, solv->update_targets->elements + solv->update_targets->elements[p - installed->start], &q);
- if (solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p))
- prune_to_dup_packages(solv, p, &q);
- /* select best packages, just look at prio and version */
- policy_filter_unwanted(solv, &q, POLICY_MODE_RECOMMEND);
+ if (lockedmap)
+ {
+ queue_empty(&q2);
+ queue_insertn(&q2, 0, q.count, q.elements);
+ }
+ prune_best_update(solv, p, &q);
if (!q.count)
continue; /* orphaned */
+ if (lockedmap)
+ {
+ int j;
+ /* always ok to keep installed locked packages */
+ if (MAPTST(lockedmap, p))
+ queue_pushunique(&q2, p);
+ for (j = 0; j < q2.count; j++)
+ {
+ Id p2 = q2.elements[j];
+ if (pool->solvables[p2].repo == installed && MAPTST(lockedmap, p2))
+ queue_pushunique(&q, p2);
+ }
+ /* filter out locked packages */
+ for (i = j = 0; j < q2.count; j++)
+ {
+ Id p2 = q2.elements[j];
+ if (pool->solvables[p2].repo == installed || !MAPTST(lockedmap, p2))
+ q2.elements[i++] = p2;
+ }
+ queue_truncate(&q2, i);
+ prune_best_update(solv, p, &q2);
+ for (j = 0; j < q2.count; j++)
+ queue_pushunique(&q, q2.elements[j]);
+ }
if (solv->bestobeypolicy)
{
/* also filter the best of the feature rule packages and add them */
@@ -3380,13 +3500,20 @@ solver_addbestrules(Solver *solv, int havebestinstalljobs)
FOR_RULELITERALS(p2, pp2, r)
if (p2 > 0)
queue_push(&q2, p2);
- if (solv->update_targets && solv->update_targets->elements[p - installed->start])
- prune_to_update_targets(solv, solv->update_targets->elements + solv->update_targets->elements[p - installed->start], &q2);
- if (solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p))
- prune_to_dup_packages(solv, p, &q2);
- policy_filter_unwanted(solv, &q2, POLICY_MODE_RECOMMEND);
+ prune_best_update(solv, p, &q2);
for (j = 0; j < q2.count; j++)
queue_pushunique(&q, q2.elements[j]);
+ if (lockedmap)
+ {
+ queue_empty(&q2);
+ FOR_RULELITERALS(p2, pp2, r)
+ if (p2 > 0)
+ if (pool->solvables[p2].repo == installed || !MAPTST(lockedmap, p2))
+ queue_push(&q2, p2);
+ prune_best_update(solv, p, &q2);
+ for (j = 0; j < q2.count; j++)
+ queue_pushunique(&q, q2.elements[j]);
+ }
}
}
if (solv->allowuninstall || solv->allowuninstall_all || (solv->allowuninstallmap.size && MAPTST(&solv->allowuninstallmap, p - installed->start)))
@@ -3427,6 +3554,11 @@ solver_addbestrules(Solver *solv, int havebestinstalljobs)
queue_free(&q);
queue_free(&q2);
queue_free(&infoq);
+ if (lockedmap)
+ {
+ map_free(lockedmap);
+ solv_free(lockedmap);
+ }
}
diff --git a/src/rules.h b/src/rules.h
index 1db0551..6b8511f 100644
--- a/src/rules.h
+++ b/src/rules.h
@@ -129,7 +129,7 @@ extern void solver_addchoicerules(struct s_Solver *solv);
extern void solver_disablechoicerules(struct s_Solver *solv, Rule *r);
/* best rules */
-extern void solver_addbestrules(struct s_Solver *solv, int havebestinstalljobs);
+extern void solver_addbestrules(struct s_Solver *solv, int havebestinstalljobs, int haslockjob);
/* yumobs rules */
extern void solver_addyumobsrules(struct s_Solver *solv);
diff --git a/src/selection.c b/src/selection.c
index a160122..e58d731 100644
--- a/src/selection.c
+++ b/src/selection.c
@@ -21,7 +21,13 @@
#include "selection.h"
#include "solver.h"
#include "evr.h"
+#ifdef ENABLE_CONDA
+#include "conda.h"
+#endif
+#ifdef _WIN32
+#include "strfncs.h"
+#endif
static int
str2archid(Pool *pool, const char *arch)
@@ -1149,6 +1155,19 @@ selection_canon(Pool *pool, Queue *selection, const char *name, int flags)
flags |= SELECTION_NAME;
flags &= ~SELECTION_PROVIDES;
+#ifdef ENABLE_CONDA
+ if (pool->disttype == DISTTYPE_CONDA)
+ {
+ Id *wp, id = pool_conda_matchspec(pool, name);
+ if (!id)
+ return 0;
+ wp = pool_whatprovides_ptr(pool, id); /* check if there is a match */
+ if (!wp || !*wp)
+ return 0;
+ queue_push2(selection, SOLVER_SOLVABLE_PROVIDES, id);
+ return SELECTION_CANON;
+ }
+#endif
if (pool->disttype == DISTTYPE_DEB)
{
if ((r = strchr(name, '_')) == 0)
@@ -1552,7 +1571,7 @@ selection_make_matchdeps_common(Pool *pool, Queue *selection, const char *name,
revr = pool_str2id(pool, r, 1);
ret |= SELECTION_REL;
}
- if ((flags & SELECTION_GLOB) != 0 && !strpbrk(rname, "[*?") != 0)
+ if ((flags & SELECTION_GLOB) != 0 && strpbrk(rname, "[*?") == 0)
flags &= ~SELECTION_GLOB;
if ((flags & SELECTION_GLOB) == 0 && (flags & SELECTION_NOCASE) == 0 && (flags & SELECTION_MATCH_DEPSTR) == 0)
diff --git a/src/solvable.c b/src/solvable.c
index 331f290..d3d2d31 100644
--- a/src/solvable.c
+++ b/src/solvable.c
@@ -32,9 +32,9 @@ pool_solvable2str(Pool *pool, Solvable *s)
int nl, el, al;
char *p;
n = pool_id2str(pool, s->name);
- e = pool_id2str(pool, s->evr);
+ e = s->evr ? pool_id2str(pool, s->evr) : "";
/* XXX: may want to skip the epoch here */
- a = pool_id2str(pool, s->arch);
+ a = s->arch ? pool_id2str(pool, s->arch) : "";
nl = strlen(n);
el = strlen(e);
al = strlen(a);
@@ -58,6 +58,16 @@ pool_solvable2str(Pool *pool, Solvable *s)
p[nl + el] = pool->disttype == DISTTYPE_HAIKU ? '-' : '.';
strcpy(p + nl + el + 1, a);
}
+ if (pool->disttype == DISTTYPE_CONDA && solvable_lookup_type(s, SOLVABLE_BUILDFLAVOR))
+ {
+ Queue flavorq;
+ int i;
+ queue_init(&flavorq);
+ solvable_lookup_idarray(s, SOLVABLE_BUILDFLAVOR, &flavorq);
+ for (i = 0; i < flavorq.count; i++)
+ p = pool_tmpappend(pool, p, "-", pool_id2str(pool, flavorq.elements[i]));
+ queue_free(&flavorq);
+ }
return p;
}
@@ -110,10 +120,15 @@ solvable_lookup_str_joinarray(Solvable *s, Id keyname, const char *joinstr)
if (solvable_lookup_idarray(s, keyname, &q) && q.count)
{
Pool *pool = s->repo->pool;
- int i;
- str = pool_tmpjoin(pool, pool_id2str(pool, q.elements[0]), 0, 0);
- for (i = 1; i < q.count; i++)
- str = pool_tmpappend(pool, str, joinstr, pool_id2str(pool, q.elements[i]));
+ if (q.count == 1)
+ str = (char *)pool_id2str(pool, q.elements[0]);
+ else
+ {
+ int i;
+ str = pool_tmpjoin(pool, pool_id2str(pool, q.elements[0]), 0, 0);
+ for (i = 1; i < q.count; i++)
+ str = pool_tmpappend(pool, str, joinstr, pool_id2str(pool, q.elements[i]));
+ }
}
queue_free(&q);
return str;
@@ -126,7 +141,7 @@ solvable_lookup_str(Solvable *s, Id keyname)
if (!s->repo)
return 0;
str = repo_lookup_str(s->repo, s - s->repo->pool->solvables, keyname);
- if (!str && (keyname == SOLVABLE_LICENSE || keyname == SOLVABLE_GROUP))
+ if (!str && (keyname == SOLVABLE_LICENSE || keyname == SOLVABLE_GROUP || keyname == SOLVABLE_BUILDFLAVOR))
str = solvable_lookup_str_joinarray(s, keyname, ", ");
return str;
}
@@ -238,14 +253,14 @@ solvable_lookup_str_poollang(Solvable *s, Id keyname)
const char *
solvable_lookup_str_lang(Solvable *s, Id keyname, const char *lang, int usebase)
{
- if (s->repo)
- {
- Id id = pool_id2langid(s->repo->pool, keyname, lang, 0);
- if (id)
- return solvable_lookup_str_base(s, id, keyname, usebase);
- if (!usebase)
- return 0;
- }
+ Id id;
+ if (!s->repo)
+ return 0;
+ id = pool_id2langid(s->repo->pool, keyname, lang, 0);
+ if (id)
+ return solvable_lookup_str_base(s, id, keyname, usebase);
+ if (!usebase)
+ return 0;
return solvable_lookup_str(s, keyname);
}
@@ -278,25 +293,27 @@ solvable_lookup_void(Solvable *s, Id keyname)
int
solvable_lookup_bool(Solvable *s, Id keyname)
{
+ Id type;
if (!s->repo)
return 0;
/* historic nonsense: there are two ways of storing a bool, as num == 1 or void. test both. */
- if (repo_lookup_type(s->repo, s - s->repo->pool->solvables, keyname) == REPOKEY_TYPE_VOID)
+ type = repo_lookup_type(s->repo, s - s->repo->pool->solvables, keyname);
+ if (type == REPOKEY_TYPE_VOID)
return 1;
- return repo_lookup_num(s->repo, s - s->repo->pool->solvables, keyname, 0) == 1;
+ if (type == REPOKEY_TYPE_NUM || type == REPOKEY_TYPE_CONSTANT)
+ return repo_lookup_num(s->repo, s - s->repo->pool->solvables, keyname, 0) == 1;
+ return 0;
}
const unsigned char *
solvable_lookup_bin_checksum(Solvable *s, Id keyname, Id *typep)
{
- Repo *repo = s->repo;
-
- if (!repo)
+ if (!s->repo)
{
*typep = 0;
return 0;
}
- return repo_lookup_bin_checksum(repo, s - repo->pool->solvables, keyname, typep);
+ return repo_lookup_bin_checksum(s->repo, s - s->repo->pool->solvables, keyname, typep);
}
const char *
@@ -456,7 +473,7 @@ pool_create_state_maps(Pool *pool, Queue *installed, Map *installedmap, Map *con
int
solvable_identical(Solvable *s1, Solvable *s2)
{
- unsigned int bt1, bt2;
+ unsigned long long bt1, bt2;
Id rq1, rq2;
Id *reqp;
if (s1->name != s2->name)
@@ -504,6 +521,19 @@ solvable_identical(Solvable *s1, Solvable *s2)
if (rq1 != rq2)
return 0;
}
+ if (s1->repo && s1->repo->pool->disttype == DISTTYPE_CONDA)
+ {
+ /* check buildflavor and buildversion */
+ const char *str1, *str2;
+ str1 = solvable_lookup_str(s1, SOLVABLE_BUILDFLAVOR);
+ str2 = solvable_lookup_str(s2, SOLVABLE_BUILDFLAVOR);
+ if (str1 != str2 && (!str1 || !str2 || strcmp(str1, str2) != 0))
+ return 0;
+ str1 = solvable_lookup_str(s1, SOLVABLE_BUILDVERSION);
+ str2 = solvable_lookup_str(s2, SOLVABLE_BUILDVERSION);
+ if (str1 != str2 && (!str1 || !str2 || strcmp(str1, str2) != 0))
+ return 0;
+ }
return 1;
}
diff --git a/src/solver.c b/src/solver.c
index b335e6a..e7a9dc0 100644
--- a/src/solver.c
+++ b/src/solver.c
@@ -1382,7 +1382,6 @@ solver_free(Solver *solv)
queuep_free(&solv->recommendscplxq);
queuep_free(&solv->suggestscplxq);
queuep_free(&solv->brokenorphanrules);
- queuep_free(&solv->favorq);
queuep_free(&solv->recommendsruleq);
map_free(&solv->recommendsmap);
@@ -1914,6 +1913,8 @@ resolve_dependencies(Solver *solv, int level, int disablerules, Queue *dq)
}
if (i == solv->nrules)
i = 1;
+ if (solv->focus_best && solv->do_extra_reordering && i >= solv->featurerules)
+ continue;
r = solv->rules + i;
if (r->d < 0) /* ignore disabled rules */
continue;
@@ -3281,36 +3282,44 @@ add_complex_jobrules(Solver *solv, Id dep, int flags, int jobidx, int weak)
}
#endif
-/* sort by package id, last entry wins */
-static int
-setup_favormap_cmp(const void *ap, const void *bp, void *dp)
-{
- const Id *a = ap, *b = bp;
- if ((*a - *b) != 0)
- return *a - *b;
- return (b[1] < 0 ? -b[1] : b[1]) - (a[1] < 0 ? -a[1] : a[1]);
-}
-
static void
setup_favormap(Solver *solv)
{
- Queue *q = solv->favorq;
+ Queue *job = &solv->job;
Pool *pool = solv->pool;
- int i;
- Id oldp = 0;
- if (q->count > 2)
- solv_sort(q->elements, q->count / 2, 2 * sizeof(Id), setup_favormap_cmp, solv);
+ int i, idx;
+ Id p, pp, how, what, select;
+
+ solv_free(solv->favormap);
solv->favormap = solv_calloc(pool->nsolvables, sizeof(Id));
- solv->havedisfavored = 0;
- for (i = 0; i < q->count; i += 2)
+ for (i = 0; i < job->count; i += 2)
{
- Id p = q->elements[i];
- if (p == oldp)
+ how = job->elements[i];
+ if ((how & SOLVER_JOBMASK) != SOLVER_FAVOR && (how & SOLVER_JOBMASK) != SOLVER_DISFAVOR)
continue;
- oldp = p;
- solv->favormap[p] = q->elements[i + 1];
- if (q->elements[i + 1] < 0)
- solv->havedisfavored = 1;
+ what = job->elements[i + 1];
+ select = how & SOLVER_SELECTMASK;
+ idx = (how & SOLVER_JOBMASK) == SOLVER_FAVOR ? i + 1 : -(i + 1);
+ if (select == SOLVER_SOLVABLE_REPO)
+ {
+ Repo *repo = pool_id2repo(pool, what);
+ if (repo)
+ {
+ Solvable *s;
+ FOR_REPO_SOLVABLES(repo, p, s)
+ {
+ solv->favormap[p] = idx;
+ if (idx < 0)
+ solv->havedisfavored = 1;
+ }
+ }
+ }
+ FOR_JOB_SELECT(p, pp, select, what)
+ {
+ solv->favormap[p] = idx;
+ if (idx < 0)
+ solv->havedisfavored = 1;
+ }
}
}
@@ -3336,6 +3345,8 @@ solver_solve(Solver *solv, Queue *job)
int now, solve_start;
int needduprules = 0;
int hasbestinstalljob = 0;
+ int hasfavorjob = 0;
+ int haslockjob = 0;
solve_start = solv_timems(0);
@@ -3343,6 +3354,7 @@ solver_solve(Solver *solv, Queue *job)
POOL_DEBUG(SOLV_DEBUG_STATS, "solver started\n");
POOL_DEBUG(SOLV_DEBUG_STATS, "dosplitprovides=%d, noupdateprovide=%d, noinfarchcheck=%d\n", solv->dosplitprovides, solv->noupdateprovide, solv->noinfarchcheck);
POOL_DEBUG(SOLV_DEBUG_STATS, "allowuninstall=%d, allowdowngrade=%d, allownamechange=%d, allowarchchange=%d, allowvendorchange=%d\n", solv->allowuninstall, solv->allowdowngrade, solv->allownamechange, solv->allowarchchange, solv->allowvendorchange);
+ POOL_DEBUG(SOLV_DEBUG_STATS, "dupallowdowngrade=%d, dupallownamechange=%d, dupallowarchchange=%d, dupallowvendorchange=%d\n", solv->dup_allowdowngrade, solv->dup_allownamechange, solv->dup_allowarchchange, solv->dup_allowvendorchange);
POOL_DEBUG(SOLV_DEBUG_STATS, "promoteepoch=%d, forbidselfconflicts=%d\n", pool->promoteepoch, pool->forbidselfconflicts);
POOL_DEBUG(SOLV_DEBUG_STATS, "obsoleteusesprovides=%d, implicitobsoleteusesprovides=%d, obsoleteusescolors=%d, implicitobsoleteusescolors=%d\n", pool->obsoleteusesprovides, pool->implicitobsoleteusesprovides, pool->obsoleteusescolors, pool->implicitobsoleteusescolors);
POOL_DEBUG(SOLV_DEBUG_STATS, "dontinstallrecommended=%d, addalreadyrecommended=%d onlynamespacerecommended=%d\n", solv->dontinstallrecommended, solv->addalreadyrecommended, solv->only_namespace_recommended);
@@ -3914,6 +3926,8 @@ solver_solve(Solver *solv, Queue *job)
}
FOR_JOB_SELECT(p, pp, select, what)
solver_addjobrule(solv, installed && pool->solvables[p].repo == installed ? p : -p, 0, 0, i, weak);
+ if (solv->nrules != oldnrules)
+ haslockjob = 1;
break;
case SOLVER_DISTUPGRADE:
POOL_DEBUG(SOLV_DEBUG_JOB, "job: distupgrade %s\n", solver_select2str(pool, select, what));
@@ -3930,15 +3944,7 @@ solver_solve(Solver *solv, Queue *job)
case SOLVER_FAVOR:
case SOLVER_DISFAVOR:
POOL_DEBUG(SOLV_DEBUG_JOB, "job: %s %s\n", (how & SOLVER_JOBMASK) == SOLVER_FAVOR ? "favor" : "disfavor", solver_select2str(pool, select, what));
- FOR_JOB_SELECT(p, pp, select, what)
- {
- if (!solv->favorq)
- {
- solv->favorq = solv_calloc(1, sizeof(Queue));
- queue_init(solv->favorq);
- }
- queue_push2(solv->favorq, p, (how & SOLVER_JOBMASK) == SOLVER_FAVOR ? i + 1 : -(i + 1));
- }
+ hasfavorjob = 1;
break;
default:
POOL_DEBUG(SOLV_DEBUG_JOB, "job: unknown job\n");
@@ -3960,8 +3966,8 @@ solver_solve(Solver *solv, Queue *job)
assert(solv->ruletojob.count == solv->nrules - solv->jobrules);
solv->jobrules_end = solv->nrules;
- /* sort favorq and transform it into two maps */
- if (solv->favorq)
+ /* create favormap if we have favor jobs */
+ if (hasfavorjob)
setup_favormap(solv);
/* now create infarch and dup rules */
@@ -3981,7 +3987,7 @@ solver_solve(Solver *solv, Queue *job)
#endif
if (solv->bestupdatemap_all || solv->bestupdatemap.size || hasbestinstalljob)
- solver_addbestrules(solv, hasbestinstalljob);
+ solver_addbestrules(solv, hasbestinstalljob, haslockjob);
else
solv->bestrules = solv->bestrules_end = solv->bestrules_up = solv->nrules;
diff --git a/src/solver.h b/src/solver.h
index 93baa34..daf4f63 100644
--- a/src/solver.h
+++ b/src/solver.h
@@ -205,7 +205,6 @@ struct s_Solver {
Map allowuninstallmap; /* ok to uninstall those */
int allowuninstall_all;
- Queue *favorq;
Id *favormap; /* favor job index, > 0: favored, < 0: disfavored */
int havedisfavored; /* do we have disfavored packages? */
diff --git a/src/util.c b/src/util.c
index 65c8629..72426e0 100644
--- a/src/util.c
+++ b/src/util.c
@@ -11,7 +11,13 @@
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
-#include <sys/time.h>
+#include <fcntl.h>
+#ifdef _WIN32
+ #include <windows.h>
+ #include <io.h>
+#else
+ #include <sys/time.h>
+#endif
#include "util.h"
@@ -123,6 +129,9 @@ solv_strdup(const char *s)
unsigned int
solv_timems(unsigned int subtract)
{
+#ifdef _WIN32
+ return GetTickCount() - subtract;
+#else
struct timeval tv;
unsigned int r;
@@ -132,6 +141,17 @@ solv_timems(unsigned int subtract)
r += ((unsigned int)tv.tv_sec & 0xffff) * 1000;
r += (unsigned int)tv.tv_usec / 1000;
return r - subtract;
+#endif
+}
+
+int
+solv_setcloexec(int fd, int state)
+{
+ #ifdef _WIN32
+ return SetHandleInformation((HANDLE) _get_osfhandle(fd), HANDLE_FLAG_INHERIT, state ? 0 : HANDLE_FLAG_INHERIT);
+ #else
+ return fcntl(fd, F_SETFD, state ? FD_CLOEXEC : 0) == 0;
+ #endif
}
/* bsd's qsort_r has different arguments, so we define our
diff --git a/src/util.h b/src/util.h
index 5f7a93a..1dc5f32 100644
--- a/src/util.h
+++ b/src/util.h
@@ -34,6 +34,7 @@ extern void *solv_free(void *);
extern char *solv_strdup(const char *);
extern void solv_oom(size_t, size_t);
extern unsigned int solv_timems(unsigned int subtract);
+extern int solv_setcloexec(int fd, int state);
extern void solv_sort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *, void *), void *compard);
extern char *solv_dupjoin(const char *str1, const char *str2, const char *str3);
extern char *solv_dupappend(const char *str1, const char *str2, const char *str3);
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 92a5e7a..8a5cd8a 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -1,11 +1,21 @@
+IF (NOT WIN32)
+ SET (RUNTESTCASES_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/runtestcases.sh)
+ELSE ()
+ SET (RUNTESTCASES_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/runtestcases.bat)
+ENDIF ()
+
FOREACH(tcdir testcases libsolv-zypptestcases)
IF(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${tcdir}")
FILE(GLOB dirs "${CMAKE_CURRENT_SOURCE_DIR}/${tcdir}/[_a-zA-Z0-9]*")
FOREACH(dir ${dirs})
- IF(IS_DIRECTORY ${dir})
- FILE(RELATIVE_PATH myname "${CMAKE_CURRENT_SOURCE_DIR}/${tcdir}" ${dir})
- ADD_TEST(${myname} ${CMAKE_CURRENT_SOURCE_DIR}/runtestcases ${CMAKE_BINARY_DIR}/tools/testsolv ${dir})
- ENDIF(IS_DIRECTORY ${dir})
- ENDFOREACH(dir)
- ENDIF(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${tcdir}")
-ENDFOREACH(tcdir)
+ IF(IS_DIRECTORY ${dir})
+ FILE(RELATIVE_PATH myname "${CMAKE_CURRENT_SOURCE_DIR}/${tcdir}" ${dir})
+ FILE(TO_NATIVE_PATH "${CMAKE_BINARY_DIR}/tools/testsolv" TESTSOLV_BINARY)
+ FILE(TO_NATIVE_PATH "${dir}" dir)
+ STRING(REPLACE \\ \\\\ dir ${dir})
+ STRING(REPLACE \\ \\\\ TESTSOLV_BINARY ${TESTSOLV_BINARY})
+ ADD_TEST(${myname} ${RUNTESTCASES_SCRIPT} "${TESTSOLV_BINARY}" "${dir}")
+ ENDIF ()
+ ENDFOREACH ()
+ ENDIF ()
+ENDFOREACH () \ No newline at end of file
diff --git a/test/runtestcases.bat b/test/runtestcases.bat
new file mode 100644
index 0000000..4d4b5e0
--- /dev/null
+++ b/test/runtestcases.bat
@@ -0,0 +1,24 @@
+@ECHO OFF
+SETLOCAL EnableDelayedExpansion
+SET cmd=%1
+SET dir=%2
+
+SET ex=0
+
+FOR /f "tokens=*" %%G IN ('dir /b %dir%\*.t') DO (
+ ECHO "%dir%\%%G"
+ CALL %cmd% "%dir%\%%G"
+
+ IF !ERRORLEVEL! EQU 0 (
+ ECHO "PASSED";
+ ) ELSE (
+ IF !ERRORLEVEL! EQU 77 (
+ ECHO "SKIPPED";
+ ) ELSE (
+ ECHO "FAILED";
+ SET ex=1;
+ )
+ )
+)
+
+exit /B %ex% \ No newline at end of file
diff --git a/test/runtestcases b/test/runtestcases.sh
index 3d73663..3d73663 100755
--- a/test/runtestcases
+++ b/test/runtestcases.sh
diff --git a/test/testcases/focus/best.t b/test/testcases/focus/best.t
index a7a46b9..7527285 100644
--- a/test/testcases/focus/best.t
+++ b/test/testcases/focus/best.t
@@ -1,6 +1,7 @@
repo system 0 testtags <inline>
#>=Pkg: A 1 1 noarch
#>=Pkg: A2 1 1 noarch
+#>=Pkg: D 1 1 noarch
repo available 0 testtags <inline>
#>=Pkg: A 1 1 noarch
#>=Pkg: A 2 1 noarch
@@ -16,8 +17,15 @@ repo available 0 testtags <inline>
#>=Req: A2 = 2
#>=Pkg: C 1 1 noarch
#>=Req: B2
+#>=Pkg: D 2 1 noarch
system i686 rpm system
solverflags focusbest
job install name B
job install name C
+result transaction,problems <inline>
+#>install B-2-1.noarch@available
+#>install B2-2-1.noarch@available
+#>install C-1-1.noarch@available
+#>upgrade A-1-1.noarch@system A-2-1.noarch@available
+#>upgrade A2-1-1.noarch@system A2-2-1.noarch@available
diff --git a/test/testcases/focus/installed.t b/test/testcases/focus/installed.t
index 4f49d35..f272ee0 100644
--- a/test/testcases/focus/installed.t
+++ b/test/testcases/focus/installed.t
@@ -1,6 +1,7 @@
repo system 0 testtags <inline>
#>=Pkg: A 1 1 noarch
#>=Pkg: A2 1 1 noarch
+#>=Pkg: D 1 1 noarch
repo available 0 testtags <inline>
#>=Pkg: A 1 1 noarch
#>=Pkg: A 2 1 noarch
@@ -16,8 +17,13 @@ repo available 0 testtags <inline>
#>=Req: A2 = 2
#>=Pkg: C 1 1 noarch
#>=Req: B2
+#>=Pkg: D 2 1 noarch
system i686 rpm system
solverflags focusinstalled
job install name B
job install name C
+result transaction,problems <inline>
+#>install B-1-1.noarch@available
+#>install B2-1-1.noarch@available
+#>install C-1-1.noarch@available
diff --git a/test/testcases/focus/normal.t b/test/testcases/focus/normal.t
index 6e562a3..0056b8a 100644
--- a/test/testcases/focus/normal.t
+++ b/test/testcases/focus/normal.t
@@ -1,6 +1,7 @@
repo system 0 testtags <inline>
#>=Pkg: A 1 1 noarch
#>=Pkg: A2 1 1 noarch
+#>=Pkg: D 1 1 noarch
repo available 0 testtags <inline>
#>=Pkg: A 1 1 noarch
#>=Pkg: A 2 1 noarch
@@ -16,7 +17,13 @@ repo available 0 testtags <inline>
#>=Req: A2 = 2
#>=Pkg: C 1 1 noarch
#>=Req: B2
+#>=Pkg: D 2 1 noarch
system i686 rpm system
job install name B
job install name C
+result transaction,problems <inline>
+#>install B-2-1.noarch@available
+#>install B2-1-1.noarch@available
+#>install C-1-1.noarch@available
+#>upgrade A-1-1.noarch@system A-2-1.noarch@available
diff --git a/test/testcases/lock/best.t b/test/testcases/lock/best.t
new file mode 100644
index 0000000..5c992d6
--- /dev/null
+++ b/test/testcases/lock/best.t
@@ -0,0 +1,27 @@
+# test that locked packages trump best rules
+
+repo system 0 testtags <inline>
+#>=Pkg: b 1 1 i686
+repo available 0 testtags <inline>
+#>=Pkg: a 2 1 i686
+#>=Pkg: a 3 1 i686
+#>=Pkg: b 2 1 i686
+#>=Pkg: b 3 1 i686
+
+system i686 * system
+
+job install name a [forcebest]
+job lock name a = 3-1
+result transaction,problems <inline>
+#>install a-2-1.i686@available
+
+nextjob
+job update name b [forcebest]
+job lock name b = 3-1
+result transaction,problems <inline>
+#>upgrade b-1-1.i686@system b-2-1.i686@available
+
+nextjob
+job update name b [forcebest]
+job lock name b = 1-1
+result transaction,problems <inline>
diff --git a/test/testcases/lock/dup.t b/test/testcases/lock/dup.t
new file mode 100644
index 0000000..3c400da
--- /dev/null
+++ b/test/testcases/lock/dup.t
@@ -0,0 +1,25 @@
+# test that locked packages trump dup rules
+
+repo system 0 testtags <inline>
+#>=Pkg: a 1 1 i686
+repo available 0 testtags <inline>
+#>=Pkg: a 2 1 i686
+
+system i686 * system
+
+job distupgrade all packages
+job lock name a
+result transaction,problems <inline>
+
+# but we still get a problem if only the available packages
+# are locked
+#
+nextjob
+job distupgrade all packages
+job lock name a = 2-1
+result transaction,problems <inline>
+#>problem 1889163e info problem with installed package a-1-1.i686
+#>problem 1889163e solution 25ae2253 allow a-1-1.i686@system
+#>problem 1889163e solution 06ec856f deljob lock name a = 2-1
+#>problem 1889163e solution e5fc66c9 erase a-1-1.i686@system
+#>upgrade a-1-1.i686@system a-2-1.i686@available
diff --git a/test/testcases/lockstep/lockstep_install.t b/test/testcases/lockstep/lockstep_install.t
index f626da0..6e0a5e5 100644
--- a/test/testcases/lockstep/lockstep_install.t
+++ b/test/testcases/lockstep/lockstep_install.t
@@ -7,3 +7,6 @@ repo test 0 testtags <inline>
system x86_64 rpm system
poolflags implicitobsoleteusescolors
job install provides A(x32)
+result transaction,problems <inline>
+#>install A-1-1.i586@test
+#>install A-1-1.x86_64@test
diff --git a/test/testcases/lockstep/lockstep_update.t b/test/testcases/lockstep/lockstep_update.t
index 128bcc3..a70a339 100644
--- a/test/testcases/lockstep/lockstep_update.t
+++ b/test/testcases/lockstep/lockstep_update.t
@@ -9,3 +9,6 @@ repo test 0 testtags <inline>
system x86_64 rpm system
poolflags implicitobsoleteusescolors
job update all packages
+result transaction,problems <inline>
+#>upgrade A-1-1.i586@system A-2-1.i586@test
+#>upgrade A-1-1.x86_64@system A-2-1.x86_64@test
diff --git a/test/testcases/weakdeps/supplements_implicitobsoleteusescolors.t b/test/testcases/weakdeps/supplements_implicitobsoleteusescolors.t
index 6de4544..46a1139 100644
--- a/test/testcases/weakdeps/supplements_implicitobsoleteusescolors.t
+++ b/test/testcases/weakdeps/supplements_implicitobsoleteusescolors.t
@@ -12,9 +12,16 @@ repo test 0 testtags <inline>
#>=Req: XX
#>=Pkg: B2 1 1 i686
#>=Sup: A2
-system x86_64 * system
+system x86_64 rpm system
poolflags implicitobsoleteusescolors
job install name A
+result transaction,problems <inline>
+#>install A-1-1.noarch@test
+#>install B-1-1.x86_64@test
nextjob
job install name A2
+result transaction,problems <inline>
+#>install A2-1-1.noarch@test
+
+
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
index 802dc50..f19030e 100644
--- a/tools/CMakeLists.txt
+++ b/tools/CMakeLists.txt
@@ -4,7 +4,11 @@
ADD_LIBRARY (toolstuff STATIC common_write.c)
-SET (tools_list mergesolv dumpsolv installcheck testsolv repo2solv)
+IF (WIN32)
+ INCLUDE_DIRECTORIES (${PROJECT_SOURCE_DIR}/win32/)
+ENDIF ()
+
+SET (tools_list testsolv mergesolv dumpsolv installcheck testsolv)
IF (ENABLE_RPMDB)
ADD_EXECUTABLE (rpmdb2solv rpmdb2solv.c)
@@ -98,6 +102,19 @@ TARGET_LINK_LIBRARIES (appdata2solv toolstuff libsolvext libsolv ${SYSTEM_LIBRAR
SET (tools_list ${tools_list} appdata2solv)
ENDIF (ENABLE_APPDATA)
+IF (ENABLE_CONDA)
+ADD_EXECUTABLE (conda2solv conda2solv.c)
+TARGET_LINK_LIBRARIES (conda2solv toolstuff libsolvext libsolv ${SYSTEM_LIBRARIES})
+
+SET (tools_list ${tools_list} conda2solv)
+ENDIF (ENABLE_CONDA)
+
+IF (NOT WIN32)
+ADD_EXECUTABLE (repo2solv repo2solv.c )
+TARGET_LINK_LIBRARIES (repo2solv toolstuff libsolvext libsolv ${SYSTEM_LIBRARIES})
+SET(tools_list ${tools_list} repo2solv)
+ENDIF (NOT WIN32)
+
ADD_EXECUTABLE (dumpsolv dumpsolv.c )
TARGET_LINK_LIBRARIES (dumpsolv libsolv)
@@ -107,8 +124,4 @@ TARGET_LINK_LIBRARIES (mergesolv toolstuff libsolvext libsolv ${SYSTEM_LIBRARIES
ADD_EXECUTABLE (testsolv testsolv.c)
TARGET_LINK_LIBRARIES (testsolv libsolvext libsolv ${SYSTEM_LIBRARIES})
-ADD_EXECUTABLE (repo2solv repo2solv.c )
-TARGET_LINK_LIBRARIES (repo2solv toolstuff libsolvext libsolv ${SYSTEM_LIBRARIES})
-
-INSTALL (TARGETS ${tools_list} DESTINATION ${BIN_INSTALL_DIR})
-
+INSTALL (TARGETS ${tools_list} DESTINATION ${CMAKE_INSTALL_BINDIR})
diff --git a/tools/conda2solv.c b/tools/conda2solv.c
new file mode 100644
index 0000000..2b8f3c4
--- /dev/null
+++ b/tools/conda2solv.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2019, SUSE LLC
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * conda2solv.c
+ *
+ * parse a conda repository file
+ *
+ * reads from stdin
+ * writes to stdout
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "repo_conda.h"
+#include "solv_xfopen.h"
+#include "common_write.h"
+
+
+static void
+usage(int status)
+{
+ fprintf(stderr, "\nUsage:\n"
+ "conda2solv\n"
+ " reads a 'synthesis' repository from <stdin> and writes a .solv file to <stdout>\n"
+ " -h : print help & exit\n"
+ );
+ exit(status);
+}
+
+int
+main(int argc, char **argv)
+{
+ Pool *pool;
+ Repo *repo;
+ int c;
+
+ while ((c = getopt(argc, argv, "h")) >= 0)
+ {
+ switch(c)
+ {
+ case 'h':
+ usage(0);
+ break;
+ default:
+ usage(1);
+ break;
+ }
+ }
+ pool = pool_create();
+ repo = repo_create(pool, "<stdin>");
+ if (repo_add_conda(repo, stdin, 0))
+ {
+ fprintf(stderr, "conda2solv: %s\n", pool_errstr(pool));
+ exit(1);
+ }
+ repo_internalize(repo);
+ tool_write(repo, stdout);
+ pool_free(pool);
+ exit(0);
+}
diff --git a/tools/deb2solv.c b/tools/deb2solv.c
index ad27541..4fac3f0 100644
--- a/tools/deb2solv.c
+++ b/tools/deb2solv.c
@@ -15,6 +15,7 @@
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
+#include <getopt.h>
#include "util.h"
#include "pool.h"
@@ -57,7 +58,6 @@ main(int argc, char **argv)
Repo *repo;
FILE *fp;
char buf[4096], *p;
- const char *basefile = 0;
int is_repo = 0;
while ((c = getopt(argc, argv, "0:m:r")) >= 0)
diff --git a/tools/diskusagexml2solv.c b/tools/diskusagexml2solv.c
deleted file mode 100644
index 850b02c..0000000
--- a/tools/diskusagexml2solv.c
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-#include <sys/types.h>
-#include <limits.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "pool.h"
-#include "repo.h"
-#include "repo_diskusagexml.h"
-#include "common_write.h"
-
-static void
-usage(int status)
-{
- fprintf(stderr, "\nUsage:\n"
- "diskusagexml2solv [-h]\n"
- " reads a 'diskusage.xml' file from <stdin> and writes a .solv file to <stdout>\n"
- " -h : print help & exit\n"
- );
- exit(status);
-}
-
-int
-main(int argc, char **argv)
-{
- int c, flags = 0;
-
- Pool *pool = pool_create();
- Repo *repo = repo_create(pool, "<stdin>");
-
- while ((c = getopt(argc, argv, "h")) >= 0)
- {
- switch(c)
- {
- case 'h':
- usage(0);
- break;
- default:
- usage(1);
- break;
- }
- }
- if (repo_add_diskusagexml(repo, stdin, flags))
- {
- fprintf(stderr, "diskusagexml2solv: %s\n", pool_errstr(pool));
- exit(1);
- }
- tool_write(repo, stdout);
- pool_free(pool);
- exit(0);
-}
diff --git a/tools/dumpsolv.c b/tools/dumpsolv.c
index bdd521e..1307657 100644
--- a/tools/dumpsolv.c
+++ b/tools/dumpsolv.c
@@ -9,6 +9,7 @@
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
+#include <getopt.h>
static int with_attr;
static int dump_json;
diff --git a/tools/installcheck.c b/tools/installcheck.c
index 6c090d8..4eb66a0 100644
--- a/tools/installcheck.c
+++ b/tools/installcheck.c
@@ -34,6 +34,9 @@
#include "solver.h"
#include "solv_xfopen.h"
+#ifdef _WIN32
+#include "strfncs.h"
+#endif
void
usage(char** argv)
diff --git a/tools/mergesolv.c b/tools/mergesolv.c
index 8746ac6..a10c686 100644
--- a/tools/mergesolv.c
+++ b/tools/mergesolv.c
@@ -16,6 +16,7 @@
#include <stdlib.h>
#include <string.h>
#include <assert.h>
+#include <getopt.h>
#include "pool.h"
#include "repo_solv.h"
diff --git a/tools/testsolv.c b/tools/testsolv.c
index 3331fae..18dfcfe 100644
--- a/tools/testsolv.c
+++ b/tools/testsolv.c
@@ -1,6 +1,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
+#include <getopt.h>
#include "pool.h"
#include "repo.h"
diff --git a/win32/CMakeLists.txt b/win32/CMakeLists.txt
new file mode 100644
index 0000000..9a87af7
--- /dev/null
+++ b/win32/CMakeLists.txt
@@ -0,0 +1,9 @@
+INCLUDE_DIRECTORIES (${PROJECT_SOURCE_DIR}/win32)
+SET (WIN32_COMPAT_SOURCES
+ ${PROJECT_SOURCE_DIR}/win32/fnmatch.c
+ ${PROJECT_SOURCE_DIR}/win32/getopt.c
+ ${PROJECT_SOURCE_DIR}/win32/regcomp.c
+ ${PROJECT_SOURCE_DIR}/win32/regexec.c
+ ${PROJECT_SOURCE_DIR}/win32/strfncs.c
+ ${PROJECT_SOURCE_DIR}/win32/tre-mem.c
+) \ No newline at end of file
diff --git a/win32/LICENSE b/win32/LICENSE
new file mode 100644
index 0000000..c9ad4f4
--- /dev/null
+++ b/win32/LICENSE
@@ -0,0 +1,70 @@
+All files for the Windows compatibility layer are taken from musl,
+except for unistd.h and fmemopen.c.
+
+MUSL
+
+musl as a whole is licensed under the following standard MIT license:
+
+----------------------------------------------------------------------
+Copyright © 2005-2014 Rich Felker, et al.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER 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.
+----------------------------------------------------------------------
+
+
+TRE (tre.h, tre-mem.c)
+
+The TRE regular expression implementation (reg* and
+tre*) is Copyright © 2001-2008 Ville Laurikari and licensed
+under a 2-clause BSD license (license text in the source files). The
+included version has been heavily modified by Rich Felker in 2012, in
+the interests of size, simplicity, and namespace cleanliness.
+
+----------------------------------------------------------------------
+This is the license, copyright notice, and disclaimer for TRE, a regex
+matching package (library and tools) with support for approximate
+matching.
+
+Copyright (c) 2001-2009 Ville Laurikari <vl@iki.fi>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+----------------------------------------------------------------------
diff --git a/win32/fmemopen.c b/win32/fmemopen.c
new file mode 100644
index 0000000..ea88b08
--- /dev/null
+++ b/win32/fmemopen.c
@@ -0,0 +1,41 @@
+#include <stdio.h>
+#include <io.h>
+#include <fcntl.h>
+#include <windows.h>
+
+FILE *
+fmemopen(void *buf, size_t size, const char *mode)
+{
+ char temppath[MAX_PATH + 1];
+ char tempnam[MAX_PATH + 1];
+ DWORD l;
+ HANDLE fh;
+ FILE *fp;
+
+ if (strcmp(mode, "r") != 0 && strcmp(mode, "r+") != 0)
+ return 0;
+ l = GetTempPath(MAX_PATH, temppath);
+ if (!l || l >= MAX_PATH)
+ return 0;
+ if (!GetTempFileName(temppath, "solvtmp", 0, tempnam))
+ return 0;
+ fh = CreateFile(tempnam, DELETE | GENERIC_READ | GENERIC_WRITE, 0,
+ NULL, CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE,
+ NULL);
+ if (fh == INVALID_HANDLE_VALUE)
+ return 0;
+ fp = _fdopen(_open_osfhandle((intptr_t)fh, 0), "w+b");
+ if (!fp)
+ {
+ CloseHandle(fh);
+ return 0;
+ }
+ if (buf && size && fwrite(buf, size, 1, fp) != 1)
+ {
+ fclose(fp);
+ return 0;
+ }
+ rewind(fp);
+ return fp;
+}
diff --git a/win32/fnmatch.c b/win32/fnmatch.c
new file mode 100644
index 0000000..9e90852
--- /dev/null
+++ b/win32/fnmatch.c
@@ -0,0 +1,320 @@
+/*
+ * An implementation of what I call the "Sea of Stars" algorithm for
+ * POSIX fnmatch(). The basic idea is that we factor the pattern into
+ * a head component (which we match first and can reject without ever
+ * measuring the length of the string), an optional tail component
+ * (which only exists if the pattern contains at least one star), and
+ * an optional "sea of stars", a set of star-separated components
+ * between the head and tail. After the head and tail matches have
+ * been removed from the input string, the components in the "sea of
+ * stars" are matched sequentially by searching for their first
+ * occurrence past the end of the previous match.
+ *
+ * - Rich Felker, April 2012
+ */
+
+#include <string.h>
+#include <fnmatch.h>
+#include <stdlib.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#define END 0
+#define UNMATCHABLE -2
+#define BRACKET -3
+#define QUESTION -4
+#define STAR -5
+
+static int str_next(const char *str, size_t n, size_t *step)
+{
+ if (!n) {
+ *step = 0;
+ return 0;
+ }
+ if (str[0] >= 128U) {
+ wchar_t wc;
+ int k = mbtowc(&wc, str, n);
+ if (k<0) {
+ *step = 1;
+ return -1;
+ }
+ *step = k;
+ return wc;
+ }
+ *step = 1;
+ return str[0];
+}
+
+static int pat_next(const char *pat, size_t m, size_t *step, int flags)
+{
+ int esc = 0;
+ if (!m || !*pat) {
+ *step = 0;
+ return END;
+ }
+ *step = 1;
+ if (pat[0]=='\\' && pat[1] && !(flags & FNM_NOESCAPE)) {
+ *step = 2;
+ pat++;
+ esc = 1;
+ goto escaped;
+ }
+ if (pat[0]=='[') {
+ size_t k = 1;
+ if (k<m) if (pat[k] == '^' || pat[k] == '!') k++;
+ if (k<m) if (pat[k] == ']') k++;
+ for (; k<m && pat[k] && pat[k]!=']'; k++) {
+ if (k+1<m && pat[k+1] && pat[k]=='[' && (pat[k+1]==':' || pat[k+1]=='.' || pat[k+1]=='=')) {
+ int z = pat[k+1];
+ k+=2;
+ if (k<m && pat[k]) k++;
+ while (k<m && pat[k] && (pat[k-1]!=z || pat[k]!=']')) k++;
+ if (k==m || !pat[k]) break;
+ }
+ }
+ if (k==m || !pat[k]) {
+ *step = 1;
+ return '[';
+ }
+ *step = k+1;
+ return BRACKET;
+ }
+ if (pat[0] == '*')
+ return STAR;
+ if (pat[0] == '?')
+ return QUESTION;
+escaped:
+ if (pat[0] >= 128U) {
+ wchar_t wc;
+ int k = mbtowc(&wc, pat, m);
+ if (k<0) {
+ *step = 0;
+ return UNMATCHABLE;
+ }
+ *step = k + esc;
+ return wc;
+ }
+ return pat[0];
+}
+
+static int casefold(int k)
+{
+ int c = towupper(k);
+ return c == k ? towlower(k) : c;
+}
+
+static int match_bracket(const char *p, int k, int kfold)
+{
+ wchar_t wc;
+ int inv = 0;
+ p++;
+ if (*p=='^' || *p=='!') {
+ inv = 1;
+ p++;
+ }
+ if (*p==']') {
+ if (k==']') return !inv;
+ p++;
+ } else if (*p=='-') {
+ if (k=='-') return !inv;
+ p++;
+ }
+ wc = p[-1];
+ for (; *p != ']'; p++) {
+ if (p[0]=='-' && p[1]!=']') {
+ wchar_t wc2;
+ int l = mbtowc(&wc2, p+1, 4);
+ if (l < 0) return 0;
+ if (wc <= wc2)
+ if ((unsigned)k-wc <= wc2-wc ||
+ (unsigned)kfold-wc <= wc2-wc)
+ return !inv;
+ p += l-1;
+ continue;
+ }
+ if (p[0]=='[' && (p[1]==':' || p[1]=='.' || p[1]=='=')) {
+ const char *p0 = p+2;
+ int z = p[1];
+ p+=3;
+ while (p[-1]!=z || p[0]!=']') p++;
+ if (z == ':' && p-1-p0 < 16) {
+ char buf[16];
+ memcpy(buf, p0, p-1-p0);
+ buf[p-1-p0] = 0;
+ if (iswctype(k, wctype(buf)) ||
+ iswctype(kfold, wctype(buf)))
+ return !inv;
+ }
+ continue;
+ }
+ if (*p < 128U) {
+ wc = (unsigned char)*p;
+ } else {
+ int l = mbtowc(&wc, p, 4);
+ if (l < 0) return 0;
+ p += l-1;
+ }
+ if (wc==k || wc==kfold) return !inv;
+ }
+ return inv;
+}
+
+static int fnmatch_internal(const char *pat, size_t m, const char *str, size_t n, int flags)
+{
+ const char *p, *ptail, *endpat;
+ const char *s, *stail, *endstr;
+ size_t pinc, sinc, tailcnt=0;
+ int c, k, kfold;
+
+ if (flags & FNM_PERIOD) {
+ if (*str == '.' && *pat != '.')
+ return FNM_NOMATCH;
+ }
+ for (;;) {
+ switch ((c = pat_next(pat, m, &pinc, flags))) {
+ case UNMATCHABLE:
+ return FNM_NOMATCH;
+ case STAR:
+ pat++;
+ m--;
+ break;
+ default:
+ k = str_next(str, n, &sinc);
+ if (k <= 0)
+ return (c==END) ? 0 : FNM_NOMATCH;
+ str += sinc;
+ n -= sinc;
+ kfold = flags & FNM_CASEFOLD ? casefold(k) : k;
+ if (c == BRACKET) {
+ if (!match_bracket(pat, k, kfold))
+ return FNM_NOMATCH;
+ } else if (c != QUESTION && k != c && kfold != c) {
+ return FNM_NOMATCH;
+ }
+ pat+=pinc;
+ m-=pinc;
+ continue;
+ }
+ break;
+ }
+
+ /* Compute real pat length if it was initially unknown/-1 */
+ m = strnlen(pat, m);
+ endpat = pat + m;
+
+ /* Find the last * in pat and count chars needed after it */
+ for (p=ptail=pat; p<endpat; p+=pinc) {
+ switch (pat_next(p, endpat-p, &pinc, flags)) {
+ case UNMATCHABLE:
+ return FNM_NOMATCH;
+ case STAR:
+ tailcnt=0;
+ ptail = p+1;
+ break;
+ default:
+ tailcnt++;
+ break;
+ }
+ }
+
+ /* Past this point we need not check for UNMATCHABLE in pat,
+ * because all of pat has already been parsed once. */
+
+ /* Compute real str length if it was initially unknown/-1 */
+ n = strnlen(str, n);
+ endstr = str + n;
+ if (n < tailcnt) return FNM_NOMATCH;
+
+ /* Find the final tailcnt chars of str, accounting for UTF-8.
+ * On illegal sequences we may get it wrong, but in that case
+ * we necessarily have a matching failure anyway. */
+ for (s=endstr; s>str && tailcnt; tailcnt--) {
+ if (s[-1] < 128U || MB_CUR_MAX==1) s--;
+ else while ((unsigned char)*--s-0x80U<0x40 && s>str);
+ }
+ if (tailcnt) return FNM_NOMATCH;
+ stail = s;
+
+ /* Check that the pat and str tails match */
+ p = ptail;
+ for (;;) {
+ c = pat_next(p, endpat-p, &pinc, flags);
+ p += pinc;
+ if ((k = str_next(s, endstr-s, &sinc)) <= 0) {
+ if (c != END) return FNM_NOMATCH;
+ break;
+ }
+ s += sinc;
+ kfold = flags & FNM_CASEFOLD ? casefold(k) : k;
+ if (c == BRACKET) {
+ if (!match_bracket(p-pinc, k, kfold))
+ return FNM_NOMATCH;
+ } else if (c != QUESTION && k != c && kfold != c) {
+ return FNM_NOMATCH;
+ }
+ }
+
+ /* We're all done with the tails now, so throw them out */
+ endstr = stail;
+ endpat = ptail;
+
+ /* Match pattern components until there are none left */
+ while (pat<endpat) {
+ p = pat;
+ s = str;
+ for (;;) {
+ c = pat_next(p, endpat-p, &pinc, flags);
+ p += pinc;
+ /* Encountering * completes/commits a component */
+ if (c == STAR) {
+ pat = p;
+ str = s;
+ break;
+ }
+ k = str_next(s, endstr-s, &sinc);
+ if (!k)
+ return FNM_NOMATCH;
+ kfold = flags & FNM_CASEFOLD ? casefold(k) : k;
+ if (c == BRACKET) {
+ if (!match_bracket(p-pinc, k, kfold))
+ break;
+ } else if (c != QUESTION && k != c && kfold != c) {
+ break;
+ }
+ s += sinc;
+ }
+ if (c == STAR) continue;
+ /* If we failed, advance str, by 1 char if it's a valid
+ * char, or past all invalid bytes otherwise. */
+ k = str_next(str, endstr-str, &sinc);
+ if (k > 0) str += sinc;
+ else for (str++; str_next(str, endstr-str, &sinc)<0; str++);
+ }
+
+ return 0;
+}
+
+int fnmatch(const char *pat, const char *str, int flags)
+{
+ const char *s, *p;
+ size_t inc;
+ int c;
+ if (flags & FNM_PATHNAME) for (;;) {
+ for (s=str; *s && *s!='/'; s++);
+ for (p=pat; (c=pat_next(p, -1, &inc, flags))!=END && c!='/'; p+=inc);
+ if (c!=*s && (!*s || !(flags & FNM_LEADING_DIR)))
+ return FNM_NOMATCH;
+ if (fnmatch_internal(pat, p-pat, str, s-str, flags))
+ return FNM_NOMATCH;
+ if (!c) return 0;
+ str = s+1;
+ pat = p+inc;
+ } else if (flags & FNM_LEADING_DIR) {
+ for (s=str; *s; s++) {
+ if (*s != '/') continue;
+ if (!fnmatch_internal(pat, -1, str, s-str, flags))
+ return 0;
+ }
+ }
+ return fnmatch_internal(pat, -1, str, -1, flags);
+}
diff --git a/win32/fnmatch.h b/win32/fnmatch.h
new file mode 100644
index 0000000..f959321
--- /dev/null
+++ b/win32/fnmatch.h
@@ -0,0 +1,24 @@
+#ifndef _FNMATCH_H
+#define _FNMATCH_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define FNM_PATHNAME 0x1
+#define FNM_NOESCAPE 0x2
+#define FNM_PERIOD 0x4
+#define FNM_LEADING_DIR 0x8
+#define FNM_CASEFOLD 0x10
+#define FNM_FILE_NAME FNM_PATHNAME
+
+#define FNM_NOMATCH 1
+#define FNM_NOSYS (-1)
+
+int fnmatch(const char *, const char *, int);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/win32/getopt.c b/win32/getopt.c
new file mode 100644
index 0000000..1e7e451
--- /dev/null
+++ b/win32/getopt.c
@@ -0,0 +1,97 @@
+#include <unistd.h>
+#include <wchar.h>
+#include <string.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+char *optarg;
+int optind=1, opterr=1, optopt, __optpos, __optreset=0;
+
+#define optpos __optpos
+
+void __getopt_msg(const char *a, const char *b, const char *c, size_t l)
+{
+ FILE *f = stderr;
+ fputs(a, f)>=0
+ && fwrite(b, strlen(b), 1, f)
+ && fwrite(c, 1, l, f)==l
+ && putc('\n', f);
+}
+
+int getopt(int argc, char * const argv[], const char *optstring)
+{
+ int i;
+ wchar_t c, d;
+ int k, l;
+ char *optchar;
+
+ if (!optind || __optreset) {
+ __optreset = 0;
+ __optpos = 0;
+ optind = 1;
+ }
+
+ if (optind >= argc || !argv[optind])
+ return -1;
+
+ if (argv[optind][0] != '-') {
+ if (optstring[0] == '-') {
+ optarg = argv[optind++];
+ return 1;
+ }
+ return -1;
+ }
+
+ if (!argv[optind][1])
+ return -1;
+
+ if (argv[optind][1] == '-' && !argv[optind][2])
+ return optind++, -1;
+
+ if (!optpos) optpos++;
+ if ((k = mbtowc(&c, argv[optind]+optpos, MB_LEN_MAX)) < 0) {
+ k = 1;
+ c = 0xfffd; /* replacement char */
+ }
+ optchar = argv[optind]+optpos;
+ optpos += k;
+
+ if (!argv[optind][optpos]) {
+ optind++;
+ optpos = 0;
+ }
+
+ if (optstring[0] == '-' || optstring[0] == '+')
+ optstring++;
+
+ i = 0;
+ d = 0;
+ do {
+ l = mbtowc(&d, optstring+i, MB_LEN_MAX);
+ if (l>0) i+=l; else i++;
+ } while (l && d != c);
+
+ if (d != c || c == ':') {
+ optopt = c;
+ if (optstring[0] != ':' && opterr)
+ __getopt_msg(argv[0], ": unrecognized option: ", optchar, k);
+ return '?';
+ }
+ if (optstring[i] == ':') {
+ optarg = 0;
+ if (optstring[i+1] != ':' || optpos) {
+ optarg = argv[optind++] + optpos;
+ optpos = 0;
+ }
+ if (optind > argc) {
+ optopt = c;
+ if (optstring[0] == ':') return ':';
+ if (opterr) __getopt_msg(argv[0],
+ ": option requires an argument: ",
+ optchar, k);
+ return '?';
+ }
+ }
+ return c;
+}
diff --git a/win32/getopt.h b/win32/getopt.h
new file mode 100644
index 0000000..35cbd35
--- /dev/null
+++ b/win32/getopt.h
@@ -0,0 +1,30 @@
+#ifndef _GETOPT_H
+#define _GETOPT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int getopt(int, char * const [], const char *);
+extern char *optarg;
+extern int optind, opterr, optopt, optreset;
+
+struct option {
+ const char *name;
+ int has_arg;
+ int *flag;
+ int val;
+};
+
+int getopt_long(int, char *const *, const char *, const struct option *, int *);
+int getopt_long_only(int, char *const *, const char *, const struct option *, int *);
+
+#define no_argument 0
+#define required_argument 1
+#define optional_argument 2
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/win32/regcomp.c b/win32/regcomp.c
new file mode 100644
index 0000000..7ef8327
--- /dev/null
+++ b/win32/regcomp.c
@@ -0,0 +1,2953 @@
+/*
+ regcomp.c - TRE POSIX compatible regex compilation functions.
+
+ Copyright (c) 2001-2009 Ville Laurikari <vl@iki.fi>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+#include <string.h>
+#include <stdlib.h>
+#include <regex.h>
+#include <limits.h>
+#include <stdint.h>
+#include <ctype.h>
+
+#include "tre.h"
+
+#include <assert.h>
+
+/***********************************************************************
+ from tre-compile.h
+***********************************************************************/
+
+typedef struct {
+ int position;
+ int code_min;
+ int code_max;
+ int *tags;
+ int assertions;
+ tre_ctype_t class;
+ tre_ctype_t *neg_classes;
+ int backref;
+} tre_pos_and_tags_t;
+
+
+/***********************************************************************
+ from tre-ast.c and tre-ast.h
+***********************************************************************/
+
+/* The different AST node types. */
+typedef enum {
+ LITERAL,
+ CATENATION,
+ ITERATION,
+ UNION
+} tre_ast_type_t;
+
+/* Special subtypes of TRE_LITERAL. */
+#define EMPTY -1 /* Empty leaf (denotes empty string). */
+#define ASSERTION -2 /* Assertion leaf. */
+#define TAG -3 /* Tag leaf. */
+#define BACKREF -4 /* Back reference leaf. */
+
+#define IS_SPECIAL(x) ((x)->code_min < 0)
+#define IS_EMPTY(x) ((x)->code_min == EMPTY)
+#define IS_ASSERTION(x) ((x)->code_min == ASSERTION)
+#define IS_TAG(x) ((x)->code_min == TAG)
+#define IS_BACKREF(x) ((x)->code_min == BACKREF)
+
+
+/* A generic AST node. All AST nodes consist of this node on the top
+ level with `obj' pointing to the actual content. */
+typedef struct {
+ tre_ast_type_t type; /* Type of the node. */
+ void *obj; /* Pointer to actual node. */
+ int nullable;
+ int submatch_id;
+ int num_submatches;
+ int num_tags;
+ tre_pos_and_tags_t *firstpos;
+ tre_pos_and_tags_t *lastpos;
+} tre_ast_node_t;
+
+
+/* A "literal" node. These are created for assertions, back references,
+ tags, matching parameter settings, and all expressions that match one
+ character. */
+typedef struct {
+ long code_min;
+ long code_max;
+ int position;
+ tre_ctype_t class;
+ tre_ctype_t *neg_classes;
+} tre_literal_t;
+
+/* A "catenation" node. These are created when two regexps are concatenated.
+ If there are more than one subexpressions in sequence, the `left' part
+ holds all but the last, and `right' part holds the last subexpression
+ (catenation is left associative). */
+typedef struct {
+ tre_ast_node_t *left;
+ tre_ast_node_t *right;
+} tre_catenation_t;
+
+/* An "iteration" node. These are created for the "*", "+", "?", and "{m,n}"
+ operators. */
+typedef struct {
+ /* Subexpression to match. */
+ tre_ast_node_t *arg;
+ /* Minimum number of consecutive matches. */
+ int min;
+ /* Maximum number of consecutive matches. */
+ int max;
+ /* If 0, match as many characters as possible, if 1 match as few as
+ possible. Note that this does not always mean the same thing as
+ matching as many/few repetitions as possible. */
+ unsigned int minimal:1;
+} tre_iteration_t;
+
+/* An "union" node. These are created for the "|" operator. */
+typedef struct {
+ tre_ast_node_t *left;
+ tre_ast_node_t *right;
+} tre_union_t;
+
+
+static tre_ast_node_t *
+tre_ast_new_node(tre_mem_t mem, int type, void *obj)
+{
+ tre_ast_node_t *node = tre_mem_calloc(mem, sizeof *node);
+ if (!node || !obj)
+ return 0;
+ node->obj = obj;
+ node->type = type;
+ node->nullable = -1;
+ node->submatch_id = -1;
+ return node;
+}
+
+static tre_ast_node_t *
+tre_ast_new_literal(tre_mem_t mem, int code_min, int code_max, int position)
+{
+ tre_ast_node_t *node;
+ tre_literal_t *lit;
+
+ lit = tre_mem_calloc(mem, sizeof *lit);
+ node = tre_ast_new_node(mem, LITERAL, lit);
+ if (!node)
+ return 0;
+ lit->code_min = code_min;
+ lit->code_max = code_max;
+ lit->position = position;
+ return node;
+}
+
+static tre_ast_node_t *
+tre_ast_new_iter(tre_mem_t mem, tre_ast_node_t *arg, int min, int max, int minimal)
+{
+ tre_ast_node_t *node;
+ tre_iteration_t *iter;
+
+ iter = tre_mem_calloc(mem, sizeof *iter);
+ node = tre_ast_new_node(mem, ITERATION, iter);
+ if (!node)
+ return 0;
+ iter->arg = arg;
+ iter->min = min;
+ iter->max = max;
+ iter->minimal = minimal;
+ node->num_submatches = arg->num_submatches;
+ return node;
+}
+
+static tre_ast_node_t *
+tre_ast_new_union(tre_mem_t mem, tre_ast_node_t *left, tre_ast_node_t *right)
+{
+ tre_ast_node_t *node;
+ tre_union_t *un;
+
+ if (!left)
+ return right;
+ un = tre_mem_calloc(mem, sizeof *un);
+ node = tre_ast_new_node(mem, UNION, un);
+ if (!node || !right)
+ return 0;
+ un->left = left;
+ un->right = right;
+ node->num_submatches = left->num_submatches + right->num_submatches;
+ return node;
+}
+
+static tre_ast_node_t *
+tre_ast_new_catenation(tre_mem_t mem, tre_ast_node_t *left, tre_ast_node_t *right)
+{
+ tre_ast_node_t *node;
+ tre_catenation_t *cat;
+
+ if (!left)
+ return right;
+ cat = tre_mem_calloc(mem, sizeof *cat);
+ node = tre_ast_new_node(mem, CATENATION, cat);
+ if (!node)
+ return 0;
+ cat->left = left;
+ cat->right = right;
+ node->num_submatches = left->num_submatches + right->num_submatches;
+ return node;
+}
+
+
+/***********************************************************************
+ from tre-stack.c and tre-stack.h
+***********************************************************************/
+
+typedef struct tre_stack_rec tre_stack_t;
+
+/* Creates a new stack object. `size' is initial size in bytes, `max_size'
+ is maximum size, and `increment' specifies how much more space will be
+ allocated with realloc() if all space gets used up. Returns the stack
+ object or NULL if out of memory. */
+static tre_stack_t *
+tre_stack_new(int size, int max_size, int increment);
+
+/* Frees the stack object. */
+static void
+tre_stack_destroy(tre_stack_t *s);
+
+/* Returns the current number of objects in the stack. */
+static int
+tre_stack_num_objects(tre_stack_t *s);
+
+/* Each tre_stack_push_*(tre_stack_t *s, <type> value) function pushes
+ `value' on top of stack `s'. Returns REG_ESPACE if out of memory.
+ This tries to realloc() more space before failing if maximum size
+ has not yet been reached. Returns REG_OK if successful. */
+#define declare_pushf(typetag, type) \
+ static reg_errcode_t tre_stack_push_ ## typetag(tre_stack_t *s, type value)
+
+declare_pushf(voidptr, void *);
+declare_pushf(int, int);
+
+/* Each tre_stack_pop_*(tre_stack_t *s) function pops the topmost
+ element off of stack `s' and returns it. The stack must not be
+ empty. */
+#define declare_popf(typetag, type) \
+ static type tre_stack_pop_ ## typetag(tre_stack_t *s)
+
+declare_popf(voidptr, void *);
+declare_popf(int, int);
+
+/* Just to save some typing. */
+#define STACK_PUSH(s, typetag, value) \
+ do \
+ { \
+ status = tre_stack_push_ ## typetag(s, value); \
+ } \
+ while (/*CONSTCOND*/0)
+
+#define STACK_PUSHX(s, typetag, value) \
+ { \
+ status = tre_stack_push_ ## typetag(s, value); \
+ if (status != REG_OK) \
+ break; \
+ }
+
+#define STACK_PUSHR(s, typetag, value) \
+ { \
+ reg_errcode_t _status; \
+ _status = tre_stack_push_ ## typetag(s, value); \
+ if (_status != REG_OK) \
+ return _status; \
+ }
+
+union tre_stack_item {
+ void *voidptr_value;
+ int int_value;
+};
+
+struct tre_stack_rec {
+ int size;
+ int max_size;
+ int increment;
+ int ptr;
+ union tre_stack_item *stack;
+};
+
+
+static tre_stack_t *
+tre_stack_new(int size, int max_size, int increment)
+{
+ tre_stack_t *s;
+
+ s = xmalloc(sizeof(*s));
+ if (s != NULL)
+ {
+ s->stack = xmalloc(sizeof(*s->stack) * size);
+ if (s->stack == NULL)
+ {
+ xfree(s);
+ return NULL;
+ }
+ s->size = size;
+ s->max_size = max_size;
+ s->increment = increment;
+ s->ptr = 0;
+ }
+ return s;
+}
+
+static void
+tre_stack_destroy(tre_stack_t *s)
+{
+ xfree(s->stack);
+ xfree(s);
+}
+
+static int
+tre_stack_num_objects(tre_stack_t *s)
+{
+ return s->ptr;
+}
+
+static reg_errcode_t
+tre_stack_push(tre_stack_t *s, union tre_stack_item value)
+{
+ if (s->ptr < s->size)
+ {
+ s->stack[s->ptr] = value;
+ s->ptr++;
+ }
+ else
+ {
+ if (s->size >= s->max_size)
+ {
+ return REG_ESPACE;
+ }
+ else
+ {
+ union tre_stack_item *new_buffer;
+ int new_size;
+ new_size = s->size + s->increment;
+ if (new_size > s->max_size)
+ new_size = s->max_size;
+ new_buffer = xrealloc(s->stack, sizeof(*new_buffer) * new_size);
+ if (new_buffer == NULL)
+ {
+ return REG_ESPACE;
+ }
+ assert(new_size > s->size);
+ s->size = new_size;
+ s->stack = new_buffer;
+ tre_stack_push(s, value);
+ }
+ }
+ return REG_OK;
+}
+
+#define define_pushf(typetag, type) \
+ declare_pushf(typetag, type) { \
+ union tre_stack_item item; \
+ item.typetag ## _value = value; \
+ return tre_stack_push(s, item); \
+}
+
+define_pushf(int, int)
+define_pushf(voidptr, void *)
+
+#define define_popf(typetag, type) \
+ declare_popf(typetag, type) { \
+ return s->stack[--s->ptr].typetag ## _value; \
+ }
+
+define_popf(int, int)
+define_popf(voidptr, void *)
+
+
+/***********************************************************************
+ from tre-parse.c and tre-parse.h
+***********************************************************************/
+
+/* Parse context. */
+typedef struct {
+ /* Memory allocator. The AST is allocated using this. */
+ tre_mem_t mem;
+ /* Stack used for keeping track of regexp syntax. */
+ tre_stack_t *stack;
+ /* The parsed node after a parse function returns. */
+ tre_ast_node_t *n;
+ /* Position in the regexp pattern after a parse function returns. */
+ const char *s;
+ /* The first character of the last subexpression parsed. */
+ const char *start;
+ /* Current submatch ID. */
+ int submatch_id;
+ /* Current position (number of literal). */
+ int position;
+ /* The highest back reference or -1 if none seen so far. */
+ int max_backref;
+ /* Compilation flags. */
+ int cflags;
+} tre_parse_ctx_t;
+
+/* Some macros for expanding \w, \s, etc. */
+static const struct {
+ char c;
+ const char *expansion;
+} tre_macros[] = {
+ {'t', "\t"}, {'n', "\n"}, {'r', "\r"},
+ {'f', "\f"}, {'a', "\a"}, {'e', "\033"},
+ {'w', "[[:alnum:]_]"}, {'W', "[^[:alnum:]_]"}, {'s', "[[:space:]]"},
+ {'S', "[^[:space:]]"}, {'d', "[[:digit:]]"}, {'D', "[^[:digit:]]"},
+ { 0, 0 }
+};
+
+/* Expands a macro delimited by `regex' and `regex_end' to `buf', which
+ must have at least `len' items. Sets buf[0] to zero if the there
+ is no match in `tre_macros'. */
+static const char *tre_expand_macro(const char *s)
+{
+ int i;
+ for (i = 0; tre_macros[i].c && tre_macros[i].c != *s; i++);
+ return tre_macros[i].expansion;
+}
+
+static int
+tre_compare_lit(const void *a, const void *b)
+{
+ const tre_literal_t *const *la = a;
+ const tre_literal_t *const *lb = b;
+ /* assumes the range of valid code_min is < INT_MAX */
+ return la[0]->code_min - lb[0]->code_min;
+}
+
+struct literals {
+ tre_mem_t mem;
+ tre_literal_t **a;
+ int len;
+ int cap;
+};
+
+static tre_literal_t *tre_new_lit(struct literals *p)
+{
+ tre_literal_t **a;
+ if (p->len >= p->cap) {
+ if (p->cap >= 1<<15)
+ return 0;
+ p->cap *= 2;
+ a = xrealloc(p->a, p->cap * sizeof *p->a);
+ if (!a)
+ return 0;
+ p->a = a;
+ }
+ a = p->a + p->len++;
+ *a = tre_mem_calloc(p->mem, sizeof **a);
+ return *a;
+}
+
+static int add_icase_literals(struct literals *ls, int min, int max)
+{
+ tre_literal_t *lit;
+ int b, e, c;
+ for (c=min; c<=max; ) {
+ /* assumes islower(c) and isupper(c) are exclusive
+ and toupper(c)!=c if islower(c).
+ multiple opposite case characters are not supported */
+ if (tre_islower(c)) {
+ b = e = tre_toupper(c);
+ for (c++, e++; c<=max; c++, e++)
+ if (tre_toupper(c) != e) break;
+ } else if (tre_isupper(c)) {
+ b = e = tre_tolower(c);
+ for (c++, e++; c<=max; c++, e++)
+ if (tre_tolower(c) != e) break;
+ } else {
+ c++;
+ continue;
+ }
+ lit = tre_new_lit(ls);
+ if (!lit)
+ return -1;
+ lit->code_min = b;
+ lit->code_max = e-1;
+ lit->position = -1;
+ }
+ return 0;
+}
+
+
+/* Maximum number of character classes in a negated bracket expression. */
+#define MAX_NEG_CLASSES 64
+
+struct neg {
+ int negate;
+ int len;
+ tre_ctype_t a[MAX_NEG_CLASSES];
+};
+
+// TODO: parse bracket into a set of non-overlapping [lo,hi] ranges
+
+/*
+bracket grammar:
+Bracket = '[' List ']' | '[^' List ']'
+List = Term | List Term
+Term = Char | Range | Chclass | Eqclass
+Range = Char '-' Char | Char '-' '-'
+Char = Coll | coll_single
+Meta = ']' | '-'
+Coll = '[.' coll_single '.]' | '[.' coll_multi '.]' | '[.' Meta '.]'
+Eqclass = '[=' coll_single '=]' | '[=' coll_multi '=]'
+Chclass = '[:' class ':]'
+
+coll_single is a single char collating element but it can be
+ '-' only at the beginning or end of a List and
+ ']' only at the beginning of a List and
+ '^' anywhere except after the openning '['
+*/
+
+static reg_errcode_t parse_bracket_terms(tre_parse_ctx_t *ctx, const char *s, struct literals *ls, struct neg *neg)
+{
+ const char *start = s;
+ tre_ctype_t class;
+ int min, max;
+ wchar_t wc;
+ int len;
+
+ for (;;) {
+ class = 0;
+ len = mbtowc(&wc, s, -1);
+ if (len <= 0)
+ return *s ? REG_BADPAT : REG_EBRACK;
+ if (*s == ']' && s != start) {
+ ctx->s = s+1;
+ return REG_OK;
+ }
+ if (*s == '-' && s != start && s[1] != ']' &&
+ /* extension: [a-z--@] is accepted as [a-z]|[--@] */
+ (s[1] != '-' || s[2] == ']'))
+ return REG_ERANGE;
+ if (*s == '[' && (s[1] == '.' || s[1] == '='))
+ /* collating symbols and equivalence classes are not supported */
+ return REG_ECOLLATE;
+ if (*s == '[' && s[1] == ':') {
+ char tmp[CHARCLASS_NAME_MAX+1];
+ s += 2;
+ for (len=0; len < CHARCLASS_NAME_MAX && s[len]; len++) {
+ if (s[len] == ':') {
+ memcpy(tmp, s, len);
+ tmp[len] = 0;
+ class = tre_ctype(tmp);
+ break;
+ }
+ }
+ if (!class || s[len+1] != ']')
+ return REG_ECTYPE;
+ min = 0;
+ max = TRE_CHAR_MAX;
+ s += len+2;
+ } else {
+ min = max = wc;
+ s += len;
+ if (*s == '-' && s[1] != ']') {
+ s++;
+ len = mbtowc(&wc, s, -1);
+ max = wc;
+ /* XXX - Should use collation order instead of
+ encoding values in character ranges. */
+ if (len <= 0 || min > max)
+ return REG_ERANGE;
+ s += len;
+ }
+ }
+
+ if (class && neg->negate) {
+ if (neg->len >= MAX_NEG_CLASSES)
+ return REG_ESPACE;
+ neg->a[neg->len++] = class;
+ } else {
+ tre_literal_t *lit = tre_new_lit(ls);
+ if (!lit)
+ return REG_ESPACE;
+ lit->code_min = min;
+ lit->code_max = max;
+ lit->class = class;
+ lit->position = -1;
+
+ /* Add opposite-case codepoints if REG_ICASE is present.
+ It seems that POSIX requires that bracket negation
+ should happen before case-folding, but most practical
+ implementations do it the other way around. Changing
+ the order would need efficient representation of
+ case-fold ranges and bracket range sets even with
+ simple patterns so this is ok for now. */
+ if (ctx->cflags & REG_ICASE && !class)
+ if (add_icase_literals(ls, min, max))
+ return REG_ESPACE;
+ }
+ }
+}
+
+static reg_errcode_t parse_bracket(tre_parse_ctx_t *ctx, const char *s)
+{
+ int i, max, min, negmax, negmin;
+ tre_ast_node_t *node = 0, *n;
+ tre_ctype_t *nc = 0;
+ tre_literal_t *lit;
+ struct literals ls;
+ struct neg neg;
+ reg_errcode_t err;
+
+ ls.mem = ctx->mem;
+ ls.len = 0;
+ ls.cap = 32;
+ ls.a = xmalloc(ls.cap * sizeof *ls.a);
+ if (!ls.a)
+ return REG_ESPACE;
+ neg.len = 0;
+ neg.negate = *s == '^';
+ if (neg.negate)
+ s++;
+
+ err = parse_bracket_terms(ctx, s, &ls, &neg);
+ if (err != REG_OK)
+ goto parse_bracket_done;
+
+ if (neg.negate) {
+ /*
+ * With REG_NEWLINE, POSIX requires that newlines are not matched by
+ * any form of a non-matching list.
+ */
+ if (ctx->cflags & REG_NEWLINE) {
+ lit = tre_new_lit(&ls);
+ if (!lit) {
+ err = REG_ESPACE;
+ goto parse_bracket_done;
+ }
+ lit->code_min = '\n';
+ lit->code_max = '\n';
+ lit->position = -1;
+ }
+ /* Sort the array if we need to negate it. */
+ qsort(ls.a, ls.len, sizeof *ls.a, tre_compare_lit);
+ /* extra lit for the last negated range */
+ lit = tre_new_lit(&ls);
+ if (!lit) {
+ err = REG_ESPACE;
+ goto parse_bracket_done;
+ }
+ lit->code_min = TRE_CHAR_MAX+1;
+ lit->code_max = TRE_CHAR_MAX+1;
+ lit->position = -1;
+ /* negated classes */
+ if (neg.len) {
+ nc = tre_mem_alloc(ctx->mem, (neg.len+1)*sizeof *neg.a);
+ if (!nc) {
+ err = REG_ESPACE;
+ goto parse_bracket_done;
+ }
+ memcpy(nc, neg.a, neg.len*sizeof *neg.a);
+ nc[neg.len] = 0;
+ }
+ }
+
+ /* Build a union of the items in the array, negated if necessary. */
+ negmax = negmin = 0;
+ for (i = 0; i < ls.len; i++) {
+ lit = ls.a[i];
+ min = lit->code_min;
+ max = lit->code_max;
+ if (neg.negate) {
+ if (min <= negmin) {
+ /* Overlap. */
+ negmin = MAX(max + 1, negmin);
+ continue;
+ }
+ negmax = min - 1;
+ lit->code_min = negmin;
+ lit->code_max = negmax;
+ negmin = max + 1;
+ }
+ lit->position = ctx->position;
+ lit->neg_classes = nc;
+ n = tre_ast_new_node(ctx->mem, LITERAL, lit);
+ node = tre_ast_new_union(ctx->mem, node, n);
+ if (!node) {
+ err = REG_ESPACE;
+ break;
+ }
+ }
+
+parse_bracket_done:
+ xfree(ls.a);
+ ctx->position++;
+ ctx->n = node;
+ return err;
+}
+
+static const char *parse_dup_count(const char *s, int *n)
+{
+ *n = -1;
+ if (!isdigit(*s))
+ return s;
+ *n = 0;
+ for (;;) {
+ *n = 10 * *n + (*s - '0');
+ s++;
+ if (!isdigit(*s) || *n > RE_DUP_MAX)
+ break;
+ }
+ return s;
+}
+
+static const char *parse_dup(const char *s, int ere, int *pmin, int *pmax)
+{
+ int min, max;
+
+ s = parse_dup_count(s, &min);
+ if (*s == ',')
+ s = parse_dup_count(s+1, &max);
+ else
+ max = min;
+
+ if (
+ (max < min && max >= 0) ||
+ max > RE_DUP_MAX ||
+ min > RE_DUP_MAX ||
+ min < 0 ||
+ (!ere && *s++ != '\\') ||
+ *s++ != '}'
+ )
+ return 0;
+ *pmin = min;
+ *pmax = max;
+ return s;
+}
+
+static int hexval(unsigned c)
+{
+ if (c-'0'<10) return c-'0';
+ c |= 32;
+ if (c-'a'<6) return c-'a'+10;
+ return -1;
+}
+
+static reg_errcode_t marksub(tre_parse_ctx_t *ctx, tre_ast_node_t *node, int subid)
+{
+ if (node->submatch_id >= 0) {
+ tre_ast_node_t *n = tre_ast_new_literal(ctx->mem, EMPTY, -1, -1);
+ if (!n)
+ return REG_ESPACE;
+ n = tre_ast_new_catenation(ctx->mem, n, node);
+ if (!n)
+ return REG_ESPACE;
+ n->num_submatches = node->num_submatches;
+ node = n;
+ }
+ node->submatch_id = subid;
+ node->num_submatches++;
+ ctx->n = node;
+ return REG_OK;
+}
+
+/*
+BRE grammar:
+Regex = Branch | '^' | '$' | '^$' | '^' Branch | Branch '$' | '^' Branch '$'
+Branch = Atom | Branch Atom
+Atom = char | quoted_char | '.' | Bracket | Atom Dup | '\(' Branch '\)' | back_ref
+Dup = '*' | '\{' Count '\}' | '\{' Count ',\}' | '\{' Count ',' Count '\}'
+
+(leading ^ and trailing $ in a sub expr may be an anchor or literal as well)
+
+ERE grammar:
+Regex = Branch | Regex '|' Branch
+Branch = Atom | Branch Atom
+Atom = char | quoted_char | '.' | Bracket | Atom Dup | '(' Regex ')' | '^' | '$'
+Dup = '*' | '+' | '?' | '{' Count '}' | '{' Count ',}' | '{' Count ',' Count '}'
+
+(a*+?, ^*, $+, \X, {, (|a) are unspecified)
+*/
+
+static reg_errcode_t parse_atom(tre_parse_ctx_t *ctx, const char *s)
+{
+ int len, ere = ctx->cflags & REG_EXTENDED;
+ const char *p;
+ tre_ast_node_t *node;
+ wchar_t wc;
+ switch (*s) {
+ case '[':
+ return parse_bracket(ctx, s+1);
+ case '\\':
+ p = tre_expand_macro(s+1);
+ if (p) {
+ /* assume \X expansion is a single atom */
+ reg_errcode_t err = parse_atom(ctx, p);
+ ctx->s = s+2;
+ return err;
+ }
+ /* extensions: \b, \B, \<, \>, \xHH \x{HHHH} */
+ switch (*++s) {
+ case 0:
+ return REG_EESCAPE;
+ case 'b':
+ node = tre_ast_new_literal(ctx->mem, ASSERTION, ASSERT_AT_WB, -1);
+ break;
+ case 'B':
+ node = tre_ast_new_literal(ctx->mem, ASSERTION, ASSERT_AT_WB_NEG, -1);
+ break;
+ case '<':
+ node = tre_ast_new_literal(ctx->mem, ASSERTION, ASSERT_AT_BOW, -1);
+ break;
+ case '>':
+ node = tre_ast_new_literal(ctx->mem, ASSERTION, ASSERT_AT_EOW, -1);
+ break;
+ case 'x':
+ s++;
+ int i, v = 0, c;
+ len = 2;
+ if (*s == '{') {
+ len = 8;
+ s++;
+ }
+ for (i=0; i<len && v<0x110000; i++) {
+ c = hexval(s[i]);
+ if (c < 0) break;
+ v = 16*v + c;
+ }
+ s += i;
+ if (len == 8) {
+ if (*s != '}')
+ return REG_EBRACE;
+ s++;
+ }
+ node = tre_ast_new_literal(ctx->mem, v, v, ctx->position++);
+ s--;
+ break;
+ case '{':
+ case '+':
+ case '?':
+ /* extension: treat \+, \? as repetitions in BRE */
+ /* reject repetitions after empty expression in BRE */
+ if (!ere)
+ return REG_BADRPT;
+ case '|':
+ /* extension: treat \| as alternation in BRE */
+ if (!ere) {
+ node = tre_ast_new_literal(ctx->mem, EMPTY, -1, -1);
+ s--;
+ goto end;
+ }
+ /* fallthrough */
+ default:
+ if (!ere && (unsigned)*s-'1' < 9) {
+ /* back reference */
+ int val = *s - '0';
+ node = tre_ast_new_literal(ctx->mem, BACKREF, val, ctx->position++);
+ ctx->max_backref = MAX(val, ctx->max_backref);
+ } else {
+ /* extension: accept unknown escaped char
+ as a literal */
+ goto parse_literal;
+ }
+ }
+ s++;
+ break;
+ case '.':
+ if (ctx->cflags & REG_NEWLINE) {
+ tre_ast_node_t *tmp1, *tmp2;
+ tmp1 = tre_ast_new_literal(ctx->mem, 0, '\n'-1, ctx->position++);
+ tmp2 = tre_ast_new_literal(ctx->mem, '\n'+1, TRE_CHAR_MAX, ctx->position++);
+ if (tmp1 && tmp2)
+ node = tre_ast_new_union(ctx->mem, tmp1, tmp2);
+ else
+ node = 0;
+ } else {
+ node = tre_ast_new_literal(ctx->mem, 0, TRE_CHAR_MAX, ctx->position++);
+ }
+ s++;
+ break;
+ case '^':
+ /* '^' has a special meaning everywhere in EREs, and at beginning of BRE. */
+ if (!ere && s != ctx->start)
+ goto parse_literal;
+ node = tre_ast_new_literal(ctx->mem, ASSERTION, ASSERT_AT_BOL, -1);
+ s++;
+ break;
+ case '$':
+ /* '$' is special everywhere in EREs, and at the end of a BRE subexpression. */
+ if (!ere && s[1] && (s[1]!='\\'|| (s[2]!=')' && s[2]!='|')))
+ goto parse_literal;
+ node = tre_ast_new_literal(ctx->mem, ASSERTION, ASSERT_AT_EOL, -1);
+ s++;
+ break;
+ case '*':
+ case '{':
+ case '+':
+ case '?':
+ /* reject repetitions after empty expression in ERE */
+ if (ere)
+ return REG_BADRPT;
+ case '|':
+ if (!ere)
+ goto parse_literal;
+ case 0:
+ node = tre_ast_new_literal(ctx->mem, EMPTY, -1, -1);
+ break;
+ default:
+parse_literal:
+ len = mbtowc(&wc, s, -1);
+ if (len < 0)
+ return REG_BADPAT;
+ if (ctx->cflags & REG_ICASE && (tre_isupper(wc) || tre_islower(wc))) {
+ tre_ast_node_t *tmp1, *tmp2;
+ /* multiple opposite case characters are not supported */
+ tmp1 = tre_ast_new_literal(ctx->mem, tre_toupper(wc), tre_toupper(wc), ctx->position);
+ tmp2 = tre_ast_new_literal(ctx->mem, tre_tolower(wc), tre_tolower(wc), ctx->position);
+ if (tmp1 && tmp2)
+ node = tre_ast_new_union(ctx->mem, tmp1, tmp2);
+ else
+ node = 0;
+ } else {
+ node = tre_ast_new_literal(ctx->mem, wc, wc, ctx->position);
+ }
+ ctx->position++;
+ s += len;
+ break;
+ }
+end:
+ if (!node)
+ return REG_ESPACE;
+ ctx->n = node;
+ ctx->s = s;
+ return REG_OK;
+}
+
+#define PUSHPTR(err, s, v) do { \
+ if ((err = tre_stack_push_voidptr(s, v)) != REG_OK) \
+ return err; \
+} while(0)
+
+#define PUSHINT(err, s, v) do { \
+ if ((err = tre_stack_push_int(s, v)) != REG_OK) \
+ return err; \
+} while(0)
+
+static reg_errcode_t tre_parse(tre_parse_ctx_t *ctx)
+{
+ tre_ast_node_t *nbranch=0, *nunion=0;
+ int ere = ctx->cflags & REG_EXTENDED;
+ const char *s = ctx->start;
+ int subid = 0;
+ int depth = 0;
+ reg_errcode_t err;
+ tre_stack_t *stack = ctx->stack;
+
+ PUSHINT(err, stack, subid++);
+ for (;;) {
+ if ((!ere && *s == '\\' && s[1] == '(') ||
+ (ere && *s == '(')) {
+ PUSHPTR(err, stack, nunion);
+ PUSHPTR(err, stack, nbranch);
+ PUSHINT(err, stack, subid++);
+ s++;
+ if (!ere)
+ s++;
+ depth++;
+ nbranch = nunion = 0;
+ ctx->start = s;
+ continue;
+ }
+ if ((!ere && *s == '\\' && s[1] == ')') ||
+ (ere && *s == ')' && depth)) {
+ ctx->n = tre_ast_new_literal(ctx->mem, EMPTY, -1, -1);
+ if (!ctx->n)
+ return REG_ESPACE;
+ } else {
+ err = parse_atom(ctx, s);
+ if (err != REG_OK)
+ return err;
+ s = ctx->s;
+ }
+
+ parse_iter:
+ for (;;) {
+ int min, max;
+
+ if (*s!='\\' && *s!='*') {
+ if (!ere)
+ break;
+ if (*s!='+' && *s!='?' && *s!='{')
+ break;
+ }
+ if (*s=='\\' && ere)
+ break;
+ /* extension: treat \+, \? as repetitions in BRE */
+ if (*s=='\\' && s[1]!='+' && s[1]!='?' && s[1]!='{')
+ break;
+ if (*s=='\\')
+ s++;
+
+ /* handle ^* at the start of a BRE. */
+ if (!ere && s==ctx->start+1 && s[-1]=='^')
+ break;
+
+ /* extension: multiple consecutive *+?{,} is unspecified,
+ but (a+)+ has to be supported so accepting a++ makes
+ sense, note however that the RE_DUP_MAX limit can be
+ circumvented: (a{255}){255} uses a lot of memory.. */
+ if (*s=='{') {
+ s = parse_dup(s+1, ere, &min, &max);
+ if (!s)
+ return REG_BADBR;
+ } else {
+ min=0;
+ max=-1;
+ if (*s == '+')
+ min = 1;
+ if (*s == '?')
+ max = 1;
+ s++;
+ }
+ if (max == 0)
+ ctx->n = tre_ast_new_literal(ctx->mem, EMPTY, -1, -1);
+ else
+ ctx->n = tre_ast_new_iter(ctx->mem, ctx->n, min, max, 0);
+ if (!ctx->n)
+ return REG_ESPACE;
+ }
+
+ nbranch = tre_ast_new_catenation(ctx->mem, nbranch, ctx->n);
+ if ((ere && *s == '|') ||
+ (ere && *s == ')' && depth) ||
+ (!ere && *s == '\\' && s[1] == ')') ||
+ /* extension: treat \| as alternation in BRE */
+ (!ere && *s == '\\' && s[1] == '|') ||
+ !*s) {
+ /* extension: empty branch is unspecified (), (|a), (a|)
+ here they are not rejected but match on empty string */
+ int c = *s;
+ nunion = tre_ast_new_union(ctx->mem, nunion, nbranch);
+ nbranch = 0;
+
+ if (c == '\\' && s[1] == '|') {
+ s+=2;
+ ctx->start = s;
+ } else if (c == '|') {
+ s++;
+ ctx->start = s;
+ } else {
+ if (c == '\\') {
+ if (!depth) return REG_EPAREN;
+ s+=2;
+ } else if (c == ')')
+ s++;
+ depth--;
+ err = marksub(ctx, nunion, tre_stack_pop_int(stack));
+ if (err != REG_OK)
+ return err;
+ if (!c && depth<0) {
+ ctx->submatch_id = subid;
+ return REG_OK;
+ }
+ if (!c || depth<0)
+ return REG_EPAREN;
+ nbranch = tre_stack_pop_voidptr(stack);
+ nunion = tre_stack_pop_voidptr(stack);
+ goto parse_iter;
+ }
+ }
+ }
+}
+
+
+/***********************************************************************
+ from tre-compile.c
+***********************************************************************/
+
+
+/*
+ TODO:
+ - Fix tre_ast_to_tnfa() to recurse using a stack instead of recursive
+ function calls.
+*/
+
+/*
+ Algorithms to setup tags so that submatch addressing can be done.
+*/
+
+
+/* Inserts a catenation node to the root of the tree given in `node'.
+ As the left child a new tag with number `tag_id' to `node' is added,
+ and the right child is the old root. */
+static reg_errcode_t
+tre_add_tag_left(tre_mem_t mem, tre_ast_node_t *node, int tag_id)
+{
+ tre_catenation_t *c;
+
+ c = tre_mem_alloc(mem, sizeof(*c));
+ if (c == NULL)
+ return REG_ESPACE;
+ c->left = tre_ast_new_literal(mem, TAG, tag_id, -1);
+ if (c->left == NULL)
+ return REG_ESPACE;
+ c->right = tre_mem_alloc(mem, sizeof(tre_ast_node_t));
+ if (c->right == NULL)
+ return REG_ESPACE;
+
+ c->right->obj = node->obj;
+ c->right->type = node->type;
+ c->right->nullable = -1;
+ c->right->submatch_id = -1;
+ c->right->firstpos = NULL;
+ c->right->lastpos = NULL;
+ c->right->num_tags = 0;
+ c->right->num_submatches = 0;
+ node->obj = c;
+ node->type = CATENATION;
+ return REG_OK;
+}
+
+/* Inserts a catenation node to the root of the tree given in `node'.
+ As the right child a new tag with number `tag_id' to `node' is added,
+ and the left child is the old root. */
+static reg_errcode_t
+tre_add_tag_right(tre_mem_t mem, tre_ast_node_t *node, int tag_id)
+{
+ tre_catenation_t *c;
+
+ c = tre_mem_alloc(mem, sizeof(*c));
+ if (c == NULL)
+ return REG_ESPACE;
+ c->right = tre_ast_new_literal(mem, TAG, tag_id, -1);
+ if (c->right == NULL)
+ return REG_ESPACE;
+ c->left = tre_mem_alloc(mem, sizeof(tre_ast_node_t));
+ if (c->left == NULL)
+ return REG_ESPACE;
+
+ c->left->obj = node->obj;
+ c->left->type = node->type;
+ c->left->nullable = -1;
+ c->left->submatch_id = -1;
+ c->left->firstpos = NULL;
+ c->left->lastpos = NULL;
+ c->left->num_tags = 0;
+ c->left->num_submatches = 0;
+ node->obj = c;
+ node->type = CATENATION;
+ return REG_OK;
+}
+
+typedef enum {
+ ADDTAGS_RECURSE,
+ ADDTAGS_AFTER_ITERATION,
+ ADDTAGS_AFTER_UNION_LEFT,
+ ADDTAGS_AFTER_UNION_RIGHT,
+ ADDTAGS_AFTER_CAT_LEFT,
+ ADDTAGS_AFTER_CAT_RIGHT,
+ ADDTAGS_SET_SUBMATCH_END
+} tre_addtags_symbol_t;
+
+
+typedef struct {
+ int tag;
+ int next_tag;
+} tre_tag_states_t;
+
+
+/* Go through `regset' and set submatch data for submatches that are
+ using this tag. */
+static void
+tre_purge_regset(int *regset, tre_tnfa_t *tnfa, int tag)
+{
+ int i;
+
+ for (i = 0; regset[i] >= 0; i++)
+ {
+ int id = regset[i] / 2;
+ int start = !(regset[i] % 2);
+ if (start)
+ tnfa->submatch_data[id].so_tag = tag;
+ else
+ tnfa->submatch_data[id].eo_tag = tag;
+ }
+ regset[0] = -1;
+}
+
+
+/* Adds tags to appropriate locations in the parse tree in `tree', so that
+ subexpressions marked for submatch addressing can be traced. */
+static reg_errcode_t
+tre_add_tags(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *tree,
+ tre_tnfa_t *tnfa)
+{
+ reg_errcode_t status = REG_OK;
+ tre_addtags_symbol_t symbol;
+ tre_ast_node_t *node = tree; /* Tree node we are currently looking at. */
+ int bottom = tre_stack_num_objects(stack);
+ /* True for first pass (counting number of needed tags) */
+ int first_pass = (mem == NULL || tnfa == NULL);
+ int *regset, *orig_regset;
+ int num_tags = 0; /* Total number of tags. */
+ int num_minimals = 0; /* Number of special minimal tags. */
+ int tag = 0; /* The tag that is to be added next. */
+ int next_tag = 1; /* Next tag to use after this one. */
+ int *parents; /* Stack of submatches the current submatch is
+ contained in. */
+ int minimal_tag = -1; /* Tag that marks the beginning of a minimal match. */
+ tre_tag_states_t *saved_states;
+
+ tre_tag_direction_t direction = TRE_TAG_MINIMIZE;
+ if (!first_pass)
+ {
+ tnfa->end_tag = 0;
+ tnfa->minimal_tags[0] = -1;
+ }
+
+ regset = xmalloc(sizeof(*regset) * ((tnfa->num_submatches + 1) * 2));
+ if (regset == NULL)
+ return REG_ESPACE;
+ regset[0] = -1;
+ orig_regset = regset;
+
+ parents = xmalloc(sizeof(*parents) * (tnfa->num_submatches + 1));
+ if (parents == NULL)
+ {
+ xfree(regset);
+ return REG_ESPACE;
+ }
+ parents[0] = -1;
+
+ saved_states = xmalloc(sizeof(*saved_states) * (tnfa->num_submatches + 1));
+ if (saved_states == NULL)
+ {
+ xfree(regset);
+ xfree(parents);
+ return REG_ESPACE;
+ }
+ else
+ {
+ unsigned int i;
+ for (i = 0; i <= tnfa->num_submatches; i++)
+ saved_states[i].tag = -1;
+ }
+
+ STACK_PUSH(stack, voidptr, node);
+ STACK_PUSH(stack, int, ADDTAGS_RECURSE);
+
+ while (tre_stack_num_objects(stack) > bottom)
+ {
+ if (status != REG_OK)
+ break;
+
+ symbol = (tre_addtags_symbol_t)tre_stack_pop_int(stack);
+ switch (symbol)
+ {
+
+ case ADDTAGS_SET_SUBMATCH_END:
+ {
+ int id = tre_stack_pop_int(stack);
+ int i;
+
+ /* Add end of this submatch to regset. */
+ for (i = 0; regset[i] >= 0; i++);
+ regset[i] = id * 2 + 1;
+ regset[i + 1] = -1;
+
+ /* Pop this submatch from the parents stack. */
+ for (i = 0; parents[i] >= 0; i++);
+ parents[i - 1] = -1;
+ break;
+ }
+
+ case ADDTAGS_RECURSE:
+ node = tre_stack_pop_voidptr(stack);
+
+ if (node->submatch_id >= 0)
+ {
+ int id = node->submatch_id;
+ int i;
+
+
+ /* Add start of this submatch to regset. */
+ for (i = 0; regset[i] >= 0; i++);
+ regset[i] = id * 2;
+ regset[i + 1] = -1;
+
+ if (!first_pass)
+ {
+ for (i = 0; parents[i] >= 0; i++);
+ tnfa->submatch_data[id].parents = NULL;
+ if (i > 0)
+ {
+ int *p = xmalloc(sizeof(*p) * (i + 1));
+ if (p == NULL)
+ {
+ status = REG_ESPACE;
+ break;
+ }
+ assert(tnfa->submatch_data[id].parents == NULL);
+ tnfa->submatch_data[id].parents = p;
+ for (i = 0; parents[i] >= 0; i++)
+ p[i] = parents[i];
+ p[i] = -1;
+ }
+ }
+
+ /* Add end of this submatch to regset after processing this
+ node. */
+ STACK_PUSHX(stack, int, node->submatch_id);
+ STACK_PUSHX(stack, int, ADDTAGS_SET_SUBMATCH_END);
+ }
+
+ switch (node->type)
+ {
+ case LITERAL:
+ {
+ tre_literal_t *lit = node->obj;
+
+ if (!IS_SPECIAL(lit) || IS_BACKREF(lit))
+ {
+ int i;
+ if (regset[0] >= 0)
+ {
+ /* Regset is not empty, so add a tag before the
+ literal or backref. */
+ if (!first_pass)
+ {
+ status = tre_add_tag_left(mem, node, tag);
+ tnfa->tag_directions[tag] = direction;
+ if (minimal_tag >= 0)
+ {
+ for (i = 0; tnfa->minimal_tags[i] >= 0; i++);
+ tnfa->minimal_tags[i] = tag;
+ tnfa->minimal_tags[i + 1] = minimal_tag;
+ tnfa->minimal_tags[i + 2] = -1;
+ minimal_tag = -1;
+ num_minimals++;
+ }
+ tre_purge_regset(regset, tnfa, tag);
+ }
+ else
+ {
+ node->num_tags = 1;
+ }
+
+ regset[0] = -1;
+ tag = next_tag;
+ num_tags++;
+ next_tag++;
+ }
+ }
+ else
+ {
+ assert(!IS_TAG(lit));
+ }
+ break;
+ }
+ case CATENATION:
+ {
+ tre_catenation_t *cat = node->obj;
+ tre_ast_node_t *left = cat->left;
+ tre_ast_node_t *right = cat->right;
+ int reserved_tag = -1;
+
+
+ /* After processing right child. */
+ STACK_PUSHX(stack, voidptr, node);
+ STACK_PUSHX(stack, int, ADDTAGS_AFTER_CAT_RIGHT);
+
+ /* Process right child. */
+ STACK_PUSHX(stack, voidptr, right);
+ STACK_PUSHX(stack, int, ADDTAGS_RECURSE);
+
+ /* After processing left child. */
+ STACK_PUSHX(stack, int, next_tag + left->num_tags);
+ if (left->num_tags > 0 && right->num_tags > 0)
+ {
+ /* Reserve the next tag to the right child. */
+ reserved_tag = next_tag;
+ next_tag++;
+ }
+ STACK_PUSHX(stack, int, reserved_tag);
+ STACK_PUSHX(stack, int, ADDTAGS_AFTER_CAT_LEFT);
+
+ /* Process left child. */
+ STACK_PUSHX(stack, voidptr, left);
+ STACK_PUSHX(stack, int, ADDTAGS_RECURSE);
+
+ }
+ break;
+ case ITERATION:
+ {
+ tre_iteration_t *iter = node->obj;
+
+ if (first_pass)
+ {
+ STACK_PUSHX(stack, int, regset[0] >= 0 || iter->minimal);
+ }
+ else
+ {
+ STACK_PUSHX(stack, int, tag);
+ STACK_PUSHX(stack, int, iter->minimal);
+ }
+ STACK_PUSHX(stack, voidptr, node);
+ STACK_PUSHX(stack, int, ADDTAGS_AFTER_ITERATION);
+
+ STACK_PUSHX(stack, voidptr, iter->arg);
+ STACK_PUSHX(stack, int, ADDTAGS_RECURSE);
+
+ /* Regset is not empty, so add a tag here. */
+ if (regset[0] >= 0 || iter->minimal)
+ {
+ if (!first_pass)
+ {
+ int i;
+ status = tre_add_tag_left(mem, node, tag);
+ if (iter->minimal)
+ tnfa->tag_directions[tag] = TRE_TAG_MAXIMIZE;
+ else
+ tnfa->tag_directions[tag] = direction;
+ if (minimal_tag >= 0)
+ {
+ for (i = 0; tnfa->minimal_tags[i] >= 0; i++);
+ tnfa->minimal_tags[i] = tag;
+ tnfa->minimal_tags[i + 1] = minimal_tag;
+ tnfa->minimal_tags[i + 2] = -1;
+ minimal_tag = -1;
+ num_minimals++;
+ }
+ tre_purge_regset(regset, tnfa, tag);
+ }
+
+ regset[0] = -1;
+ tag = next_tag;
+ num_tags++;
+ next_tag++;
+ }
+ direction = TRE_TAG_MINIMIZE;
+ }
+ break;
+ case UNION:
+ {
+ tre_union_t *uni = node->obj;
+ tre_ast_node_t *left = uni->left;
+ tre_ast_node_t *right = uni->right;
+ int left_tag;
+ int right_tag;
+
+ if (regset[0] >= 0)
+ {
+ left_tag = next_tag;
+ right_tag = next_tag + 1;
+ }
+ else
+ {
+ left_tag = tag;
+ right_tag = next_tag;
+ }
+
+ /* After processing right child. */
+ STACK_PUSHX(stack, int, right_tag);
+ STACK_PUSHX(stack, int, left_tag);
+ STACK_PUSHX(stack, voidptr, regset);
+ STACK_PUSHX(stack, int, regset[0] >= 0);
+ STACK_PUSHX(stack, voidptr, node);
+ STACK_PUSHX(stack, voidptr, right);
+ STACK_PUSHX(stack, voidptr, left);
+ STACK_PUSHX(stack, int, ADDTAGS_AFTER_UNION_RIGHT);
+
+ /* Process right child. */
+ STACK_PUSHX(stack, voidptr, right);
+ STACK_PUSHX(stack, int, ADDTAGS_RECURSE);
+
+ /* After processing left child. */
+ STACK_PUSHX(stack, int, ADDTAGS_AFTER_UNION_LEFT);
+
+ /* Process left child. */
+ STACK_PUSHX(stack, voidptr, left);
+ STACK_PUSHX(stack, int, ADDTAGS_RECURSE);
+
+ /* Regset is not empty, so add a tag here. */
+ if (regset[0] >= 0)
+ {
+ if (!first_pass)
+ {
+ int i;
+ status = tre_add_tag_left(mem, node, tag);
+ tnfa->tag_directions[tag] = direction;
+ if (minimal_tag >= 0)
+ {
+ for (i = 0; tnfa->minimal_tags[i] >= 0; i++);
+ tnfa->minimal_tags[i] = tag;
+ tnfa->minimal_tags[i + 1] = minimal_tag;
+ tnfa->minimal_tags[i + 2] = -1;
+ minimal_tag = -1;
+ num_minimals++;
+ }
+ tre_purge_regset(regset, tnfa, tag);
+ }
+
+ regset[0] = -1;
+ tag = next_tag;
+ num_tags++;
+ next_tag++;
+ }
+
+ if (node->num_submatches > 0)
+ {
+ /* The next two tags are reserved for markers. */
+ next_tag++;
+ tag = next_tag;
+ next_tag++;
+ }
+
+ break;
+ }
+ }
+
+ if (node->submatch_id >= 0)
+ {
+ int i;
+ /* Push this submatch on the parents stack. */
+ for (i = 0; parents[i] >= 0; i++);
+ parents[i] = node->submatch_id;
+ parents[i + 1] = -1;
+ }
+
+ break; /* end case: ADDTAGS_RECURSE */
+
+ case ADDTAGS_AFTER_ITERATION:
+ {
+ int minimal = 0;
+ int enter_tag;
+ node = tre_stack_pop_voidptr(stack);
+ if (first_pass)
+ {
+ node->num_tags = ((tre_iteration_t *)node->obj)->arg->num_tags
+ + tre_stack_pop_int(stack);
+ minimal_tag = -1;
+ }
+ else
+ {
+ minimal = tre_stack_pop_int(stack);
+ enter_tag = tre_stack_pop_int(stack);
+ if (minimal)
+ minimal_tag = enter_tag;
+ }
+
+ if (!first_pass)
+ {
+ if (minimal)
+ direction = TRE_TAG_MINIMIZE;
+ else
+ direction = TRE_TAG_MAXIMIZE;
+ }
+ break;
+ }
+
+ case ADDTAGS_AFTER_CAT_LEFT:
+ {
+ int new_tag = tre_stack_pop_int(stack);
+ next_tag = tre_stack_pop_int(stack);
+ if (new_tag >= 0)
+ {
+ tag = new_tag;
+ }
+ break;
+ }
+
+ case ADDTAGS_AFTER_CAT_RIGHT:
+ node = tre_stack_pop_voidptr(stack);
+ if (first_pass)
+ node->num_tags = ((tre_catenation_t *)node->obj)->left->num_tags
+ + ((tre_catenation_t *)node->obj)->right->num_tags;
+ break;
+
+ case ADDTAGS_AFTER_UNION_LEFT:
+ /* Lift the bottom of the `regset' array so that when processing
+ the right operand the items currently in the array are
+ invisible. The original bottom was saved at ADDTAGS_UNION and
+ will be restored at ADDTAGS_AFTER_UNION_RIGHT below. */
+ while (*regset >= 0)
+ regset++;
+ break;
+
+ case ADDTAGS_AFTER_UNION_RIGHT:
+ {
+ int added_tags, tag_left, tag_right;
+ tre_ast_node_t *left = tre_stack_pop_voidptr(stack);
+ tre_ast_node_t *right = tre_stack_pop_voidptr(stack);
+ node = tre_stack_pop_voidptr(stack);
+ added_tags = tre_stack_pop_int(stack);
+ if (first_pass)
+ {
+ node->num_tags = ((tre_union_t *)node->obj)->left->num_tags
+ + ((tre_union_t *)node->obj)->right->num_tags + added_tags
+ + ((node->num_submatches > 0) ? 2 : 0);
+ }
+ regset = tre_stack_pop_voidptr(stack);
+ tag_left = tre_stack_pop_int(stack);
+ tag_right = tre_stack_pop_int(stack);
+
+ /* Add tags after both children, the left child gets a smaller
+ tag than the right child. This guarantees that we prefer
+ the left child over the right child. */
+ /* XXX - This is not always necessary (if the children have
+ tags which must be seen for every match of that child). */
+ /* XXX - Check if this is the only place where tre_add_tag_right
+ is used. If so, use tre_add_tag_left (putting the tag before
+ the child as opposed after the child) and throw away
+ tre_add_tag_right. */
+ if (node->num_submatches > 0)
+ {
+ if (!first_pass)
+ {
+ status = tre_add_tag_right(mem, left, tag_left);
+ tnfa->tag_directions[tag_left] = TRE_TAG_MAXIMIZE;
+ if (status == REG_OK)
+ status = tre_add_tag_right(mem, right, tag_right);
+ tnfa->tag_directions[tag_right] = TRE_TAG_MAXIMIZE;
+ }
+ num_tags += 2;
+ }
+ direction = TRE_TAG_MAXIMIZE;
+ break;
+ }
+
+ default:
+ assert(0);
+ break;
+
+ } /* end switch(symbol) */
+ } /* end while(tre_stack_num_objects(stack) > bottom) */
+
+ if (!first_pass)
+ tre_purge_regset(regset, tnfa, tag);
+
+ if (!first_pass && minimal_tag >= 0)
+ {
+ int i;
+ for (i = 0; tnfa->minimal_tags[i] >= 0; i++);
+ tnfa->minimal_tags[i] = tag;
+ tnfa->minimal_tags[i + 1] = minimal_tag;
+ tnfa->minimal_tags[i + 2] = -1;
+ minimal_tag = -1;
+ num_minimals++;
+ }
+
+ assert(tree->num_tags == num_tags);
+ tnfa->end_tag = num_tags;
+ tnfa->num_tags = num_tags;
+ tnfa->num_minimals = num_minimals;
+ xfree(orig_regset);
+ xfree(parents);
+ xfree(saved_states);
+ return status;
+}
+
+
+
+/*
+ AST to TNFA compilation routines.
+*/
+
+typedef enum {
+ COPY_RECURSE,
+ COPY_SET_RESULT_PTR
+} tre_copyast_symbol_t;
+
+/* Flags for tre_copy_ast(). */
+#define COPY_REMOVE_TAGS 1
+#define COPY_MAXIMIZE_FIRST_TAG 2
+
+static reg_errcode_t
+tre_copy_ast(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *ast,
+ int flags, int *pos_add, tre_tag_direction_t *tag_directions,
+ tre_ast_node_t **copy, int *max_pos)
+{
+ reg_errcode_t status = REG_OK;
+ int bottom = tre_stack_num_objects(stack);
+ int num_copied = 0;
+ int first_tag = 1;
+ tre_ast_node_t **result = copy;
+ tre_copyast_symbol_t symbol;
+
+ STACK_PUSH(stack, voidptr, ast);
+ STACK_PUSH(stack, int, COPY_RECURSE);
+
+ while (status == REG_OK && tre_stack_num_objects(stack) > bottom)
+ {
+ tre_ast_node_t *node;
+ if (status != REG_OK)
+ break;
+
+ symbol = (tre_copyast_symbol_t)tre_stack_pop_int(stack);
+ switch (symbol)
+ {
+ case COPY_SET_RESULT_PTR:
+ result = tre_stack_pop_voidptr(stack);
+ break;
+ case COPY_RECURSE:
+ node = tre_stack_pop_voidptr(stack);
+ switch (node->type)
+ {
+ case LITERAL:
+ {
+ tre_literal_t *lit = node->obj;
+ int pos = lit->position;
+ int min = lit->code_min;
+ int max = lit->code_max;
+ if (!IS_SPECIAL(lit) || IS_BACKREF(lit))
+ {
+ /* XXX - e.g. [ab] has only one position but two
+ nodes, so we are creating holes in the state space
+ here. Not fatal, just wastes memory. */
+ pos += *pos_add;
+ num_copied++;
+ }
+ else if (IS_TAG(lit) && (flags & COPY_REMOVE_TAGS))
+ {
+ /* Change this tag to empty. */
+ min = EMPTY;
+ max = pos = -1;
+ }
+ else if (IS_TAG(lit) && (flags & COPY_MAXIMIZE_FIRST_TAG)
+ && first_tag)
+ {
+ /* Maximize the first tag. */
+ tag_directions[max] = TRE_TAG_MAXIMIZE;
+ first_tag = 0;
+ }
+ *result = tre_ast_new_literal(mem, min, max, pos);
+ if (*result == NULL)
+ status = REG_ESPACE;
+ else {
+ tre_literal_t *p = (*result)->obj;
+ p->class = lit->class;
+ p->neg_classes = lit->neg_classes;
+ }
+
+ if (pos > *max_pos)
+ *max_pos = pos;
+ break;
+ }
+ case UNION:
+ {
+ tre_union_t *uni = node->obj;
+ tre_union_t *tmp;
+ *result = tre_ast_new_union(mem, uni->left, uni->right);
+ if (*result == NULL)
+ {
+ status = REG_ESPACE;
+ break;
+ }
+ tmp = (*result)->obj;
+ result = &tmp->left;
+ STACK_PUSHX(stack, voidptr, uni->right);
+ STACK_PUSHX(stack, int, COPY_RECURSE);
+ STACK_PUSHX(stack, voidptr, &tmp->right);
+ STACK_PUSHX(stack, int, COPY_SET_RESULT_PTR);
+ STACK_PUSHX(stack, voidptr, uni->left);
+ STACK_PUSHX(stack, int, COPY_RECURSE);
+ break;
+ }
+ case CATENATION:
+ {
+ tre_catenation_t *cat = node->obj;
+ tre_catenation_t *tmp;
+ *result = tre_ast_new_catenation(mem, cat->left, cat->right);
+ if (*result == NULL)
+ {
+ status = REG_ESPACE;
+ break;
+ }
+ tmp = (*result)->obj;
+ tmp->left = NULL;
+ tmp->right = NULL;
+ result = &tmp->left;
+
+ STACK_PUSHX(stack, voidptr, cat->right);
+ STACK_PUSHX(stack, int, COPY_RECURSE);
+ STACK_PUSHX(stack, voidptr, &tmp->right);
+ STACK_PUSHX(stack, int, COPY_SET_RESULT_PTR);
+ STACK_PUSHX(stack, voidptr, cat->left);
+ STACK_PUSHX(stack, int, COPY_RECURSE);
+ break;
+ }
+ case ITERATION:
+ {
+ tre_iteration_t *iter = node->obj;
+ STACK_PUSHX(stack, voidptr, iter->arg);
+ STACK_PUSHX(stack, int, COPY_RECURSE);
+ *result = tre_ast_new_iter(mem, iter->arg, iter->min,
+ iter->max, iter->minimal);
+ if (*result == NULL)
+ {
+ status = REG_ESPACE;
+ break;
+ }
+ iter = (*result)->obj;
+ result = &iter->arg;
+ break;
+ }
+ default:
+ assert(0);
+ break;
+ }
+ break;
+ }
+ }
+ *pos_add += num_copied;
+ return status;
+}
+
+typedef enum {
+ EXPAND_RECURSE,
+ EXPAND_AFTER_ITER
+} tre_expand_ast_symbol_t;
+
+/* Expands each iteration node that has a finite nonzero minimum or maximum
+ iteration count to a catenated sequence of copies of the node. */
+static reg_errcode_t
+tre_expand_ast(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *ast,
+ int *position, tre_tag_direction_t *tag_directions)
+{
+ reg_errcode_t status = REG_OK;
+ int bottom = tre_stack_num_objects(stack);
+ int pos_add = 0;
+ int pos_add_total = 0;
+ int max_pos = 0;
+ int iter_depth = 0;
+
+ STACK_PUSHR(stack, voidptr, ast);
+ STACK_PUSHR(stack, int, EXPAND_RECURSE);
+ while (status == REG_OK && tre_stack_num_objects(stack) > bottom)
+ {
+ tre_ast_node_t *node;
+ tre_expand_ast_symbol_t symbol;
+
+ if (status != REG_OK)
+ break;
+
+ symbol = (tre_expand_ast_symbol_t)tre_stack_pop_int(stack);
+ node = tre_stack_pop_voidptr(stack);
+ switch (symbol)
+ {
+ case EXPAND_RECURSE:
+ switch (node->type)
+ {
+ case LITERAL:
+ {
+ tre_literal_t *lit= node->obj;
+ if (!IS_SPECIAL(lit) || IS_BACKREF(lit))
+ {
+ lit->position += pos_add;
+ if (lit->position > max_pos)
+ max_pos = lit->position;
+ }
+ break;
+ }
+ case UNION:
+ {
+ tre_union_t *uni = node->obj;
+ STACK_PUSHX(stack, voidptr, uni->right);
+ STACK_PUSHX(stack, int, EXPAND_RECURSE);
+ STACK_PUSHX(stack, voidptr, uni->left);
+ STACK_PUSHX(stack, int, EXPAND_RECURSE);
+ break;
+ }
+ case CATENATION:
+ {
+ tre_catenation_t *cat = node->obj;
+ STACK_PUSHX(stack, voidptr, cat->right);
+ STACK_PUSHX(stack, int, EXPAND_RECURSE);
+ STACK_PUSHX(stack, voidptr, cat->left);
+ STACK_PUSHX(stack, int, EXPAND_RECURSE);
+ break;
+ }
+ case ITERATION:
+ {
+ tre_iteration_t *iter = node->obj;
+ STACK_PUSHX(stack, int, pos_add);
+ STACK_PUSHX(stack, voidptr, node);
+ STACK_PUSHX(stack, int, EXPAND_AFTER_ITER);
+ STACK_PUSHX(stack, voidptr, iter->arg);
+ STACK_PUSHX(stack, int, EXPAND_RECURSE);
+ /* If we are going to expand this node at EXPAND_AFTER_ITER
+ then don't increase the `pos' fields of the nodes now, it
+ will get done when expanding. */
+ if (iter->min > 1 || iter->max > 1)
+ pos_add = 0;
+ iter_depth++;
+ break;
+ }
+ default:
+ assert(0);
+ break;
+ }
+ break;
+ case EXPAND_AFTER_ITER:
+ {
+ tre_iteration_t *iter = node->obj;
+ int pos_add_last;
+ pos_add = tre_stack_pop_int(stack);
+ pos_add_last = pos_add;
+ if (iter->min > 1 || iter->max > 1)
+ {
+ tre_ast_node_t *seq1 = NULL, *seq2 = NULL;
+ int j;
+ int pos_add_save = pos_add;
+
+ /* Create a catenated sequence of copies of the node. */
+ for (j = 0; j < iter->min; j++)
+ {
+ tre_ast_node_t *copy;
+ /* Remove tags from all but the last copy. */
+ int flags = ((j + 1 < iter->min)
+ ? COPY_REMOVE_TAGS
+ : COPY_MAXIMIZE_FIRST_TAG);
+ pos_add_save = pos_add;
+ status = tre_copy_ast(mem, stack, iter->arg, flags,
+ &pos_add, tag_directions, &copy,
+ &max_pos);
+ if (status != REG_OK)
+ return status;
+ if (seq1 != NULL)
+ seq1 = tre_ast_new_catenation(mem, seq1, copy);
+ else
+ seq1 = copy;
+ if (seq1 == NULL)
+ return REG_ESPACE;
+ }
+
+ if (iter->max == -1)
+ {
+ /* No upper limit. */
+ pos_add_save = pos_add;
+ status = tre_copy_ast(mem, stack, iter->arg, 0,
+ &pos_add, NULL, &seq2, &max_pos);
+ if (status != REG_OK)
+ return status;
+ seq2 = tre_ast_new_iter(mem, seq2, 0, -1, 0);
+ if (seq2 == NULL)
+ return REG_ESPACE;
+ }
+ else
+ {
+ for (j = iter->min; j < iter->max; j++)
+ {
+ tre_ast_node_t *tmp, *copy;
+ pos_add_save = pos_add;
+ status = tre_copy_ast(mem, stack, iter->arg, 0,
+ &pos_add, NULL, &copy, &max_pos);
+ if (status != REG_OK)
+ return status;
+ if (seq2 != NULL)
+ seq2 = tre_ast_new_catenation(mem, copy, seq2);
+ else
+ seq2 = copy;
+ if (seq2 == NULL)
+ return REG_ESPACE;
+ tmp = tre_ast_new_literal(mem, EMPTY, -1, -1);
+ if (tmp == NULL)
+ return REG_ESPACE;
+ seq2 = tre_ast_new_union(mem, tmp, seq2);
+ if (seq2 == NULL)
+ return REG_ESPACE;
+ }
+ }
+
+ pos_add = pos_add_save;
+ if (seq1 == NULL)
+ seq1 = seq2;
+ else if (seq2 != NULL)
+ seq1 = tre_ast_new_catenation(mem, seq1, seq2);
+ if (seq1 == NULL)
+ return REG_ESPACE;
+ node->obj = seq1->obj;
+ node->type = seq1->type;
+ }
+
+ iter_depth--;
+ pos_add_total += pos_add - pos_add_last;
+ if (iter_depth == 0)
+ pos_add = pos_add_total;
+
+ break;
+ }
+ default:
+ assert(0);
+ break;
+ }
+ }
+
+ *position += pos_add_total;
+
+ /* `max_pos' should never be larger than `*position' if the above
+ code works, but just an extra safeguard let's make sure
+ `*position' is set large enough so enough memory will be
+ allocated for the transition table. */
+ if (max_pos > *position)
+ *position = max_pos;
+
+ return status;
+}
+
+static tre_pos_and_tags_t *
+tre_set_empty(tre_mem_t mem)
+{
+ tre_pos_and_tags_t *new_set;
+
+ new_set = tre_mem_calloc(mem, sizeof(*new_set));
+ if (new_set == NULL)
+ return NULL;
+
+ new_set[0].position = -1;
+ new_set[0].code_min = -1;
+ new_set[0].code_max = -1;
+
+ return new_set;
+}
+
+static tre_pos_and_tags_t *
+tre_set_one(tre_mem_t mem, int position, int code_min, int code_max,
+ tre_ctype_t class, tre_ctype_t *neg_classes, int backref)
+{
+ tre_pos_and_tags_t *new_set;
+
+ new_set = tre_mem_calloc(mem, sizeof(*new_set) * 2);
+ if (new_set == NULL)
+ return NULL;
+
+ new_set[0].position = position;
+ new_set[0].code_min = code_min;
+ new_set[0].code_max = code_max;
+ new_set[0].class = class;
+ new_set[0].neg_classes = neg_classes;
+ new_set[0].backref = backref;
+ new_set[1].position = -1;
+ new_set[1].code_min = -1;
+ new_set[1].code_max = -1;
+
+ return new_set;
+}
+
+static tre_pos_and_tags_t *
+tre_set_union(tre_mem_t mem, tre_pos_and_tags_t *set1, tre_pos_and_tags_t *set2,
+ int *tags, int assertions)
+{
+ int s1, s2, i, j;
+ tre_pos_and_tags_t *new_set;
+ int *new_tags;
+ int num_tags;
+
+ for (num_tags = 0; tags != NULL && tags[num_tags] >= 0; num_tags++);
+ for (s1 = 0; set1[s1].position >= 0; s1++);
+ for (s2 = 0; set2[s2].position >= 0; s2++);
+ new_set = tre_mem_calloc(mem, sizeof(*new_set) * (s1 + s2 + 1));
+ if (!new_set )
+ return NULL;
+
+ for (s1 = 0; set1[s1].position >= 0; s1++)
+ {
+ new_set[s1].position = set1[s1].position;
+ new_set[s1].code_min = set1[s1].code_min;
+ new_set[s1].code_max = set1[s1].code_max;
+ new_set[s1].assertions = set1[s1].assertions | assertions;
+ new_set[s1].class = set1[s1].class;
+ new_set[s1].neg_classes = set1[s1].neg_classes;
+ new_set[s1].backref = set1[s1].backref;
+ if (set1[s1].tags == NULL && tags == NULL)
+ new_set[s1].tags = NULL;
+ else
+ {
+ for (i = 0; set1[s1].tags != NULL && set1[s1].tags[i] >= 0; i++);
+ new_tags = tre_mem_alloc(mem, (sizeof(*new_tags)
+ * (i + num_tags + 1)));
+ if (new_tags == NULL)
+ return NULL;
+ for (j = 0; j < i; j++)
+ new_tags[j] = set1[s1].tags[j];
+ for (i = 0; i < num_tags; i++)
+ new_tags[j + i] = tags[i];
+ new_tags[j + i] = -1;
+ new_set[s1].tags = new_tags;
+ }
+ }
+
+ for (s2 = 0; set2[s2].position >= 0; s2++)
+ {
+ new_set[s1 + s2].position = set2[s2].position;
+ new_set[s1 + s2].code_min = set2[s2].code_min;
+ new_set[s1 + s2].code_max = set2[s2].code_max;
+ /* XXX - why not | assertions here as well? */
+ new_set[s1 + s2].assertions = set2[s2].assertions;
+ new_set[s1 + s2].class = set2[s2].class;
+ new_set[s1 + s2].neg_classes = set2[s2].neg_classes;
+ new_set[s1 + s2].backref = set2[s2].backref;
+ if (set2[s2].tags == NULL)
+ new_set[s1 + s2].tags = NULL;
+ else
+ {
+ for (i = 0; set2[s2].tags[i] >= 0; i++);
+ new_tags = tre_mem_alloc(mem, sizeof(*new_tags) * (i + 1));
+ if (new_tags == NULL)
+ return NULL;
+ for (j = 0; j < i; j++)
+ new_tags[j] = set2[s2].tags[j];
+ new_tags[j] = -1;
+ new_set[s1 + s2].tags = new_tags;
+ }
+ }
+ new_set[s1 + s2].position = -1;
+ return new_set;
+}
+
+/* Finds the empty path through `node' which is the one that should be
+ taken according to POSIX.2 rules, and adds the tags on that path to
+ `tags'. `tags' may be NULL. If `num_tags_seen' is not NULL, it is
+ set to the number of tags seen on the path. */
+static reg_errcode_t
+tre_match_empty(tre_stack_t *stack, tre_ast_node_t *node, int *tags,
+ int *assertions, int *num_tags_seen)
+{
+ tre_literal_t *lit;
+ tre_union_t *uni;
+ tre_catenation_t *cat;
+ tre_iteration_t *iter;
+ int i;
+ int bottom = tre_stack_num_objects(stack);
+ reg_errcode_t status = REG_OK;
+ if (num_tags_seen)
+ *num_tags_seen = 0;
+
+ status = tre_stack_push_voidptr(stack, node);
+
+ /* Walk through the tree recursively. */
+ while (status == REG_OK && tre_stack_num_objects(stack) > bottom)
+ {
+ node = tre_stack_pop_voidptr(stack);
+
+ switch (node->type)
+ {
+ case LITERAL:
+ lit = (tre_literal_t *)node->obj;
+ switch (lit->code_min)
+ {
+ case TAG:
+ if (lit->code_max >= 0)
+ {
+ if (tags != NULL)
+ {
+ /* Add the tag to `tags'. */
+ for (i = 0; tags[i] >= 0; i++)
+ if (tags[i] == lit->code_max)
+ break;
+ if (tags[i] < 0)
+ {
+ tags[i] = lit->code_max;
+ tags[i + 1] = -1;
+ }
+ }
+ if (num_tags_seen)
+ (*num_tags_seen)++;
+ }
+ break;
+ case ASSERTION:
+ assert(lit->code_max >= 1
+ || lit->code_max <= ASSERT_LAST);
+ if (assertions != NULL)
+ *assertions |= lit->code_max;
+ break;
+ case EMPTY:
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ break;
+
+ case UNION:
+ /* Subexpressions starting earlier take priority over ones
+ starting later, so we prefer the left subexpression over the
+ right subexpression. */
+ uni = (tre_union_t *)node->obj;
+ if (uni->left->nullable)
+ STACK_PUSHX(stack, voidptr, uni->left)
+ else if (uni->right->nullable)
+ STACK_PUSHX(stack, voidptr, uni->right)
+ else
+ assert(0);
+ break;
+
+ case CATENATION:
+ /* The path must go through both children. */
+ cat = (tre_catenation_t *)node->obj;
+ assert(cat->left->nullable);
+ assert(cat->right->nullable);
+ STACK_PUSHX(stack, voidptr, cat->left);
+ STACK_PUSHX(stack, voidptr, cat->right);
+ break;
+
+ case ITERATION:
+ /* A match with an empty string is preferred over no match at
+ all, so we go through the argument if possible. */
+ iter = (tre_iteration_t *)node->obj;
+ if (iter->arg->nullable)
+ STACK_PUSHX(stack, voidptr, iter->arg);
+ break;
+
+ default:
+ assert(0);
+ break;
+ }
+ }
+
+ return status;
+}
+
+
+typedef enum {
+ NFL_RECURSE,
+ NFL_POST_UNION,
+ NFL_POST_CATENATION,
+ NFL_POST_ITERATION
+} tre_nfl_stack_symbol_t;
+
+
+/* Computes and fills in the fields `nullable', `firstpos', and `lastpos' for
+ the nodes of the AST `tree'. */
+static reg_errcode_t
+tre_compute_nfl(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *tree)
+{
+ int bottom = tre_stack_num_objects(stack);
+
+ STACK_PUSHR(stack, voidptr, tree);
+ STACK_PUSHR(stack, int, NFL_RECURSE);
+
+ while (tre_stack_num_objects(stack) > bottom)
+ {
+ tre_nfl_stack_symbol_t symbol;
+ tre_ast_node_t *node;
+
+ symbol = (tre_nfl_stack_symbol_t)tre_stack_pop_int(stack);
+ node = tre_stack_pop_voidptr(stack);
+ switch (symbol)
+ {
+ case NFL_RECURSE:
+ switch (node->type)
+ {
+ case LITERAL:
+ {
+ tre_literal_t *lit = (tre_literal_t *)node->obj;
+ if (IS_BACKREF(lit))
+ {
+ /* Back references: nullable = false, firstpos = {i},
+ lastpos = {i}. */
+ node->nullable = 0;
+ node->firstpos = tre_set_one(mem, lit->position, 0,
+ TRE_CHAR_MAX, 0, NULL, -1);
+ if (!node->firstpos)
+ return REG_ESPACE;
+ node->lastpos = tre_set_one(mem, lit->position, 0,
+ TRE_CHAR_MAX, 0, NULL,
+ (int)lit->code_max);
+ if (!node->lastpos)
+ return REG_ESPACE;
+ }
+ else if (lit->code_min < 0)
+ {
+ /* Tags, empty strings, params, and zero width assertions:
+ nullable = true, firstpos = {}, and lastpos = {}. */
+ node->nullable = 1;
+ node->firstpos = tre_set_empty(mem);
+ if (!node->firstpos)
+ return REG_ESPACE;
+ node->lastpos = tre_set_empty(mem);
+ if (!node->lastpos)
+ return REG_ESPACE;
+ }
+ else
+ {
+ /* Literal at position i: nullable = false, firstpos = {i},
+ lastpos = {i}. */
+ node->nullable = 0;
+ node->firstpos =
+ tre_set_one(mem, lit->position, (int)lit->code_min,
+ (int)lit->code_max, 0, NULL, -1);
+ if (!node->firstpos)
+ return REG_ESPACE;
+ node->lastpos = tre_set_one(mem, lit->position,
+ (int)lit->code_min,
+ (int)lit->code_max,
+ lit->class, lit->neg_classes,
+ -1);
+ if (!node->lastpos)
+ return REG_ESPACE;
+ }
+ break;
+ }
+
+ case UNION:
+ /* Compute the attributes for the two subtrees, and after that
+ for this node. */
+ STACK_PUSHR(stack, voidptr, node);
+ STACK_PUSHR(stack, int, NFL_POST_UNION);
+ STACK_PUSHR(stack, voidptr, ((tre_union_t *)node->obj)->right);
+ STACK_PUSHR(stack, int, NFL_RECURSE);
+ STACK_PUSHR(stack, voidptr, ((tre_union_t *)node->obj)->left);
+ STACK_PUSHR(stack, int, NFL_RECURSE);
+ break;
+
+ case CATENATION:
+ /* Compute the attributes for the two subtrees, and after that
+ for this node. */
+ STACK_PUSHR(stack, voidptr, node);
+ STACK_PUSHR(stack, int, NFL_POST_CATENATION);
+ STACK_PUSHR(stack, voidptr, ((tre_catenation_t *)node->obj)->right);
+ STACK_PUSHR(stack, int, NFL_RECURSE);
+ STACK_PUSHR(stack, voidptr, ((tre_catenation_t *)node->obj)->left);
+ STACK_PUSHR(stack, int, NFL_RECURSE);
+ break;
+
+ case ITERATION:
+ /* Compute the attributes for the subtree, and after that for
+ this node. */
+ STACK_PUSHR(stack, voidptr, node);
+ STACK_PUSHR(stack, int, NFL_POST_ITERATION);
+ STACK_PUSHR(stack, voidptr, ((tre_iteration_t *)node->obj)->arg);
+ STACK_PUSHR(stack, int, NFL_RECURSE);
+ break;
+ }
+ break; /* end case: NFL_RECURSE */
+
+ case NFL_POST_UNION:
+ {
+ tre_union_t *uni = (tre_union_t *)node->obj;
+ node->nullable = uni->left->nullable || uni->right->nullable;
+ node->firstpos = tre_set_union(mem, uni->left->firstpos,
+ uni->right->firstpos, NULL, 0);
+ if (!node->firstpos)
+ return REG_ESPACE;
+ node->lastpos = tre_set_union(mem, uni->left->lastpos,
+ uni->right->lastpos, NULL, 0);
+ if (!node->lastpos)
+ return REG_ESPACE;
+ break;
+ }
+
+ case NFL_POST_ITERATION:
+ {
+ tre_iteration_t *iter = (tre_iteration_t *)node->obj;
+
+ if (iter->min == 0 || iter->arg->nullable)
+ node->nullable = 1;
+ else
+ node->nullable = 0;
+ node->firstpos = iter->arg->firstpos;
+ node->lastpos = iter->arg->lastpos;
+ break;
+ }
+
+ case NFL_POST_CATENATION:
+ {
+ int num_tags, *tags, assertions;
+ reg_errcode_t status;
+ tre_catenation_t *cat = node->obj;
+ node->nullable = cat->left->nullable && cat->right->nullable;
+
+ /* Compute firstpos. */
+ if (cat->left->nullable)
+ {
+ /* The left side matches the empty string. Make a first pass
+ with tre_match_empty() to get the number of tags and
+ parameters. */
+ status = tre_match_empty(stack, cat->left,
+ NULL, NULL, &num_tags);
+ if (status != REG_OK)
+ return status;
+ /* Allocate arrays for the tags and parameters. */
+ tags = xmalloc(sizeof(*tags) * (num_tags + 1));
+ if (!tags)
+ return REG_ESPACE;
+ tags[0] = -1;
+ assertions = 0;
+ /* Second pass with tre_mach_empty() to get the list of
+ tags and parameters. */
+ status = tre_match_empty(stack, cat->left, tags,
+ &assertions, NULL);
+ if (status != REG_OK)
+ {
+ xfree(tags);
+ return status;
+ }
+ node->firstpos =
+ tre_set_union(mem, cat->right->firstpos, cat->left->firstpos,
+ tags, assertions);
+ xfree(tags);
+ if (!node->firstpos)
+ return REG_ESPACE;
+ }
+ else
+ {
+ node->firstpos = cat->left->firstpos;
+ }
+
+ /* Compute lastpos. */
+ if (cat->right->nullable)
+ {
+ /* The right side matches the empty string. Make a first pass
+ with tre_match_empty() to get the number of tags and
+ parameters. */
+ status = tre_match_empty(stack, cat->right,
+ NULL, NULL, &num_tags);
+ if (status != REG_OK)
+ return status;
+ /* Allocate arrays for the tags and parameters. */
+ tags = xmalloc(sizeof(int) * (num_tags + 1));
+ if (!tags)
+ return REG_ESPACE;
+ tags[0] = -1;
+ assertions = 0;
+ /* Second pass with tre_mach_empty() to get the list of
+ tags and parameters. */
+ status = tre_match_empty(stack, cat->right, tags,
+ &assertions, NULL);
+ if (status != REG_OK)
+ {
+ xfree(tags);
+ return status;
+ }
+ node->lastpos =
+ tre_set_union(mem, cat->left->lastpos, cat->right->lastpos,
+ tags, assertions);
+ xfree(tags);
+ if (!node->lastpos)
+ return REG_ESPACE;
+ }
+ else
+ {
+ node->lastpos = cat->right->lastpos;
+ }
+ break;
+ }
+
+ default:
+ assert(0);
+ break;
+ }
+ }
+
+ return REG_OK;
+}
+
+
+/* Adds a transition from each position in `p1' to each position in `p2'. */
+static reg_errcode_t
+tre_make_trans(tre_pos_and_tags_t *p1, tre_pos_and_tags_t *p2,
+ tre_tnfa_transition_t *transitions,
+ int *counts, int *offs)
+{
+ tre_pos_and_tags_t *orig_p2 = p2;
+ tre_tnfa_transition_t *trans;
+ int i, j, k, l, dup, prev_p2_pos;
+
+ if (transitions != NULL)
+ while (p1->position >= 0)
+ {
+ p2 = orig_p2;
+ prev_p2_pos = -1;
+ while (p2->position >= 0)
+ {
+ /* Optimization: if this position was already handled, skip it. */
+ if (p2->position == prev_p2_pos)
+ {
+ p2++;
+ continue;
+ }
+ prev_p2_pos = p2->position;
+ /* Set `trans' to point to the next unused transition from
+ position `p1->position'. */
+ trans = transitions + offs[p1->position];
+ while (trans->state != NULL)
+ {
+#if 0
+ /* If we find a previous transition from `p1->position' to
+ `p2->position', it is overwritten. This can happen only
+ if there are nested loops in the regexp, like in "((a)*)*".
+ In POSIX.2 repetition using the outer loop is always
+ preferred over using the inner loop. Therefore the
+ transition for the inner loop is useless and can be thrown
+ away. */
+ /* XXX - The same position is used for all nodes in a bracket
+ expression, so this optimization cannot be used (it will
+ break bracket expressions) unless I figure out a way to
+ detect it here. */
+ if (trans->state_id == p2->position)
+ {
+ break;
+ }
+#endif
+ trans++;
+ }
+
+ if (trans->state == NULL)
+ (trans + 1)->state = NULL;
+ /* Use the character ranges, assertions, etc. from `p1' for
+ the transition from `p1' to `p2'. */
+ trans->code_min = p1->code_min;
+ trans->code_max = p1->code_max;
+ trans->state = transitions + offs[p2->position];
+ trans->state_id = p2->position;
+ trans->assertions = p1->assertions | p2->assertions
+ | (p1->class ? ASSERT_CHAR_CLASS : 0)
+ | (p1->neg_classes != NULL ? ASSERT_CHAR_CLASS_NEG : 0);
+ if (p1->backref >= 0)
+ {
+ assert((trans->assertions & ASSERT_CHAR_CLASS) == 0);
+ assert(p2->backref < 0);
+ trans->u.backref = p1->backref;
+ trans->assertions |= ASSERT_BACKREF;
+ }
+ else
+ trans->u.class = p1->class;
+ if (p1->neg_classes != NULL)
+ {
+ for (i = 0; p1->neg_classes[i] != (tre_ctype_t)0; i++);
+ trans->neg_classes =
+ xmalloc(sizeof(*trans->neg_classes) * (i + 1));
+ if (trans->neg_classes == NULL)
+ return REG_ESPACE;
+ for (i = 0; p1->neg_classes[i] != (tre_ctype_t)0; i++)
+ trans->neg_classes[i] = p1->neg_classes[i];
+ trans->neg_classes[i] = (tre_ctype_t)0;
+ }
+ else
+ trans->neg_classes = NULL;
+
+ /* Find out how many tags this transition has. */
+ i = 0;
+ if (p1->tags != NULL)
+ while(p1->tags[i] >= 0)
+ i++;
+ j = 0;
+ if (p2->tags != NULL)
+ while(p2->tags[j] >= 0)
+ j++;
+
+ /* If we are overwriting a transition, free the old tag array. */
+ if (trans->tags != NULL)
+ xfree(trans->tags);
+ trans->tags = NULL;
+
+ /* If there were any tags, allocate an array and fill it. */
+ if (i + j > 0)
+ {
+ trans->tags = xmalloc(sizeof(*trans->tags) * (i + j + 1));
+ if (!trans->tags)
+ return REG_ESPACE;
+ i = 0;
+ if (p1->tags != NULL)
+ while(p1->tags[i] >= 0)
+ {
+ trans->tags[i] = p1->tags[i];
+ i++;
+ }
+ l = i;
+ j = 0;
+ if (p2->tags != NULL)
+ while (p2->tags[j] >= 0)
+ {
+ /* Don't add duplicates. */
+ dup = 0;
+ for (k = 0; k < i; k++)
+ if (trans->tags[k] == p2->tags[j])
+ {
+ dup = 1;
+ break;
+ }
+ if (!dup)
+ trans->tags[l++] = p2->tags[j];
+ j++;
+ }
+ trans->tags[l] = -1;
+ }
+
+ p2++;
+ }
+ p1++;
+ }
+ else
+ /* Compute a maximum limit for the number of transitions leaving
+ from each state. */
+ while (p1->position >= 0)
+ {
+ p2 = orig_p2;
+ while (p2->position >= 0)
+ {
+ counts[p1->position]++;
+ p2++;
+ }
+ p1++;
+ }
+ return REG_OK;
+}
+
+/* Converts the syntax tree to a TNFA. All the transitions in the TNFA are
+ labelled with one character range (there are no transitions on empty
+ strings). The TNFA takes O(n^2) space in the worst case, `n' is size of
+ the regexp. */
+static reg_errcode_t
+tre_ast_to_tnfa(tre_ast_node_t *node, tre_tnfa_transition_t *transitions,
+ int *counts, int *offs)
+{
+ tre_union_t *uni;
+ tre_catenation_t *cat;
+ tre_iteration_t *iter;
+ reg_errcode_t errcode = REG_OK;
+
+ /* XXX - recurse using a stack!. */
+ switch (node->type)
+ {
+ case LITERAL:
+ break;
+ case UNION:
+ uni = (tre_union_t *)node->obj;
+ errcode = tre_ast_to_tnfa(uni->left, transitions, counts, offs);
+ if (errcode != REG_OK)
+ return errcode;
+ errcode = tre_ast_to_tnfa(uni->right, transitions, counts, offs);
+ break;
+
+ case CATENATION:
+ cat = (tre_catenation_t *)node->obj;
+ /* Add a transition from each position in cat->left->lastpos
+ to each position in cat->right->firstpos. */
+ errcode = tre_make_trans(cat->left->lastpos, cat->right->firstpos,
+ transitions, counts, offs);
+ if (errcode != REG_OK)
+ return errcode;
+ errcode = tre_ast_to_tnfa(cat->left, transitions, counts, offs);
+ if (errcode != REG_OK)
+ return errcode;
+ errcode = tre_ast_to_tnfa(cat->right, transitions, counts, offs);
+ break;
+
+ case ITERATION:
+ iter = (tre_iteration_t *)node->obj;
+ assert(iter->max == -1 || iter->max == 1);
+
+ if (iter->max == -1)
+ {
+ assert(iter->min == 0 || iter->min == 1);
+ /* Add a transition from each last position in the iterated
+ expression to each first position. */
+ errcode = tre_make_trans(iter->arg->lastpos, iter->arg->firstpos,
+ transitions, counts, offs);
+ if (errcode != REG_OK)
+ return errcode;
+ }
+ errcode = tre_ast_to_tnfa(iter->arg, transitions, counts, offs);
+ break;
+ }
+ return errcode;
+}
+
+
+#define ERROR_EXIT(err) \
+ do \
+ { \
+ errcode = err; \
+ if (/*CONSTCOND*/1) \
+ goto error_exit; \
+ } \
+ while (/*CONSTCOND*/0)
+
+
+int
+regcomp(regex_t *__restrict preg, const char *__restrict regex, int cflags)
+{
+ tre_stack_t *stack;
+ tre_ast_node_t *tree, *tmp_ast_l, *tmp_ast_r;
+ tre_pos_and_tags_t *p;
+ int *counts = NULL, *offs = NULL;
+ int i, add = 0;
+ tre_tnfa_transition_t *transitions, *initial;
+ tre_tnfa_t *tnfa = NULL;
+ tre_submatch_data_t *submatch_data;
+ tre_tag_direction_t *tag_directions = NULL;
+ reg_errcode_t errcode;
+ tre_mem_t mem;
+
+ /* Parse context. */
+ tre_parse_ctx_t parse_ctx;
+
+ /* Allocate a stack used throughout the compilation process for various
+ purposes. */
+ stack = tre_stack_new(512, 1024000, 128);
+ if (!stack)
+ return REG_ESPACE;
+ /* Allocate a fast memory allocator. */
+ mem = tre_mem_new();
+ if (!mem)
+ {
+ tre_stack_destroy(stack);
+ return REG_ESPACE;
+ }
+
+ /* Parse the regexp. */
+ memset(&parse_ctx, 0, sizeof(parse_ctx));
+ parse_ctx.mem = mem;
+ parse_ctx.stack = stack;
+ parse_ctx.start = regex;
+ parse_ctx.cflags = cflags;
+ parse_ctx.max_backref = -1;
+ errcode = tre_parse(&parse_ctx);
+ if (errcode != REG_OK)
+ ERROR_EXIT(errcode);
+ preg->re_nsub = parse_ctx.submatch_id - 1;
+ tree = parse_ctx.n;
+
+#ifdef TRE_DEBUG
+ tre_ast_print(tree);
+#endif /* TRE_DEBUG */
+
+ /* Referring to nonexistent subexpressions is illegal. */
+ if (parse_ctx.max_backref > (int)preg->re_nsub)
+ ERROR_EXIT(REG_ESUBREG);
+
+ /* Allocate the TNFA struct. */
+ tnfa = xcalloc(1, sizeof(tre_tnfa_t));
+ if (tnfa == NULL)
+ ERROR_EXIT(REG_ESPACE);
+ tnfa->have_backrefs = parse_ctx.max_backref >= 0;
+ tnfa->have_approx = 0;
+ tnfa->num_submatches = parse_ctx.submatch_id;
+
+ /* Set up tags for submatch addressing. If REG_NOSUB is set and the
+ regexp does not have back references, this can be skipped. */
+ if (tnfa->have_backrefs || !(cflags & REG_NOSUB))
+ {
+
+ /* Figure out how many tags we will need. */
+ errcode = tre_add_tags(NULL, stack, tree, tnfa);
+ if (errcode != REG_OK)
+ ERROR_EXIT(errcode);
+
+ if (tnfa->num_tags > 0)
+ {
+ tag_directions = xmalloc(sizeof(*tag_directions)
+ * (tnfa->num_tags + 1));
+ if (tag_directions == NULL)
+ ERROR_EXIT(REG_ESPACE);
+ tnfa->tag_directions = tag_directions;
+ memset(tag_directions, -1,
+ sizeof(*tag_directions) * (tnfa->num_tags + 1));
+ }
+ tnfa->minimal_tags = xcalloc((unsigned)tnfa->num_tags * 2 + 1,
+ sizeof(*tnfa->minimal_tags));
+ if (tnfa->minimal_tags == NULL)
+ ERROR_EXIT(REG_ESPACE);
+
+ submatch_data = xcalloc((unsigned)parse_ctx.submatch_id,
+ sizeof(*submatch_data));
+ if (submatch_data == NULL)
+ ERROR_EXIT(REG_ESPACE);
+ tnfa->submatch_data = submatch_data;
+
+ errcode = tre_add_tags(mem, stack, tree, tnfa);
+ if (errcode != REG_OK)
+ ERROR_EXIT(errcode);
+
+ }
+
+ /* Expand iteration nodes. */
+ errcode = tre_expand_ast(mem, stack, tree, &parse_ctx.position,
+ tag_directions);
+ if (errcode != REG_OK)
+ ERROR_EXIT(errcode);
+
+ /* Add a dummy node for the final state.
+ XXX - For certain patterns this dummy node can be optimized away,
+ for example "a*" or "ab*". Figure out a simple way to detect
+ this possibility. */
+ tmp_ast_l = tree;
+ tmp_ast_r = tre_ast_new_literal(mem, 0, 0, parse_ctx.position++);
+ if (tmp_ast_r == NULL)
+ ERROR_EXIT(REG_ESPACE);
+
+ tree = tre_ast_new_catenation(mem, tmp_ast_l, tmp_ast_r);
+ if (tree == NULL)
+ ERROR_EXIT(REG_ESPACE);
+
+ errcode = tre_compute_nfl(mem, stack, tree);
+ if (errcode != REG_OK)
+ ERROR_EXIT(errcode);
+
+ counts = xmalloc(sizeof(int) * parse_ctx.position);
+ if (counts == NULL)
+ ERROR_EXIT(REG_ESPACE);
+
+ offs = xmalloc(sizeof(int) * parse_ctx.position);
+ if (offs == NULL)
+ ERROR_EXIT(REG_ESPACE);
+
+ for (i = 0; i < parse_ctx.position; i++)
+ counts[i] = 0;
+ tre_ast_to_tnfa(tree, NULL, counts, NULL);
+
+ add = 0;
+ for (i = 0; i < parse_ctx.position; i++)
+ {
+ offs[i] = add;
+ add += counts[i] + 1;
+ counts[i] = 0;
+ }
+ transitions = xcalloc((unsigned)add + 1, sizeof(*transitions));
+ if (transitions == NULL)
+ ERROR_EXIT(REG_ESPACE);
+ tnfa->transitions = transitions;
+ tnfa->num_transitions = add;
+
+ errcode = tre_ast_to_tnfa(tree, transitions, counts, offs);
+ if (errcode != REG_OK)
+ ERROR_EXIT(errcode);
+
+ tnfa->firstpos_chars = NULL;
+
+ p = tree->firstpos;
+ i = 0;
+ while (p->position >= 0)
+ {
+ i++;
+ p++;
+ }
+
+ initial = xcalloc((unsigned)i + 1, sizeof(tre_tnfa_transition_t));
+ if (initial == NULL)
+ ERROR_EXIT(REG_ESPACE);
+ tnfa->initial = initial;
+
+ i = 0;
+ for (p = tree->firstpos; p->position >= 0; p++)
+ {
+ initial[i].state = transitions + offs[p->position];
+ initial[i].state_id = p->position;
+ initial[i].tags = NULL;
+ /* Copy the arrays p->tags, and p->params, they are allocated
+ from a tre_mem object. */
+ if (p->tags)
+ {
+ int j;
+ for (j = 0; p->tags[j] >= 0; j++);
+ initial[i].tags = xmalloc(sizeof(*p->tags) * (j + 1));
+ if (!initial[i].tags)
+ ERROR_EXIT(REG_ESPACE);
+ memcpy(initial[i].tags, p->tags, sizeof(*p->tags) * (j + 1));
+ }
+ initial[i].assertions = p->assertions;
+ i++;
+ }
+ initial[i].state = NULL;
+
+ tnfa->num_transitions = add;
+ tnfa->final = transitions + offs[tree->lastpos[0].position];
+ tnfa->num_states = parse_ctx.position;
+ tnfa->cflags = cflags;
+
+ tre_mem_destroy(mem);
+ tre_stack_destroy(stack);
+ xfree(counts);
+ xfree(offs);
+
+ preg->TRE_REGEX_T_FIELD = (void *)tnfa;
+ return REG_OK;
+
+ error_exit:
+ /* Free everything that was allocated and return the error code. */
+ tre_mem_destroy(mem);
+ if (stack != NULL)
+ tre_stack_destroy(stack);
+ if (counts != NULL)
+ xfree(counts);
+ if (offs != NULL)
+ xfree(offs);
+ preg->TRE_REGEX_T_FIELD = (void *)tnfa;
+ regfree(preg);
+ return errcode;
+}
+
+
+
+
+void
+regfree(regex_t *preg)
+{
+ tre_tnfa_t *tnfa;
+ unsigned int i;
+ tre_tnfa_transition_t *trans;
+
+ tnfa = (void *)preg->TRE_REGEX_T_FIELD;
+ if (!tnfa)
+ return;
+
+ for (i = 0; i < tnfa->num_transitions; i++)
+ if (tnfa->transitions[i].state)
+ {
+ if (tnfa->transitions[i].tags)
+ xfree(tnfa->transitions[i].tags);
+ if (tnfa->transitions[i].neg_classes)
+ xfree(tnfa->transitions[i].neg_classes);
+ }
+ if (tnfa->transitions)
+ xfree(tnfa->transitions);
+
+ if (tnfa->initial)
+ {
+ for (trans = tnfa->initial; trans->state; trans++)
+ {
+ if (trans->tags)
+ xfree(trans->tags);
+ }
+ xfree(tnfa->initial);
+ }
+
+ if (tnfa->submatch_data)
+ {
+ for (i = 0; i < tnfa->num_submatches; i++)
+ if (tnfa->submatch_data[i].parents)
+ xfree(tnfa->submatch_data[i].parents);
+ xfree(tnfa->submatch_data);
+ }
+
+ if (tnfa->tag_directions)
+ xfree(tnfa->tag_directions);
+ if (tnfa->firstpos_chars)
+ xfree(tnfa->firstpos_chars);
+ if (tnfa->minimal_tags)
+ xfree(tnfa->minimal_tags);
+ xfree(tnfa);
+}
diff --git a/win32/regerror.c b/win32/regerror.c
new file mode 100644
index 0000000..bc7ae31
--- /dev/null
+++ b/win32/regerror.c
@@ -0,0 +1,37 @@
+#include <string.h>
+#include <regex.h>
+#include <stdio.h>
+// #include "locale_impl.h"
+
+/* Error message strings for error codes listed in `regex.h'. This list
+ needs to be in sync with the codes listed there, naturally. */
+
+/* Converted to single string by Rich Felker to remove the need for
+ * data relocations at runtime, 27 Feb 2006. */
+
+static const char messages[] = {
+ "No error\0"
+ "No match\0"
+ "Invalid regexp\0"
+ "Unknown collating element\0"
+ "Unknown character class name\0"
+ "Trailing backslash\0"
+ "Invalid back reference\0"
+ "Missing ']'\0"
+ "Missing ')'\0"
+ "Missing '}'\0"
+ "Invalid contents of {}\0"
+ "Invalid character range\0"
+ "Out of memory\0"
+ "Repetition not preceded by valid expression\0"
+ "\0Unknown error"
+};
+
+size_t regerror(int e, const regex_t *__restrict preg, char *__restrict buf, size_t size)
+{
+ const char *s;
+ for (s=messages; e && *s; e--, s+=strlen(s)+1);
+ if (!*s) s++;
+ // s = LCTRANS_CUR(s);
+ return 1+snprintf(buf, size, "%s", s);
+}
diff --git a/win32/regex.h b/win32/regex.h
new file mode 100644
index 0000000..7996b58
--- /dev/null
+++ b/win32/regex.h
@@ -0,0 +1,65 @@
+#ifndef _REGEX_H
+#define _REGEX_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// #include <features.h>
+
+// #define __NEED_regoff_t
+// #define __NEED_size_t
+#define CHARCLASS_NAME_MAX 14
+#define RE_DUP_MAX 255
+
+typedef size_t regoff_t;
+// #include <bits/alltypes.h>
+
+typedef struct re_pattern_buffer {
+ size_t re_nsub;
+ void *__opaque, *__padding[4];
+ size_t __nsub2;
+ char __padding2;
+} regex_t;
+
+typedef struct {
+ regoff_t rm_so;
+ regoff_t rm_eo;
+} regmatch_t;
+
+#define REG_EXTENDED 1
+#define REG_ICASE 2
+#define REG_NEWLINE 4
+#define REG_NOSUB 8
+
+#define REG_NOTBOL 1
+#define REG_NOTEOL 2
+
+#define REG_OK 0
+#define REG_NOMATCH 1
+#define REG_BADPAT 2
+#define REG_ECOLLATE 3
+#define REG_ECTYPE 4
+#define REG_EESCAPE 5
+#define REG_ESUBREG 6
+#define REG_EBRACK 7
+#define REG_EPAREN 8
+#define REG_EBRACE 9
+#define REG_BADBR 10
+#define REG_ERANGE 11
+#define REG_ESPACE 12
+#define REG_BADRPT 13
+
+#define REG_ENOSYS -1
+
+int regcomp(regex_t *__restrict, const char *__restrict, int);
+int regexec(const regex_t *__restrict, const char *__restrict, size_t, regmatch_t *__restrict, int);
+void regfree(regex_t *);
+
+size_t regerror(int, const regex_t *__restrict, char *__restrict, size_t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif \ No newline at end of file
diff --git a/win32/regexec.c b/win32/regexec.c
new file mode 100644
index 0000000..f22586e
--- /dev/null
+++ b/win32/regexec.c
@@ -0,0 +1,1028 @@
+/*
+ regexec.c - TRE POSIX compatible matching functions (and more).
+
+ Copyright (c) 2001-2009 Ville Laurikari <vl@iki.fi>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+#include <wctype.h>
+#include <limits.h>
+#include <stdint.h>
+
+#include <regex.h>
+
+#include "tre.h"
+
+#include <assert.h>
+
+static void
+tre_fill_pmatch(size_t nmatch, regmatch_t pmatch[], int cflags,
+ const tre_tnfa_t *tnfa, regoff_t *tags, regoff_t match_eo);
+
+/***********************************************************************
+ from tre-match-utils.h
+***********************************************************************/
+
+#define GET_NEXT_WCHAR() do { \
+ prev_c = next_c; pos += pos_add_next; \
+ if ((pos_add_next = mbtowc(&next_c, str_byte, MB_LEN_MAX)) <= 0) { \
+ if (pos_add_next < 0) { ret = REG_NOMATCH; goto error_exit; } \
+ else pos_add_next++; \
+ } \
+ str_byte += pos_add_next; \
+ } while (0)
+
+#define IS_WORD_CHAR(c) ((c) == L'_' || tre_isalnum(c))
+
+#define CHECK_ASSERTIONS(assertions) \
+ (((assertions & ASSERT_AT_BOL) \
+ && (pos > 0 || reg_notbol) \
+ && (prev_c != L'\n' || !reg_newline)) \
+ || ((assertions & ASSERT_AT_EOL) \
+ && (next_c != L'\0' || reg_noteol) \
+ && (next_c != L'\n' || !reg_newline)) \
+ || ((assertions & ASSERT_AT_BOW) \
+ && (IS_WORD_CHAR(prev_c) || !IS_WORD_CHAR(next_c))) \
+ || ((assertions & ASSERT_AT_EOW) \
+ && (!IS_WORD_CHAR(prev_c) || IS_WORD_CHAR(next_c))) \
+ || ((assertions & ASSERT_AT_WB) \
+ && (pos != 0 && next_c != L'\0' \
+ && IS_WORD_CHAR(prev_c) == IS_WORD_CHAR(next_c))) \
+ || ((assertions & ASSERT_AT_WB_NEG) \
+ && (pos == 0 || next_c == L'\0' \
+ || IS_WORD_CHAR(prev_c) != IS_WORD_CHAR(next_c))))
+
+#define CHECK_CHAR_CLASSES(trans_i, tnfa, eflags) \
+ (((trans_i->assertions & ASSERT_CHAR_CLASS) \
+ && !(tnfa->cflags & REG_ICASE) \
+ && !tre_isctype((tre_cint_t)prev_c, trans_i->u.class)) \
+ || ((trans_i->assertions & ASSERT_CHAR_CLASS) \
+ && (tnfa->cflags & REG_ICASE) \
+ && !tre_isctype(tre_tolower((tre_cint_t)prev_c),trans_i->u.class) \
+ && !tre_isctype(tre_toupper((tre_cint_t)prev_c),trans_i->u.class)) \
+ || ((trans_i->assertions & ASSERT_CHAR_CLASS_NEG) \
+ && tre_neg_char_classes_match(trans_i->neg_classes,(tre_cint_t)prev_c,\
+ tnfa->cflags & REG_ICASE)))
+
+
+
+
+/* Returns 1 if `t1' wins `t2', 0 otherwise. */
+static int
+tre_tag_order(int num_tags, tre_tag_direction_t *tag_directions,
+ regoff_t *t1, regoff_t *t2)
+{
+ int i;
+ for (i = 0; i < num_tags; i++)
+ {
+ if (tag_directions[i] == TRE_TAG_MINIMIZE)
+ {
+ if (t1[i] < t2[i])
+ return 1;
+ if (t1[i] > t2[i])
+ return 0;
+ }
+ else
+ {
+ if (t1[i] > t2[i])
+ return 1;
+ if (t1[i] < t2[i])
+ return 0;
+ }
+ }
+ /* assert(0);*/
+ return 0;
+}
+
+static int
+tre_neg_char_classes_match(tre_ctype_t *classes, tre_cint_t wc, int icase)
+{
+ while (*classes != (tre_ctype_t)0)
+ if ((!icase && tre_isctype(wc, *classes))
+ || (icase && (tre_isctype(tre_toupper(wc), *classes)
+ || tre_isctype(tre_tolower(wc), *classes))))
+ return 1; /* Match. */
+ else
+ classes++;
+ return 0; /* No match. */
+}
+
+
+/***********************************************************************
+ from tre-match-parallel.c
+***********************************************************************/
+
+/*
+ This algorithm searches for matches basically by reading characters
+ in the searched string one by one, starting at the beginning. All
+ matching paths in the TNFA are traversed in parallel. When two or
+ more paths reach the same state, exactly one is chosen according to
+ tag ordering rules; if returning submatches is not required it does
+ not matter which path is chosen.
+
+ The worst case time required for finding the leftmost and longest
+ match, or determining that there is no match, is always linearly
+ dependent on the length of the text being searched.
+
+ This algorithm cannot handle TNFAs with back referencing nodes.
+ See `tre-match-backtrack.c'.
+*/
+
+typedef struct {
+ tre_tnfa_transition_t *state;
+ regoff_t *tags;
+} tre_tnfa_reach_t;
+
+typedef struct {
+ regoff_t pos;
+ regoff_t **tags;
+} tre_reach_pos_t;
+
+
+static reg_errcode_t
+tre_tnfa_run_parallel(const tre_tnfa_t *tnfa, const void *string,
+ regoff_t *match_tags, int eflags,
+ regoff_t *match_end_ofs)
+{
+ /* State variables required by GET_NEXT_WCHAR. */
+ tre_char_t prev_c = 0, next_c = 0;
+ const char *str_byte = string;
+ regoff_t pos = -1;
+ regoff_t pos_add_next = 1;
+#ifdef TRE_MBSTATE
+ mbstate_t mbstate;
+#endif /* TRE_MBSTATE */
+ int reg_notbol = eflags & REG_NOTBOL;
+ int reg_noteol = eflags & REG_NOTEOL;
+ int reg_newline = tnfa->cflags & REG_NEWLINE;
+ reg_errcode_t ret;
+
+ char *buf;
+ tre_tnfa_transition_t *trans_i;
+ tre_tnfa_reach_t *reach, *reach_next, *reach_i, *reach_next_i;
+ tre_reach_pos_t *reach_pos;
+ int *tag_i;
+ int num_tags, i;
+
+ regoff_t match_eo = -1; /* end offset of match (-1 if no match found yet) */
+ int new_match = 0;
+ regoff_t *tmp_tags = NULL;
+ regoff_t *tmp_iptr;
+
+#ifdef TRE_MBSTATE
+ memset(&mbstate, '\0', sizeof(mbstate));
+#endif /* TRE_MBSTATE */
+
+ if (!match_tags)
+ num_tags = 0;
+ else
+ num_tags = tnfa->num_tags;
+
+ /* Allocate memory for temporary data required for matching. This needs to
+ be done for every matching operation to be thread safe. This allocates
+ everything in a single large block with calloc(). */
+ {
+ size_t tbytes, rbytes, pbytes, xbytes, total_bytes;
+ char *tmp_buf;
+
+ /* Ensure that tbytes and xbytes*num_states cannot overflow, and that
+ * they don't contribute more than 1/8 of SIZE_MAX to total_bytes. */
+ if (num_tags > SIZE_MAX/(8 * sizeof(regoff_t) * tnfa->num_states))
+ return REG_ESPACE;
+
+ /* Likewise check rbytes. */
+ if (tnfa->num_states+1 > SIZE_MAX/(8 * sizeof(*reach_next)))
+ return REG_ESPACE;
+
+ /* Likewise check pbytes. */
+ if (tnfa->num_states > SIZE_MAX/(8 * sizeof(*reach_pos)))
+ return REG_ESPACE;
+
+ /* Compute the length of the block we need. */
+ tbytes = sizeof(*tmp_tags) * num_tags;
+ rbytes = sizeof(*reach_next) * (tnfa->num_states + 1);
+ pbytes = sizeof(*reach_pos) * tnfa->num_states;
+ xbytes = sizeof(regoff_t) * num_tags;
+ total_bytes =
+ (sizeof(long) - 1) * 4 /* for alignment paddings */
+ + (rbytes + xbytes * tnfa->num_states) * 2 + tbytes + pbytes;
+
+ /* Allocate the memory. */
+ buf = calloc(total_bytes, 1);
+ if (buf == NULL)
+ return REG_ESPACE;
+
+ /* Get the various pointers within tmp_buf (properly aligned). */
+ tmp_tags = (void *)buf;
+ tmp_buf = buf + tbytes;
+ tmp_buf += ALIGN(tmp_buf, long);
+ reach_next = (void *)tmp_buf;
+ tmp_buf += rbytes;
+ tmp_buf += ALIGN(tmp_buf, long);
+ reach = (void *)tmp_buf;
+ tmp_buf += rbytes;
+ tmp_buf += ALIGN(tmp_buf, long);
+ reach_pos = (void *)tmp_buf;
+ tmp_buf += pbytes;
+ tmp_buf += ALIGN(tmp_buf, long);
+ for (i = 0; i < tnfa->num_states; i++)
+ {
+ reach[i].tags = (void *)tmp_buf;
+ tmp_buf += xbytes;
+ reach_next[i].tags = (void *)tmp_buf;
+ tmp_buf += xbytes;
+ }
+ }
+
+ for (i = 0; i < tnfa->num_states; i++)
+ reach_pos[i].pos = -1;
+
+ GET_NEXT_WCHAR();
+ pos = 0;
+
+ reach_next_i = reach_next;
+ while (1)
+ {
+ /* If no match found yet, add the initial states to `reach_next'. */
+ if (match_eo < 0)
+ {
+ trans_i = tnfa->initial;
+ while (trans_i->state != NULL)
+ {
+ if (reach_pos[trans_i->state_id].pos < pos)
+ {
+ if (trans_i->assertions
+ && CHECK_ASSERTIONS(trans_i->assertions))
+ {
+ trans_i++;
+ continue;
+ }
+
+ reach_next_i->state = trans_i->state;
+ for (i = 0; i < num_tags; i++)
+ reach_next_i->tags[i] = -1;
+ tag_i = trans_i->tags;
+ if (tag_i)
+ while (*tag_i >= 0)
+ {
+ if (*tag_i < num_tags)
+ reach_next_i->tags[*tag_i] = pos;
+ tag_i++;
+ }
+ if (reach_next_i->state == tnfa->final)
+ {
+ match_eo = pos;
+ new_match = 1;
+ for (i = 0; i < num_tags; i++)
+ match_tags[i] = reach_next_i->tags[i];
+ }
+ reach_pos[trans_i->state_id].pos = pos;
+ reach_pos[trans_i->state_id].tags = &reach_next_i->tags;
+ reach_next_i++;
+ }
+ trans_i++;
+ }
+ reach_next_i->state = NULL;
+ }
+ else
+ {
+ if (num_tags == 0 || reach_next_i == reach_next)
+ /* We have found a match. */
+ break;
+ }
+
+ /* Check for end of string. */
+ if (!next_c) break;
+
+ GET_NEXT_WCHAR();
+
+ /* Swap `reach' and `reach_next'. */
+ reach_i = reach;
+ reach = reach_next;
+ reach_next = reach_i;
+
+ /* For each state in `reach', weed out states that don't fulfill the
+ minimal matching conditions. */
+ if (tnfa->num_minimals && new_match)
+ {
+ new_match = 0;
+ reach_next_i = reach_next;
+ for (reach_i = reach; reach_i->state; reach_i++)
+ {
+ int skip = 0;
+ for (i = 0; tnfa->minimal_tags[i] >= 0; i += 2)
+ {
+ int end = tnfa->minimal_tags[i];
+ int start = tnfa->minimal_tags[i + 1];
+ if (end >= num_tags)
+ {
+ skip = 1;
+ break;
+ }
+ else if (reach_i->tags[start] == match_tags[start]
+ && reach_i->tags[end] < match_tags[end])
+ {
+ skip = 1;
+ break;
+ }
+ }
+ if (!skip)
+ {
+ reach_next_i->state = reach_i->state;
+ tmp_iptr = reach_next_i->tags;
+ reach_next_i->tags = reach_i->tags;
+ reach_i->tags = tmp_iptr;
+ reach_next_i++;
+ }
+ }
+ reach_next_i->state = NULL;
+
+ /* Swap `reach' and `reach_next'. */
+ reach_i = reach;
+ reach = reach_next;
+ reach_next = reach_i;
+ }
+
+ /* For each state in `reach' see if there is a transition leaving with
+ the current input symbol to a state not yet in `reach_next', and
+ add the destination states to `reach_next'. */
+ reach_next_i = reach_next;
+ for (reach_i = reach; reach_i->state; reach_i++)
+ {
+ for (trans_i = reach_i->state; trans_i->state; trans_i++)
+ {
+ /* Does this transition match the input symbol? */
+ if (trans_i->code_min <= (tre_cint_t)prev_c &&
+ trans_i->code_max >= (tre_cint_t)prev_c)
+ {
+ if (trans_i->assertions
+ && (CHECK_ASSERTIONS(trans_i->assertions)
+ || CHECK_CHAR_CLASSES(trans_i, tnfa, eflags)))
+ {
+ continue;
+ }
+
+ /* Compute the tags after this transition. */
+ for (i = 0; i < num_tags; i++)
+ tmp_tags[i] = reach_i->tags[i];
+ tag_i = trans_i->tags;
+ if (tag_i != NULL)
+ while (*tag_i >= 0)
+ {
+ if (*tag_i < num_tags)
+ tmp_tags[*tag_i] = pos;
+ tag_i++;
+ }
+
+ if (reach_pos[trans_i->state_id].pos < pos)
+ {
+ /* Found an unvisited node. */
+ reach_next_i->state = trans_i->state;
+ tmp_iptr = reach_next_i->tags;
+ reach_next_i->tags = tmp_tags;
+ tmp_tags = tmp_iptr;
+ reach_pos[trans_i->state_id].pos = pos;
+ reach_pos[trans_i->state_id].tags = &reach_next_i->tags;
+
+ if (reach_next_i->state == tnfa->final
+ && (match_eo == -1
+ || (num_tags > 0
+ && reach_next_i->tags[0] <= match_tags[0])))
+ {
+ match_eo = pos;
+ new_match = 1;
+ for (i = 0; i < num_tags; i++)
+ match_tags[i] = reach_next_i->tags[i];
+ }
+ reach_next_i++;
+
+ }
+ else
+ {
+ assert(reach_pos[trans_i->state_id].pos == pos);
+ /* Another path has also reached this state. We choose
+ the winner by examining the tag values for both
+ paths. */
+ if (tre_tag_order(num_tags, tnfa->tag_directions,
+ tmp_tags,
+ *reach_pos[trans_i->state_id].tags))
+ {
+ /* The new path wins. */
+ tmp_iptr = *reach_pos[trans_i->state_id].tags;
+ *reach_pos[trans_i->state_id].tags = tmp_tags;
+ if (trans_i->state == tnfa->final)
+ {
+ match_eo = pos;
+ new_match = 1;
+ for (i = 0; i < num_tags; i++)
+ match_tags[i] = tmp_tags[i];
+ }
+ tmp_tags = tmp_iptr;
+ }
+ }
+ }
+ }
+ }
+ reach_next_i->state = NULL;
+ }
+
+ *match_end_ofs = match_eo;
+ ret = match_eo >= 0 ? REG_OK : REG_NOMATCH;
+error_exit:
+ xfree(buf);
+ return ret;
+}
+
+
+
+/***********************************************************************
+ from tre-match-backtrack.c
+***********************************************************************/
+
+/*
+ This matcher is for regexps that use back referencing. Regexp matching
+ with back referencing is an NP-complete problem on the number of back
+ references. The easiest way to match them is to use a backtracking
+ routine which basically goes through all possible paths in the TNFA
+ and chooses the one which results in the best (leftmost and longest)
+ match. This can be spectacularly expensive and may run out of stack
+ space, but there really is no better known generic algorithm. Quoting
+ Henry Spencer from comp.compilers:
+ <URL: http://compilers.iecc.com/comparch/article/93-03-102>
+
+ POSIX.2 REs require longest match, which is really exciting to
+ implement since the obsolete ("basic") variant also includes
+ \<digit>. I haven't found a better way of tackling this than doing
+ a preliminary match using a DFA (or simulation) on a modified RE
+ that just replicates subREs for \<digit>, and then doing a
+ backtracking match to determine whether the subRE matches were
+ right. This can be rather slow, but I console myself with the
+ thought that people who use \<digit> deserve very slow execution.
+ (Pun unintentional but very appropriate.)
+
+*/
+
+typedef struct {
+ regoff_t pos;
+ const char *str_byte;
+ tre_tnfa_transition_t *state;
+ int state_id;
+ int next_c;
+ regoff_t *tags;
+#ifdef TRE_MBSTATE
+ mbstate_t mbstate;
+#endif /* TRE_MBSTATE */
+} tre_backtrack_item_t;
+
+typedef struct tre_backtrack_struct {
+ tre_backtrack_item_t item;
+ struct tre_backtrack_struct *prev;
+ struct tre_backtrack_struct *next;
+} *tre_backtrack_t;
+
+#ifdef TRE_MBSTATE
+#define BT_STACK_MBSTATE_IN stack->item.mbstate = (mbstate)
+#define BT_STACK_MBSTATE_OUT (mbstate) = stack->item.mbstate
+#else /* !TRE_MBSTATE */
+#define BT_STACK_MBSTATE_IN
+#define BT_STACK_MBSTATE_OUT
+#endif /* !TRE_MBSTATE */
+
+#define tre_bt_mem_new tre_mem_new
+#define tre_bt_mem_alloc tre_mem_alloc
+#define tre_bt_mem_destroy tre_mem_destroy
+
+
+#define BT_STACK_PUSH(_pos, _str_byte, _str_wide, _state, _state_id, _next_c, _tags, _mbstate) \
+ do \
+ { \
+ int i; \
+ if (!stack->next) \
+ { \
+ tre_backtrack_t s; \
+ s = tre_bt_mem_alloc(mem, sizeof(*s)); \
+ if (!s) \
+ { \
+ tre_bt_mem_destroy(mem); \
+ if (tags) \
+ xfree(tags); \
+ if (pmatch) \
+ xfree(pmatch); \
+ if (states_seen) \
+ xfree(states_seen); \
+ return REG_ESPACE; \
+ } \
+ s->prev = stack; \
+ s->next = NULL; \
+ s->item.tags = tre_bt_mem_alloc(mem, \
+ sizeof(*tags) * tnfa->num_tags); \
+ if (!s->item.tags) \
+ { \
+ tre_bt_mem_destroy(mem); \
+ if (tags) \
+ xfree(tags); \
+ if (pmatch) \
+ xfree(pmatch); \
+ if (states_seen) \
+ xfree(states_seen); \
+ return REG_ESPACE; \
+ } \
+ stack->next = s; \
+ stack = s; \
+ } \
+ else \
+ stack = stack->next; \
+ stack->item.pos = (_pos); \
+ stack->item.str_byte = (_str_byte); \
+ stack->item.state = (_state); \
+ stack->item.state_id = (_state_id); \
+ stack->item.next_c = (_next_c); \
+ for (i = 0; i < tnfa->num_tags; i++) \
+ stack->item.tags[i] = (_tags)[i]; \
+ BT_STACK_MBSTATE_IN; \
+ } \
+ while (0)
+
+#define BT_STACK_POP() \
+ do \
+ { \
+ int i; \
+ assert(stack->prev); \
+ pos = stack->item.pos; \
+ str_byte = stack->item.str_byte; \
+ state = stack->item.state; \
+ next_c = stack->item.next_c; \
+ for (i = 0; i < tnfa->num_tags; i++) \
+ tags[i] = stack->item.tags[i]; \
+ BT_STACK_MBSTATE_OUT; \
+ stack = stack->prev; \
+ } \
+ while (0)
+
+#undef MIN
+#define MIN(a, b) ((a) <= (b) ? (a) : (b))
+
+static reg_errcode_t
+tre_tnfa_run_backtrack(const tre_tnfa_t *tnfa, const void *string,
+ regoff_t *match_tags, int eflags, regoff_t *match_end_ofs)
+{
+ /* State variables required by GET_NEXT_WCHAR. */
+ tre_char_t prev_c = 0, next_c = 0;
+ const char *str_byte = string;
+ regoff_t pos = 0;
+ regoff_t pos_add_next = 1;
+#ifdef TRE_MBSTATE
+ mbstate_t mbstate;
+#endif /* TRE_MBSTATE */
+ int reg_notbol = eflags & REG_NOTBOL;
+ int reg_noteol = eflags & REG_NOTEOL;
+ int reg_newline = tnfa->cflags & REG_NEWLINE;
+
+ /* These are used to remember the necessary values of the above
+ variables to return to the position where the current search
+ started from. */
+ int next_c_start;
+ const char *str_byte_start;
+ regoff_t pos_start = -1;
+#ifdef TRE_MBSTATE
+ mbstate_t mbstate_start;
+#endif /* TRE_MBSTATE */
+
+ /* End offset of best match so far, or -1 if no match found yet. */
+ regoff_t match_eo = -1;
+ /* Tag arrays. */
+ int *next_tags;
+ regoff_t *tags = NULL;
+ /* Current TNFA state. */
+ tre_tnfa_transition_t *state;
+ int *states_seen = NULL;
+
+ /* Memory allocator to for allocating the backtracking stack. */
+ tre_mem_t mem = tre_bt_mem_new();
+
+ /* The backtracking stack. */
+ tre_backtrack_t stack;
+
+ tre_tnfa_transition_t *trans_i;
+ regmatch_t *pmatch = NULL;
+ int ret;
+
+#ifdef TRE_MBSTATE
+ memset(&mbstate, '\0', sizeof(mbstate));
+#endif /* TRE_MBSTATE */
+
+ if (!mem)
+ return REG_ESPACE;
+ stack = tre_bt_mem_alloc(mem, sizeof(*stack));
+ if (!stack)
+ {
+ ret = REG_ESPACE;
+ goto error_exit;
+ }
+ stack->prev = NULL;
+ stack->next = NULL;
+
+ if (tnfa->num_tags)
+ {
+ tags = xmalloc(sizeof(*tags) * tnfa->num_tags);
+ if (!tags)
+ {
+ ret = REG_ESPACE;
+ goto error_exit;
+ }
+ }
+ if (tnfa->num_submatches)
+ {
+ pmatch = xmalloc(sizeof(*pmatch) * tnfa->num_submatches);
+ if (!pmatch)
+ {
+ ret = REG_ESPACE;
+ goto error_exit;
+ }
+ }
+ if (tnfa->num_states)
+ {
+ states_seen = xmalloc(sizeof(*states_seen) * tnfa->num_states);
+ if (!states_seen)
+ {
+ ret = REG_ESPACE;
+ goto error_exit;
+ }
+ }
+
+ retry:
+ {
+ int i;
+ for (i = 0; i < tnfa->num_tags; i++)
+ {
+ tags[i] = -1;
+ if (match_tags)
+ match_tags[i] = -1;
+ }
+ for (i = 0; i < tnfa->num_states; i++)
+ states_seen[i] = 0;
+ }
+
+ state = NULL;
+ pos = pos_start;
+ GET_NEXT_WCHAR();
+ pos_start = pos;
+ next_c_start = next_c;
+ str_byte_start = str_byte;
+#ifdef TRE_MBSTATE
+ mbstate_start = mbstate;
+#endif /* TRE_MBSTATE */
+
+ /* Handle initial states. */
+ next_tags = NULL;
+ for (trans_i = tnfa->initial; trans_i->state; trans_i++)
+ {
+ if (trans_i->assertions && CHECK_ASSERTIONS(trans_i->assertions))
+ {
+ continue;
+ }
+ if (state == NULL)
+ {
+ /* Start from this state. */
+ state = trans_i->state;
+ next_tags = trans_i->tags;
+ }
+ else
+ {
+ /* Backtrack to this state. */
+ BT_STACK_PUSH(pos, str_byte, 0, trans_i->state,
+ trans_i->state_id, next_c, tags, mbstate);
+ {
+ int *tmp = trans_i->tags;
+ if (tmp)
+ while (*tmp >= 0)
+ stack->item.tags[*tmp++] = pos;
+ }
+ }
+ }
+
+ if (next_tags)
+ for (; *next_tags >= 0; next_tags++)
+ tags[*next_tags] = pos;
+
+
+ if (state == NULL)
+ goto backtrack;
+
+ while (1)
+ {
+ tre_tnfa_transition_t *next_state;
+ int empty_br_match;
+
+ if (state == tnfa->final)
+ {
+ if (match_eo < pos
+ || (match_eo == pos
+ && match_tags
+ && tre_tag_order(tnfa->num_tags, tnfa->tag_directions,
+ tags, match_tags)))
+ {
+ int i;
+ /* This match wins the previous match. */
+ match_eo = pos;
+ if (match_tags)
+ for (i = 0; i < tnfa->num_tags; i++)
+ match_tags[i] = tags[i];
+ }
+ /* Our TNFAs never have transitions leaving from the final state,
+ so we jump right to backtracking. */
+ goto backtrack;
+ }
+
+ /* Go to the next character in the input string. */
+ empty_br_match = 0;
+ trans_i = state;
+ if (trans_i->state && trans_i->assertions & ASSERT_BACKREF)
+ {
+ /* This is a back reference state. All transitions leaving from
+ this state have the same back reference "assertion". Instead
+ of reading the next character, we match the back reference. */
+ regoff_t so, eo;
+ int bt = trans_i->u.backref;
+ regoff_t bt_len;
+ int result;
+
+ /* Get the substring we need to match against. Remember to
+ turn off REG_NOSUB temporarily. */
+ tre_fill_pmatch(bt + 1, pmatch, tnfa->cflags & ~REG_NOSUB,
+ tnfa, tags, pos);
+ so = pmatch[bt].rm_so;
+ eo = pmatch[bt].rm_eo;
+ bt_len = eo - so;
+
+ result = strncmp((const char*)string + so, str_byte - 1,
+ (size_t)bt_len);
+
+ if (result == 0)
+ {
+ /* Back reference matched. Check for infinite loop. */
+ if (bt_len == 0)
+ empty_br_match = 1;
+ if (empty_br_match && states_seen[trans_i->state_id])
+ {
+ goto backtrack;
+ }
+
+ states_seen[trans_i->state_id] = empty_br_match;
+
+ /* Advance in input string and resync `prev_c', `next_c'
+ and pos. */
+ str_byte += bt_len - 1;
+ pos += bt_len - 1;
+ GET_NEXT_WCHAR();
+ }
+ else
+ {
+ goto backtrack;
+ }
+ }
+ else
+ {
+ /* Check for end of string. */
+ if (next_c == L'\0')
+ goto backtrack;
+
+ /* Read the next character. */
+ GET_NEXT_WCHAR();
+ }
+
+ next_state = NULL;
+ for (trans_i = state; trans_i->state; trans_i++)
+ {
+ if (trans_i->code_min <= (tre_cint_t)prev_c
+ && trans_i->code_max >= (tre_cint_t)prev_c)
+ {
+ if (trans_i->assertions
+ && (CHECK_ASSERTIONS(trans_i->assertions)
+ || CHECK_CHAR_CLASSES(trans_i, tnfa, eflags)))
+ {
+ continue;
+ }
+
+ if (next_state == NULL)
+ {
+ /* First matching transition. */
+ next_state = trans_i->state;
+ next_tags = trans_i->tags;
+ }
+ else
+ {
+ /* Second matching transition. We may need to backtrack here
+ to take this transition instead of the first one, so we
+ push this transition in the backtracking stack so we can
+ jump back here if needed. */
+ BT_STACK_PUSH(pos, str_byte, 0, trans_i->state,
+ trans_i->state_id, next_c, tags, mbstate);
+ {
+ int *tmp;
+ for (tmp = trans_i->tags; tmp && *tmp >= 0; tmp++)
+ stack->item.tags[*tmp] = pos;
+ }
+#if 0 /* XXX - it's important not to look at all transitions here to keep
+ the stack small! */
+ break;
+#endif
+ }
+ }
+ }
+
+ if (next_state != NULL)
+ {
+ /* Matching transitions were found. Take the first one. */
+ state = next_state;
+
+ /* Update the tag values. */
+ if (next_tags)
+ while (*next_tags >= 0)
+ tags[*next_tags++] = pos;
+ }
+ else
+ {
+ backtrack:
+ /* A matching transition was not found. Try to backtrack. */
+ if (stack->prev)
+ {
+ if (stack->item.state->assertions & ASSERT_BACKREF)
+ {
+ states_seen[stack->item.state_id] = 0;
+ }
+
+ BT_STACK_POP();
+ }
+ else if (match_eo < 0)
+ {
+ /* Try starting from a later position in the input string. */
+ /* Check for end of string. */
+ if (next_c == L'\0')
+ {
+ break;
+ }
+ next_c = next_c_start;
+#ifdef TRE_MBSTATE
+ mbstate = mbstate_start;
+#endif /* TRE_MBSTATE */
+ str_byte = str_byte_start;
+ goto retry;
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+
+ ret = match_eo >= 0 ? REG_OK : REG_NOMATCH;
+ *match_end_ofs = match_eo;
+
+ error_exit:
+ tre_bt_mem_destroy(mem);
+#ifndef TRE_USE_ALLOCA
+ if (tags)
+ xfree(tags);
+ if (pmatch)
+ xfree(pmatch);
+ if (states_seen)
+ xfree(states_seen);
+#endif /* !TRE_USE_ALLOCA */
+
+ return ret;
+}
+
+/***********************************************************************
+ from regexec.c
+***********************************************************************/
+
+/* Fills the POSIX.2 regmatch_t array according to the TNFA tag and match
+ endpoint values. */
+static void
+tre_fill_pmatch(size_t nmatch, regmatch_t pmatch[], int cflags,
+ const tre_tnfa_t *tnfa, regoff_t *tags, regoff_t match_eo)
+{
+ tre_submatch_data_t *submatch_data;
+ unsigned int i, j;
+ int *parents;
+
+ i = 0;
+ if (match_eo >= 0 && !(cflags & REG_NOSUB))
+ {
+ /* Construct submatch offsets from the tags. */
+ submatch_data = tnfa->submatch_data;
+ while (i < tnfa->num_submatches && i < nmatch)
+ {
+ if (submatch_data[i].so_tag == tnfa->end_tag)
+ pmatch[i].rm_so = match_eo;
+ else
+ pmatch[i].rm_so = tags[submatch_data[i].so_tag];
+
+ if (submatch_data[i].eo_tag == tnfa->end_tag)
+ pmatch[i].rm_eo = match_eo;
+ else
+ pmatch[i].rm_eo = tags[submatch_data[i].eo_tag];
+
+ /* If either of the endpoints were not used, this submatch
+ was not part of the match. */
+ if (pmatch[i].rm_so == -1 || pmatch[i].rm_eo == -1)
+ pmatch[i].rm_so = pmatch[i].rm_eo = -1;
+
+ i++;
+ }
+ /* Reset all submatches that are not within all of their parent
+ submatches. */
+ i = 0;
+ while (i < tnfa->num_submatches && i < nmatch)
+ {
+ if (pmatch[i].rm_eo == -1)
+ assert(pmatch[i].rm_so == -1);
+ assert(pmatch[i].rm_so <= pmatch[i].rm_eo);
+
+ parents = submatch_data[i].parents;
+ if (parents != NULL)
+ for (j = 0; parents[j] >= 0; j++)
+ {
+ if (pmatch[i].rm_so < pmatch[parents[j]].rm_so
+ || pmatch[i].rm_eo > pmatch[parents[j]].rm_eo)
+ pmatch[i].rm_so = pmatch[i].rm_eo = -1;
+ }
+ i++;
+ }
+ }
+
+ while (i < nmatch)
+ {
+ pmatch[i].rm_so = -1;
+ pmatch[i].rm_eo = -1;
+ i++;
+ }
+}
+
+
+/*
+ Wrapper functions for POSIX compatible regexp matching.
+*/
+
+int
+regexec(const regex_t *__restrict preg, const char *__restrict string,
+ size_t nmatch, regmatch_t * __restrict pmatch, int eflags)
+{
+ tre_tnfa_t *tnfa = (void *)preg->TRE_REGEX_T_FIELD;
+ reg_errcode_t status;
+ regoff_t *tags = NULL, eo;
+ if (tnfa->cflags & REG_NOSUB) nmatch = 0;
+ if (tnfa->num_tags > 0 && nmatch > 0)
+ {
+ tags = xmalloc(sizeof(*tags) * tnfa->num_tags);
+ if (tags == NULL)
+ return REG_ESPACE;
+ }
+
+ /* Dispatch to the appropriate matcher. */
+ if (tnfa->have_backrefs)
+ {
+ /* The regex has back references, use the backtracking matcher. */
+ status = tre_tnfa_run_backtrack(tnfa, string, tags, eflags, &eo);
+ }
+ else
+ {
+ /* Exact matching, no back references, use the parallel matcher. */
+ status = tre_tnfa_run_parallel(tnfa, string, tags, eflags, &eo);
+ }
+
+ if (status == REG_OK)
+ /* A match was found, so fill the submatch registers. */
+ tre_fill_pmatch(nmatch, pmatch, tnfa->cflags, tnfa, tags, eo);
+ if (tags)
+ xfree(tags);
+ return status;
+} \ No newline at end of file
diff --git a/win32/strfncs.c b/win32/strfncs.c
new file mode 100644
index 0000000..2f7c011
--- /dev/null
+++ b/win32/strfncs.c
@@ -0,0 +1,35 @@
+#include <string.h>
+#include <ctype.h>
+
+int strcasecmp(const char *_l, const char *_r)
+{
+ const unsigned char *l=(void *)_l, *r=(void *)_r;
+ for (; *l && *r && (*l == *r || tolower(*l) == tolower(*r)); l++, r++);
+ return tolower(*l) - tolower(*r);
+}
+
+int strncasecmp(const char *_l, const char *_r, size_t n)
+{
+ const unsigned char *l=(void *)_l, *r=(void *)_r;
+ if (!n--) return 0;
+ for (; *l && *r && n && (*l == *r || tolower(*l) == tolower(*r)); l++, r++, n--);
+ return tolower(*l) - tolower(*r);
+}
+
+char *strcasestr(const char *h, const char *n)
+{
+ size_t l = strlen(n);
+ for (; *h; h++) if (!strncasecmp(h, n, l)) return (char *)h;
+ return 0;
+}
+
+char *strtok_r(char *s, const char *sep, char **p)
+{
+ if (!s && !(s = *p)) return NULL;
+ s += strspn(s, sep);
+ if (!*s) return *p = 0;
+ *p = s + strcspn(s, sep);
+ if (**p) *(*p)++ = 0;
+ else *p = 0;
+ return s;
+}
diff --git a/win32/strfncs.h b/win32/strfncs.h
new file mode 100644
index 0000000..733bee7
--- /dev/null
+++ b/win32/strfncs.h
@@ -0,0 +1,8 @@
+#include <string.h>
+
+int strcasecmp(const char *_l, const char *_r);
+int strncasecmp(const char *_l, const char *_r, size_t n);
+
+char *strcasestr(const char *h, const char *n);
+
+char *strtok_r(char *s, const char *sep, char **p);
diff --git a/win32/tre-mem.c b/win32/tre-mem.c
new file mode 100644
index 0000000..0fbf7b2
--- /dev/null
+++ b/win32/tre-mem.c
@@ -0,0 +1,158 @@
+/*
+ tre-mem.c - TRE memory allocator
+
+ Copyright (c) 2001-2009 Ville Laurikari <vl@iki.fi>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+/*
+ This memory allocator is for allocating small memory blocks efficiently
+ in terms of memory overhead and execution speed. The allocated blocks
+ cannot be freed individually, only all at once. There can be multiple
+ allocators, though.
+*/
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "tre.h"
+
+/*
+ This memory allocator is for allocating small memory blocks efficiently
+ in terms of memory overhead and execution speed. The allocated blocks
+ cannot be freed individually, only all at once. There can be multiple
+ allocators, though.
+*/
+
+/* Returns a new memory allocator or NULL if out of memory. */
+tre_mem_t
+tre_mem_new_impl(int provided, void *provided_block)
+{
+ tre_mem_t mem;
+ if (provided)
+ {
+ mem = provided_block;
+ memset(mem, 0, sizeof(*mem));
+ }
+ else
+ mem = xcalloc(1, sizeof(*mem));
+ if (mem == NULL)
+ return NULL;
+ return mem;
+}
+
+
+/* Frees the memory allocator and all memory allocated with it. */
+void
+tre_mem_destroy(tre_mem_t mem)
+{
+ tre_list_t *tmp, *l = mem->blocks;
+
+ while (l != NULL)
+ {
+ xfree(l->data);
+ tmp = l->next;
+ xfree(l);
+ l = tmp;
+ }
+ xfree(mem);
+}
+
+
+/* Allocates a block of `size' bytes from `mem'. Returns a pointer to the
+ allocated block or NULL if an underlying malloc() failed. */
+void *
+tre_mem_alloc_impl(tre_mem_t mem, int provided, void *provided_block,
+ int zero, size_t size)
+{
+ void *ptr;
+
+ if (mem->failed)
+ {
+ return NULL;
+ }
+
+ if (mem->n < size)
+ {
+ /* We need more memory than is available in the current block.
+ Allocate a new block. */
+ tre_list_t *l;
+ if (provided)
+ {
+ if (provided_block == NULL)
+ {
+ mem->failed = 1;
+ return NULL;
+ }
+ mem->ptr = provided_block;
+ mem->n = TRE_MEM_BLOCK_SIZE;
+ }
+ else
+ {
+ int block_size;
+ if (size * 8 > TRE_MEM_BLOCK_SIZE)
+ block_size = size * 8;
+ else
+ block_size = TRE_MEM_BLOCK_SIZE;
+ l = xmalloc(sizeof(*l));
+ if (l == NULL)
+ {
+ mem->failed = 1;
+ return NULL;
+ }
+ l->data = xmalloc(block_size);
+ if (l->data == NULL)
+ {
+ xfree(l);
+ mem->failed = 1;
+ return NULL;
+ }
+ l->next = NULL;
+ if (mem->current != NULL)
+ mem->current->next = l;
+ if (mem->blocks == NULL)
+ mem->blocks = l;
+ mem->current = l;
+ mem->ptr = l->data;
+ mem->n = block_size;
+ }
+ }
+
+ /* Make sure the next pointer will be aligned. */
+ size += ALIGN(mem->ptr + size, long);
+
+ /* Allocate from current block. */
+ ptr = mem->ptr;
+ mem->ptr += size;
+ mem->n -= size;
+
+ /* Set to zero if needed. */
+ if (zero)
+ memset(ptr, 0, size);
+
+ return ptr;
+} \ No newline at end of file
diff --git a/win32/tre.h b/win32/tre.h
new file mode 100644
index 0000000..c07c959
--- /dev/null
+++ b/win32/tre.h
@@ -0,0 +1,230 @@
+/*
+ tre-internal.h - TRE internal definitions
+
+ Copyright (c) 2001-2009 Ville Laurikari <vl@iki.fi>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+#include <regex.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#undef TRE_MBSTATE
+
+#define NDEBUG
+
+#define TRE_REGEX_T_FIELD __opaque
+typedef int reg_errcode_t;
+
+typedef wchar_t tre_char_t;
+
+#define DPRINT(msg) do { } while(0)
+
+#define elementsof(x) ( sizeof(x) / sizeof(x[0]) )
+
+#define tre_mbrtowc(pwc, s, n, ps) (mbtowc((pwc), (s), (n)))
+
+/* Wide characters. */
+typedef wint_t tre_cint_t;
+#define TRE_CHAR_MAX 0x10ffff
+
+#define tre_isalnum iswalnum
+#define tre_isalpha iswalpha
+#define tre_isblank iswblank
+#define tre_iscntrl iswcntrl
+#define tre_isdigit iswdigit
+#define tre_isgraph iswgraph
+#define tre_islower iswlower
+#define tre_isprint iswprint
+#define tre_ispunct iswpunct
+#define tre_isspace iswspace
+#define tre_isupper iswupper
+#define tre_isxdigit iswxdigit
+
+#define tre_tolower towlower
+#define tre_toupper towupper
+#define tre_strlen wcslen
+
+/* Use system provided iswctype() and wctype(). */
+typedef wctype_t tre_ctype_t;
+#define tre_isctype iswctype
+#define tre_ctype wctype
+
+/* Returns number of bytes to add to (char *)ptr to make it
+ properly aligned for the type. */
+#define ALIGN(ptr, type) \
+ ((((long)ptr) % sizeof(type)) \
+ ? (sizeof(type) - (((long)ptr) % sizeof(type))) \
+ : 0)
+
+#undef MAX
+#undef MIN
+#define MAX(a, b) (((a) >= (b)) ? (a) : (b))
+#define MIN(a, b) (((a) <= (b)) ? (a) : (b))
+
+/* TNFA transition type. A TNFA state is an array of transitions,
+ the terminator is a transition with NULL `state'. */
+typedef struct tnfa_transition tre_tnfa_transition_t;
+
+struct tnfa_transition {
+ /* Range of accepted characters. */
+ tre_cint_t code_min;
+ tre_cint_t code_max;
+ /* Pointer to the destination state. */
+ tre_tnfa_transition_t *state;
+ /* ID number of the destination state. */
+ int state_id;
+ /* -1 terminated array of tags (or NULL). */
+ int *tags;
+ /* Assertion bitmap. */
+ int assertions;
+ /* Assertion parameters. */
+ union {
+ /* Character class assertion. */
+ tre_ctype_t class;
+ /* Back reference assertion. */
+ int backref;
+ } u;
+ /* Negative character class assertions. */
+ tre_ctype_t *neg_classes;
+};
+
+
+/* Assertions. */
+#define ASSERT_AT_BOL 1 /* Beginning of line. */
+#define ASSERT_AT_EOL 2 /* End of line. */
+#define ASSERT_CHAR_CLASS 4 /* Character class in `class'. */
+#define ASSERT_CHAR_CLASS_NEG 8 /* Character classes in `neg_classes'. */
+#define ASSERT_AT_BOW 16 /* Beginning of word. */
+#define ASSERT_AT_EOW 32 /* End of word. */
+#define ASSERT_AT_WB 64 /* Word boundary. */
+#define ASSERT_AT_WB_NEG 128 /* Not a word boundary. */
+#define ASSERT_BACKREF 256 /* A back reference in `backref'. */
+#define ASSERT_LAST 256
+
+/* Tag directions. */
+typedef enum {
+ TRE_TAG_MINIMIZE = 0,
+ TRE_TAG_MAXIMIZE = 1
+} tre_tag_direction_t;
+
+/* Instructions to compute submatch register values from tag values
+ after a successful match. */
+struct tre_submatch_data {
+ /* Tag that gives the value for rm_so (submatch start offset). */
+ int so_tag;
+ /* Tag that gives the value for rm_eo (submatch end offset). */
+ int eo_tag;
+ /* List of submatches this submatch is contained in. */
+ int *parents;
+};
+
+typedef struct tre_submatch_data tre_submatch_data_t;
+
+
+/* TNFA definition. */
+typedef struct tnfa tre_tnfa_t;
+
+struct tnfa {
+ tre_tnfa_transition_t *transitions;
+ unsigned int num_transitions;
+ tre_tnfa_transition_t *initial;
+ tre_tnfa_transition_t *final;
+ tre_submatch_data_t *submatch_data;
+ char *firstpos_chars;
+ int first_char;
+ unsigned int num_submatches;
+ tre_tag_direction_t *tag_directions;
+ int *minimal_tags;
+ int num_tags;
+ int num_minimals;
+ int end_tag;
+ int num_states;
+ int cflags;
+ int have_backrefs;
+ int have_approx;
+};
+
+/* from tre-mem.h: */
+
+#define TRE_MEM_BLOCK_SIZE 1024
+
+typedef struct tre_list {
+ void *data;
+ struct tre_list *next;
+} tre_list_t;
+
+typedef struct tre_mem_struct {
+ tre_list_t *blocks;
+ tre_list_t *current;
+ char *ptr;
+ size_t n;
+ int failed;
+ void **provided;
+} *tre_mem_t;
+
+#define tre_mem_new_impl __tre_mem_new_impl
+#define tre_mem_alloc_impl __tre_mem_alloc_impl
+#define tre_mem_destroy __tre_mem_destroy
+
+tre_mem_t tre_mem_new_impl(int provided, void *provided_block);
+void *tre_mem_alloc_impl(tre_mem_t mem, int provided, void *provided_block,
+ int zero, size_t size);
+
+/* Returns a new memory allocator or NULL if out of memory. */
+#define tre_mem_new() tre_mem_new_impl(0, NULL)
+
+/* Allocates a block of `size' bytes from `mem'. Returns a pointer to the
+ allocated block or NULL if an underlying malloc() failed. */
+#define tre_mem_alloc(mem, size) tre_mem_alloc_impl(mem, 0, NULL, 0, size)
+
+/* Allocates a block of `size' bytes from `mem'. Returns a pointer to the
+ allocated block or NULL if an underlying malloc() failed. The memory
+ is set to zero. */
+#define tre_mem_calloc(mem, size) tre_mem_alloc_impl(mem, 0, NULL, 1, size)
+
+#ifdef TRE_USE_ALLOCA
+/* alloca() versions. Like above, but memory is allocated with alloca()
+ instead of malloc(). */
+
+#define tre_mem_newa() \
+ tre_mem_new_impl(1, alloca(sizeof(struct tre_mem_struct)))
+
+#define tre_mem_alloca(mem, size) \
+ ((mem)->n >= (size) \
+ ? tre_mem_alloc_impl((mem), 1, NULL, 0, (size)) \
+ : tre_mem_alloc_impl((mem), 1, alloca(TRE_MEM_BLOCK_SIZE), 0, (size)))
+#endif /* TRE_USE_ALLOCA */
+
+
+/* Frees the memory allocator and all memory allocated with it. */
+void tre_mem_destroy(tre_mem_t mem);
+
+#define xmalloc malloc
+#define xcalloc calloc
+#define xfree free
+#define xrealloc realloc
diff --git a/win32/unistd.h b/win32/unistd.h
new file mode 100644
index 0000000..41b121c
--- /dev/null
+++ b/win32/unistd.h
@@ -0,0 +1,4 @@
+#ifndef _UNISTD_H
+#define _UNISTD_H
+
+#endif \ No newline at end of file