build dll on Windows
authorWolf Vollprecht <w.vollprecht@gmail.com>
Wed, 15 Dec 2021 15:12:19 +0000 (16:12 +0100)
committerWolf Vollprecht <w.vollprecht@gmail.com>
Fri, 7 Jan 2022 09:08:31 +0000 (10:08 +0100)
22 files changed:
include/zck.h.in
meson.build
src/lib/comp/comp.c
src/lib/dl/dl.c
src/lib/dl/range.c
src/lib/error.c
src/lib/hash/hash.c
src/lib/header.c
src/lib/index/index_read.c
src/lib/log.c
src/lib/meson.build
src/lib/win32/LICENSE [new file with mode: 0644]
src/lib/win32/regcomp.c [new file with mode: 0644]
src/lib/win32/regerror.c [new file with mode: 0644]
src/lib/win32/regex.h [new file with mode: 0644]
src/lib/win32/regexec.c [new file with mode: 0644]
src/lib/win32/tre-mem.c [new file with mode: 0644]
src/lib/win32/tre.h [new file with mode: 0644]
src/lib/win32/unistd.h [new file with mode: 0644]
src/lib/zck.c
src/lib/zck_private.h
src/meson.build

index 17b7d203ff8656121d33e3bcac0286e88d5cd11e..0ca5c5fb49c8376274671baabd19fe9c60afd3f9 100644 (file)
@@ -58,125 +58,142 @@ typedef struct zckDL zckDL;
 
 typedef size_t (*zck_wcb)(void *ptr, size_t l, size_t c, void *dl_v);
 
+#ifdef _WIN32
+    #define ZCK_WARN_UNUSED
+    typedef ptrdiff_t ssize_t;
+    #ifdef ZCHUNK_STATIC_LIB
+        #define ZCK_PUBLIC_API
+    #else
+        #ifdef ZCHUNK_EXPORTS
+            #define ZCK_PUBLIC_API __declspec(dllexport)
+        #else
+            #define ZCK_PUBLIC_API __declspec(dllimport)
+        #endif
+    #endif
+#else
+    #define ZCK_WARN_UNUSED __attribute__ ((warn_unused_result))
+    #define ZCK_PUBLIC_API __attribute__((visibility("default")))
+#endif
+
 /*******************************************************************
  * Reading a zchunk file
  *******************************************************************/
 /* Initialize zchunk context */
-zckCtx *zck_create(void)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API zckCtx *zck_create(void)
+    ZCK_WARN_UNUSED;
 /* Initialize zchunk for reading */
-bool zck_init_read (zckCtx *zck, int src_fd)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API bool zck_init_read (zckCtx *zck, int src_fd)
+    ZCK_WARN_UNUSED;
 /* Decompress dst_size bytes from zchunk file to dst, while verifying hashes */
-ssize_t zck_read(zckCtx *zck, char *dst, size_t dst_size)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API ssize_t zck_read(zckCtx *zck, char *dst, size_t dst_size)
+    ZCK_WARN_UNUSED;
 
 
 /*******************************************************************
  * Writing a zchunk file
  *******************************************************************/
 /* Initialize zchunk for writing */
-bool zck_init_write (zckCtx *zck, int dst_fd)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API bool zck_init_write (zckCtx *zck, int dst_fd)
+    ZCK_WARN_UNUSED;
 /* Compress data src of size src_size, and write to zchunk file
  * Due to the nature of zchunk files and how they are built, no data will
  * actually appear in the zchunk file until zck_close() is called */
-ssize_t zck_write(zckCtx *zck, const char *src, const size_t src_size)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API ssize_t zck_write(zckCtx *zck, const char *src, const size_t src_size)
+    ZCK_WARN_UNUSED;
 /* Create a chunk boundary */
-ssize_t zck_end_chunk(zckCtx *zck)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API ssize_t zck_end_chunk(zckCtx *zck)
+    ZCK_WARN_UNUSED;
 /* Create the database for uthash if not present (done automatically by read */
-bool zck_generate_hashdb(zckCtx *zck);
+ZCK_PUBLIC_API bool zck_generate_hashdb(zckCtx *zck);
 
 /*******************************************************************
  * Common functions for finishing a zchunk file
  *******************************************************************/
 /* Close a zchunk file so it may no longer be read from or written to. The
  * context still contains information about the file */
-bool zck_close(zckCtx *zck)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API bool zck_close(zckCtx *zck)
+    ZCK_WARN_UNUSED;
 /* Free a zchunk context.  You must pass the address of the context, and the
  * context will automatically be set to NULL after it is freed */
-void zck_free(zckCtx **zck);
+ZCK_PUBLIC_API void zck_free(zckCtx **zck);
 
 
 /*******************************************************************
  * Options
  *******************************************************************/
 /* Set string option */
-bool zck_set_soption(zckCtx *zck, zck_soption option, const char *value,
+ZCK_PUBLIC_API bool zck_set_soption(zckCtx *zck, zck_soption option, const char *value,
                      size_t length)
-    __attribute__ ((warn_unused_result));
+    ZCK_WARN_UNUSED;
 /* Set integer option */
-bool zck_set_ioption(zckCtx *zck, zck_ioption option, ssize_t value)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API bool zck_set_ioption(zckCtx *zck, zck_ioption option, ssize_t value)
+    ZCK_WARN_UNUSED;
 
 
 /*******************************************************************
  * Error handling
  *******************************************************************/
 /* Set logging level */
-void zck_set_log_level(zck_log_type ll);
+ZCK_PUBLIC_API void zck_set_log_level(zck_log_type ll);
 /* Set logging fd */
-void zck_set_log_fd(int fd);
+ZCK_PUBLIC_API void zck_set_log_fd(int fd);
 /* Check whether zck is in error state
  * Returns 0 if not, 1 if recoverable error, 2 if fatal error */
-int zck_is_error(zckCtx *zck)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API int zck_is_error(zckCtx *zck)
+    ZCK_WARN_UNUSED;
 /* Get error message
  * Returns char* containing error message.  char* will contain empty string if
  * there is no error message */
-const char *zck_get_error(zckCtx *zck);
+ZCK_PUBLIC_API const char *zck_get_error(zckCtx *zck);
 /* Clear error message
  * Returns 1 if message was cleared, 0 if error is fatal and can't be cleared */
-bool zck_clear_error(zckCtx *zck);
+ZCK_PUBLIC_API bool zck_clear_error(zckCtx *zck);
 /* Set a callback for logs instead to write into a fd */
 typedef void (*logcallback)(const char *function, zck_log_type lt, const char *format, va_list args);
-void zck_set_log_callback(logcallback function);
+ZCK_PUBLIC_API void zck_set_log_callback(logcallback function);
 
 /*******************************************************************
  * Miscellaneous utilities
  *******************************************************************/
 /* Validate the chunk and data checksums for the current file.
  * Returns 0 for error, -1 for invalid checksum and 1 for valid checksum */
-int zck_validate_checksums(zckCtx *zck)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API int zck_validate_checksums(zckCtx *zck)
+    ZCK_WARN_UNUSED;
 /* Validate just the data checksum for the current file
  * Returns 0 for error, -1 for invalid checksum and 1 for valid checksum */
-int zck_validate_data_checksum(zckCtx *zck)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API int zck_validate_data_checksum(zckCtx *zck)
+    ZCK_WARN_UNUSED;
 /* Go through file and mark valid chunks as valid
  * Returns 0 for error, -1 for invalid checksum and 1 for valid checksum */
-int zck_find_valid_chunks(zckCtx *zck)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API int zck_find_valid_chunks(zckCtx *zck)
+    ZCK_WARN_UNUSED;
 
 /* Get a zckRange of ranges that need to still be downloaded.
  * max_ranges is the maximum number of ranges supported in a single request
  *     by the server.  If the server supports unlimited ranges, set this to -1
  * Returns NULL if there's an error */
-zckRange *zck_get_missing_range(zckCtx *zck, int max_ranges)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API zckRange *zck_get_missing_range(zckCtx *zck, int max_ranges)
+    ZCK_WARN_UNUSED;
 /* Get a string representation of a zckRange */
-char *zck_get_range_char(zckCtx *zck, zckRange *range)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API char *zck_get_range_char(zckCtx *zck, zckRange *range)
+    ZCK_WARN_UNUSED;
 /* Get file descriptor attached to zchunk context */
-int zck_get_fd(zckCtx *zck)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API int zck_get_fd(zckCtx *zck)
+    ZCK_WARN_UNUSED;
 /* Set file descriptor attached to zchunk context */
-bool zck_set_fd(zckCtx *zck, int fd)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API bool zck_set_fd(zckCtx *zck, int fd)
+    ZCK_WARN_UNUSED;
 
 /* Return number of missing chunks (-1 if error) */
-int zck_missing_chunks(zckCtx *zck)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API int zck_missing_chunks(zckCtx *zck)
+    ZCK_WARN_UNUSED;
 /* Return number of failed chunks (-1 if error) */
-int zck_failed_chunks(zckCtx *zck)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API int zck_failed_chunks(zckCtx *zck)
+    ZCK_WARN_UNUSED;
 /* Reset failed chunks to become missing */
-void zck_reset_failed_chunks(zckCtx *zck);
+ZCK_PUBLIC_API void zck_reset_failed_chunks(zckCtx *zck);
 /* Find chunks from a source */
-bool zck_find_matching_chunks(zckCtx *src, zckCtx *tgt);
+ZCK_PUBLIC_API bool zck_find_matching_chunks(zckCtx *src, zckCtx *tgt);
 
 /*******************************************************************
  * The functions should be all you need to read and write a zchunk
@@ -189,31 +206,31 @@ bool zck_find_matching_chunks(zckCtx *src, zckCtx *tgt);
  * Advanced miscellaneous zchunk functions
  *******************************************************************/
 /* Get lead length */
-ssize_t zck_get_lead_length(zckCtx *zck)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API ssize_t zck_get_lead_length(zckCtx *zck)
+    ZCK_WARN_UNUSED;
 /* Get header length (lead + preface + index + sigs) */
-ssize_t zck_get_header_length(zckCtx *zck)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API ssize_t zck_get_header_length(zckCtx *zck)
+    ZCK_WARN_UNUSED;
 /* Get data length */
-ssize_t zck_get_data_length(zckCtx *zck)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API ssize_t zck_get_data_length(zckCtx *zck)
+    ZCK_WARN_UNUSED;
 /* Get file length */
-ssize_t zck_get_length(zckCtx *zck)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API ssize_t zck_get_length(zckCtx *zck)
+    ZCK_WARN_UNUSED;
 /* Get index digest */
-char *zck_get_header_digest(zckCtx *zck)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API char *zck_get_header_digest(zckCtx *zck)
+    ZCK_WARN_UNUSED;
 /* Get data digest */
-char *zck_get_data_digest(zckCtx *zck)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API char *zck_get_data_digest(zckCtx *zck)
+    ZCK_WARN_UNUSED;
 
 
 /*******************************************************************
  * Advanced compression functions
  *******************************************************************/
 /* Get name of compression type */
-const char *zck_comp_name_from_type(int comp_type)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API const char *zck_comp_name_from_type(int comp_type)
+    ZCK_WARN_UNUSED;
 /* Initialize compression.  Compression type and parameters *must* be done
  * before this is called */
 
@@ -222,85 +239,85 @@ const char *zck_comp_name_from_type(int comp_type)
  * Advanced zchunk reading functions
  *******************************************************************/
 /* Initialize zchunk for reading using advanced options */
-bool zck_init_adv_read (zckCtx *zck, int src_fd)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API bool zck_init_adv_read (zckCtx *zck, int src_fd)
+    ZCK_WARN_UNUSED;
 /* Read zchunk lead */
-bool zck_read_lead(zckCtx *zck)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API bool zck_read_lead(zckCtx *zck)
+    ZCK_WARN_UNUSED;
 /* Read zchunk header */
-bool zck_read_header(zckCtx *zck)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API bool zck_read_header(zckCtx *zck)
+    ZCK_WARN_UNUSED;
 /* Validate lead */
-bool zck_validate_lead(zckCtx *zck)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API bool zck_validate_lead(zckCtx *zck)
+    ZCK_WARN_UNUSED;
 
 /*******************************************************************
  * Indexes
  *******************************************************************/
 /* Get chunk count */
-ssize_t zck_get_chunk_count(zckCtx *zck)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API ssize_t zck_get_chunk_count(zckCtx *zck)
+    ZCK_WARN_UNUSED;
 /* Get chunk by number */
-zckChunk *zck_get_chunk(zckCtx *zck, size_t number)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API zckChunk *zck_get_chunk(zckCtx *zck, size_t number)
+    ZCK_WARN_UNUSED;
 /* Get first chunk */
-zckChunk *zck_get_first_chunk(zckCtx *zck)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API zckChunk *zck_get_first_chunk(zckCtx *zck)
+    ZCK_WARN_UNUSED;
 /* Get next chunk */
-zckChunk *zck_get_next_chunk(zckChunk *idx)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API zckChunk *zck_get_next_chunk(zckChunk *idx)
+    ZCK_WARN_UNUSED;
 /* Get chunk starting location */
-ssize_t zck_get_chunk_start(zckChunk *idx)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API ssize_t zck_get_chunk_start(zckChunk *idx)
+    ZCK_WARN_UNUSED;
 /* Get uncompressed chunk size */
-ssize_t zck_get_chunk_size(zckChunk *idx)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API ssize_t zck_get_chunk_size(zckChunk *idx)
+    ZCK_WARN_UNUSED;
 /* Get compressed chunk size */
-ssize_t zck_get_chunk_comp_size(zckChunk *idx)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API ssize_t zck_get_chunk_comp_size(zckChunk *idx)
+    ZCK_WARN_UNUSED;
 /* Get chunk number */
-ssize_t zck_get_chunk_number(zckChunk *idx)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API ssize_t zck_get_chunk_number(zckChunk *idx)
+    ZCK_WARN_UNUSED;
 /* Get validity of current chunk - 1 = valid, 0 = missing, -1 = invalid */
-int zck_get_chunk_valid(zckChunk *idx)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API int zck_get_chunk_valid(zckChunk *idx)
+    ZCK_WARN_UNUSED;
 /* Get chunk digest */
-char *zck_get_chunk_digest(zckChunk *item)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API char *zck_get_chunk_digest(zckChunk *item)
+    ZCK_WARN_UNUSED;
 /* Get digest size of chunk hash type */
-ssize_t zck_get_chunk_digest_size(zckCtx *zck)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API ssize_t zck_get_chunk_digest_size(zckCtx *zck)
+    ZCK_WARN_UNUSED;
 /* Get uncompressed chunk digest */
-char *zck_get_chunk_digest_uncompressed(zckChunk *item)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API char *zck_get_chunk_digest_uncompressed(zckChunk *item)
+    ZCK_WARN_UNUSED;
 /* Get chunk data */
-ssize_t zck_get_chunk_data(zckChunk *idx, char *dst, size_t dst_size)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API ssize_t zck_get_chunk_data(zckChunk *idx, char *dst, size_t dst_size)
+    ZCK_WARN_UNUSED;
 /* Get compressed chunk data */
-ssize_t zck_get_chunk_comp_data(zckChunk *idx, char *dst, size_t dst_size)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API ssize_t zck_get_chunk_comp_data(zckChunk *idx, char *dst, size_t dst_size)
+    ZCK_WARN_UNUSED;
 /* Find out if two chunk digests are the same */
-bool zck_compare_chunk_digest(zckChunk *a, zckChunk *b)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API bool zck_compare_chunk_digest(zckChunk *a, zckChunk *b)
+    ZCK_WARN_UNUSED;
 /* Get associate src chunk if any */
-zckChunk *zck_get_src_chunk(zckChunk *idx)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API zckChunk *zck_get_src_chunk(zckChunk *idx)
+    ZCK_WARN_UNUSED;
 
 /*******************************************************************
  * Advanced hash functions
  *******************************************************************/
 /* Get overall hash type */
-int zck_get_full_hash_type(zckCtx *zck)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API int zck_get_full_hash_type(zckCtx *zck)
+    ZCK_WARN_UNUSED;
 /* Get digest size of overall hash type */
-ssize_t zck_get_full_digest_size(zckCtx *zck)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API ssize_t zck_get_full_digest_size(zckCtx *zck)
+    ZCK_WARN_UNUSED;
 /* Get chunk hash type */
-int zck_get_chunk_hash_type(zckCtx *zck)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API int zck_get_chunk_hash_type(zckCtx *zck)
+    ZCK_WARN_UNUSED;
 /* Get name of hash type */
-const char *zck_hash_name_from_type(int hash_type)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API const char *zck_hash_name_from_type(int hash_type)
+    ZCK_WARN_UNUSED;
 
 
 
@@ -312,74 +329,74 @@ const char *zck_hash_name_from_type(int hash_type)
  * Ranges
  *******************************************************************/
 /* Get any matching chunks from src and put them in the right place in tgt */
-bool zck_copy_chunks(zckCtx *src, zckCtx *tgt)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API bool zck_copy_chunks(zckCtx *src, zckCtx *tgt)
+    ZCK_WARN_UNUSED;
 /* Free zckRange */
-void zck_range_free(zckRange **info);
+ZCK_PUBLIC_API void zck_range_free(zckRange **info);
 /* Get range string from start and end location */
-char *zck_get_range(size_t start, size_t end)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API char *zck_get_range(size_t start, size_t end)
+    ZCK_WARN_UNUSED;
 /* Get the minimum size needed to download in order to know how large the header
  * is */
-int zck_get_min_download_size(void)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API int zck_get_min_download_size(void)
+    ZCK_WARN_UNUSED;
 /* Get the number of separate range items in the range */
-int zck_get_range_count(zckRange *range)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API int zck_get_range_count(zckRange *range)
+    ZCK_WARN_UNUSED;
 
 /*******************************************************************
  * Downloading
  *******************************************************************/
 /* Initialize zchunk download context */
-zckDL *zck_dl_init(zckCtx *zck)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API zckDL *zck_dl_init(zckCtx *zck)
+    ZCK_WARN_UNUSED;
 /* Reset zchunk download context for reuse */
-void zck_dl_reset(zckDL *dl);
+ZCK_PUBLIC_API void zck_dl_reset(zckDL *dl);
 /* Free zchunk download context */
-void zck_dl_free(zckDL **dl);
+ZCK_PUBLIC_API void zck_dl_free(zckDL **dl);
 /* Get zchunk context from download context */
-zckCtx *zck_dl_get_zck(zckDL *dl)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API zckCtx *zck_dl_get_zck(zckDL *dl)
+    ZCK_WARN_UNUSED;
 /* Set zchunk context in download context */
-bool zck_dl_set_zck(zckDL *dl, zckCtx *zck)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API bool zck_dl_set_zck(zckDL *dl, zckCtx *zck)
+    ZCK_WARN_UNUSED;
 /* Clear regex used for extracting download ranges from multipart download */
-void zck_dl_clear_regex(zckDL *dl);
+ZCK_PUBLIC_API void zck_dl_clear_regex(zckDL *dl);
 /* Download and process the header from url */
-bool zck_dl_get_header(zckCtx *zck, zckDL *dl, char *url)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API bool zck_dl_get_header(zckCtx *zck, zckDL *dl, char *url)
+    ZCK_WARN_UNUSED;
 /* Get number of bytes downloaded using download context */
-ssize_t zck_dl_get_bytes_downloaded(zckDL *dl)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API ssize_t zck_dl_get_bytes_downloaded(zckDL *dl)
+    ZCK_WARN_UNUSED;
 /* Get number of bytes uploaded using download context */
-ssize_t zck_dl_get_bytes_uploaded(zckDL *dl)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API ssize_t zck_dl_get_bytes_uploaded(zckDL *dl)
+    ZCK_WARN_UNUSED;
 /* Set download ranges for zchunk download context */
-bool zck_dl_set_range(zckDL *dl, zckRange *range)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API bool zck_dl_set_range(zckDL *dl, zckRange *range)
+    ZCK_WARN_UNUSED;
 /* Get download ranges from zchunk download context */
-zckRange *zck_dl_get_range(zckDL *dl)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API zckRange *zck_dl_get_range(zckDL *dl)
+    ZCK_WARN_UNUSED;
 
 /* Set header callback function */
-bool zck_dl_set_header_cb(zckDL *dl, zck_wcb func)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API bool zck_dl_set_header_cb(zckDL *dl, zck_wcb func)
+    ZCK_WARN_UNUSED;
 /* Set header userdata */
-bool zck_dl_set_header_data(zckDL *dl, void *data)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API bool zck_dl_set_header_data(zckDL *dl, void *data)
+    ZCK_WARN_UNUSED;
 /* Set write callback function */
-bool zck_dl_set_write_cb(zckDL *dl, zck_wcb func)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API bool zck_dl_set_write_cb(zckDL *dl, zck_wcb func)
+    ZCK_WARN_UNUSED;
 /* Set write userdata */
-bool zck_dl_set_write_data(zckDL *dl, void *data)
-    __attribute__ ((warn_unused_result));
+ZCK_PUBLIC_API bool zck_dl_set_write_data(zckDL *dl, void *data)
+    ZCK_WARN_UNUSED;
 
 /* Write callback.  You *must* pass this and your initialized zchunk download
  * context to the downloader when downloading a zchunk file.  If you have your
  * own callback, set dl->write_cb to your callback and dl->wdata to your
  * callback data. */
-size_t zck_write_chunk_cb(void *ptr, size_t l, size_t c, void *dl_v);
-size_t zck_write_zck_header_cb(void *ptr, size_t l, size_t c, void *dl_v);
-size_t zck_header_cb(char *b, size_t l, size_t c, void *dl_v);
+ZCK_PUBLIC_API size_t zck_write_chunk_cb(void *ptr, size_t l, size_t c, void *dl_v);
+ZCK_PUBLIC_API size_t zck_write_zck_header_cb(void *ptr, size_t l, size_t c, void *dl_v);
+ZCK_PUBLIC_API size_t zck_header_cb(char *b, size_t l, size_t c, void *dl_v);
 
 #endif
index 31dc079a8460b4ffca6e6e0056858d57fa2fe2b4..34fecbecb2379cd1a1ce973e5597dd64ebf99d82 100644 (file)
@@ -20,8 +20,12 @@ endif
 if get_option('with-zstd') == 'disabled'
     zstd_dep = dependency('', required : false)
 else
-    zstd_dep = dependency('libzstd', required : get_option('with-zstd') == 'enabled',
-                          default_options: ['bin_programs=false', 'zlib=disabled', 'lzma=disabled', 'lz4=disabled'])
+    if build_machine.system() == 'windows'
+        zstd_dep = dependency('zstd', modules: 'zstd::libzstd_shared', required : get_option('with-zstd') == 'enabled')
+    else
+        zstd_dep = dependency('libzstd', required : get_option('with-zstd') == 'enabled',
+                              default_options: ['bin_programs=false', 'zlib=disabled', 'lzma=disabled', 'lz4=disabled'])
+    endif
     if zstd_dep.found()
         add_project_arguments('-DZCHUNK_ZSTD', language : 'c')
         if zstd_dep.version().version_compare('<=1.4.9')
@@ -30,28 +34,42 @@ else
     endif
 endif
 
-curl_dep = dependency('libcurl')
-
-if build_machine.system() == 'darwin' or build_machine.system() == 'freebsd' or not cc.links('#include <argp.h>\nstatic error_t parse_opt (int key, char *arg, struct argp_state *state) { argp_usage(state); return 0; }; void main() {}')
-    argplib = cc.find_library('argp')
+if build_machine.system() == 'windows'
+    curl_dep = dependency('curl', modules: 'CURL::libcurl')
 else
-    argplib = dependency('', required : false)
+    curl_dep = dependency('libcurl')
+endif
+
+if build_machine.system() != 'windows'
+    if build_machine.system() == 'darwin' or build_machine.system() == 'freebsd' or not cc.links('#include <argp.h>\nstatic error_t parse_opt (int key, char *arg, struct argp_state *state) { argp_usage(state); return 0; }; void main() {}')
+       argplib = cc.find_library('argp')
+    else
+       argplib = dependency('', required : false)
+    endif
 endif
 
 if get_option('with-openssl') == 'disabled'
     openssl_dep = dependency('', required : false)
 else
-    openssl_dep = dependency('openssl', required : get_option('with-openssl') == 'enabled')
+    if build_machine.system() == 'windows'
+        openssl_dep = dependency('openssl', modules : ['OpenSSL::SSL', 'OpenSSL::Crypto'], required : get_option('with-openssl') == 'enabled')
+    else
+        openssl_dep = dependency('openssl', required : get_option('with-openssl') == 'enabled')
+    endif
     if openssl_dep.found()
         add_project_arguments('-DZCHUNK_OPENSSL', language : 'c')
     endif
 endif
 
-inc = include_directories('include')
+inc = []
+inc += include_directories('include')
+if host_machine.system() == 'windows'
+    inc += include_directories('src/lib/win32')
+endif
 
 subdir('include')
 subdir('src')
-if not get_option('coverity')
+if not get_option('coverity') and host_machine.system() != 'windows'
     subdir('test')
 endif
 
index 02b87878e0bc48f9c6a3f736d39d91d5746024f8..b2b5384a23c2f99e50e54b02f6e8a73d16983579 100644 (file)
@@ -543,7 +543,7 @@ hash_error:
     return -2;
 }
 
-const char PUBLIC *zck_comp_name_from_type(int comp_type) {
+const char ZCK_PUBLIC_API *zck_comp_name_from_type(int comp_type) {
     if(comp_type > 2) {
         snprintf(unknown+8, 21, "%i)", comp_type);
         return unknown;
@@ -551,7 +551,7 @@ const char PUBLIC *zck_comp_name_from_type(int comp_type) {
     return COMP_NAME[comp_type];
 }
 
-ssize_t PUBLIC zck_write(zckCtx *zck, const char *src, const size_t src_size) {
+ssize_t ZCK_PUBLIC_API zck_write(zckCtx *zck, const char *src, const size_t src_size) {
     VALIDATE_WRITE_INT(zck);
 
     if(src_size == 0)
@@ -618,7 +618,7 @@ ssize_t PUBLIC zck_write(zckCtx *zck, const char *src, const size_t src_size) {
     }
 }
 
-ssize_t PUBLIC zck_end_chunk(zckCtx *zck) {
+ssize_t ZCK_PUBLIC_API zck_end_chunk(zckCtx *zck) {
     VALIDATE_WRITE_INT(zck);
 
     if(!zck->comp.started && !comp_init(zck))
@@ -657,14 +657,14 @@ ssize_t PUBLIC zck_end_chunk(zckCtx *zck) {
     return data_size;
 }
 
-ssize_t PUBLIC zck_read(zckCtx *zck, char *dst, size_t dst_size) {
+ssize_t ZCK_PUBLIC_API zck_read(zckCtx *zck, char *dst, size_t dst_size) {
     VALIDATE_READ_INT(zck);
     ALLOCD_INT(zck, dst);
 
     return comp_read(zck, dst, dst_size, 1);
 }
 
-ssize_t PUBLIC zck_get_chunk_comp_data(zckChunk *idx, char *dst,
+ssize_t ZCK_PUBLIC_API zck_get_chunk_comp_data(zckChunk *idx, char *dst,
                                        size_t dst_size) {
     zckCtx *zck = NULL;
     if(idx && idx->zck) {
@@ -694,7 +694,7 @@ ssize_t PUBLIC zck_get_chunk_comp_data(zckChunk *idx, char *dst,
     return read_data(zck, dst, dst_size);
 }
 
-ssize_t PUBLIC zck_get_chunk_data(zckChunk *idx, char *dst,
+ssize_t ZCK_PUBLIC_API zck_get_chunk_data(zckChunk *idx, char *dst,
                                   size_t dst_size) {
     zckCtx *zck = NULL;
     if(idx && idx->zck) {
index 303b42b2ee87d53995db07151846476f8ebdaa24..db8d0d3ee090d8646d5ba0ae6df868e453c3fdc2 100644 (file)
@@ -232,7 +232,7 @@ int dl_write_range(zckDL *dl, const char *at, size_t length) {
     return wb + wb2;
 }
 
-bool PUBLIC zck_copy_chunks(zckCtx *src, zckCtx *tgt) {
+ZCK_PUBLIC_API bool zck_copy_chunks(zckCtx *src, zckCtx *tgt) {
     VALIDATE_READ_BOOL(src);
     VALIDATE_READ_BOOL(tgt);
 
@@ -256,7 +256,7 @@ bool PUBLIC zck_copy_chunks(zckCtx *src, zckCtx *tgt) {
     return true;
 }
 
-bool PUBLIC zck_find_matching_chunks(zckCtx *src, zckCtx *tgt) {
+ZCK_PUBLIC_API bool zck_find_matching_chunks(zckCtx *src, zckCtx *tgt) {
 
     if (!src || !tgt)
         return false;
@@ -297,20 +297,20 @@ bool PUBLIC zck_find_matching_chunks(zckCtx *src, zckCtx *tgt) {
     return true;
 }
 
-ssize_t PUBLIC zck_dl_get_bytes_downloaded(zckDL *dl) {
+ZCK_PUBLIC_API ssize_t zck_dl_get_bytes_downloaded(zckDL *dl) {
     ALLOCD_INT(NULL, dl);
 
     return dl->dl;
 }
 
-ssize_t PUBLIC zck_dl_get_bytes_uploaded(zckDL *dl) {
+ZCK_PUBLIC_API ssize_t zck_dl_get_bytes_uploaded(zckDL *dl) {
     ALLOCD_INT(NULL, dl);
 
     return dl->ul;
 }
 
 /* Initialize zckDL.  When finished, zckDL *must* be freed by zck_dl_free() */
-zckDL PUBLIC *zck_dl_init(zckCtx *zck) {
+ZCK_PUBLIC_API zckDL *zck_dl_init(zckCtx *zck) {
     zckDL *dl = zmalloc(sizeof(zckDL));
     if (!dl) {
        zck_log(ZCK_LOG_ERROR, "OOM in %s", __func__);
@@ -322,7 +322,7 @@ zckDL PUBLIC *zck_dl_init(zckCtx *zck) {
 }
 
 /* Reset dl while maintaining download statistics and private information */
-void PUBLIC zck_dl_reset(zckDL *dl) {
+ZCK_PUBLIC_API void zck_dl_reset(zckDL *dl) {
     if(!dl)
         return;
 
@@ -345,7 +345,7 @@ void PUBLIC zck_dl_reset(zckDL *dl) {
 }
 
 /* Free zckDL and set pointer to NULL */
-void PUBLIC zck_dl_free(zckDL **dl) {
+ZCK_PUBLIC_API void zck_dl_free(zckDL **dl) {
     zck_dl_reset(*dl);
     if((*dl)->mp)
         free((*dl)->mp);
@@ -353,53 +353,53 @@ void PUBLIC zck_dl_free(zckDL **dl) {
     *dl = NULL;
 }
 
-zckCtx PUBLIC *zck_dl_get_zck(zckDL *dl) {
+ZCK_PUBLIC_API zckCtx *zck_dl_get_zck(zckDL *dl) {
     ALLOCD_PTR(NULL, dl);
 
     return dl->zck;
 }
 
-bool PUBLIC zck_dl_set_zck(zckDL *dl, zckCtx *zck) {
+ZCK_PUBLIC_API bool zck_dl_set_zck(zckDL *dl, zckCtx *zck) {
     ALLOCD_BOOL(NULL, dl);
 
     dl->zck = zck;
     return true;
 }
-bool PUBLIC zck_dl_set_range(zckDL *dl, zckRange *range) {
+ZCK_PUBLIC_API bool zck_dl_set_range(zckDL *dl, zckRange *range) {
     ALLOCD_BOOL(NULL, dl);
 
     dl->range = range;
     return true;
 }
 
-zckRange PUBLIC *zck_dl_get_range(zckDL *dl) {
+ZCK_PUBLIC_API zckRange *zck_dl_get_range(zckDL *dl) {
     ALLOCD_PTR(NULL, dl);
 
     return dl->range;
 }
 
-bool PUBLIC zck_dl_set_header_cb(zckDL *dl, zck_wcb func) {
+ZCK_PUBLIC_API bool zck_dl_set_header_cb(zckDL *dl, zck_wcb func) {
     ALLOCD_BOOL(NULL, dl);
 
     dl->header_cb = func;
     return true;
 }
 
-bool PUBLIC zck_dl_set_header_data(zckDL *dl, void *data) {
+ZCK_PUBLIC_API bool zck_dl_set_header_data(zckDL *dl, void *data) {
     ALLOCD_BOOL(NULL, dl);
 
     dl->header_data = data;
     return true;
 }
 
-bool PUBLIC zck_dl_set_write_cb(zckDL *dl, zck_wcb func) {
+ZCK_PUBLIC_API bool zck_dl_set_write_cb(zckDL *dl, zck_wcb func) {
     ALLOCD_BOOL(NULL, dl);
 
     dl->write_cb = func;
     return true;
 }
 
-bool PUBLIC zck_dl_set_write_data(zckDL *dl, void *data) {
+ZCK_PUBLIC_API bool zck_dl_set_write_data(zckDL *dl, void *data) {
     ALLOCD_BOOL(NULL, dl);
 
     dl->write_data = data;
@@ -410,7 +410,7 @@ bool PUBLIC zck_dl_set_write_data(zckDL *dl, void *data) {
  * Callbacks
  *******************************************************************/
 
-size_t PUBLIC zck_header_cb(char *b, size_t l, size_t c, void *dl_v) {
+ZCK_PUBLIC_API size_t zck_header_cb(char *b, size_t l, size_t c, void *dl_v) {
     ALLOCD_BOOL(NULL, dl_v);
     zckDL *dl = (zckDL*)dl_v;
 
@@ -422,7 +422,7 @@ size_t PUBLIC zck_header_cb(char *b, size_t l, size_t c, void *dl_v) {
     return c*l;
 }
 
-size_t PUBLIC zck_write_zck_header_cb(void *ptr, size_t l, size_t c,
+ZCK_PUBLIC_API size_t zck_write_zck_header_cb(void *ptr, size_t l, size_t c,
                                       void *dl_v) {
     ALLOCD_BOOL(NULL, dl_v);
     zckDL *dl = (zckDL*)dl_v;
@@ -437,7 +437,7 @@ size_t PUBLIC zck_write_zck_header_cb(void *ptr, size_t l, size_t c,
     return wb;
 }
 
-size_t PUBLIC zck_write_chunk_cb(void *ptr, size_t l, size_t c, void *dl_v) {
+ZCK_PUBLIC_API size_t zck_write_chunk_cb(void *ptr, size_t l, size_t c, void *dl_v) {
     ALLOCD_BOOL(NULL, dl_v);
     zckDL *dl = (zckDL*)dl_v;
 
index 5ca0bff1261d323b093e943917ede27dada54c61..d35076c2afef8ea36c037e3fbcf96a7df3e2737e 100644 (file)
@@ -140,7 +140,7 @@ static bool range_add(zckRange *info, zckChunk *chk, zckCtx *zck) {
     return true;
 }
 
-void PUBLIC zck_range_free(zckRange **info) {
+void ZCK_PUBLIC_API zck_range_free(zckRange **info) {
     zckRangeItem *next = (*info)->first;
     while(next) {
         zckRangeItem *tmp = next;
@@ -152,7 +152,7 @@ void PUBLIC zck_range_free(zckRange **info) {
     *info = NULL;
 }
 
-char PUBLIC *zck_get_range_char(zckCtx *zck, zckRange *range) {
+char ZCK_PUBLIC_API *zck_get_range_char(zckCtx *zck, zckRange *range) {
     int buf_size = BUF_SIZE;
     char *output = zmalloc(buf_size);
     if (!output) {
@@ -189,7 +189,7 @@ char PUBLIC *zck_get_range_char(zckCtx *zck, zckRange *range) {
     return output;
 }
 
-zckRange PUBLIC *zck_get_missing_range(zckCtx *zck, int max_ranges) {
+zckRange ZCK_PUBLIC_API *zck_get_missing_range(zckCtx *zck, int max_ranges) {
     VALIDATE_PTR(zck);
 
     zckRange *range = zmalloc(sizeof(zckRange));
@@ -211,7 +211,7 @@ zckRange PUBLIC *zck_get_missing_range(zckCtx *zck, int max_ranges) {
     return range;
 }
 
-char PUBLIC *zck_get_range(size_t start, size_t end) {
+char ZCK_PUBLIC_API *zck_get_range(size_t start, size_t end) {
     zckRange range = {0};
     zckRangeItem ri = {0};
     zckCtx *zck = zck_create();
@@ -223,7 +223,7 @@ char PUBLIC *zck_get_range(size_t start, size_t end) {
     return ret;
 }
 
-int PUBLIC zck_get_range_count(zckRange *range) {
+int ZCK_PUBLIC_API zck_get_range_count(zckRange *range) {
     ALLOCD_INT(NULL, range);
 
     return range->count;
index bd3ce9e2030c25f02d46139d5a5632a129285806..26a4f78ca966f01fff6d396a359b666ecfe896ba 100644 (file)
@@ -81,14 +81,14 @@ void set_error_wf(zckCtx *zck, int fatal, const char *function,
 
 }
 
-int PUBLIC zck_is_error(zckCtx *zck) {
+int ZCK_PUBLIC_API zck_is_error(zckCtx *zck) {
     if(zck == NULL)
         zck = &zck_none;
 
     return zck->error_state;
 }
 
-const char PUBLIC *zck_get_error(zckCtx *zck) {
+const char ZCK_PUBLIC_API *zck_get_error(zckCtx *zck) {
     if(zck == NULL)
         zck = &zck_none;
 
@@ -97,7 +97,7 @@ const char PUBLIC *zck_get_error(zckCtx *zck) {
     return zck->msg;
 }
 
-bool PUBLIC zck_clear_error(zckCtx *zck) {
+bool ZCK_PUBLIC_API zck_clear_error(zckCtx *zck) {
     if(zck != NULL && zck->error_state > 1)
         return false;
 
index fc240e557b1f7c4b4536d6bd9e6255a198a0e8e6..699c2bbd293065822363605d5770b2b901763b86 100644 (file)
@@ -463,7 +463,7 @@ int validate_header(zckCtx *zck) {
 }
 
 /* Returns 1 if data hash matches, -1 if it doesn't and 0 if error */
-int PUBLIC zck_validate_data_checksum(zckCtx *zck) {
+ZCK_PUBLIC_API int zck_validate_data_checksum(zckCtx *zck) {
     VALIDATE_READ_BOOL(zck);
 
     if(!seek_data(zck, zck->data_offset, SEEK_SET))
@@ -495,7 +495,7 @@ int PUBLIC zck_validate_data_checksum(zckCtx *zck) {
     return ret;
 }
 
-const char PUBLIC *zck_hash_name_from_type(int hash_type) {
+ZCK_PUBLIC_API const char *zck_hash_name_from_type(int hash_type) {
     if(hash_type >= ZCK_HASH_UNKNOWN || hash_type < 0) {
         snprintf(unknown+8, 21, "%i)", hash_type);
         return unknown;
@@ -503,49 +503,49 @@ const char PUBLIC *zck_hash_name_from_type(int hash_type) {
     return HASH_NAME[hash_type];
 }
 
-int PUBLIC zck_get_full_hash_type(zckCtx *zck) {
+ZCK_PUBLIC_API int zck_get_full_hash_type(zckCtx *zck) {
     VALIDATE_INT(zck);
 
     return zck->hash_type.type;
 }
 
-ssize_t PUBLIC zck_get_full_digest_size(zckCtx *zck) {
+ZCK_PUBLIC_API ssize_t zck_get_full_digest_size(zckCtx *zck) {
     VALIDATE_INT(zck);
 
     return zck->hash_type.digest_size;
 }
 
-int PUBLIC zck_get_chunk_hash_type(zckCtx *zck) {
+ZCK_PUBLIC_API int zck_get_chunk_hash_type(zckCtx *zck) {
     VALIDATE_INT(zck);
 
     return zck->index.hash_type;
 }
 
-ssize_t PUBLIC zck_get_chunk_digest_size(zckCtx *zck) {
+ZCK_PUBLIC_API ssize_t zck_get_chunk_digest_size(zckCtx *zck) {
     VALIDATE_INT(zck);
 
     return zck->index.digest_size;
 }
 
-char PUBLIC *zck_get_header_digest(zckCtx *zck) {
+ZCK_PUBLIC_API char *zck_get_header_digest(zckCtx *zck) {
     VALIDATE_PTR(zck);
 
     return get_digest_string(zck->header_digest, zck->hash_type.digest_size);
 }
 
-char PUBLIC *zck_get_data_digest(zckCtx *zck) {
+ZCK_PUBLIC_API char *zck_get_data_digest(zckCtx *zck) {
     VALIDATE_PTR(zck);
 
     return get_digest_string(zck->full_hash_digest, zck->hash_type.digest_size);
 }
 
-char PUBLIC *zck_get_chunk_digest(zckChunk *item) {
+ZCK_PUBLIC_API char *zck_get_chunk_digest(zckChunk *item) {
     if(item == NULL)
         return NULL;
     return get_digest_string(item->digest, item->digest_size);
 }
 
-char PUBLIC *zck_get_chunk_digest_uncompressed(zckChunk *item) {
+ZCK_PUBLIC_API char *zck_get_chunk_digest_uncompressed(zckChunk *item) {
     if(item == NULL || !item->zck->has_uncompressed_source)
         return NULL;
     return get_digest_string(item->digest_uncompressed, item->digest_size);
@@ -553,14 +553,14 @@ char PUBLIC *zck_get_chunk_digest_uncompressed(zckChunk *item) {
 
 
 /* Returns 1 if all chunks are valid, -1 if even one isn't and 0 if error */
-int PUBLIC zck_find_valid_chunks(zckCtx *zck) {
+ZCK_PUBLIC_API int zck_find_valid_chunks(zckCtx *zck) {
     VALIDATE_READ_BOOL(zck);
 
     return validate_checksums(zck, ZCK_LOG_DEBUG);
 }
 
 /* Returns 1 if all checksums matched, -1 if even one doesn't and 0 if error */
-int PUBLIC zck_validate_checksums(zckCtx *zck) {
+ZCK_PUBLIC_API int zck_validate_checksums(zckCtx *zck) {
     VALIDATE_READ_BOOL(zck);
 
     return validate_checksums(zck, ZCK_LOG_WARNING);
index e5f2bc6acc2de92ef5edaccd729f3a0ca38c6497..da52e61e3261a189ca478c37e8139d44e463cd66 100644 (file)
@@ -553,13 +553,13 @@ static bool read_lead(zckCtx *zck) {
     return true;
 }
 
-bool PUBLIC zck_read_lead(zckCtx *zck) {
+bool ZCK_PUBLIC_API zck_read_lead(zckCtx *zck) {
     VALIDATE_BOOL(zck);
 
     return read_lead(zck);
 }
 
-bool PUBLIC zck_validate_lead(zckCtx *zck) {
+bool ZCK_PUBLIC_API zck_validate_lead(zckCtx *zck) {
     VALIDATE_BOOL(zck);
 
     int retval = read_lead(zck);
@@ -581,7 +581,7 @@ bool PUBLIC zck_validate_lead(zckCtx *zck) {
     return retval;
 }
 
-bool PUBLIC zck_read_header(zckCtx *zck) {
+bool ZCK_PUBLIC_API zck_read_header(zckCtx *zck) {
     VALIDATE_READ_BOOL(zck);
 
     if(!read_header_from_file(zck))
@@ -595,17 +595,17 @@ bool PUBLIC zck_read_header(zckCtx *zck) {
     return true;
 }
 
-ssize_t PUBLIC zck_get_header_length(zckCtx *zck) {
+ssize_t ZCK_PUBLIC_API zck_get_header_length(zckCtx *zck) {
     VALIDATE_INT(zck);
     return zck->lead_size + zck->header_length;
 }
 
-ssize_t PUBLIC zck_get_lead_length(zckCtx *zck) {
+ssize_t ZCK_PUBLIC_API zck_get_lead_length(zckCtx *zck) {
     VALIDATE_INT(zck);
     return zck->lead_size;
 }
 
-ssize_t PUBLIC zck_get_data_length(zckCtx *zck) {
+ssize_t ZCK_PUBLIC_API zck_get_data_length(zckCtx *zck) {
     VALIDATE_INT(zck);
     zckChunk *idx = zck->index.first;
     while(idx->next != NULL)
@@ -613,7 +613,7 @@ ssize_t PUBLIC zck_get_data_length(zckCtx *zck) {
     return idx->start + idx->comp_length;
 }
 
-ssize_t PUBLIC zck_get_length(zckCtx *zck) {
+ssize_t ZCK_PUBLIC_API zck_get_length(zckCtx *zck) {
     VALIDATE_INT(zck);
     return zck_get_header_length(zck) + zck_get_data_length(zck);
 }
index db2e86e47892431136dff60524e23c82fab3ec87..ce2ad083d7dc8e8b08b04ab1f6a6f10661db75f8 100644 (file)
@@ -32,6 +32,8 @@
 #include <sys/endian.h>
 #elif __APPLE__
 #include <machine/endian.h>
+#elif defined(_WIN32)
+// no endian.h available
 #else
 #include <endian.h>
 #endif
@@ -149,13 +151,13 @@ bool index_read(zckCtx *zck, char *data, size_t size, size_t max_length) {
     return true;
 }
 
-ssize_t PUBLIC zck_get_chunk_count(zckCtx *zck) {
+ZCK_PUBLIC_API ssize_t zck_get_chunk_count(zckCtx *zck) {
     VALIDATE_INT(zck);
 
     return zck->index.count;
 }
 
-zckChunk PUBLIC *zck_get_chunk(zckCtx *zck, size_t number) {
+ZCK_PUBLIC_API zckChunk *zck_get_chunk(zckCtx *zck, size_t number) {
     VALIDATE_PTR(zck);
 
     for(zckChunk *idx=zck->index.first; idx!=NULL; idx=idx->next) {
@@ -166,13 +168,13 @@ zckChunk PUBLIC *zck_get_chunk(zckCtx *zck, size_t number) {
     return NULL;
 }
 
-zckChunk PUBLIC *zck_get_first_chunk(zckCtx *zck) {
+ZCK_PUBLIC_API zckChunk *zck_get_first_chunk(zckCtx *zck) {
     VALIDATE_PTR(zck);
 
     return zck->index.first;
 }
 
-zckChunk PUBLIC *zck_get_next_chunk(zckChunk *idx) {
+ZCK_PUBLIC_API zckChunk *zck_get_next_chunk(zckChunk *idx) {
     if(idx && idx->zck) {
         VALIDATE_PTR(idx->zck);
         ALLOCD_PTR(idx->zck, idx);
@@ -183,7 +185,7 @@ zckChunk PUBLIC *zck_get_next_chunk(zckChunk *idx) {
     return idx->next;
 }
 
-zckChunk PUBLIC *zck_get_src_chunk(zckChunk *idx) {
+ZCK_PUBLIC_API zckChunk *zck_get_src_chunk(zckChunk *idx) {
     if(idx && idx->zck) {
         VALIDATE_PTR(idx->zck);
         ALLOCD_PTR(idx->zck, idx);
@@ -194,7 +196,7 @@ zckChunk PUBLIC *zck_get_src_chunk(zckChunk *idx) {
     return idx->src;
 }
 
-ssize_t PUBLIC zck_get_chunk_start(zckChunk *idx) {
+ZCK_PUBLIC_API ssize_t zck_get_chunk_start(zckChunk *idx) {
     if(idx && idx->zck) {
         VALIDATE_INT(idx->zck);
         ALLOCD_INT(idx->zck, idx);
@@ -208,7 +210,7 @@ ssize_t PUBLIC zck_get_chunk_start(zckChunk *idx) {
         return idx->start;
 }
 
-ssize_t PUBLIC zck_get_chunk_size(zckChunk *idx) {
+ZCK_PUBLIC_API ssize_t zck_get_chunk_size(zckChunk *idx) {
     if(idx && idx->zck) {
         VALIDATE_INT(idx->zck);
         ALLOCD_INT(idx->zck, idx);
@@ -219,7 +221,7 @@ ssize_t PUBLIC zck_get_chunk_size(zckChunk *idx) {
     return idx->length;
 }
 
-ssize_t PUBLIC zck_get_chunk_comp_size(zckChunk *idx) {
+ZCK_PUBLIC_API ssize_t zck_get_chunk_comp_size(zckChunk *idx) {
     if(idx && idx->zck) {
         VALIDATE_INT(idx->zck);
         ALLOCD_INT(idx->zck, idx);
@@ -230,7 +232,7 @@ ssize_t PUBLIC zck_get_chunk_comp_size(zckChunk *idx) {
     return idx->comp_length;
 }
 
-ssize_t PUBLIC zck_get_chunk_number(zckChunk *idx) {
+ZCK_PUBLIC_API ssize_t zck_get_chunk_number(zckChunk *idx) {
     if(idx && idx->zck) {
         VALIDATE_INT(idx->zck);
         ALLOCD_INT(idx->zck, idx);
@@ -241,7 +243,7 @@ ssize_t PUBLIC zck_get_chunk_number(zckChunk *idx) {
     return idx->number;
 }
 
-int PUBLIC zck_get_chunk_valid(zckChunk *idx) {
+ZCK_PUBLIC_API int zck_get_chunk_valid(zckChunk *idx) {
     if(idx && idx->zck) {
         VALIDATE_INT(idx->zck);
         ALLOCD_INT(idx->zck, idx);
@@ -252,7 +254,7 @@ int PUBLIC zck_get_chunk_valid(zckChunk *idx) {
     return idx->valid;
 }
 
-bool PUBLIC zck_compare_chunk_digest(zckChunk *a, zckChunk *b) {
+ZCK_PUBLIC_API bool zck_compare_chunk_digest(zckChunk *a, zckChunk *b) {
     if(a && a->zck) {
         VALIDATE_BOOL(a->zck);
         ALLOCD_BOOL(a->zck, a);
@@ -273,7 +275,7 @@ bool PUBLIC zck_compare_chunk_digest(zckChunk *a, zckChunk *b) {
     return true;
 }
 
-int PUBLIC zck_missing_chunks(zckCtx *zck) {
+ZCK_PUBLIC_API int zck_missing_chunks(zckCtx *zck) {
     VALIDATE_READ_INT(zck);
 
     int missing = 0;
@@ -283,7 +285,7 @@ int PUBLIC zck_missing_chunks(zckCtx *zck) {
     return missing;
 }
 
-int PUBLIC zck_failed_chunks(zckCtx *zck) {
+ZCK_PUBLIC_API int zck_failed_chunks(zckCtx *zck) {
     VALIDATE_READ_INT(zck);
 
     int failed = 0;
@@ -293,7 +295,7 @@ int PUBLIC zck_failed_chunks(zckCtx *zck) {
     return failed;
 }
 
-void PUBLIC zck_reset_failed_chunks(zckCtx *zck) {
+ZCK_PUBLIC_API void zck_reset_failed_chunks(zckCtx *zck) {
     if(!zck)
         return;
 
@@ -303,7 +305,7 @@ void PUBLIC zck_reset_failed_chunks(zckCtx *zck) {
     return;
 }
 
-bool PUBLIC zck_generate_hashdb(zckCtx *zck) {
+ZCK_PUBLIC_API bool zck_generate_hashdb(zckCtx *zck) {
     if (zck->index.ht || zck->index.htuncomp) {
         zck_log(ZCK_LOG_ERROR, "Hash DB already present, it could not be created");
         return false;
index a238b8709348747c8d9eb9fbdae8d0a2eb95fcfe..42119f73207074cfdfa162b3a9df736315eb0ebe 100644 (file)
 #include "zck_private.h"
 
 static zck_log_type log_level = ZCK_LOG_ERROR;
+#ifdef _WIN32
+static int log_fd = 2;
+#else
 static int log_fd = STDERR_FILENO;
+#endif
 
 static logcallback callback = NULL; 
 
-void PUBLIC zck_set_log_level(zck_log_type ll) {
+void ZCK_PUBLIC_API zck_set_log_level(zck_log_type ll) {
     log_level = ll;
 }
 
-void PUBLIC zck_set_log_fd(int fd) {
+void ZCK_PUBLIC_API zck_set_log_fd(int fd) {
     log_fd = fd;
 }
 
-void PUBLIC zck_set_log_callback(logcallback function) {
+void ZCK_PUBLIC_API zck_set_log_callback(logcallback function) {
     if (!function)
         return;
     callback = function;
@@ -60,9 +64,26 @@ void zck_log_v(const char *function, zck_log_type lt, const char *format,
     if (callback) {
         callback(function, lt, format, args);
     } else {
+#ifdef _WIN32
+        if (log_fd == 2)
+        {
+            fprintf(stderr, "%s: ", function);
+            vfprintf(stderr, format, args);
+            fprintf(stderr, "\n");
+        }
+        else
+        {
+            FILE *fstream = _fdopen(log_fd, "a+");
+            fprintf(fstream, "%s: ", function);
+            vfprintf(fstream, format, args);
+            fprintf(fstream, "\n");
+            _close(fstream);
+        }
+#else
         dprintf(log_fd, "%s: ", function);
         vdprintf(log_fd, format, args);
         dprintf(log_fd, "\n");
+#endif
     }
 }
 
index cae5058695c1a5bb1177f8bfb053f7d6a1f8911c..261981afa163b303934002aaca0d191926719122 100644 (file)
@@ -5,10 +5,19 @@ subdir('hash')
 subdir('index')
 subdir('dl')
 lib_sources += files('zck.c', 'header.c', 'io.c', 'log.c', 'compint.c', 'error.c')
+
+extra_c_args = []
+if host_machine.system() != 'windows'
+    extra_c_args += ['-fvisibility=hidden']
+else
+    lib_sources += files(['win32/regerror.c', 'win32/regcomp.c', 'win32/regexec.c', 'win32/tre-mem.c'])
+    extra_c_args += ['-DZCHUNK_EXPORTS=1']
+endif
+
 zcklib = library('zck',
                         lib_sources,
                         # in meson 0.48, use `gnu_symbol_visibility: 'hidden'` kwarg
-                        c_args: ['-fvisibility=hidden'],
+                        c_args: extra_c_args,
                         include_directories: inc,
                         dependencies: [zstd_dep, openssl_dep],
                         install: true,
diff --git a/src/lib/win32/LICENSE b/src/lib/win32/LICENSE
new file mode 100644 (file)
index 0000000..c9ad4f4
--- /dev/null
@@ -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/src/lib/win32/regcomp.c b/src/lib/win32/regcomp.c
new file mode 100644 (file)
index 0000000..53bc7b0
--- /dev/null
@@ -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/src/lib/win32/regerror.c b/src/lib/win32/regerror.c
new file mode 100644 (file)
index 0000000..bc7ae31
--- /dev/null
@@ -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/src/lib/win32/regex.h b/src/lib/win32/regex.h
new file mode 100644 (file)
index 0000000..06a1090
--- /dev/null
@@ -0,0 +1,66 @@
+#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
+
+#include <BaseTsd.h>
+typedef SSIZE_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/src/lib/win32/regexec.c b/src/lib/win32/regexec.c
new file mode 100644 (file)
index 0000000..61e40a5
--- /dev/null
@@ -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(size_t) - 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, size_t);
+    reach_next = (void *)tmp_buf;
+    tmp_buf += rbytes;
+    tmp_buf += ALIGN(tmp_buf, size_t);
+    reach = (void *)tmp_buf;
+    tmp_buf += rbytes;
+    tmp_buf += ALIGN(tmp_buf, size_t);
+    reach_pos = (void *)tmp_buf;
+    tmp_buf += pbytes;
+    tmp_buf += ALIGN(tmp_buf, size_t);
+    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/src/lib/win32/tre-mem.c b/src/lib/win32/tre-mem.c
new file mode 100644 (file)
index 0000000..327515b
--- /dev/null
@@ -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, size_t);
+
+  /* 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/src/lib/win32/tre.h b/src/lib/win32/tre.h
new file mode 100644 (file)
index 0000000..390df94
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+  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>
+#include <stdint.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) \
+  ((((uintptr_t)ptr) % sizeof(type)) \
+   ? (sizeof(type) - (((uintptr_t)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/src/lib/win32/unistd.h b/src/lib/win32/unistd.h
new file mode 100644 (file)
index 0000000..41b121c
--- /dev/null
@@ -0,0 +1,4 @@
+#ifndef _UNISTD_H
+#define _UNISTD_H
+
+#endif
\ No newline at end of file
index 9cffcdfbae8ba151ba1413c204422da7b0156032..a256dff0cc6997e88039efe746d05f2097eb89f0 100644 (file)
@@ -41,7 +41,7 @@
 
 
 /* If lead format changes, this needs to be changed */
-int PUBLIC zck_get_min_download_size() {
+ZCK_PUBLIC_API int zck_get_min_download_size() {
     /* Magic + hash type + hash digest + header size */
     return 5 + MAX_COMP_SIZE*2 + get_max_hash_size();
 }
@@ -97,6 +97,7 @@ static int hex_to_int (char c) {
     return result;
 }
 
+
 static char *ascii_checksum_to_bin (zckCtx *zck, char *checksum,
                                     int checksum_length) {
     char *raw_checksum = zmalloc(checksum_length/2);
@@ -171,10 +172,20 @@ int get_tmp_fd(zckCtx *zck) {
     offset += i;
     fname[offset] = '\0';
 
+    typedef int mode_t;
     mode_t old_mode_mask;
+
+    #ifdef _WIN32
+    errno_t out = _mktemp_s(
+        fname,
+        offset
+    );
+    temp_fd = open(fname);
+    #else
     old_mode_mask = umask (S_IXUSR | S_IRWXG | S_IRWXO);
     temp_fd = mkstemp(fname);
     umask(old_mode_mask);
+    #endif
     if(temp_fd < 0) {
         free(fname);
         set_error(zck, "Unable to create temporary file");
@@ -220,7 +231,7 @@ bool import_dict(zckCtx *zck) {
     return true;
 }
 
-bool PUBLIC zck_set_soption(zckCtx *zck, zck_soption option, const char *value,
+ZCK_PUBLIC_API bool zck_set_soption(zckCtx *zck, zck_soption option, const char *value,
                             size_t length) {
     VALIDATE_BOOL(zck);
     char *data = zmalloc(length);
@@ -275,7 +286,7 @@ bool PUBLIC zck_set_soption(zckCtx *zck, zck_soption option, const char *value,
     return true;
 }
 
-bool PUBLIC zck_set_ioption(zckCtx *zck, zck_ioption option, ssize_t value) {
+ZCK_PUBLIC_API bool zck_set_ioption(zckCtx *zck, zck_ioption option, ssize_t value) {
     VALIDATE_BOOL(zck);
 
     /* Set hash type */
@@ -333,7 +344,7 @@ bool PUBLIC zck_set_ioption(zckCtx *zck, zck_ioption option, ssize_t value) {
     return true;
 }
 
-bool PUBLIC zck_close(zckCtx *zck) {
+ZCK_PUBLIC_API bool zck_close(zckCtx *zck) {
     VALIDATE_BOOL(zck);
 
     if(zck->mode == ZCK_MODE_WRITE) {
@@ -361,7 +372,7 @@ bool PUBLIC zck_close(zckCtx *zck) {
     return true;
 }
 
-void PUBLIC zck_free(zckCtx **zck) {
+ZCK_PUBLIC_API void zck_free(zckCtx **zck) {
     if(zck == NULL || *zck == NULL)
         return;
     zck_clear(*zck);
@@ -369,7 +380,7 @@ void PUBLIC zck_free(zckCtx **zck) {
     *zck = NULL;
 }
 
-zckCtx PUBLIC *zck_create() {
+ZCK_PUBLIC_API zckCtx *zck_create() {
     zckCtx *zck = zmalloc(sizeof(zckCtx));
     if (!zck) {
        zck_log(ZCK_LOG_ERROR, "OOM in %s", __func__);
@@ -381,7 +392,7 @@ zckCtx PUBLIC *zck_create() {
     return zck;
 }
 
-bool PUBLIC zck_init_adv_read (zckCtx *zck, int src_fd) {
+ZCK_PUBLIC_API bool zck_init_adv_read (zckCtx *zck, int src_fd) {
     VALIDATE_BOOL(zck);
 
     zck->mode = ZCK_MODE_READ;
@@ -389,7 +400,7 @@ bool PUBLIC zck_init_adv_read (zckCtx *zck, int src_fd) {
     return true;
 }
 
-bool PUBLIC zck_init_read (zckCtx *zck, int src_fd) {
+ZCK_PUBLIC_API bool zck_init_read (zckCtx *zck, int src_fd) {
     VALIDATE_BOOL(zck);
 
     if(!zck_init_adv_read(zck, src_fd)) {
@@ -410,7 +421,7 @@ bool PUBLIC zck_init_read (zckCtx *zck, int src_fd) {
     return true;
 }
 
-bool PUBLIC zck_init_write (zckCtx *zck, int dst_fd) {
+ZCK_PUBLIC_API bool zck_init_write (zckCtx *zck, int dst_fd) {
     VALIDATE_BOOL(zck);
 
     zck->mode = ZCK_MODE_WRITE;
@@ -435,12 +446,12 @@ bool PUBLIC zck_init_write (zckCtx *zck, int dst_fd) {
     return true;
 }
 
-int PUBLIC zck_get_fd(zckCtx *zck) {
+ZCK_PUBLIC_API int zck_get_fd(zckCtx *zck) {
     VALIDATE_BOOL(zck);
     return zck->fd;
 }
 
-bool PUBLIC zck_set_fd(zckCtx *zck, int fd) {
+ZCK_PUBLIC_API bool zck_set_fd(zckCtx *zck, int fd) {
     VALIDATE_BOOL(zck);
     zck->fd = fd;
     return true;
index fa7ee77b230aa4ebaac9a9d79352959a4457da05..889efe00a3181adb4ba0b74c66de3107a24b39b0 100644 (file)
@@ -22,8 +22,6 @@
 #define CHUNK_DEFAULT_MIN 1
 #define CHUNK_DEFAULT_MAX 10485760 // 10MB
 
-#define PUBLIC __attribute__((visibility("default")))
-
 #define zck_log(...) zck_log_wf(__func__, __VA_ARGS__)
 
 #define set_error(zck, ...) set_error_wf(zck, 0, __func__, __VA_ARGS__)
@@ -301,125 +299,125 @@ struct zckCtx {
 };
 
 int get_tmp_fd()
-    __attribute__ ((warn_unused_result));
+    ZCK_WARN_UNUSED;
 bool import_dict(zckCtx *zck)
-    __attribute__ ((warn_unused_result));
+    ZCK_WARN_UNUSED;
 void *zmalloc(size_t size)
-    __attribute__ ((warn_unused_result));
+    ZCK_WARN_UNUSED;
 void *zrealloc(void *ptr, size_t size)
-    __attribute__ ((warn_unused_result));
+    ZCK_WARN_UNUSED;
 
 
 /* hash/hash.h */
 bool hash_setup(zckCtx *zck, zckHashType *ht, int h)
-    __attribute__ ((warn_unused_result));
+    ZCK_WARN_UNUSED;
 bool hash_init(zckCtx *zck, zckHash *hash, zckHashType *hash_type)
-    __attribute__ ((warn_unused_result));
+    ZCK_WARN_UNUSED;
 bool hash_update(zckCtx *zck, zckHash *hash, const char *message,
                  const size_t size)
-    __attribute__ ((warn_unused_result));
+    ZCK_WARN_UNUSED;
 char *hash_finalize(zckCtx *zck, zckHash *hash)
-    __attribute__ ((warn_unused_result));
+    ZCK_WARN_UNUSED;
 void hash_close(zckHash *hash);
 void hash_reset(zckHashType *ht);
 int validate_chunk(zckChunk *idx, zck_log_type bad_checksum)
-    __attribute__ ((warn_unused_result));
+    ZCK_WARN_UNUSED;
 int validate_file(zckCtx *zck, zck_log_type bad_checksums)
-    __attribute__ ((warn_unused_result));
+    ZCK_WARN_UNUSED;
 int validate_current_chunk(zckCtx *zck)
-    __attribute__ ((warn_unused_result));
+    ZCK_WARN_UNUSED;
 int validate_header(zckCtx *zck)
-    __attribute__ ((warn_unused_result));
+    ZCK_WARN_UNUSED;
 bool set_full_hash_type(zckCtx *zck, int hash_type)
-    __attribute__ ((warn_unused_result));
+    ZCK_WARN_UNUSED;
 bool set_chunk_hash_type(zckCtx *zck, int hash_type)
-    __attribute__ ((warn_unused_result));
+    ZCK_WARN_UNUSED;
 int get_max_hash_size()
-    __attribute__ ((warn_unused_result));
+    ZCK_WARN_UNUSED;
 char *get_digest_string(const char *digest, int size)
-    __attribute__ ((warn_unused_result));
+    ZCK_WARN_UNUSED;
 
 
 /* index/index.c */
 bool index_read(zckCtx *zck, char *data, size_t size, size_t max_length)
-    __attribute__ ((warn_unused_result));
+    ZCK_WARN_UNUSED;
 bool index_create(zckCtx *zck)
-    __attribute__ ((warn_unused_result));
+    ZCK_WARN_UNUSED;
 bool index_new_chunk(zckCtx *zck, zckIndex *index, char *digest, int digest_size,
                      char* digest_uncompressed, size_t comp_size, size_t orig_size, zckChunk *src, bool valid)
-    __attribute__ ((warn_unused_result));
+    ZCK_WARN_UNUSED;
 bool index_add_to_chunk(zckCtx *zck, char *data, size_t comp_size,
                         size_t orig_size)
-    __attribute__ ((warn_unused_result));
+    ZCK_WARN_UNUSED;
 bool index_finish_chunk(zckCtx *zck)
-    __attribute__ ((warn_unused_result));
+    ZCK_WARN_UNUSED;
 void index_clean(zckIndex *index);
 void index_free(zckCtx *zck);
 void clear_work_index(zckCtx *zck);
 bool write_index(zckCtx *zck)
-    __attribute__ ((warn_unused_result));
+    ZCK_WARN_UNUSED;
 
 
 /* io.c */
 int seek_data(zckCtx *zck, off_t offset, int whence)
-    __attribute__ ((warn_unused_result));
+    ZCK_WARN_UNUSED;
 ssize_t tell_data(zckCtx *zck)
-    __attribute__ ((warn_unused_result));
+    ZCK_WARN_UNUSED;
 ssize_t read_data(zckCtx *zck, char *data, size_t length)
-    __attribute__ ((warn_unused_result));
+    ZCK_WARN_UNUSED;
 int write_data(zckCtx *zck, int fd, const char *data, size_t length)
-    __attribute__ ((warn_unused_result));
+    ZCK_WARN_UNUSED;
 int chunks_from_temp(zckCtx *zck)
-    __attribute__ ((warn_unused_result));
+    ZCK_WARN_UNUSED;
 
 /* header.c */
 bool header_create(zckCtx *zck)
-    __attribute__ ((warn_unused_result));
+    ZCK_WARN_UNUSED;
 bool write_header(zckCtx *zck)
-    __attribute__ ((warn_unused_result));
+    ZCK_WARN_UNUSED;
 
 /* comp/comp.c */
 bool comp_init(zckCtx *zck)
-    __attribute__ ((warn_unused_result));
+    ZCK_WARN_UNUSED;
 bool comp_close(zckCtx *zck)
-    __attribute__ ((warn_unused_result));
+    ZCK_WARN_UNUSED;
 bool comp_reset(zckCtx *zck)
-    __attribute__ ((warn_unused_result));
+    ZCK_WARN_UNUSED;
 bool comp_add_to_dc(zckCtx *zck, zckComp *comp, const char *src, size_t src_size)
-    __attribute__ ((warn_unused_result));
+    ZCK_WARN_UNUSED;
 ssize_t comp_read(zckCtx *zck, char *dst, size_t dst_size, bool use_dict)
-    __attribute__ ((warn_unused_result));
+    ZCK_WARN_UNUSED;
 bool comp_ioption(zckCtx *zck, zck_ioption option, ssize_t value)
-    __attribute__ ((warn_unused_result));
+    ZCK_WARN_UNUSED;
 bool comp_soption(zckCtx *zck, zck_soption option, const void *value,
                   size_t length)
-    __attribute__ ((warn_unused_result));
+    ZCK_WARN_UNUSED;
 
 /* dl/range.c */
 char *range_get_char(zckRangeItem **range, int max_ranges)
-    __attribute__ ((warn_unused_result));
+    ZCK_WARN_UNUSED;
 
 /* dl/multipart.c */
 size_t multipart_extract(zckDL *dl, char *b, size_t l)
-    __attribute__ ((warn_unused_result));
+    ZCK_WARN_UNUSED;
 size_t multipart_get_boundary(zckDL *dl, char *b, size_t size)
-    __attribute__ ((warn_unused_result));
+    ZCK_WARN_UNUSED;
 void reset_mp(zckMP *mp);
 
 /* dl/dl.c */
 int dl_write_range(zckDL *dl, const char *at, size_t length)
-    __attribute__ ((warn_unused_result));
+    ZCK_WARN_UNUSED;
 
 /* compint.c */
 int compint_from_int(zckCtx *zck, char *compint, int val, size_t *length)
-    __attribute__ ((warn_unused_result));
+    ZCK_WARN_UNUSED;
 void compint_from_size(char *compint, size_t val, size_t *length);
 int compint_to_int(zckCtx *zck, int *val, const char *compint, size_t *length,
                    size_t max_length)
-    __attribute__ ((warn_unused_result));
+    ZCK_WARN_UNUSED;
 int compint_to_size(zckCtx *zck, size_t *val, const char *compint,
                     size_t *length, size_t max_length)
-    __attribute__ ((warn_unused_result));
+    ZCK_WARN_UNUSED;
 
 /* log.c */
 void zck_log_v(const char *function, zck_log_type lt, const char *format,
index d5ad75aa8a68efc09d419f1d3483aa78df0c9134..b0316dd435f60555628de7aeba9b7ec0f07c9ca3 100644 (file)
@@ -1,50 +1,52 @@
 subdir('lib')
-zck = executable(
-    'zck',
-    ['zck.c', 'util_common.c'],
-    include_directories: inc,
-    dependencies: argplib,
-    link_with: zcklib,
-    install: true
-)
-unzck = executable(
-    'unzck',
-    ['unzck.c', 'util_common.c'],
-    include_directories: inc,
-    dependencies: argplib,
-    link_with: zcklib,
-    install: true
-)
-zckdl = executable(
-    'zckdl',
-    ['zck_dl.c', 'util_common.c'],
-    include_directories: inc,
-    dependencies: [argplib, curl_dep],
-    link_with: zcklib,
-    install: true
-)
-zck_gen_zdict = executable(
-    'zck_gen_zdict',
-    ['zck_gen_zdict.c', 'util_common.c'],
-    include_directories: inc,
-    dependencies: argplib,
-    link_with: zcklib,
-    install: true
-)
-zck_read_header = executable(
-    'zck_read_header',
-    ['zck_read_header.c', 'util_common.c'],
-    include_directories: inc,
-    dependencies: argplib,
-    link_with: zcklib,
-    install: true
-)
-zck_delta_size = executable(
-    'zck_delta_size',
-    ['zck_delta_size.c', 'util_common.c', 'memmem.c'],
-    include_directories: inc,
-    dependencies: argplib,
-    link_with: zcklib,
-    install: true
-)
 
+if host_machine.system() != 'windows'
+    zck = executable(
+        'zck',
+        ['zck.c', 'util_common.c'],
+        include_directories: inc,
+        dependencies: argplib,
+        link_with: zcklib,
+        install: true
+    )
+    unzck = executable(
+        'unzck',
+        ['unzck.c', 'util_common.c'],
+        include_directories: inc,
+        dependencies: argplib,
+        link_with: zcklib,
+        install: true
+    )
+    zckdl = executable(
+        'zckdl',
+        ['zck_dl.c', 'util_common.c'],
+        include_directories: inc,
+        dependencies: [argplib, curl_dep],
+        link_with: zcklib,
+        install: true
+    )
+    zck_gen_zdict = executable(
+        'zck_gen_zdict',
+        ['zck_gen_zdict.c', 'util_common.c'],
+        include_directories: inc,
+        dependencies: argplib,
+        link_with: zcklib,
+        install: true
+    )
+    zck_read_header = executable(
+        'zck_read_header',
+        ['zck_read_header.c', 'util_common.c'],
+        include_directories: inc,
+        dependencies: argplib,
+        link_with: zcklib,
+        install: true
+    )
+    zck_delta_size = executable(
+        'zck_delta_size',
+        ['zck_delta_size.c', 'util_common.c', 'memmem.c'],
+        include_directories: inc,
+        dependencies: argplib,
+        link_with: zcklib,
+        install: true
+    )
+endif