summaryrefslogtreecommitdiff
path: root/util
diff options
context:
space:
mode:
authorKibum Kim <kb0929.kim@samsung.com>2012-01-07 00:46:38 +0900
committerKibum Kim <kb0929.kim@samsung.com>2012-01-07 00:46:38 +0900
commitf5660c6460a863b19f9ef745575780e37cc192a9 (patch)
tree0b478679da32d706de7b0de546d2e4daf03b160c /util
parent06b9124a4f9d38acc78e6af686bc49a06f6354f8 (diff)
downloadgnupg-2.0alpha.tar.gz
gnupg-2.0alpha.tar.bz2
gnupg-2.0alpha.zip
Diffstat (limited to 'util')
-rw-r--r--util/ChangeLog1710
-rw-r--r--util/Makefile.am82
-rw-r--r--util/Makefile.in641
-rw-r--r--util/argparse.c1004
-rw-r--r--util/assuan-buffer.c485
-rw-r--r--util/assuan-client.c282
-rw-r--r--util/assuan-connect.c96
-rw-r--r--util/assuan-defs.h244
-rw-r--r--util/assuan-errors.c105
-rw-r--r--util/assuan-logging.c116
-rw-r--r--util/assuan-socket-connect.c191
-rw-r--r--util/assuan-socket.c97
-rw-r--r--util/assuan-util.c171
-rw-r--r--util/cert.c251
-rw-r--r--util/compat.c24
-rw-r--r--util/dotlock.c454
-rw-r--r--util/errors.c116
-rw-r--r--util/fileutil.c297
-rw-r--r--util/http.c1076
-rw-r--r--util/iobuf.c2276
-rw-r--r--util/isascii.c30
-rw-r--r--util/logger.c265
-rw-r--r--util/membuf.c84
-rw-r--r--util/memory.c647
-rw-r--r--util/memrchr.c49
-rw-r--r--util/miscutil.c455
-rw-r--r--util/mkdtemp.c99
-rw-r--r--util/pka.c254
-rw-r--r--util/regcomp.c3495
-rw-r--r--util/regex.c62
-rw-r--r--util/regex_internal.c1229
-rw-r--r--util/regex_internal.h643
-rw-r--r--util/regexec.c3225
-rw-r--r--util/secmem.c499
-rw-r--r--util/simple-gettext.c467
-rw-r--r--util/srv.c257
-rw-r--r--util/srv.h51
-rw-r--r--util/strgutil.c1323
-rw-r--r--util/strsep.c48
-rw-r--r--util/timegm.c68
-rw-r--r--util/ttyio.c629
-rw-r--r--util/w32reg.c181
42 files changed, 23778 insertions, 0 deletions
diff --git a/util/ChangeLog b/util/ChangeLog
new file mode 100644
index 0000000..29ba476
--- /dev/null
+++ b/util/ChangeLog
@@ -0,0 +1,1710 @@
+2006-10-02 David Shaw <dshaw@jabberwocky.com>
+
+ * strsep.c (strsep): It's strsep, not strsep2 from testing. (If I
+ had a dollar for every time I've done that...)
+
+2006-09-28 David Shaw <dshaw@jabberwocky.com>
+
+ * strgutil.c (strsep): Move to strsep.c for AC_REPLACE_FUNCS.
+
+ * Makefile.am: Build libcompat.a for keyserver helpers. libutil.a
+ always contains everything in libcompat.a, so we only need to link
+ to one or the other.
+
+ * miscutil.c: Move hextobyte to new file compat.c.
+
+2006-07-31 Werner Koch <wk@g10code.com>
+
+ * iobuf.c (iobuf_ioctl, fd_cache_invalidate): Allow closing all
+ open files.
+
+2006-07-29 Marcus Brinkmann <marcus@g10code.de>
+
+ * secmem.c (init_pool): Close FD after establishing the mapping.
+
+2006-07-20 David Shaw <dshaw@jabberwocky.com>
+
+ * http.c (send_request): A zero-length proxy is the same as no
+ proxy at all. Suggested by J. Scott Berg.
+
+2006-04-17 David Shaw <dshaw@jabberwocky.com>
+
+ * miscutil.c (make_printable_string): Fix bug where some control
+ characters lose part of their ASCII representation.
+
+2006-04-11 David Shaw <dshaw@jabberwocky.com>
+
+ * memory.c (realloc): Revert m_guard fix and stick an #error in
+ there to inform people not to use it.
+
+2006-04-11 Werner Koch <wk@g10code.com>
+
+ * iobuf.c (IOBUF_BUFFER_SIZE): New to replace hardwired 8192.
+
+2006-04-06 David Shaw <dshaw@jabberwocky.com>
+
+ * memory.c (realloc): Fix compile problem with --enable-m-guard.
+
+2006-03-30 David Shaw <dshaw@jabberwocky.com>
+
+ * cert.c (main): Fix test program build warning on OSX.
+
+2006-03-16 David Shaw <dshaw@jabberwocky.com>
+
+ * cert.c (get_cert): Handle the fixed IPGP type with fingerprint.
+
+2006-03-08 David Shaw <dshaw@jabberwocky.com>
+
+ * argparse.c (default_strusage): Update copyright year to 2006.
+
+2006-02-19 David Shaw <dshaw@jabberwocky.com>
+
+ * http.c (send_request): A zero length proxy is the same as no
+ proxy.
+
+2006-02-14 Werner Koch <wk@gnupg.org>
+
+ * errors.c (g10_errstr): Add NO_DATA.
+
+2006-01-26 David Shaw <dshaw@jabberwocky.com>
+
+ * cert.c (get_cert): Disable IPGP types for now until the format
+ questions in the draft are settled.
+
+ * srv.c (getsrv): Error on oversize SRV responses.
+
+2005-12-24 David Shaw <dshaw@jabberwocky.com>
+
+ * cert.c (get_cert): Properly chase down CNAMEs pointing to CERTs.
+
+2005-12-23 David Shaw <dshaw@jabberwocky.com>
+
+ * cert.c, Makefile.am: New code to do DNS CERT queries.
+
+2005-12-22 David Shaw <dshaw@jabberwocky.com>
+
+ * srv.c, Makefile.am: Only build srv.c if we need to.
+
+2005-12-10 Ryan Lortie <desrt@desrt.ca> (dshaw)
+
+ * ttyio.c (tty_enable_completion, tty_disable_completion): Add
+ checks for no_terminal so we don't try to open("/dev/tty") when
+ invoked with --no-tty.
+
+2005-12-06 David Shaw <dshaw@jabberwocky.com>
+
+ * Makefile.am: Some cleanup so we don't build files that are
+ completely ifdeffed out. This causes a warning on Sun's cc. Do
+ the internal regex code as well for consistency.
+
+ * mkdtemp.c (mkdtemp): Fix warning.
+
+ * secmem.c, assuan-buffer.c, dotlock.c: Fix a few warnings from
+ printf-ing %p where the arg wasn't void *.
+
+2005-11-02 David Shaw <dshaw@jabberwocky.com>
+
+ * util.c [!HAVE_DECL_GETPAGESIZE]: Prototype getpagesize() if
+ unistd.h doesn't have it (for MinGW).
+
+2005-09-22 Werner Koch <wk@g10code.com>
+
+ * iobuf.c (iobuf_get_filelength): New arg OVERFLOW.
+ (iobuf_get_filelength) [W32]: Use GetFileSizeEx if available.
+ * fileutil.c (is_file_compressed): Take care of the OVERFLOW
+
+2005-08-31 David Shaw <dshaw@jabberwocky.com>
+
+ * fileutil.c (untilde): New. Expand ~/foo and ~username/foo
+ filenames into full paths using $HOME if possible, or
+ getpwuid/getpwnam if necessary.
+ (make_filename): Use it here.
+
+2005-07-28 Werner Koch <wk@g10code.com>
+
+ * pka.c: New.
+ (parse_txt_record): Changed from v=1 to v=pka1.
+
+ * Makefile.am (pka-test): new.
+
+2005-07-27 Werner Koch <wk@g10code.com>
+
+ * memory.c (FNAMEX, FNAMEXM): New macros to cope with the now used
+ names xmalloc style names.
+
+2005-07-18 Werner Koch <wk@g10code.com>
+
+ * ttyio.c (do_get): Move printing of the prompt after disabling
+ echo. Suggested by Scott Worley.
+
+2005-06-23 David Shaw <dshaw@jabberwocky.com>
+
+ * http.c (make_radix64_string): Add '=' padding as per standard.
+ (send_request, http_open, http_open_document): Clean up auth code.
+ Can now support different auth for a proxy and the file being
+ requested via that proxy. Unescape auth strings.
+
+2005-06-22 David Shaw <dshaw@jabberwocky.com>
+
+ * memrchr.c (memrchr): Not all compilers allow initializing based
+ on a variable that is also being initialized. Noted by Nelson
+ H. F. Beebe.
+
+2005-06-21 David Shaw <dshaw@jabberwocky.com>
+
+ * http.c (send_request, http_open, http_open_document): Pass in
+ auth and proxyauth that can override the in-url auth.
+
+ * http.c (send_request): Need == after the radix64-encoded basic
+ auth string.
+
+2005-06-08 David Shaw <dshaw@jabberwocky.com>
+
+ * dotlock.c [HAVE_DOSISH_SYSTEM]: Fix unused function warnings on
+ mingw32. Noted by Joe Vender.
+
+2005-05-31 Werner Koch <wk@g10code.com>
+
+ * regcomp.c (MB_CUR_MAX) [_WIN32]: Define it only if not defined.
+
+2005-05-29 David Shaw <dshaw@jabberwocky.com>
+
+ * strgutil.c (set_native_charset) [_WIN32]: Add alias for codepage
+ CP65001 to utf-8.
+
+2005-05-19 Werner Koch <wk@g10code.com>
+
+ * membuf.c: New. Taken from gnupg 1.9.
+
+2005-05-05 David Shaw <dshaw@jabberwocky.com>
+
+ * miscutil.c (add_days_to_timestamp): Remove as unused.
+
+2005-04-12 David Shaw <dshaw@jabberwocky.com>
+
+ * assuan-client.c: Fix warning on OSX.
+
+ * memrchr.c: New replacement function.
+
+ * assuan-buffer.c: Use it here.
+
+2005-04-04 Werner Koch <wk@g10code.com>
+
+ * memory.c (xcalloc, xcalloc_secure): New wrappers.
+
+ * assuan-client.c (assuan_transact): Factored all code out to ..
+ (assuan_transact2): .. new. Add arg OKAY_CB. Wipe the memory
+ processed though that callback.
+
+2005-03-31 Werner Koch <wk@g10code.com>
+
+ * isascii.c: New. This is an autoconf replacement function.
+
+ * Makefile.am (assuan_source): New. Only used when agent support
+ has been requested.
+ * assuan-buffer.c, assuan-client.c, assuan-defs.h,
+ * assuan-errors.c, assuan-logging.c, assuan-socket-connect.c,
+ * assuan-socket.c, assuan-util.c, assuan-connect.c: New. Taken
+ from libassuan 0.6.9 and adjusted for our limited use of Assuan.
+
+2005-03-18 David Shaw <dshaw@jabberwocky.com>
+
+ * ttyio.c (tty_enable_completion, tty_disable_completion): Enable
+ and disable readline completion.
+ (init_ttyfp): Completion is disabled by default.
+
+2005-03-14 Werner Koch <wk@g10code.com>
+
+ * miscutil.c (isotimestamp): New.
+
+2005-03-10 Werner Koch <wk@g10code.com>
+
+ * secmem.c (secmem_realloc): Take control information into account
+ when checking whether a resize is needed.
+
+2005-03-08 Werner Koch <wk@g10code.com>
+
+ * miscutil.c (asctimestamp) [W32]: Don't use %Z.
+
+2005-02-03 Werner Koch <wk@g10code.com>
+
+ * w32reg.c (read_w32_registry_string): Fallback to HKLM also for a
+ missing name.
+
+ * http.c (connect_server): Define ERR outside of the !W32 block.
+
+2005-02-01 David Shaw <dshaw@jabberwocky.com>
+
+ * http.c (connect_server): Fix fd leak when connecting to a
+ round-robin server set that has some down servers. Noted by Phil
+ Pennock.
+
+2005-01-20 Werner Koch <wk@g10code.com>
+
+ * simple-gettext.c (set_gettext_file): Use MO files depending on
+ the installation directory. Add new arg REGKEY.
+
+2005-01-18 Werner Koch <wk@g10code.com>
+
+ * argparse.c (default_strusage): Changed default copyright year to
+ 2005.
+
+ * strgutil.c (handle_iconv_error): Print error messages only once.
+ (native_to_utf8, utf8_to_native): Ditto.
+
+2005-01-11 Werner Koch <wk@g10code.com>
+
+ * strgutil.c (set_native_charset) [W32]: Use the alias table from
+ libiconv 1.9.2.
+
+2005-01-13 David Shaw <dshaw@jabberwocky.com>
+
+ * http.c (connect_server): Use INADDR_NONE instead of
+ SOCKET_ERROR. Noted by Timo.
+
+2005-01-06 Werner Koch <wk@g10code.com>
+
+ * strgutil.c (set_native_charset): Assume that ASCII,
+ ANSI_X3.4-1968 and 646 are actually meant as Latin-1. If
+ nl_langinfo is not available get the charset from environment
+ variables. For W32 use GetACP as error fallback. Removed Latin-15
+ to Latin-1 aliasing.
+
+2004-12-28 David Shaw <dshaw@jabberwocky.com>
+
+ * srv.h: Better implementation for the SRV check. We don't need
+ to actually check all the header files individually since the SRV
+ test compile uses them together.
+
+2004-12-20 Werner Koch <wk@g10code.com>
+
+ * strgutil.c (handle_iconv_error): Turn diagnostics into warnings
+ so that gpg does not return with failure.
+ (native_to_utf8, utf8_to_native): Ditto.
+
+2004-12-16 Werner Koch <wk@g10code.com>
+
+ * iobuf.c (fd_cache_strcmp) [W32]: Casting is a Bad Thing. Cast to
+ an unsigned char pointer and to an unsigned integer.
+
+2004-12-18 David Shaw <dshaw@jabberwocky.com>
+
+ * ttyio.c: Use only HAVE_LIBREADLINE to detect readline
+ availability.
+
+2004-12-16 David Shaw <dshaw@jabberwocky.com>
+
+ * srv.h: Don't include arpa/nameser.h unless we have it. Include
+ "types.h" for u16.
+
+ * secmem.c (secmem_init): Return a flag to indicate whether we got
+ the lock.
+
+2004-12-06 Werner Koch <wk@g10code.com>
+
+ * iobuf.c (fd_cache_strcmp): New. Use whenever we compare
+ filenames for the fd_cache. This is needed because the backslash
+ is an alias for a slash under W32. Reported by Tobias Winkler.
+
+2004-12-03 David Shaw <dshaw@jabberwocky.com>
+
+ * http.c (send_request): Include the port if non-80 in the Host:
+ header. Noted by Jason Harris.
+
+2004-11-03 Timo Schulz <twoaday@g10code.com>
+
+ * strgutil.c (w32_strerror): New.
+ * ttyio.c (init_ttyfp, tty_printf, do_get): Use it here.
+ * iobuf.c (fd_cache_open, file_filter): Likewise.
+ (iobuf_seek, translate_file_handle): Likewise.
+
+2004-11-02 Werner Koch <wk@g10code.com>
+
+ * strgutil.c (load_libiconv): Use log_info to avoid failures when
+ iconv.dll is not installed.
+
+2004-10-31 Werner Koch <wk@g10code.com>
+
+ * simple-gettext.c (get_string): Removed debugging hack.
+
+2004-10-27 Werner Koch <wk@g10code.com>
+
+ * simple-gettext.c: Removed windows.h.
+ (get_string): On the fly translation from utf-8 to active
+ character set.
+
+ * strgutil.c (load_libiconv) [_WIN32]: new.
+ (set_native_charset) [_WIN32]: Call it here and autodetect the
+ used code page.
+ (native_to_utf8, utf8_to_native): Reverted arguments for
+ iconv_open.
+ (handle_iconv_error): Made this function match iconv_open argumnet
+ ordering.
+ (utf8_to_native): Disable all quoting for DELIM == -1.
+
+2004-10-26 Werner Koch <wk@g10code.com>
+
+ * strgutil.c (mem2str): Translated comment to English.
+ (handle_iconv_error) [USE_GNUPG_ICONV]: New.
+ (set_native_charset) [USE_GNUPG_ICONV]: Added iconv based
+ conversion checks.
+ (native_to_utf8, utf8_to_native): Added iconv based conversion.
+
+2004-10-21 Werner Koch <wk@g10code.com>
+
+ * vasprintf.c: Removed. It was used only at one place and I don't
+ want to get into build problems in 1.4.
+
+2004-10-18 David Shaw <dshaw@jabberwocky.com>
+
+ * http.c (connect_server, send_request): Use the URI scheme as the
+ SRV tag rather than hard-coding _hkp.
+
+2004-10-16 David Shaw <dshaw@jabberwocky.com>
+
+ * http.c (connect_server): [_WIN32] actually fill in the sin_addr
+ so we aren't always talking to localhost. Add some general sanity
+ checking of parameters learned from gethostbyname().
+
+2004-10-15 Werner Koch <wk@g10code.com>
+
+ * vasprintf.c: New. Taken from gnupg 1.9.
+
+2004-10-14 Werner Koch <wk@g10code.com>
+
+ * iobuf.c (iobuf_get_fd): Removed double check on directfp and
+ cats it to FILEP becuase directfp is actually a void *. Notes by
+ Stefan.
+
+2004-10-13 Werner Koch <wk@g10code.com>
+
+ * logger.c (g10_log_error_f, g10_log_fatal_f, g10_log_info_f)
+ (g10_log_debug_f, print_prefix_f): Removed.
+
+ * iobuf.c (iobuf_is_pipe_filename): New.
+ (iobuf_get_fd): New.
+
+ * fileutil.c (is_file_compressed): Use it here.
+
+2004-09-30 David Shaw <dshaw@jabberwocky.com>
+
+ * iobuf.c (pop_filter): Make static.
+
+ * dotlock.c (destroy_dotlock): New. Remove the handle from the
+ list of locks.
+ (release_dotlock): Don't act if we don't have any locks at all.
+ From Werner on stable branch.
+
+2004-09-10 David Shaw <dshaw@jabberwocky.com>
+
+ * http.c (make_radix64_string, do_parse_uri, send_request): Add
+ basic auth for proxies and direct requests. Suggested by Florent
+ Thoumie.
+
+ * http.c (main): Fix test code for http-test.
+
+2004-09-09 Werner Koch <wk@g10code.com>
+
+ * errors.c (g10_errstr): New error codes G10ERR_NO_CARD,
+ G10ERR_CANCELED.
+
+ * ttyio.c (tty_get): Add readline support.
+
+ * iobuf.c (iobuf_skip_rest): New. Orginal patch by Florian
+ Weimer. Added new argument PARTIAL.
+
+2004-08-19 David Shaw <dshaw@jabberwocky.com>
+
+ * http.c (insert_escapes): Fix encoding problem for non-URI-safe
+ characters. Noted by Vladimir Novak.
+
+2004-05-21 David Shaw <dshaw@jabberwocky.com>
+
+ * timegm.c: New replacement function. Removes the need for
+ setenv.c and unsetenv.c.
+
+ * setenv.c: Removed.
+
+ * unsetenv.c: Removed.
+
+2004-03-04 David Shaw <dshaw@jabberwocky.com>
+
+ * iobuf.c (block_filter): Remove the old gpg indeterminate length
+ mode.
+ (iobuf_set_block_mode, iobuf_in_block_mode): Removed as
+ superfluous.
+
+2004-03-01 David Shaw <dshaw@jabberwocky.com>
+
+ * iobuf.c (block_filter): Properly handle a partial body stream
+ that ends with a 5-byte length that happens to be zero.
+
+2004-02-28 David Shaw <dshaw@jabberwocky.com>
+
+ * unsetenv.c: Fixed debugging typo.
+
+2004-02-24 Werner Koch <wk@gnupg.org>
+
+ * secmem.c (lock_pool) [_AIX]: Also set errno.
+
+2004-02-21 David Shaw <dshaw@jabberwocky.com>
+
+ * miscutil.c (hextobyte): Moved here from g10/misc.c so I can use
+ it in the keyserver helpers.
+
+2004-02-20 David Shaw <dshaw@jabberwocky.com>
+
+ * mkdtemp.c: New (moved from g10/), setenv.c: New, unsetenv.c:
+ New.
+
+ * Makefile.am: Include @LIBOBJS@ for replacement functions.
+
+2004-01-15 David Shaw <dshaw@jabberwocky.com>
+
+ * argparse.c (default_strusage): Update copyright date.
+ (initialize): Avoid a number of -Wformat-nonliteral warnings.
+ These aren't actual problems, but the warnings bothered me.
+
+ * miscutil.c (print_string2): New variation on print_string that
+ allows two delimiters.
+ (print_string): Call print_string2 to do work.
+
+2003-12-29 David Shaw <dshaw@jabberwocky.com>
+
+ * g10u.c: Dead code. Remove.
+
+ * Makefile.am: Don't compile g10u.c.
+
+ * iobuf.c (block_filter): Properly handle a partial body stream
+ that ends with a 5-byte length.
+
+2003-12-28 David Shaw <dshaw@jabberwocky.com>
+
+ * http.c (send_request, http_open_document, http_open): Pass the
+ http proxy from outside rather than pulling it from the
+ evironment.
+
+2003-12-28 Stefan Bellon <sbellon@sbellon.de>
+
+ * riscos.c [__riscos__]: Better filetype handling (use a
+ different SWI) and removal of unnecessary function.
+
+ * memory.c (out_of_core) [__riscos__]: Produce stack backtrace on
+ RISC OS if out_of_core() is called and M_GUARD is compiled in.
+
+2003-12-06 David Shaw <dshaw@jabberwocky.com>
+
+ * http.c (send_request): Add a Host: header for virtual hosts.
+
+2003-12-04 David Shaw <dshaw@jabberwocky.com>
+
+ * miscutil.c (answer_is_yes_no_default, answer_is_yes_no_quit):
+ Don't use alternate strings when not needed so we don't have to
+ re-translate them. Hopefully the comment will be enough to
+ indicate multiple match strings.
+
+2003-11-20 David Shaw <dshaw@jabberwocky.com>
+
+ * miscutil.c (match_multistr): New. Match against each segment in
+ a string with tokens separated by |.
+ (answer_is_yes_no_default, answer_is_yes_no_quit,
+ answer_is_okay_cancel): Use it here to enable alternate
+ translations.
+
+2003-11-01 David Shaw <dshaw@jabberwocky.com>
+
+ * http.c (connect_server): Differentiate between generic "can't
+ connect" errors and the more specific "host not found". Suggested
+ by Samuel Tardieu.
+
+2003-10-29 Werner Koch <wk@gnupg.org>
+
+ * miscutil.c (answer_is_okay_cancel): New.
+
+2003-10-25 Werner Koch <wk@gnupg.org>
+
+ * Makefile.am: Replaced INTLLIBS by LIBINTL.
+
+2003-10-23 Werner Koch <wk@gnupg.org>
+
+ * secmem.c (lock_pool) [_AIX]: Don't use plock.
+
+2003-10-12 David Shaw <dshaw@jabberwocky.com>
+
+ * srv.c: OSX 10.2.8/Darwin 6.8 seems to have some #include
+ ordering issues? Move sys/types.h up higher to work around.
+
+2003-10-08 Werner Koch <wk@gnupg.org>
+
+ * ttyio.c (tty_print_string, tty_print_utf8_string2)
+ (tty_print_utf8_string): Made string arg const.
+
+2003-09-28 Timo Schulz <twoaday@freakmail.de>
+
+ * strgutil.c [WIN32] (asprintf): New.
+
+2003-09-28 Werner Koch <wk@gnupg.org>
+
+ * ttyio.c (tty_fprintf): New.
+
+2003-09-21 Timo Schulz <twoaday@freakmail.de>
+
+ * http.c [WIN32]: Define MB_CUR_MAX.
+ (connect_server): use unsigned long since W32 does not have in_addr_t.
+
+2003-08-28 David Shaw <dshaw@jabberwocky.com>
+
+ * dotlock.c, http.c, iobuf.c, simple-gettext.c, srv.c, srv.h,
+ strgutil.c, ttyio.c, w32reg.c: s/__MINGW32__/_WIN32/ to help
+ building on native Windows compilers. Requested by Brian Gladman.
+ From Werner on stable branch.
+
+ * http.c (connect_server): Oops - forgot to freeaddrinfo().
+
+2003-08-24 David Shaw <dshaw@jabberwocky.com>
+
+ * http.c (connect_server): Try and use getaddrinfo if it is
+ available. Try for IPv6 via getaddrinfo() or a IPv6-ized
+ gethostbyname(). Suggested by Jun-ichiro itojun Hagino.
+
+2003-07-10 David Shaw <dshaw@jabberwocky.com> (from Werner on stable branch)
+
+ * iobuf.c (check_special_filename): Replaced is isdigit by digitp
+ to avoid passing negative values and potential locale problems.
+ Problem noted by Christian Biere.
+ * strgutil.c (strlwr,strcasecmp,strncasecmp): Make sure we don't
+ pass a negative value.
+ * miscutil.c (scan_isodatestr): Ditto.
+
+2003-05-30 David Shaw <dshaw@jabberwocky.com>
+
+ * srv.h, srv.c: Include windows.h with MINGW32.
+
+2003-05-24 David Shaw <dshaw@jabberwocky.com>
+
+ * argparse.c, dotlock.c, fileutil.c, iobuf.c, miscutil.c,
+ simple-gettext.c, errors.c, http.c, memory.c, secmem.c, ttyio.c:
+ Edit all preprocessor instructions to remove whitespace before the
+ '#'. This is not required by C89, but there are some compilers
+ out there that don't like it.
+
+2003-05-21 Werner Koch <wk@gnupg.org>
+
+ * fileutil.c (is_file_compressed): Fixed checking for "-" filename.
+
+2003-04-13 David Shaw <dshaw@jabberwocky.com>
+
+ * srv.c (main): Test against wwwkeys.pgp.net.
+
+ * srv.h: Grr. The RH7.3 Linux man page defines the fourth arg of
+ dn_expand as unsigned char*, but it is really char* according to
+ resolv.h.
+
+2003-03-23 David Shaw <dshaw@jabberwocky.com>
+
+ * argparse.c (default_strusage): Change copyright date.
+
+2003-03-14 David Shaw <dshaw@jabberwocky.com>
+
+ * srv.h, srv.c (getsrv): Use unsigned char rather than char.
+ Noted by Stefan Bellon.
+
+2003-03-11 David Shaw <dshaw@jabberwocky.com>
+
+ * http.c (connect_server): Use DNS SRV to get a server list. Fail
+ over to A records if necessary.
+
+ * Makefile.am, srv.h, srv.c: New DNS SRV handling code.
+
+2003-02-22 David Shaw <dshaw@jabberwocky.com>
+
+ * ttyio.c (tty_print_utf8_string, tty_print_utf8_string2): Use 0
+ to indicate a string with no maximum size. This prevents early
+ truncation of strings that contain control chars which are
+ expanded into \xXX form.
+
+2002-12-26 David Shaw <dshaw@jabberwocky.com>
+
+ * iobuf.c (iobuf_flush): Only print debug info if debugging is on.
+
+2002-11-13 David Shaw <dshaw@jabberwocky.com>
+
+ * secmem.c (lock_pool) [__CYGWIN__]: Don't print secmem warning.
+ From Werner on stable branch.
+
+2002-11-09 Werner Koch <wk@gnupg.org>
+
+ * ttyio.c (TERMDEVICE): Removed.
+ (tty_get_ttyname): New.
+ (init_ttyfp): Use it here instead of the TERMDEVICE macro.
+
+2002-11-06 David Shaw <dshaw@jabberwocky.com>
+
+ * w32reg.c (read_w32_registry_string): Fixed expanding of the
+ environment buffer; didn't worked at all. Reported by Thijmen
+ Klok. From Werner on stable branch.
+
+ * secmem.c (secmem_free, secmem_term): Use wipememory2() instead
+ of memset() to overwrite secure memory
+
+ * iobuf.c (direct_open): Handle mode 'b' if O_BINARY is available.
+ From Werner on stable branch.
+
+ * fileutil.c: Comment from stable branch.
+
+2002-10-31 Stefan Bellon <sbellon@sbellon.de>
+
+ * riscos.c (riscos_load_module, riscos_check_regexp): New.
+ (riscos_set_filetype_by_mimetype, riscos_dump_fdlist)
+ (riscos_fdopenfile, riscos_close_fds, riscos_renamefile)
+ (riscos_gstrans, riscos_list_openfiles, riscos_not_implemented):
+ Renamed functions to contain riscos prefix.
+ * dotlock.c [__riscos__]: Renames due to changes in riscos.c.
+ * fileutil.c [__riscos__]: Likewise.
+
+2002-10-29 Stefan Bellon <sbellon@sbellon.de>
+
+ * fileutil.c: Removed unnecessary left-over includes for RISC OS.
+ (make_filename): Tidied up RISC OS stuff.
+ (compare_filenames) [__riscos__]: Compare with ascii_strcasecmp().
+ (make_basename) [__riscos__]: Branch to own RISC OS routine from
+ here.
+
+ * riscos.c (riscos_make_basename): New.
+
+2002-10-28 Stefan Bellon <sbellon@sbellon.de>
+
+ * fileutil.c (make_basename) [__riscos__]: Cut off RISC OS' filing
+ systems from filepath.
+
+ * riscos.c (riscos_get_filetype_from_string, riscos_get_filetype):
+ Added.
+ (riscos_set_filetype_by_number): Made public.
+
+2002-10-19 David Shaw <dshaw@jabberwocky.com>
+
+ * Makefile.am, regcomp.c, regex.c, regex_internal.c,
+ regex_internal.h, regexec.c: Add new regex files from glibc 2.3.1.
+
+2002-10-17 David Shaw <dshaw@jabberwocky.com>
+
+ * http.c (connect_server): Try all A records for names with
+ multiple addresses until one answers for both MINGW32 and not
+ MINGW32.
+
+2002-10-10 David Shaw <dshaw@jabberwocky.com>
+
+ * http.c (connect_server): Properly handle a single A record that
+ fails connect().
+
+2002-10-03 David Shaw <dshaw@jabberwocky.com>
+
+ * logger.c (g10_log_warning, log_set_strict): Add new log_warning
+ logger command which can be switched between log_info and
+ log_error via log_set_strict.
+
+2002-09-24 David Shaw <dshaw@jabberwocky.com>
+
+ * http.c (connect_server): Try all A records for names with
+ multiple addresses until one answers (not MINGW32).
+
+2002-09-16 Werner Koch <wk@gnupg.org>
+
+ * w32reg.c (read_w32_registry_string): Fallback to HLM.
+
+2002-09-12 Stefan Bellon <sbellon@sbellon.de>
+
+ * fileutil.c (make_filename): Removed variable for RISC OS to
+ avoid compiler warning.
+
+ * secmem.c: Removed static variable for RISC OS to avoid
+ compiler warning.
+
+2002-09-11 Werner Koch <wk@gnupg.org>
+
+ * simple-gettext.c: Disable charset mappings. We do it now when
+ installing the files.
+
+2002-09-09 Werner Koch <wk@gnupg.org>
+
+ * w32reg.c (read_w32_registry_string): Handle REG_EXPAND_SZ.
+ Suggested by Ryan Malayter.
+
+ * strgutil.c (ascii_strcasecmp): Replaced by code from gnulib.
+ (ascii_strncasecmp): New.
+
+2002-09-02 Werner Koch <wk@gnupg.org>
+
+ * simple-gettext.c (set_gettext_file): Make sure that we only use
+ backslashes.
+
+ * strgutil.c (set_native_charset): Allow NULL as argument to use
+ nl_langinfo for selection. Mapped latin-15 to latin-1.
+
+2002-08-30 Werner Koch <wk@gnupg.org>
+
+ * iobuf.c (block_filter): Removed the assert, so that one can pass
+ the first character of a message and use the block filter for
+ non partial length encoded packets.
+
+2002-08-06 Stefan Bellon <sbellon@sbellon.de>
+
+ * ttyio.c [__riscos__]: Moved low-level RISC OS stuff to riscos.c.
+ * riscos.c: Use new SWI calling mechanism of UnixLib.
+
+2002-08-03 Stefan Bellon <sbellon@sbellon.de>
+
+ * secmem.c (init_pool, secmem_term): Changed #if to #ifdef in
+ order to avoid warning with RISC OS' Norcroft C.
+
+2002-07-25 David Shaw <dshaw@jabberwocky.com>
+
+ * secmem.c: "Warning" -> "WARNING"
+
+2002-07-05 Werner Koch <wk@gnupg.org>
+
+ * argparse.c (initialize): We better exit after a read error so
+ that we don't run into an endless loop when reading a directory.
+ Noted by Andrew Suffield.
+
+2002-07-01 David Shaw <dshaw@jabberwocky.com>
+
+ * argparse.c (optfile_parse): Fix variable typo - 'p2' should be
+ 'p' :)
+
+2002-06-29 Werner Koch <wk@gnupg.org>
+
+ * argparse.c (optfile_parse): Renamed an auto I to P2 to avoid
+ shadowing warning.
+
+2002-06-21 Stefan Bellon <sbellon@sbellon.de>
+
+ * riscos.c (riscos_global_defaults): New.
+
+2002-06-20 Stefan Bellon <sbellon@sbellon.de>
+
+ * riscos.c (riscos_set_filetype_by_number, riscos_set_filetype):
+ New. Set RISC OS filetype according to MIME type.
+
+2002-06-14 David Shaw <dshaw@jabberwocky.com>
+
+ * strgutil.c (pop_strlist): New function to pop the head off of a
+ strlist.
+
+2002-06-05 Timo Schulz <ts@winpt.org>
+
+ * fileutil.c (is_file_compressed): Corrected the magic values
+ for bzip2 and gzip. Noted by David.
+
+2002-05-22 Werner Koch <wk@gnupg.org>
+
+ * fileutil.c (compare_filenames): Replaced stricmp by strcasecmp.
+ * miscutil.c (answer_is_yes_no_quit,answer_is_yes_no_default): Ditto.
+
+ * strgutil.c (strncasecmp): New.
+ (memicmp): Removed.
+
+2002-05-10 Stefan Bellon <sbellon@sbellon.de>
+
+ * memory.c (add_entry) [M_DEBUG]: Added some missing EXTRA_ALIGN.
+ (free_entry) [M_DEBUG]: Free secure memory via secmem_free.
+ (alloc_secure): Malloc at least 1 byte.
+ (realloc) [M_GUARD]: Added missing FNAMEARG to function call.
+
+ * logger.c (g10_log_bug0) [__riscos__]: Make use of first
+ g10_log_bug0 function for later Norcroft compiler.
+
+ * riscos.c: Added stdlib.h include.
+
+2002-05-04 Werner Koch <wk@gnupg.org>
+
+ * http.c (write_server) [__MINGW32__]: Replaced WriteFile by send
+ because sockets don't work with WriteFile under NT anymore.
+
+2002-05-03 David Shaw <dshaw@jabberwocky.com>
+
+ * argparse.c (optfile_parse): Remove quotes only if they totally
+ enclose the string, and do not occur within the string. This
+ makes specifying a program under Win32 easier when you need quotes
+ around part of a string, but not around the whole string.
+
+2002-05-02 Werner Koch <wk@gnupg.org>
+
+ * memory.c (alloc): Malloc at least 1 byte. Noted by Winona Brown.
+
+2002-04-23 David Shaw <dshaw@jabberwocky.com>
+
+ * miscutil.c: New function answer_is_yes_no_default() to give a
+ default answer.
+
+2002-04-22 Stefan Bellon <sbellon@sbellon.de>
+
+ * riscos.c (riscos_open, riscos_fopen, riscos_fstat, set_filetype):
+ Removed as they're not needed anymore.
+
+ * iobuf.c (direct_open) [__riscos__]: Don't allow opening of
+ directories.
+
+2002-04-08 Werner Koch <wk@gnupg.org>
+
+ Fixed filename of last entry.
+
+2002-03-29 David Shaw <dshaw@jabberwocky.com>
+
+ * miscutil.c (print_string, utf8_to_native): If a delimiter is
+ used, then quote the backslash character as well. Problem noted
+ by Rainer Perske.
+
+2002-03-15 Werner Koch <wk@gnupg.org>
+
+ * argparse.c (optfile_parse): Fixed missing argument handling.
+
+2002-02-28 Timo Schulz <ts@winpt.org>
+
+ * http.c (write_server): Convert integer to a HANDLE for W32.
+
+2002-01-27 David Shaw <dshaw@jabberwocky.com>
+
+ * iobuf.c (iobuf_fdopen, iobuf_sockopen): Do not cache fdopened
+ fds on close.
+
+2002-01-08 Werner Koch <wk@gnupg.org>
+
+ * secmem.c (print_warn): Print a pointer to the FAQ.
+
+2002-01-05 Werner Koch <wk@gnupg.org>
+
+ * argparse.c (default_strusage): Set default copyright date to 2002.
+
+2002-01-02 Stefan Bellon <sbellon@sbellon.de>
+
+ * iobuf.c [__riscos__]: Updated include file name.
+
+ * fileutil.c [__riscos__]: Ditto.
+
+ * ttyio.d [__riscos__]: Ditto.
+
+ * riscos.c [__riscos__]: Ditto. Added debugging code and
+ unified error messages.
+
+2001-12-27 David Shaw <dshaw@jabberwocky.com>
+
+ * errors.c (g10_errstr): Added G10ERR_KEYSERVER
+
+2001-12-27 Werner Koch <wk@gnupg.org>
+
+ * simple-gettext.c [MINGW32]: Fixed last changed.
+
+2001-12-22 Stefan Bellon <sbellon@sbellon.de>
+
+ * memory.c (realloc): Fixed realloc not working when M_GUARD is
+ defined and first parameter is NULL.
+
+2001-12-22 Timo Schulz <ts@winpt.org>
+
+ * fileutil.c (is_file_compressed): New.
+
+2001-12-19 Werner Koch <wk@gnupg.org>
+
+ * simple-gettext.c, w32reg.c [CYGWIN32]: Allow to use this file
+
+2001-10-11 Werner Koch <wk@gnupg.org>
+
+ * http.c (do_parse_uri): Changed initialization of the port number
+ so that it does also work with x-hkp. By David Shaw.
+
+2001-09-19 Werner Koch <wk@gnupg.org>
+
+ * w32reg.c (get_root_key): New.
+ (read_w32_registry_string): Use it here.
+ (write_w32_registry_string): New. Contributed by Timo.
+
+ * iobuf.c (iobuf_ioctl): New command to disable fd
+ caching. Implemented no_cache flag where needed.
+ (iobuf_sockopen): Always set no_cache flag.
+
+ * strgutil.c (utf8_to_native): Add a delim arg and changed all
+ callers. Make sure that quoting is done when translation is
+ disabled.
+ * miscutil.c (print_utf8_string2): New.
+
+2001-09-17 Werner Koch <wk@gnupg.org>
+
+ * miscutil.c (print_string): Use explicit ranges and not iscntrl().
+ (make_printable_string): Ditto.
+
+2001-09-07 Werner Koch <wk@gnupg.org>
+
+ * strgutil.c (strsep): New, taken from glibc 2.2.1.
+
+2001-09-03 Werner Koch <wk@gnupg.org>
+
+ * miscutil.c (strtimestamp,asctimestamp): Avoid trigraphs.
+
+2001-08-21 Stefan Bellon <sbellon@sbellon.de>
+
+ * riscos.c [__riscos__] (close_fds): Fixed possible endless loop.
+
+2001-08-20 Werner Koch <wk@gnupg.org>
+
+ Applied patches from Stefan Bellon <sbellon@sbellon.de> to support
+ RISC OS. Nearly all of these patches are identified by the
+ __riscos__ macro.
+ * secmem.c [__riscos__]: Disabled secure memory stuff.
+ * dotlock.c, ttyio.c [__riscos__]: Adapted for RISC OS
+ * fileutil.c, iobuf.c: Adapted for RISC OS; mainly replaced
+ hardcoded path separators with EXTSEP_S like macros.
+ * http.c (send_request): Use macros for the env-var name.
+ * logger.c [__riscos__]: Do an fflush at the end of each log
+ function.
+ * memory.c [__riscos__]: Minor patches
+ * riscos.c (set_filetype): New.
+
+ * secmem.c (lock_pool): Under HPUX mlock is broken but we might
+ have plock, so we use this to lock the entire process. By Albert
+ Chin.
+
+2001-07-03 Werner Koch <wk@gnupg.org>
+
+ * strgutil.c (utf8_to_native): Fixed printing of invalid utf-8
+ characters. Thomas Roessler reported that the escaping didn't work
+ correct.
+
+2001-06-12 Werner Koch <wk@gnupg.org>
+
+ * strgutil.c (ascii_memistr,ascii_isupper,ascii_islower,
+ ascii_toupper,ascii_tolower, ascii_strcasecmp, ascii_memcasecmp): New.
+ (set_native_charset): Use ascii_strcasecmp()
+ * fileutil.c (compare_filenames): Ditto
+ * miscutil.c (answer_is_yes): Ditto.
+ (answer_is_yes_no_quit): Ditto.
+
+2001-06-06 Werner Koch <wk@gnupg.org>
+
+ * strgutil.c (vasprintf) [__MINGW32__]: New. Taken from libiberty.
+ * ttyio.c (tty_printf) [__MINGW32__]: Replaced the sprintf with
+ the new vasprintf.
+
+2001-06-05 Werner Koch <wk@gnupg.org>
+
+ * dotlock.c (make_dotlock): Typo fixes.
+
+2001-05-25 Werner Koch <wk@gnupg.org>
+
+ * ttyio.c (do_get): Fixed a serious format string bug. Thanks to
+ fish stiqz.
+
+2001-05-23 Werner Koch <wk@gnupg.org>
+
+ * secmem.c (EPERM): Try to work around a Slackware problem.
+
+2001-05-05 Werner Koch <wk@gnupg.org>
+
+ * http.c (http_start_data): Flush before writing.
+ (http_wait_response): No need to flush here.
+
+2001-04-27 Werner Koch <wk@gnupg.org>
+
+ * memory.c (out_of_core): Print an explanation on reasons why
+ secret memory can get exhausted.
+
+2001-04-23 Werner Koch <wk@gnupg.org>
+
+ * http.c (http_wait_response): Implement new flag to inhibit the
+ TCP shutdown.
+
+2001-04-20 Werner Koch <wk@gnupg.org>
+
+ * http.c (http_start_data): Use write_server and not the iobuf
+ stuff. I wonder why we are at all using write_server - shouldn't
+ it be handled by iobuf?
+
+ * strgutil.c (set_native_charset): Allow utf-8 by introducing the
+ new no_translation variable.
+ (native_to_utf8): Handle no_translation.
+ (utf8_to_native): Ditto.
+
+2001-04-19 Werner Koch <wk@gnupg.org>
+
+ * miscutil.c (asctimestamp): Handle negative times. We must do
+ this because Windoze segvs on negative times passed to gmtime().
+ (strtimestamp): Ditto.
+
+2001-04-14 Werner Koch <wk@gnupg.org>
+
+ * strgutil.c (utf8_to_native): Fixed a segv. Thanks to Keith Clayton.
+
+2001-04-13 Werner Koch <wk@gnupg.org>
+
+ * iobuf.c (iobuf_fopen): Removed because it is not used and
+ furthermore mode is ignored for an fname of "-". Suggested by
+ Florian Weimer.
+
+2001-04-02 Werner Koch <wk@gnupg.org>
+
+ * iobuf.c (translate_file_handle): New. Use this function
+ everywhere in this file.
+ (iobuf_translate_file_handle): Always use the osfhandle stuff here
+ because callers don't know the implementation details of iobuf and
+ they expect that the handles are translated.
+
+2001-03-29 Werner Koch <wk@gnupg.org>
+
+ * miscutil.c (answer_is_yes): An empty string does now return no.
+ (answer_is_yes_no_quit): Likewise.
+
+ * iobuf.c (iobuf_close): Burn the buffers.
+
+2001-03-26 Werner Koch <wk@gnupg.org>
+
+ * ttyio.c: Define TERMDEVICE depending on OS.
+
+ * http.c (http_start_data): send a CRLF and not just a LF.
+ Pointed out by Steven Murdoch.
+
+2001-03-13 Werner Koch <wk@gnupg.org>
+
+ * iobuf.c (iobuf_sockopen): New.
+ (sock_filter) [__MINGW32__]: New.
+ (iobuf_ioctl): New.
+ (file_filter): Implemented keep_open mode.
+ * http.c (http_open, http_wait_response): Replaced iobuf_fdopen by
+ iobuf_sockopen and use an iobuf_ioctl to avoid the dup().
+ (deinit_sockets, init_sockets) [__MINGW32__]: New.
+ (connect_server, write_server): Add code to work with W32 sockets.
+
+2001-03-12 Werner Koch <wk@gnupg.org>
+
+ * strgutil.c (check_trailing_chars,check_trailing_ws): New.
+
+2001-03-08 Werner Koch <wk@gnupg.org>
+
+ * argparse.c (default_strusage): Changed year of printed copyright
+ to 2001.
+
+ * iobuf.c (fd_cache_invalidate, fd_cache_close, fd_cache_open): New.
+ (direct_open): Invalidate the fd_cache for read access.
+ (file_filter): Cache the close here.
+ (iobuf_open): Use new my_fopen_ro macro to try the cache first.
+
+2001-03-07 Werner Koch <wk@gnupg.org>
+
+ * iobuf.c: Made the old stdio file handling cpp conditional
+ controlled by FILE_FILTER_USES_STDIO and added a new
+ open/read/close based one. We don't need the stdio buffering
+ becuase we are doing our own buffering anyway. And it is a
+ prerequesite to allow the use of ReadFile et al for W32 which in
+ turn is needed to make the http stuff work there. The new W32
+ stuff has also been implemented. Minor changes to all open functions.
+ (direct_open): New.
+ (file_filter): Core of the new read/write handling.
+ (iobuf_get_filelength): Use W32 API function here. But it is
+ currently limited to 2GB files.
+ (iobuf_seek): Ditto.
+
+2001-03-01 Werner Koch <wk@gnupg.org>
+
+ * errors.c (g10_errstr): New codes UNU_SECKEY and UNU_PUBKEY.
+
+2000-12-28 Werner Koch <wk@gnupg.org>
+
+ * dotlock.c: Made all_lockfiles volatile.
+ (remove_lockfiles): Made public.
+
+2000-11-30 Werner Koch <wk@gnupg.org>
+
+ * iobuf.c (iobuf_translate_file_handle): New.
+ (iobuf_open, iobuf_create): Use it for special filenames
+
+2000-11-11 Paul Eggert <eggert@twinsun.com>
+
+ * iobuf.c (iobuf_get_filelength): Now returns off_t, not u32.
+ Remove kludges to worry about large files; the callers check
+ for files that are too large, and they should already be doing
+ the right thing in an implementation-independent way.
+ (fopen, fstat): Remove macros.
+
+ * iobuf.c (iobuf_set_limit, iobuf_tell, iobuf_seek):
+ Use off_t, not ulong, for file offsets.
+ (<limits.h>): Include if needed.
+ (LONG_MAX, LONG_MIN): Define a substitute if needed.
+ (fseeko): Define a substitute if needed.
+
+ * iobuf.c (iobuf_seek): Do not use %lu to report file
+
+2000-11-09 Werner Koch <wk@gnupg.org>
+
+ * iobuf.c (iobuf_enable_special_filenames): New.
+ (check_special_filename): New.
+ (iobuf_open): check for special filenames.
+ (iobuf_create): Ditto.
+
+2000-10-23 Werner Koch <wk@gnupg.org>
+
+ * secmem.c (lock_pool): Don't print warning for Windows.
+
+2000-10-16 Werner Koch <wk@gnupg.org>
+
+ * secmem.c (lock_pool): Fixed error checking for Linux.
+ By James Troup.
+
+Thu Sep 14 14:20:38 CEST 2000 Werner Koch <wk@openit.de>
+
+ * miscutil.c (answer_is_yes_no_quit): Swapped order of yes/no test
+ so that no is returned for an empty input. By David Champion.
+
+Wed Sep 6 17:55:47 CEST 2000 Werner Koch <wk@openit.de>
+
+ * iobuf.c: Use fopen64 insead of fopen when available.
+ (iobuf_get_filelength): Use fstat64 when available but return
+ 2^32-1 if the file is larger than this value.
+
+Wed Sep 6 14:59:09 CEST 2000 Werner Koch <wk@openit.de>
+
+ * secmem.c (secmem_realloc): check for failed secmem_malloc. By
+ Matt Kraai.
+
+ * strgutil.c (utf8_to_native): Fixed null ptr problem. By
+ Giampaolo Tomassoni.
+
+Thu Jul 27 10:02:38 CEST 2000 Werner Koch <wk@openit.de>
+
+ * iobuf.c: Use setmode() at several places to set stdin and stdout
+ to binary mode for MSDOS based systems
+
+ * iobuf.c (underflow): Initialize dummy_len to keep memory checker happy.
+
+Fri Jun 9 10:09:52 CEST 2000 Werner Koch <wk@openit.de>
+
+ * ttyio.c: Simulate termios with termios. By Dave Dykstra.
+
+Thu Jun 8 20:22:00 CEST 2000 Werner Koch <wk@openit.de>
+
+ * secmem.c (lock_pool,secmem_init): Additional check for dropped privs.
+
+Tue May 30 16:37:55 CEST 2000 Werner Koch <wk@openit.de>
+
+ * iobuf.c (iobuf_cancel): Fix for MSDOS.
+
+Fri Apr 14 19:37:08 CEST 2000 Werner Koch <wk@openit.de>
+
+ * dotlock.c (disable_dotlock): New. Implmented this in the module.
+
+2000-03-09 14:04:22 Werner Koch (wk@habibti.openit.de)
+
+ * argparse.c (default_strusage): Changed year of default copyright.
+
+Tue Mar 7 18:45:31 CET 2000 Werner Koch <wk@gnupg.de>
+
+ * secmem.c (lock_pool): No more warning for QNX. By Sam Roberts.
+
+2000-03-02 15:51:04 Werner Koch (wk@habibti.gnupg.de)
+
+ * ttyio.c (tty_print_utf8_string): Oops.
+
+Thu Mar 2 15:37:46 CET 2000 Werner Koch <wk@gnupg.de>
+
+ * ttyio.c (tty_print_utf8_string2): New to allow a max output size.
+
+Wed Feb 23 10:07:57 CET 2000 Werner Koch <wk@gnupg.de>
+
+ * miscutil.c (asctimestamp): Fix for possible buffer overflow by
+ large system returned date format string.
+
+Fri Dec 31 14:08:15 CET 1999 Werner Koch <wk@gnupg.de>
+
+ * logger.c (log_inc_errorcount): New.
+
+Sat Dec 4 12:30:28 CET 1999 Werner Koch <wk@gnupg.de>
+
+ * iobuf.c (iobuf_cancel): Broadcast the new Cancel mesaage to all
+ filters.
+
+Mon Nov 22 11:14:53 CET 1999 Werner Koch <wk@gnupg.de>
+
+ * strgutil.c (strcasecmp): New.
+
+ * secmem.c (pool_is_mmapped): Made volatile.
+
+Sat Oct 9 20:34:41 CEST 1999 Werner Koch <wk@gnupg.de>
+
+ * Makefile.am: Removed libtool.
+
+Fri Oct 8 20:32:01 CEST 1999 Werner Koch <wk@gnupg.de>
+
+ * w32reg.c: New.
+ * simple-gettext.c: Use the Registry to locate the mo file.
+
+ * http.c (send_request): Add support for proxys; suggested by
+ Walter Hofmann.
+ (http_open_document): Pass flags to http_open.
+
+Fri Sep 17 12:56:42 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+
+ * secmem.c (lock_pool): Check for ENOSYS return my mlock() on
+ old SCOs.
+
+ * ttyio.c (do_get): Replaced #if __MINGW32__ by #ifdef becuase
+ gcc 2.95.1 assigns a floating point value (0.2) to this macro,
+ which in turn can't be used in an expression.
+
+Wed Sep 15 16:22:17 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+
+ * simple-gettext.c: New.
+
+Wed Sep 1 15:30:44 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+
+ * argparse.c (arg_parse): Add standard options to the dump-options
+ output.
+
+Tue Aug 31 17:20:44 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+
+ * strgutil (utf8_to_native): Implemented.
+ (check_utf8_string): Removed.
+
+ * miscutil.c (make_printable_string): Fixed possible buffer overflow.
+ (print_utf8_string): New.
+
+ * ttyio.c (tty_print_utf8_string): New.
+
+Mon Aug 30 20:38:33 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+
+ * secmem.c (pool_okay): declared volatile.
+
+ * miscutil.c (answer_is_yes): Always check for plain "yes".
+ (answer_is_yes_no_quit): Likewise.
+
+ * dotlock.c (create_dotlock): Fixed segv during cleanup.
+
+Mon Jul 12 14:55:34 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+
+ * argparse.c (initialize): Init ret_xxx.
+ (optfile_parse): Remove quotes from arguments.
+
+Wed Jul 7 13:08:40 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+
+ * memory.c (membug): Use if either M_DEBUG or M_GUARD is used.
+
+ * miscutil.c (scan_isodatestr): New.
+
+ * logger.c (g10_log_mpidump): Moved to ../mpi/mpicoder.c
+ (g10_log_print_prefix): Renamed from print_prefix and made global.
+
+ * Makefile.am: Support for libtool.
+
+Thu Jul 1 12:47:31 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+
+ * miscutil.c (make_printable_string): New.
+
+ * strgutil.c (add_to_strlist2,append_to_strlist2): New.
+
+Tue Jun 29 21:44:25 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+
+ * secmem.c (USE_CAPABILITIES): Capabilities support (Remi).
+
+Sat Jun 26 12:15:59 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+
+ * dotlock.c (create_dotlock): s/uts/utsbuf/ cause there an Amdahl
+ system with the name UTS (Dave Dykstra).
+
+ * secmem.c (DEFAULT_POOLSIZE): Doubled the size.
+
+Fri Jun 18 00:18:02 CEST 1999 Michael Roth <mroth@nessie.de>
+
+ * iobuf.c: file_filter() Detection of EOF on terminals
+ improved/fixed (see Bug #21).
+
+Mon Jun 14 21:18:54 CEST 1999 Michael Roth <mroth@nessie.de>
+
+ * ttyio.c: tty_no_terminal() new.
+
+Sat Jun 5 15:30:33 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * strgutil.c (set_native_charset): Support Latin-2
+
+Tue Jun 1 16:01:46 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * iobuf.c (iobuf_get_real_fname): Made global and now keep a
+ copy of the name in the iobuf struct.
+
+Mon May 31 19:41:10 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * iobuf.c (file_filter,block_filter): Speed patches (Rémi).
+
+Thu May 27 09:40:55 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * miscutil.c (answer_is_yes_no_quit): New.
+
+Sun May 23 14:20:22 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * dotlock.c: Tweaked to make it compile under mingw32
+ * http.c: Disabled for mingw32.
+
+Sat May 22 22:47:26 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * logger.c (log_set_logfile): New.
+
+Thu May 20 14:04:08 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * memory.c (membug): Nanu, there was a const instead of a static.
+
+ * strgutil.c (trim_trailing_chars): New.
+
+Mon May 17 21:54:43 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * logger.c (g10_log_hexdump): Made 2nd arg a const.
+
+Wed Apr 28 13:03:03 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * miscutil.c (asctimestamp): Use nl_langinfo (Gaël Quéri).
+
+Sun Apr 18 10:11:28 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * argparse.c (store_alias): Disabled becuase it is not used.
+
+ * ttyio.c (tty_batchmode): New
+
+Sat Mar 20 11:44:21 CET 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * http.c: Swapped to includes.
+
+Tue Mar 2 16:44:57 CET 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * strgutil.c (get_native_charset): New.
+
+Fri Feb 26 17:55:41 CET 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * secmem.c (memblock_struct): Force align (Rémi Guyomarch)
+
+Wed Feb 24 11:07:27 CET 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * iobuf.c (block_filter): Fixed the oscillating partial packet chunks.
+
+Fri Feb 19 15:49:15 CET 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * iobuf.c (iobuf_push_filter2): New to allow transer of context
+ ownership to the iobuf. Released the context where needed.
+
+Tue Feb 16 14:10:02 CET 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * strgutil.c (add_to_strglist): Clear the new flags field
+ (append_to_strglist): Ditto.
+
+ * dotlock.c (read_lockfile): terminate pidstr (Michael).
+
+Wed Feb 10 17:15:39 CET 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * dotlock.c (remove_lockfiles): Add cleanup function.
+ (make_dotlock): Add deadlock check.
+
+ * secmem.c (secmem_malloc): Changed error message.
+
+Wed Jan 20 21:40:21 CET 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * http.c (http_wait_response): Moved the shutdown behind the dup
+
+Wed Jan 20 18:59:49 CET 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * http.c (send_request): Removed double LF
+
+Tue Jan 19 19:34:58 CET 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * * iobuf.c (iobuf_push_filter): Allow filters for temp streams
+
+ (iobuf_write_temp): Ditto.
+ (iobuf_flush_temp): New.
+ (iobuf_unget_and_close_temp): Removed.
+
+ * http.c (close_http_document): Renamed to http_close().
+ (open_http_document): Renamed to http_open_document().
+ (http_open): New.
+ (http_start_data): New.
+ (http_wait_response): New.
+
+
+Sun Jan 17 11:04:33 CET 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * strgutil.c (trim_trailing_ws): New.
+
+Sat Jan 16 12:03:27 CET 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * http.c (connect_server): Fixed stupid bug.
+
+Sat Jan 16 09:27:30 CET 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * http.c: New
+
+
+Wed Jan 13 14:10:15 CET 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * iobuf.c (iobuf_fdopen): New.
+
+Sat Jan 9 16:02:23 CET 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * secmem.c (lock_pool): add another check that setuid() worked.
+ (secmem_init): Ditto.
+
+Thu Jan 7 18:00:58 CET 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * iobuf.c (iobuf_clear_eof): Removed.
+ (underflow): Changed the eof handling.
+ (iobuf_pop_filter): Made static and renamed to pop_filter.
+
+ * iobuf.c (iobuf_read_line): New.
+
+Sun Jan 3 15:28:44 CET 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * dotlock.c (make_dotlock): print another informal message.
+
+ (make_dotlock): Removed the cpp checks.
+
+
+Tue Dec 29 14:41:47 CET 1998 Werner Koch <wk@isil.d.shuttle.de>
+
+ * secmem.c: Moved unistd.h out of the #ifdef
+
+ * dotlock.c (make_dotlock): Sun has no SYS_NMLN
+
+ * iobuf.c (iobuf_unget_and_close_temp): Reset .start
+
+Sat Dec 12 18:40:32 CET 1998 Werner Koch <wk@isil.d.shuttle.de>
+
+ * argparse.c (arg_pars): fixed opts[i] with negative index.
+
+Fri Nov 27 21:37:41 CET 1998 Werner Koch <wk@isil.d.shuttle.de>
+
+ * dotlock.c: Implemented
+
+Wed Nov 25 11:30:07 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * iobuf.c (iobuf_pop_filter): Fixed sigsegv after error.
+
+Thu Nov 19 07:09:55 1998 Werner Koch <werner.koch@guug.de>
+
+ * miscutil.c (strtimevalue): New.
+
+Tue Nov 10 10:01:53 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * strgutil.c (set_native_charset): New.
+ (native_to_utf8): Now handles koi8-r.
+
+Tue Nov 3 16:17:56 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * strgutil.c (native_to_utf8): New.
+ (utf8_to_native): New, but only as a stub.
+
+ * argparse.c (optfile_parse): Trimmed spaces from args.
+
+
+Wed Oct 28 08:01:49 1998 me,,, (wk@tobold)
+
+ * argparse.c (find_long_option): New.
+ (arg_parse): option=value is now allowed. Add a new internal
+ option "--dump-options".
+
+Thu Oct 22 16:25:49 1998 Michael Roth (mroth@nessie.de)
+
+ * fileutil.c (make_basename): New.
+ (make_dirname): New.
+
+Wed Oct 21 12:20:29 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * util.c (iobuf_flush): autoincreasing of a temp. iobuf
+ (iobuf_temp_with_content): New.
+
+Tue Oct 13 12:40:13 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * util.c (.nofast): set this variable
+
+Wed Oct 7 19:27:50 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * memory.c (m_print_stats): New.
+
+Tue Oct 6 09:53:56 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * strgutil.c (memicmp): Add HAVE_MEMICMP.
+
+Mon Sep 21 19:45:01 1998 Werner Koch (wk@(none))
+
+ * secmem.c: New flags to allow suspend/resume of warnings.
+
+Fri Sep 18 16:25:47 1998 Werner Koch (wk@(none))
+
+ * secmem.c (lock_pool): Kludge for broken mlock on HPUX 10.20
+
+Tue Sep 15 17:52:21 1998 Werner Koch (wk@(none))
+
+ * miscutil.c (asctimestamp): New.
+
+Mon Sep 14 09:38:18 1998 Werner Koch (wk@(none))
+
+ * secmem.c (init_pool): Now mmaps /dev/zero if we do not have MAP_ANON.
+
+Wed Sep 9 13:52:28 1998 Werner Koch (wk@(none))
+
+ * ttyio.c (do_get): Ctrl-D is now a valid but special character
+
+Mon Sep 7 13:52:41 1998 Werner Koch (wk@(none))
+
+ * iobuf.c (get_real_fname): New and changed file_filter datastructures
+ and their initialization.
+
+Tue Aug 11 15:12:35 1998 Werner Koch (wk@(none))
+
+ * miscutil.c (answer_is_yes): i18ned
+
+Sat Aug 8 18:35:00 1998 Werner Koch (wk@(none))
+
+ * ttyio.c (cleanup): New.
+
+Mon Aug 3 17:06:00 1998 Werner Koch (wk@(none))
+
+ * secmem.c (MAP_ANON): Add a macro test
+
+Wed Jul 29 14:53:34 1998 Werner Koch (wk@(none))
+
+ * ttyio.c (tty_get_answer_is_yes): New.
+
+Tue Jul 21 10:35:48 1998 Werner Koch (wk@(none))
+
+ * argparse.c: New option flag to distinguish options and commands.
+
+Sat Jul 18 19:49:30 1998 Werner Koch (wk@(none))
+
+ * argparse.c (arg_parse): Added -? as alias for -h
+
+Thu Jul 9 14:47:20 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * secmem.c (secmem_init): Drops setuid if called with 0.
+
+Tue Jul 7 11:49:25 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * logger.c (log_set_filename): New.
+
+Mon Jul 6 09:03:49 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * strgutil.c (append_to_strlist): New.
+
+Thu Jul 2 15:55:44 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * iobuf.c (block_filter): Add writing of OP partial length headers.
+
+Fri Jun 26 10:38:35 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * ttyio.c (do_get): all iso8859-1 characters are now allowed.
+
+Thu Jun 25 15:57:21 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * secmem.c (lock_pool): Removed left over test code.
+
+Wed Jun 10 07:39:41 1998 Werner Koch,mobil,,, (wk@tobold)
+
+ * fileutil.c (compare_filenames): New.
+
+ * argparse.c (arg_parse): New flag bit 6 to ignore --version
+
+Thu May 14 16:45:13 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * argparse.c (show_help): Add some formatting stuff
+
+Fri May 8 17:06:49 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * errors.c (strerror): New if !HAVE_STRERROR
+
+Mon May 4 19:48:03 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * iobuf.c (iobuf_read): Code is now faster.
+ * (iobuf_write): ditto.
+
+Mon Apr 27 11:01:32 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * strgutil.c (memicmp): New.
+
+Thu Mar 19 11:29:03 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * strgutil.c (memistr): Add const to return and first arg.
+
+Sat Mar 7 11:54:35 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * miscutil.c (print_string): New arg delim; changed all callers.
+
+Thu Mar 5 12:19:30 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * errors.c: New strings.
+
+Thu Mar 5 12:06:31 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * iobuf.c (iobuf_open): A name of "-" now opens stdin.
+ * fileutil.c (print_fname_stdout, print_fname_stdin): New.
+
+Fri Feb 27 10:20:03 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * memory.c (m_is_secure): Removed.
+ * secmem.c (m_is_secure): Moved to here.
+
+ * secmem.c (secmem_realloc): New.
+ * memory.c (M_GUARD,EXTRA_ALIGN): New (all functions).
+
+Thu Feb 26 14:36:51 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * secmem.c (lock_pool): No error if EAGAIN is returned instead
+ of EPERM.
+
+Fri Feb 20 17:43:05 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * ttyio.c [MINGW32]: Add support for mingw32.
+
+Tue Feb 17 19:43:44 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * memory.c (dump_table_at_exit): New.
+
+Mon Feb 16 10:07:28 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * argparse.c (show_version, show_help, default_strusage): Changed
+ according to GNU standards.
+
+Mon Feb 16 08:58:25 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * iobuf.c (iobuf_peek): New
+
+Fri Feb 13 19:34:59 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * iobuf.c (iobuf_seek): Set counters to new offset.
+
+Fri Feb 13 17:13:04 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * logger.c (log_set_name, log_get_name): New.
+ (print_prefix, pgm_name): New, changed all function to make use it.
+ (log_mpidump): Removed the "DBG" prefix.
+ (log_hexdump): Ditto.
+
+ * logger.c (printstr): Removed.
+
+Fri Feb 13 15:14:13 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * argparse.c (show_help): New '\v' kludge.
+
+
+
+ Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+ 2005 Free Software Foundation, Inc.
+
+ This file is free software; as a special exception the author gives
+ unlimited permission to copy and/or distribute it, with or without
+ modifications, as long as this notice is preserved.
+
+ This file is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
+ implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+
diff --git a/util/Makefile.am b/util/Makefile.am
new file mode 100644
index 0000000..1da5a10
--- /dev/null
+++ b/util/Makefile.am
@@ -0,0 +1,82 @@
+# Copyright (C) 1998, 1999, 2000, 2001, 2005 Free Software Foundation, Inc.
+#
+# This file is part of GnuPG.
+#
+# GnuPG is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# GnuPG is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+
+## Process this file with automake to produce Makefile.in
+
+INCLUDES = -I.. -I$(top_srcdir)/include -I$(top_srcdir)/intl
+
+noinst_LIBRARIES = libutil.a libcompat.a
+
+libutil_a_SOURCES = logger.c fileutil.c miscutil.c strgutil.c \
+ ttyio.c argparse.c memory.c secmem.c errors.c iobuf.c \
+ dotlock.c http.c pka.c membuf.c cert.c \
+ $(libcompat_a_SOURCES)
+
+if USE_SIMPLE_GETTEXT
+libutil_a_SOURCES+=simple-gettext.c
+endif
+
+if HAVE_W32_SYSTEM
+libutil_a_SOURCES+=w32reg.c
+endif
+
+if ENABLE_AGENT_SUPPORT
+libutil_a_SOURCES+=assuan-buffer.c assuan-client.c assuan-defs.h \
+ assuan-errors.c assuan-logging.c assuan-socket-connect.c \
+ assuan-connect.c assuan-socket.c assuan-util.c
+endif
+
+if USE_INTERNAL_REGEX
+libutil_a_SOURCES+=regex.c
+endif
+
+if USE_DNS_SRV
+libutil_a_SOURCES+=srv.c srv.h
+endif
+
+# The internal regex code #includes these.
+EXTRA_libutil_a_SOURCES = regcomp.c regexec.c regex_internal.c \
+ regex_internal.h
+
+# LIBOBJS and libcompat.a are for the replacement functions and
+# similar simple stuff. They're segregated in libcompat so we can
+# link it to the keyserver helpers which have different licensing.
+# libutil.a, by definition, includes everything that libcompat.a does.
+
+libutil_a_DEPENDENCIES = @LIBOBJS@
+libutil_a_LIBADD = @LIBOBJS@
+
+libcompat_a_SOURCES=compat.c
+libcompat_a_DEPENDENCIES = @LIBOBJS@
+libcompat_a_LIBADD = @LIBOBJS@
+
+http-test: http.c
+ cc -DHAVE_CONFIG_H -I. -I. -I.. $(INCLUDES) $(LDFLAGS) -g -Wall \
+ -DTEST -o http-test http.c libutil.a @LIBINTL@ @DNSLIBS@ @CAPLIBS@
+
+srv-test: srv.c
+ cc -DHAVE_CONFIG_H -I. -I. -I.. $(INCLUDES) $(LDFLAGS) -g -Wall \
+ -DTEST -o srv-test srv.c libutil.a @LIBINTL@ @DNSLIBS@ @CAPLIBS@
+
+pka-test: pka.c
+ cc -DHAVE_CONFIG_H -I. -I. -I.. $(INCLUDES) $(LDFLAGS) -g -Wall \
+ -DTEST -o pka-test pka.c libutil.a @LIBINTL@ @DNSLIBS@ @CAPLIBS@
+
+cert-test: cert.c
+ cc -DHAVE_CONFIG_H -I. -I. -I.. $(INCLUDES) $(LDFLAGS) -g -Wall \
+ -DTEST -o cert-test cert.c libutil.a @LIBINTL@ @DNSLIBS@ @CAPLIBS@
diff --git a/util/Makefile.in b/util/Makefile.in
new file mode 100644
index 0000000..2c41844
--- /dev/null
+++ b/util/Makefile.in
@@ -0,0 +1,641 @@
+# Makefile.in generated by automake 1.9.6 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Copyright (C) 1998, 1999, 2000, 2001, 2005 Free Software Foundation, Inc.
+#
+# This file is part of GnuPG.
+#
+# GnuPG is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# GnuPG is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+top_builddir = ..
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+INSTALL = @INSTALL@
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+@USE_SIMPLE_GETTEXT_TRUE@am__append_1 = simple-gettext.c
+@HAVE_W32_SYSTEM_TRUE@am__append_2 = w32reg.c
+@ENABLE_AGENT_SUPPORT_TRUE@am__append_3 = assuan-buffer.c assuan-client.c assuan-defs.h \
+@ENABLE_AGENT_SUPPORT_TRUE@ assuan-errors.c assuan-logging.c assuan-socket-connect.c \
+@ENABLE_AGENT_SUPPORT_TRUE@ assuan-connect.c assuan-socket.c assuan-util.c
+
+@USE_INTERNAL_REGEX_TRUE@am__append_4 = regex.c
+@USE_DNS_SRV_TRUE@am__append_5 = srv.c srv.h
+subdir = util
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in ChangeLog \
+ isascii.c memrchr.c mkdtemp.c strsep.c timegm.c
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/autobuild.m4 \
+ $(top_srcdir)/m4/codeset.m4 $(top_srcdir)/m4/gettext.m4 \
+ $(top_srcdir)/m4/glibc21.m4 $(top_srcdir)/m4/iconv.m4 \
+ $(top_srcdir)/m4/intdiv0.m4 $(top_srcdir)/m4/intmax.m4 \
+ $(top_srcdir)/m4/inttypes-pri.m4 $(top_srcdir)/m4/inttypes.m4 \
+ $(top_srcdir)/m4/inttypes_h.m4 $(top_srcdir)/m4/isc-posix.m4 \
+ $(top_srcdir)/m4/lcmessage.m4 $(top_srcdir)/m4/ldap.m4 \
+ $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \
+ $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libcurl.m4 \
+ $(top_srcdir)/m4/libusb.m4 $(top_srcdir)/m4/longdouble.m4 \
+ $(top_srcdir)/m4/longlong.m4 $(top_srcdir)/m4/nls.m4 \
+ $(top_srcdir)/m4/noexecstack.m4 $(top_srcdir)/m4/po.m4 \
+ $(top_srcdir)/m4/printf-posix.m4 $(top_srcdir)/m4/progtest.m4 \
+ $(top_srcdir)/m4/readline.m4 $(top_srcdir)/m4/signed.m4 \
+ $(top_srcdir)/m4/size_max.m4 $(top_srcdir)/m4/stdint_h.m4 \
+ $(top_srcdir)/m4/tar-ustar.m4 $(top_srcdir)/m4/uintmax_t.m4 \
+ $(top_srcdir)/m4/ulonglong.m4 $(top_srcdir)/m4/wchar_t.m4 \
+ $(top_srcdir)/m4/wint_t.m4 $(top_srcdir)/m4/xsize.m4 \
+ $(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(SHELL) $(top_srcdir)/scripts/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+LIBRARIES = $(noinst_LIBRARIES)
+ARFLAGS = cru
+libcompat_a_AR = $(AR) $(ARFLAGS)
+am_libcompat_a_OBJECTS = compat.$(OBJEXT)
+libcompat_a_OBJECTS = $(am_libcompat_a_OBJECTS)
+libutil_a_AR = $(AR) $(ARFLAGS)
+am__libutil_a_SOURCES_DIST = logger.c fileutil.c miscutil.c strgutil.c \
+ ttyio.c argparse.c memory.c secmem.c errors.c iobuf.c \
+ dotlock.c http.c pka.c membuf.c cert.c compat.c \
+ simple-gettext.c w32reg.c assuan-buffer.c assuan-client.c \
+ assuan-defs.h assuan-errors.c assuan-logging.c \
+ assuan-socket-connect.c assuan-connect.c assuan-socket.c \
+ assuan-util.c regex.c srv.c srv.h
+am__objects_1 = compat.$(OBJEXT)
+@USE_SIMPLE_GETTEXT_TRUE@am__objects_2 = simple-gettext.$(OBJEXT)
+@HAVE_W32_SYSTEM_TRUE@am__objects_3 = w32reg.$(OBJEXT)
+@ENABLE_AGENT_SUPPORT_TRUE@am__objects_4 = assuan-buffer.$(OBJEXT) \
+@ENABLE_AGENT_SUPPORT_TRUE@ assuan-client.$(OBJEXT) \
+@ENABLE_AGENT_SUPPORT_TRUE@ assuan-errors.$(OBJEXT) \
+@ENABLE_AGENT_SUPPORT_TRUE@ assuan-logging.$(OBJEXT) \
+@ENABLE_AGENT_SUPPORT_TRUE@ assuan-socket-connect.$(OBJEXT) \
+@ENABLE_AGENT_SUPPORT_TRUE@ assuan-connect.$(OBJEXT) \
+@ENABLE_AGENT_SUPPORT_TRUE@ assuan-socket.$(OBJEXT) \
+@ENABLE_AGENT_SUPPORT_TRUE@ assuan-util.$(OBJEXT)
+@USE_INTERNAL_REGEX_TRUE@am__objects_5 = regex.$(OBJEXT)
+@USE_DNS_SRV_TRUE@am__objects_6 = srv.$(OBJEXT)
+am_libutil_a_OBJECTS = logger.$(OBJEXT) fileutil.$(OBJEXT) \
+ miscutil.$(OBJEXT) strgutil.$(OBJEXT) ttyio.$(OBJEXT) \
+ argparse.$(OBJEXT) memory.$(OBJEXT) secmem.$(OBJEXT) \
+ errors.$(OBJEXT) iobuf.$(OBJEXT) dotlock.$(OBJEXT) \
+ http.$(OBJEXT) pka.$(OBJEXT) membuf.$(OBJEXT) cert.$(OBJEXT) \
+ $(am__objects_1) $(am__objects_2) $(am__objects_3) \
+ $(am__objects_4) $(am__objects_5) $(am__objects_6)
+libutil_a_OBJECTS = $(am_libutil_a_OBJECTS)
+DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/scripts/depcomp
+am__depfiles_maybe = depfiles
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+SOURCES = $(libcompat_a_SOURCES) $(libutil_a_SOURCES) \
+ $(EXTRA_libutil_a_SOURCES)
+DIST_SOURCES = $(libcompat_a_SOURCES) $(am__libutil_a_SOURCES_DIST) \
+ $(EXTRA_libutil_a_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+AMDEP_FALSE = @AMDEP_FALSE@
+AMDEP_TRUE = @AMDEP_TRUE@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BUILD_INCLUDED_LIBINTL = @BUILD_INCLUDED_LIBINTL@
+CAPLIBS = @CAPLIBS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CROSS_COMPILING_FALSE = @CROSS_COMPILING_FALSE@
+CROSS_COMPILING_TRUE = @CROSS_COMPILING_TRUE@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLIBS = @DLLIBS@
+DNSLIBS = @DNSLIBS@
+DOCBOOK_TO_MAN = @DOCBOOK_TO_MAN@
+DOCBOOK_TO_TEXI = @DOCBOOK_TO_TEXI@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+ENABLE_AGENT_SUPPORT_FALSE = @ENABLE_AGENT_SUPPORT_FALSE@
+ENABLE_AGENT_SUPPORT_TRUE = @ENABLE_AGENT_SUPPORT_TRUE@
+ENABLE_BZIP2_SUPPORT_FALSE = @ENABLE_BZIP2_SUPPORT_FALSE@
+ENABLE_BZIP2_SUPPORT_TRUE = @ENABLE_BZIP2_SUPPORT_TRUE@
+ENABLE_CARD_SUPPORT_FALSE = @ENABLE_CARD_SUPPORT_FALSE@
+ENABLE_CARD_SUPPORT_TRUE = @ENABLE_CARD_SUPPORT_TRUE@
+ENABLE_LOCAL_ZLIB_FALSE = @ENABLE_LOCAL_ZLIB_FALSE@
+ENABLE_LOCAL_ZLIB_TRUE = @ENABLE_LOCAL_ZLIB_TRUE@
+EXEEXT = @EXEEXT@
+FAKE_CURL_FALSE = @FAKE_CURL_FALSE@
+FAKE_CURL_TRUE = @FAKE_CURL_TRUE@
+FAQPROG = @FAQPROG@
+GENCAT = @GENCAT@
+GETOPT = @GETOPT@
+GLIBC21 = @GLIBC21@
+GMSGFMT = @GMSGFMT@
+GPGKEYS_CURL = @GPGKEYS_CURL@
+GPGKEYS_FINGER = @GPGKEYS_FINGER@
+GPGKEYS_HKP = @GPGKEYS_HKP@
+GPGKEYS_LDAP = @GPGKEYS_LDAP@
+GPGKEYS_MAILTO = @GPGKEYS_MAILTO@
+GREP = @GREP@
+HAVE_ASPRINTF = @HAVE_ASPRINTF@
+HAVE_DOCBOOK_TO_MAN_FALSE = @HAVE_DOCBOOK_TO_MAN_FALSE@
+HAVE_DOCBOOK_TO_MAN_TRUE = @HAVE_DOCBOOK_TO_MAN_TRUE@
+HAVE_DOCBOOK_TO_TEXI_FALSE = @HAVE_DOCBOOK_TO_TEXI_FALSE@
+HAVE_DOCBOOK_TO_TEXI_TRUE = @HAVE_DOCBOOK_TO_TEXI_TRUE@
+HAVE_DOSISH_SYSTEM_FALSE = @HAVE_DOSISH_SYSTEM_FALSE@
+HAVE_DOSISH_SYSTEM_TRUE = @HAVE_DOSISH_SYSTEM_TRUE@
+HAVE_POSIX_PRINTF = @HAVE_POSIX_PRINTF@
+HAVE_SNPRINTF = @HAVE_SNPRINTF@
+HAVE_USTAR_FALSE = @HAVE_USTAR_FALSE@
+HAVE_USTAR_TRUE = @HAVE_USTAR_TRUE@
+HAVE_W32_SYSTEM_FALSE = @HAVE_W32_SYSTEM_FALSE@
+HAVE_W32_SYSTEM_TRUE = @HAVE_W32_SYSTEM_TRUE@
+HAVE_WPRINTF = @HAVE_WPRINTF@
+IDEA_O = @IDEA_O@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLBISON = @INTLBISON@
+INTLLIBS = @INTLLIBS@
+INTLOBJS = @INTLOBJS@
+INTL_LIBTOOL_SUFFIX_PREFIX = @INTL_LIBTOOL_SUFFIX_PREFIX@
+LDAPLIBS = @LDAPLIBS@
+LDAP_CPPFLAGS = @LDAP_CPPFLAGS@
+LDFLAGS = @LDFLAGS@
+LIBCURL = @LIBCURL@
+LIBCURL_CPPFLAGS = @LIBCURL_CPPFLAGS@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBOBJS = @LIBOBJS@
+LIBREADLINE = @LIBREADLINE@
+LIBS = @LIBS@
+LIBUSB = @LIBUSB@
+LIBUSB_CPPFLAGS = @LIBUSB_CPPFLAGS@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@
+MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@
+MAKEINFO = @MAKEINFO@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MPI_EXTRA_ASM_OBJS = @MPI_EXTRA_ASM_OBJS@
+MPI_OPT_FLAGS = @MPI_OPT_FLAGS@
+MPI_SFLAGS = @MPI_SFLAGS@
+MSGFMT = @MSGFMT@
+MSGMERGE = @MSGMERGE@
+NETLIBS = @NETLIBS@
+NM = @NM@
+NOEXECSTACK_FLAGS = @NOEXECSTACK_FLAGS@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+POSUB = @POSUB@
+RANLIB = @RANLIB@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+TAR = @TAR@
+USE_DNS_SRV_FALSE = @USE_DNS_SRV_FALSE@
+USE_DNS_SRV_TRUE = @USE_DNS_SRV_TRUE@
+USE_INCLUDED_LIBINTL = @USE_INCLUDED_LIBINTL@
+USE_INTERNAL_REGEX_FALSE = @USE_INTERNAL_REGEX_FALSE@
+USE_INTERNAL_REGEX_TRUE = @USE_INTERNAL_REGEX_TRUE@
+USE_NLS = @USE_NLS@
+USE_RNDEGD_FALSE = @USE_RNDEGD_FALSE@
+USE_RNDEGD_TRUE = @USE_RNDEGD_TRUE@
+USE_RNDLINUX_FALSE = @USE_RNDLINUX_FALSE@
+USE_RNDLINUX_TRUE = @USE_RNDLINUX_TRUE@
+USE_RNDUNIX_FALSE = @USE_RNDUNIX_FALSE@
+USE_RNDUNIX_TRUE = @USE_RNDUNIX_TRUE@
+USE_RNDW32_FALSE = @USE_RNDW32_FALSE@
+USE_RNDW32_TRUE = @USE_RNDW32_TRUE@
+USE_SHA512_FALSE = @USE_SHA512_FALSE@
+USE_SHA512_TRUE = @USE_SHA512_TRUE@
+USE_SIMPLE_GETTEXT_FALSE = @USE_SIMPLE_GETTEXT_FALSE@
+USE_SIMPLE_GETTEXT_TRUE = @USE_SIMPLE_GETTEXT_TRUE@
+VERSION = @VERSION@
+W32LIBS = @W32LIBS@
+WORKING_FAQPROG_FALSE = @WORKING_FAQPROG_FALSE@
+WORKING_FAQPROG_TRUE = @WORKING_FAQPROG_TRUE@
+XGETTEXT = @XGETTEXT@
+ZLIBS = @ZLIBS@
+_libcurl_config = @_libcurl_config@
+_usb_config = @_usb_config@
+ac_ct_CC = @ac_ct_CC@
+am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
+am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+INCLUDES = -I.. -I$(top_srcdir)/include -I$(top_srcdir)/intl
+noinst_LIBRARIES = libutil.a libcompat.a
+libutil_a_SOURCES = logger.c fileutil.c miscutil.c strgutil.c ttyio.c \
+ argparse.c memory.c secmem.c errors.c iobuf.c dotlock.c http.c \
+ pka.c membuf.c cert.c $(libcompat_a_SOURCES) $(am__append_1) \
+ $(am__append_2) $(am__append_3) $(am__append_4) \
+ $(am__append_5)
+
+# The internal regex code #includes these.
+EXTRA_libutil_a_SOURCES = regcomp.c regexec.c regex_internal.c \
+ regex_internal.h
+
+
+# LIBOBJS and libcompat.a are for the replacement functions and
+# similar simple stuff. They're segregated in libcompat so we can
+# link it to the keyserver helpers which have different licensing.
+# libutil.a, by definition, includes everything that libcompat.a does.
+libutil_a_DEPENDENCIES = @LIBOBJS@
+libutil_a_LIBADD = @LIBOBJS@
+libcompat_a_SOURCES = compat.c
+libcompat_a_DEPENDENCIES = @LIBOBJS@
+libcompat_a_LIBADD = @LIBOBJS@
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu util/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --gnu util/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+clean-noinstLIBRARIES:
+ -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES)
+libcompat.a: $(libcompat_a_OBJECTS) $(libcompat_a_DEPENDENCIES)
+ -rm -f libcompat.a
+ $(libcompat_a_AR) libcompat.a $(libcompat_a_OBJECTS) $(libcompat_a_LIBADD)
+ $(RANLIB) libcompat.a
+libutil.a: $(libutil_a_OBJECTS) $(libutil_a_DEPENDENCIES)
+ -rm -f libutil.a
+ $(libutil_a_AR) libutil.a $(libutil_a_OBJECTS) $(libutil_a_LIBADD)
+ $(RANLIB) libutil.a
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/isascii.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/memrchr.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/mkdtemp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/strsep.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/timegm.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/argparse.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/assuan-buffer.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/assuan-client.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/assuan-connect.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/assuan-errors.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/assuan-logging.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/assuan-socket-connect.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/assuan-socket.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/assuan-util.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cert.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/compat.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dotlock.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/errors.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fileutil.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iobuf.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/logger.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/membuf.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memory.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/miscutil.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pka.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/regcomp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/regex.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/regex_internal.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/regexec.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/secmem.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/simple-gettext.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/srv.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strgutil.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ttyio.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/w32reg.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+uninstall-info-am:
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
+ list='$(DISTFILES)'; for file in $$list; do \
+ case $$file in \
+ $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
+ $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
+ esac; \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+ dir="/$$dir"; \
+ $(mkdir_p) "$(distdir)$$dir"; \
+ else \
+ dir=''; \
+ fi; \
+ if test -d $$d/$$file; then \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LIBRARIES)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf $(DEPDIR) ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-exec-am:
+
+install-info: install-info-am
+
+install-man:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf $(DEPDIR) ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-info-am
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ clean-noinstLIBRARIES ctags distclean distclean-compile \
+ distclean-generic distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-exec install-exec-am install-info \
+ install-info-am install-man install-strip installcheck \
+ installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \
+ uninstall-am uninstall-info-am
+
+
+http-test: http.c
+ cc -DHAVE_CONFIG_H -I. -I. -I.. $(INCLUDES) $(LDFLAGS) -g -Wall \
+ -DTEST -o http-test http.c libutil.a @LIBINTL@ @DNSLIBS@ @CAPLIBS@
+
+srv-test: srv.c
+ cc -DHAVE_CONFIG_H -I. -I. -I.. $(INCLUDES) $(LDFLAGS) -g -Wall \
+ -DTEST -o srv-test srv.c libutil.a @LIBINTL@ @DNSLIBS@ @CAPLIBS@
+
+pka-test: pka.c
+ cc -DHAVE_CONFIG_H -I. -I. -I.. $(INCLUDES) $(LDFLAGS) -g -Wall \
+ -DTEST -o pka-test pka.c libutil.a @LIBINTL@ @DNSLIBS@ @CAPLIBS@
+
+cert-test: cert.c
+ cc -DHAVE_CONFIG_H -I. -I. -I.. $(INCLUDES) $(LDFLAGS) -g -Wall \
+ -DTEST -o cert-test cert.c libutil.a @LIBINTL@ @DNSLIBS@ @CAPLIBS@
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/util/argparse.c b/util/argparse.c
new file mode 100644
index 0000000..cdc56bf
--- /dev/null
+++ b/util/argparse.c
@@ -0,0 +1,1004 @@
+/* [argparse.c wk 17.06.97] Argument Parser for option handling
+ * Copyright (C) 1998, 1999, 2000, 2001, 2003,
+ * 2004 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ *
+ * Note: This is an independent version of the one in WkLib
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "util.h"
+#include "i18n.h"
+
+
+/*********************************
+ * @Summary arg_parse
+ * #include <wk/lib.h>
+ *
+ * typedef struct {
+ * char *argc; pointer to argc (value subject to change)
+ * char ***argv; pointer to argv (value subject to change)
+ * unsigned flags; Global flags (DO NOT CHANGE)
+ * int err; print error about last option
+ * 1 = warning, 2 = abort
+ * int r_opt; return option
+ * int r_type; type of return value (0 = no argument found)
+ * union {
+ * int ret_int;
+ * long ret_long
+ * ulong ret_ulong;
+ * char *ret_str;
+ * } r; Return values
+ * struct {
+ * int idx;
+ * const char *last;
+ * void *aliases;
+ * } internal; DO NOT CHANGE
+ * } ARGPARSE_ARGS;
+ *
+ * typedef struct {
+ * int short_opt;
+ * const char *long_opt;
+ * unsigned flags;
+ * } ARGPARSE_OPTS;
+ *
+ * int arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts );
+ *
+ * @Description
+ * This is my replacement for getopt(). See the example for a typical usage.
+ * Global flags are:
+ * Bit 0 : Do not remove options form argv
+ * Bit 1 : Do not stop at last option but return other args
+ * with r_opt set to -1.
+ * Bit 2 : Assume options and real args are mixed.
+ * Bit 3 : Do not use -- to stop option processing.
+ * Bit 4 : Do not skip the first arg.
+ * Bit 5 : allow usage of long option with only one dash
+ * Bit 6 : ignore --version and --help
+ * all other bits must be set to zero, this value is modified by the
+ * function, so assume this is write only.
+ * Local flags (for each option):
+ * Bit 2-0 : 0 = does not take an argument
+ * 1 = takes int argument
+ * 2 = takes string argument
+ * 3 = takes long argument
+ * 4 = takes ulong argument
+ * Bit 3 : argument is optional (r_type will the be set to 0)
+ * Bit 4 : allow 0x etc. prefixed values.
+ * Bit 7 : this is a command and not an option
+ * You stop the option processing by setting opts to NULL, the function will
+ * then return 0.
+ * @Return Value
+ * Returns the args.r_opt or 0 if ready
+ * r_opt may be -2/-7 to indicate an unknown option/command.
+ * @See Also
+ * ArgExpand
+ * @Notes
+ * You do not need to process the options 'h', '--help' or '--version'
+ * because this function includes standard help processing; but if you
+ * specify '-h', '--help' or '--version' you have to do it yourself.
+ * The option '--' stops argument processing; if bit 1 is set the function
+ * continues to return normal arguments.
+ * To process float args or unsigned args you must use a string args and do
+ * the conversion yourself.
+ * @Example
+ *
+ * ARGPARSE_OPTS opts[] = {
+ * { 'v', "verbose", 0 },
+ * { 'd', "debug", 0 },
+ * { 'o', "output", 2 },
+ * { 'c', "cross-ref", 2|8 },
+ * { 'm', "my-option", 1|8 },
+ * { 500, "have-no-short-option-for-this-long-option", 0 },
+ * {0} };
+ * ARGPARSE_ARGS pargs = { &argc, &argv, 0 }
+ *
+ * while( ArgParse( &pargs, &opts) ) {
+ * switch( pargs.r_opt ) {
+ * case 'v': opt.verbose++; break;
+ * case 'd': opt.debug++; break;
+ * case 'o': opt.outfile = pargs.r.ret_str; break;
+ * case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break;
+ * case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break;
+ * case 500: opt.a_long_one++; break
+ * default : pargs.err = 1; break; -- force warning output --
+ * }
+ * }
+ * if( argc > 1 )
+ * log_fatal( "Too many args");
+ *
+ */
+
+typedef struct alias_def_s *ALIAS_DEF;
+struct alias_def_s {
+ ALIAS_DEF next;
+ char *name; /* malloced buffer with name, \0, value */
+ const char *value; /* ptr into name */
+};
+
+static int set_opt_arg(ARGPARSE_ARGS *arg, unsigned flags, char *s);
+static void show_help(ARGPARSE_OPTS *opts, unsigned flags);
+static void show_version(void);
+
+static void
+initialize( ARGPARSE_ARGS *arg, const char *filename, unsigned *lineno )
+{
+ if( !(arg->flags & (1<<15)) ) { /* initialize this instance */
+ arg->internal.idx = 0;
+ arg->internal.last = NULL;
+ arg->internal.inarg = 0;
+ arg->internal.stopped = 0;
+ arg->internal.aliases = NULL;
+ arg->internal.cur_alias = NULL;
+ arg->err = 0;
+ arg->flags |= 1<<15; /* mark initialized */
+ if( *arg->argc < 0 )
+ log_bug("Invalid argument for ArgParse\n");
+ }
+
+
+ if( arg->err ) { /* last option was erroneous */
+
+ if( filename ) {
+ if( arg->r_opt == -6 )
+ log_error("%s:%u: argument not expected\n", filename, *lineno );
+ else if( arg->r_opt == -5 )
+ log_error("%s:%u: read error\n", filename, *lineno );
+ else if( arg->r_opt == -4 )
+ log_error("%s:%u: keyword too long\n", filename, *lineno );
+ else if( arg->r_opt == -3 )
+ log_error("%s:%u: missing argument\n", filename, *lineno );
+ else if( arg->r_opt == -7 )
+ log_error("%s:%u: invalid command\n", filename, *lineno );
+ else if( arg->r_opt == -10 )
+ log_error("%s:%u: invalid alias definition\n",filename,*lineno);
+ else
+ log_error("%s:%u: invalid option\n", filename, *lineno );
+ }
+ else {
+ if( arg->r_opt == -3 )
+ log_error("Missing argument for option \"%.50s\"\n",
+ arg->internal.last? arg->internal.last:"[??]" );
+ else if( arg->r_opt == -6 )
+ log_error("Option \"%.50s\" does not expect an argument\n",
+ arg->internal.last? arg->internal.last:"[??]" );
+ else if( arg->r_opt == -7 )
+ log_error("Invalid command \"%.50s\"\n",
+ arg->internal.last? arg->internal.last:"[??]" );
+ else if( arg->r_opt == -8 )
+ log_error("Option \"%.50s\" is ambiguous\n",
+ arg->internal.last? arg->internal.last:"[??]" );
+ else if( arg->r_opt == -9 )
+ log_error("Command \"%.50s\" is ambiguous\n",
+ arg->internal.last? arg->internal.last:"[??]" );
+ else
+ log_error("Invalid option \"%.50s\"\n",
+ arg->internal.last? arg->internal.last:"[??]" );
+ }
+ if( arg->err != 1 || arg->r_opt == -5 )
+ exit(2);
+ arg->err = 0;
+ }
+
+ /* clearout the return value union */
+ arg->r.ret_str = NULL;
+ arg->r.ret_long= 0;
+}
+
+
+static void
+store_alias( ARGPARSE_ARGS *arg, char *name, char *value )
+{
+ /* TODO: replace this dummy function with a rea one
+ * and fix the probelms IRIX has with (ALIAS_DEV)arg..
+ * used as lvalue
+ */
+#if 0
+ ALIAS_DEF a = xmalloc( sizeof *a );
+ a->name = name;
+ a->value = value;
+ a->next = (ALIAS_DEF)arg->internal.aliases;
+ (ALIAS_DEF)arg->internal.aliases = a;
+#endif
+}
+
+/****************
+ * Get options from a file.
+ * Lines starting with '#' are comment lines.
+ * Syntax is simply a keyword and the argument.
+ * Valid keywords are all keywords from the long_opt list without
+ * the leading dashes. The special keywords "help", "warranty" and "version"
+ * are not valid here.
+ * The special keyword "alias" may be used to store alias definitions,
+ * which are later expanded like long options.
+ * Caller must free returned strings.
+ * If called with FP set to NULL command line args are parse instead.
+ *
+ * Q: Should we allow the syntax
+ * keyword = value
+ * and accept for boolean options a value of 1/0, yes/no or true/false?
+ * Note: Abbreviation of options is here not allowed.
+ */
+int
+optfile_parse( FILE *fp, const char *filename, unsigned *lineno,
+ ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
+{
+ int state, i, c;
+ int idx=0;
+ char keyword[100];
+ char *buffer = NULL;
+ size_t buflen = 0;
+ int inverse=0;
+ int in_alias=0;
+
+ if( !fp ) /* same as arg_parse() in this case */
+ return arg_parse( arg, opts );
+
+ initialize( arg, filename, lineno );
+
+ /* find the next keyword */
+ state = i = 0;
+ for(;;) {
+ c=getc(fp);
+ if( c == '\n' || c== EOF ) {
+ if( c != EOF )
+ ++*lineno;
+ if( state == -1 )
+ break;
+ else if( state == 2 ) {
+ keyword[i] = 0;
+ for(i=0; opts[i].short_opt; i++ )
+ if( opts[i].long_opt && !strcmp( opts[i].long_opt, keyword) )
+ break;
+ idx = i;
+ arg->r_opt = opts[idx].short_opt;
+ if( inverse ) /* this does not have an effect, hmmm */
+ arg->r_opt = -arg->r_opt;
+ if( !opts[idx].short_opt ) /* unknown command/option */
+ arg->r_opt = (opts[idx].flags & 256)? -7:-2;
+ else if( !(opts[idx].flags & 7) ) /* does not take an arg */
+ arg->r_type = 0; /* okay */
+ else if( (opts[idx].flags & 8) ) /* argument is optional */
+ arg->r_type = 0; /* okay */
+ else /* required argument */
+ arg->r_opt = -3; /* error */
+ break;
+ }
+ else if( state == 3 ) { /* no argument found */
+ if( in_alias )
+ arg->r_opt = -3; /* error */
+ else if( !(opts[idx].flags & 7) ) /* does not take an arg */
+ arg->r_type = 0; /* okay */
+ else if( (opts[idx].flags & 8) ) /* no optional argument */
+ arg->r_type = 0; /* okay */
+ else /* no required argument */
+ arg->r_opt = -3; /* error */
+ break;
+ }
+ else if( state == 4 ) { /* have an argument */
+ if( in_alias ) {
+ if( !buffer )
+ arg->r_opt = -6;
+ else {
+ char *p;
+
+ buffer[i] = 0;
+ p = strpbrk( buffer, " \t" );
+ if( p ) {
+ *p++ = 0;
+ trim_spaces( p );
+ }
+ if( !p || !*p ) {
+ xfree( buffer );
+ arg->r_opt = -10;
+ }
+ else {
+ store_alias( arg, buffer, p );
+ }
+ }
+ }
+ else if( !(opts[idx].flags & 7) ) /* does not take an arg */
+ arg->r_opt = -6; /* error */
+ else {
+ char *p;
+ if( !buffer ) {
+ keyword[i] = 0;
+ buffer = xstrdup(keyword);
+ }
+ else
+ buffer[i] = 0;
+
+ trim_spaces( buffer );
+ p = buffer;
+ /* remove quotes if they totally enclose the
+ string, and do not occur within the string */
+ if( *p == '"' && p[strlen(p)-1]=='"') {
+ char *p2=p;
+
+ while(*(++p2))
+ if(*p2=='"')
+ break;
+
+ if(*p2=='"' && *(p2+1)=='\0') {
+ p[strlen(p)-1] = 0;
+ p++;
+ }
+ }
+ if( !set_opt_arg(arg, opts[idx].flags, p) )
+ xfree(buffer);
+ }
+ break;
+ }
+ else if( c == EOF ) {
+ if( ferror(fp) )
+ arg->r_opt = -5; /* read error */
+ else
+ arg->r_opt = 0; /* eof */
+ break;
+ }
+ state = 0;
+ i = 0;
+ }
+ else if( state == -1 )
+ ; /* skip */
+ else if( !state && isspace(c) )
+ ; /* skip leading white space */
+ else if( !state && c == '#' )
+ state = 1; /* start of a comment */
+ else if( state == 1 )
+ ; /* skip comments */
+ else if( state == 2 && isspace(c) ) {
+ keyword[i] = 0;
+ for(i=0; opts[i].short_opt; i++ )
+ if( opts[i].long_opt && !strcmp( opts[i].long_opt, keyword) )
+ break;
+ idx = i;
+ arg->r_opt = opts[idx].short_opt;
+ if( !opts[idx].short_opt ) {
+ if( !strcmp( keyword, "alias" ) ) {
+ in_alias = 1;
+ state = 3;
+ }
+ else {
+ arg->r_opt = (opts[idx].flags & 256)? -7:-2;
+ state = -1; /* skip rest of line and leave */
+ }
+ }
+ else
+ state = 3;
+ }
+ else if( state == 3 ) { /* skip leading spaces of the argument */
+ if( !isspace(c) ) {
+ i = 0;
+ keyword[i++] = c;
+ state = 4;
+ }
+ }
+ else if( state == 4 ) { /* collect the argument */
+ if( buffer ) {
+ if( i < buflen-1 )
+ buffer[i++] = c;
+ else {
+ buflen += 50;
+ buffer = xrealloc(buffer, buflen);
+ buffer[i++] = c;
+ }
+ }
+ else if( i < DIM(keyword)-1 )
+ keyword[i++] = c;
+ else {
+ buflen = DIM(keyword)+50;
+ buffer = xmalloc(buflen);
+ memcpy(buffer, keyword, i);
+ buffer[i++] = c;
+ }
+ }
+ else if( i >= DIM(keyword)-1 ) {
+ arg->r_opt = -4; /* keyword to long */
+ state = -1; /* skip rest of line and leave */
+ }
+ else {
+ keyword[i++] = c;
+ state = 2;
+ }
+ }
+
+ return arg->r_opt;
+}
+
+
+
+static int
+find_long_option( ARGPARSE_ARGS *arg,
+ ARGPARSE_OPTS *opts, const char *keyword )
+{
+ int i;
+ size_t n;
+
+ /* Would be better if we can do a binary search, but it is not
+ possible to reorder our option table because we would mess
+ up our help strings - What we can do is: Build a nice option
+ lookup table wehn this function is first invoked */
+ if( !*keyword )
+ return -1;
+ for(i=0; opts[i].short_opt; i++ )
+ if( opts[i].long_opt && !strcmp( opts[i].long_opt, keyword) )
+ return i;
+#if 0
+ {
+ ALIAS_DEF a;
+ /* see whether it is an alias */
+ for( a = args->internal.aliases; a; a = a->next ) {
+ if( !strcmp( a->name, keyword) ) {
+ /* todo: must parse the alias here */
+ args->internal.cur_alias = a;
+ return -3; /* alias available */
+ }
+ }
+ }
+#endif
+ /* not found, see whether it is an abbreviation */
+ /* aliases may not be abbreviated */
+ n = strlen( keyword );
+ for(i=0; opts[i].short_opt; i++ ) {
+ if( opts[i].long_opt && !strncmp( opts[i].long_opt, keyword, n ) ) {
+ int j;
+ for(j=i+1; opts[j].short_opt; j++ ) {
+ if( opts[j].long_opt
+ && !strncmp( opts[j].long_opt, keyword, n ) )
+ return -2; /* abbreviation is ambiguous */
+ }
+ return i;
+ }
+ }
+ return -1;
+}
+
+int
+arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
+{
+ int idx;
+ int argc;
+ char **argv;
+ char *s, *s2;
+ int i;
+
+ initialize( arg, NULL, NULL );
+ argc = *arg->argc;
+ argv = *arg->argv;
+ idx = arg->internal.idx;
+
+ if( !idx && argc && !(arg->flags & (1<<4)) ) { /* skip the first entry */
+ argc--; argv++; idx++;
+ }
+
+ next_one:
+ if( !argc ) { /* no more args */
+ arg->r_opt = 0;
+ goto leave; /* ready */
+ }
+
+ s = *argv;
+ arg->internal.last = s;
+
+ if( arg->internal.stopped && (arg->flags & (1<<1)) ) {
+ arg->r_opt = -1; /* not an option but a argument */
+ arg->r_type = 2;
+ arg->r.ret_str = s;
+ argc--; argv++; idx++; /* set to next one */
+ }
+ else if( arg->internal.stopped ) { /* ready */
+ arg->r_opt = 0;
+ goto leave;
+ }
+ else if( *s == '-' && s[1] == '-' ) { /* long option */
+ char *argpos;
+
+ arg->internal.inarg = 0;
+ if( !s[2] && !(arg->flags & (1<<3)) ) { /* stop option processing */
+ arg->internal.stopped = 1;
+ argc--; argv++; idx++;
+ goto next_one;
+ }
+
+ argpos = strchr( s+2, '=' );
+ if( argpos )
+ *argpos = 0;
+ i = find_long_option( arg, opts, s+2 );
+ if( argpos )
+ *argpos = '=';
+
+ if( i < 0 && !strcmp( "help", s+2) ) {
+ if( !(arg->flags & (1<<6)) ) {
+ show_help(opts, arg->flags);
+ }
+ }
+ else if( i < 0 && !strcmp( "version", s+2) ) {
+ if( !(arg->flags & (1<<6)) ) {
+ show_version();
+ exit(0);
+ }
+ }
+ else if( i < 0 && !strcmp( "warranty", s+2) ) {
+ puts( strusage(16) );
+ exit(0);
+ }
+ else if( i < 0 && !strcmp( "dump-options", s+2) ) {
+ for(i=0; opts[i].short_opt; i++ ) {
+ if( opts[i].long_opt )
+ printf( "--%s\n", opts[i].long_opt );
+ }
+ fputs("--dump-options\n--help\n--version\n--warranty\n", stdout );
+ exit(0);
+ }
+
+ if( i == -2 ) /* ambiguous option */
+ arg->r_opt = -8;
+ else if( i == -1 ) {
+ arg->r_opt = -2;
+ arg->r.ret_str = s+2;
+ }
+ else
+ arg->r_opt = opts[i].short_opt;
+ if( i < 0 )
+ ;
+ else if( (opts[i].flags & 7) ) {
+ if( argpos ) {
+ s2 = argpos+1;
+ if( !*s2 )
+ s2 = NULL;
+ }
+ else
+ s2 = argv[1];
+ if( !s2 && (opts[i].flags & 8) ) { /* no argument but it is okay*/
+ arg->r_type = 0; /* because it is optional */
+ }
+ else if( !s2 ) {
+ arg->r_opt = -3; /* missing argument */
+ }
+ else if( !argpos && *s2 == '-' && (opts[i].flags & 8) ) {
+ /* the argument is optional and the next seems to be
+ * an option. We do not check this possible option
+ * but assume no argument */
+ arg->r_type = 0;
+ }
+ else {
+ set_opt_arg(arg, opts[i].flags, s2);
+ if( !argpos ) {
+ argc--; argv++; idx++; /* skip one */
+ }
+ }
+ }
+ else { /* does not take an argument */
+ if( argpos )
+ arg->r_type = -6; /* argument not expected */
+ else
+ arg->r_type = 0;
+ }
+ argc--; argv++; idx++; /* set to next one */
+ }
+ else if( (*s == '-' && s[1]) || arg->internal.inarg ) { /* short option */
+ int dash_kludge = 0;
+ i = 0;
+ if( !arg->internal.inarg ) {
+ arg->internal.inarg++;
+ if( arg->flags & (1<<5) ) {
+ for(i=0; opts[i].short_opt; i++ )
+ if( opts[i].long_opt && !strcmp( opts[i].long_opt, s+1)) {
+ dash_kludge=1;
+ break;
+ }
+ }
+ }
+ s += arg->internal.inarg;
+
+ if( !dash_kludge ) {
+ for(i=0; opts[i].short_opt; i++ )
+ if( opts[i].short_opt == *s )
+ break;
+ }
+
+ if( !opts[i].short_opt && ( *s == 'h' || *s == '?' ) ) {
+ if( !(arg->flags & (1<<6)) ) {
+ show_help(opts, arg->flags);
+ }
+ }
+
+ arg->r_opt = opts[i].short_opt;
+ if( !opts[i].short_opt ) {
+ arg->r_opt = (opts[i].flags & 256)? -7:-2;
+ arg->internal.inarg++; /* point to the next arg */
+ arg->r.ret_str = s;
+ }
+ else if( (opts[i].flags & 7) ) {
+ if( s[1] && !dash_kludge ) {
+ s2 = s+1;
+ set_opt_arg(arg, opts[i].flags, s2);
+ }
+ else {
+ s2 = argv[1];
+ if( !s2 && (opts[i].flags & 8) ) { /* no argument but it is okay*/
+ arg->r_type = 0; /* because it is optional */
+ }
+ else if( !s2 ) {
+ arg->r_opt = -3; /* missing argument */
+ }
+ else if( *s2 == '-' && s2[1] && (opts[i].flags & 8) ) {
+ /* the argument is optional and the next seems to be
+ * an option. We do not check this possible option
+ * but assume no argument */
+ arg->r_type = 0;
+ }
+ else {
+ set_opt_arg(arg, opts[i].flags, s2);
+ argc--; argv++; idx++; /* skip one */
+ }
+ }
+ s = "x"; /* so that !s[1] yields false */
+ }
+ else { /* does not take an argument */
+ arg->r_type = 0;
+ arg->internal.inarg++; /* point to the next arg */
+ }
+ if( !s[1] || dash_kludge ) { /* no more concatenated short options */
+ arg->internal.inarg = 0;
+ argc--; argv++; idx++;
+ }
+ }
+ else if( arg->flags & (1<<2) ) {
+ arg->r_opt = -1; /* not an option but a argument */
+ arg->r_type = 2;
+ arg->r.ret_str = s;
+ argc--; argv++; idx++; /* set to next one */
+ }
+ else {
+ arg->internal.stopped = 1; /* stop option processing */
+ goto next_one;
+ }
+
+ leave:
+ *arg->argc = argc;
+ *arg->argv = argv;
+ arg->internal.idx = idx;
+ return arg->r_opt;
+}
+
+
+
+static int
+set_opt_arg(ARGPARSE_ARGS *arg, unsigned flags, char *s)
+{
+ int base = (flags & 16)? 0 : 10;
+
+ switch( arg->r_type = (flags & 7) ) {
+ case 1: /* takes int argument */
+ arg->r.ret_int = (int)strtol(s,NULL,base);
+ return 0;
+ case 3: /* takes long argument */
+ arg->r.ret_long= strtol(s,NULL,base);
+ return 0;
+ case 4: /* takes ulong argument */
+ arg->r.ret_ulong= strtoul(s,NULL,base);
+ return 0;
+ case 2: /* takes string argument */
+ default:
+ arg->r.ret_str = s;
+ return 1;
+ }
+}
+
+
+static size_t
+long_opt_strlen( ARGPARSE_OPTS *o )
+{
+ size_t n = strlen(o->long_opt);
+
+ if( o->description && *o->description == '|' ) {
+ const char *s;
+
+ s=o->description+1;
+ if( *s != '=' )
+ n++;
+ for(; *s && *s != '|'; s++ )
+ n++;
+ }
+ return n;
+}
+
+/****************
+ * Print formatted help. The description string has some special
+ * meanings:
+ * - A description string which is "@" suppresses help output for
+ * this option
+ * - a description,ine which starts with a '@' and is followed by
+ * any other characters is printed as is; this may be used for examples
+ * ans such.
+ * - A description which starts with a '|' outputs the string between this
+ * bar and the next one as arguments of the long option.
+ */
+static void
+show_help( ARGPARSE_OPTS *opts, unsigned flags )
+{
+ const char *s;
+
+ show_version();
+ putchar('\n');
+ s = strusage(41);
+ puts(s);
+ if( opts[0].description ) { /* auto format the option description */
+ int i,j, indent;
+ /* get max. length of long options */
+ for(i=indent=0; opts[i].short_opt; i++ ) {
+ if( opts[i].long_opt )
+ if( !opts[i].description || *opts[i].description != '@' )
+ if( (j=long_opt_strlen(opts+i)) > indent && j < 35 )
+ indent = j;
+ }
+ /* example: " -v, --verbose Viele Sachen ausgeben" */
+ indent += 10;
+ if( *opts[0].description != '@' )
+ puts("Options:");
+ for(i=0; opts[i].short_opt; i++ ) {
+ s = _( opts[i].description );
+ if( s && *s== '@' && !s[1] ) /* hide this line */
+ continue;
+ if( s && *s == '@' ) { /* unindented comment only line */
+ for(s++; *s; s++ ) {
+ if( *s == '\n' ) {
+ if( s[1] )
+ putchar('\n');
+ }
+ else
+ putchar(*s);
+ }
+ putchar('\n');
+ continue;
+ }
+
+ j = 3;
+ if( opts[i].short_opt < 256 ) {
+ printf(" -%c", opts[i].short_opt );
+ if( !opts[i].long_opt ) {
+ if(s && *s == '|' ) {
+ putchar(' '); j++;
+ for(s++ ; *s && *s != '|'; s++, j++ )
+ putchar(*s);
+ if( *s )
+ s++;
+ }
+ }
+ }
+ else
+ fputs(" ", stdout);
+ if( opts[i].long_opt ) {
+ j += printf("%c --%s", opts[i].short_opt < 256?',':' ',
+ opts[i].long_opt );
+ if(s && *s == '|' ) {
+ if( *++s != '=' ) {
+ putchar(' ');
+ j++;
+ }
+ for( ; *s && *s != '|'; s++, j++ )
+ putchar(*s);
+ if( *s )
+ s++;
+ }
+ fputs(" ", stdout);
+ j += 3;
+ }
+ for(;j < indent; j++ )
+ putchar(' ');
+ if( s ) {
+ if( *s && j > indent ) {
+ putchar('\n');
+ for(j=0;j < indent; j++ )
+ putchar(' ');
+ }
+ for(; *s; s++ ) {
+ if( *s == '\n' ) {
+ if( s[1] ) {
+ putchar('\n');
+ for(j=0;j < indent; j++ )
+ putchar(' ');
+ }
+ }
+ else
+ putchar(*s);
+ }
+ }
+ putchar('\n');
+ }
+ if( flags & 32 )
+ puts("\n(A single dash may be used instead of the double ones)");
+ }
+ if( (s=strusage(19)) ) { /* bug reports to ... */
+ putchar('\n');
+ fputs(s, stdout);
+ }
+ fflush(stdout);
+ exit(0);
+}
+
+static void
+show_version()
+{
+ const char *s;
+ int i;
+ /* version line */
+ fputs(strusage(11), stdout);
+ if( (s=strusage(12)) )
+ printf(" (%s)", s );
+ printf(" %s\n", strusage(13) );
+ /* additional version lines */
+ for(i=20; i < 30; i++ )
+ if( (s=strusage(i)) )
+ printf("%s\n", s );
+ /* copyright string */
+ if( (s=strusage(14)) )
+ printf("%s\n", s );
+ /* copying conditions */
+ if( (s=strusage(15)) )
+ fputs(s, stdout);
+ /* thanks */
+ if( (s=strusage(18)) )
+ fputs(s, stdout);
+ /* additional program info */
+ for(i=30; i < 40; i++ )
+ if( (s=strusage(i)) )
+ fputs( (const byte*)s, stdout);
+ fflush(stdout);
+}
+
+
+void
+usage( int level )
+{
+ if( !level ) {
+ fprintf(stderr,"%s %s; %s\n", strusage(11), strusage(13),
+ strusage(14) );
+ fflush(stderr);
+ }
+ else if( level == 1 ) {
+ fputs(strusage(40),stderr);
+ exit(2);
+ }
+ else if( level == 2 ) {
+ puts(strusage(41));
+ exit(0);
+ }
+}
+
+/* Level
+ * 0: Copyright String auf stderr ausgeben
+ * 1: Kurzusage auf stderr ausgeben und beenden
+ * 2: Langusage auf stdout ausgeben und beenden
+ * 11: name of program
+ * 12: optional name of package which includes this program.
+ * 13: version string
+ * 14: copyright string
+ * 15: Short copying conditions (with LFs)
+ * 16: Long copying conditions (with LFs)
+ * 17: Optional printable OS name
+ * 18: Optional thanks list (with LFs)
+ * 19: Bug report info
+ *20..29: Additional lib version strings.
+ *30..39: Additional program info (with LFs)
+ * 40: short usage note (with LF)
+ * 41: long usage note (with LF)
+ */
+const char *
+default_strusage( int level )
+{
+ const char *p = NULL;
+ switch( level ) {
+ case 11: p = "foo"; break;
+ case 13: p = "0.0"; break;
+ case 14: p = "Copyright (C) 2006 Free Software Foundation, Inc."; break;
+ case 15: p =
+"This program comes with ABSOLUTELY NO WARRANTY.\n"
+"This is free software, and you are welcome to redistribute it\n"
+"under certain conditions. See the file COPYING for details.\n"; break;
+ case 16: p =
+"This is free software; you can redistribute it and/or modify\n"
+"it under the terms of the GNU General Public License as published by\n"
+"the Free Software Foundation; either version 2 of the License, or\n"
+"(at your option) any later version.\n\n"
+"It is distributed in the hope that it will be useful,\n"
+"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
+"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
+"GNU General Public License for more details.\n\n"
+"You should have received a copy of the GNU General Public License\n"
+"along with this program; if not, write to the Free Software\n"
+"Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n";
+ break;
+ case 40: /* short and long usage */
+ case 41: p = ""; break;
+ }
+
+ return p;
+}
+
+
+
+#ifdef TEST
+static struct {
+ int verbose;
+ int debug;
+ char *outfile;
+ char *crf;
+ int myopt;
+ int echo;
+ int a_long_one;
+}opt;
+
+int
+main(int argc, char **argv)
+{
+ ARGPARSE_OPTS opts[] = {
+ { 'v', "verbose", 0 , "Laut sein"},
+ { 'e', "echo" , 0 , "Zeile ausgeben, damit wir sehen, was wir einegegeben haben"},
+ { 'd', "debug", 0 , "Debug\nfalls mal etasws\nSchief geht"},
+ { 'o', "output", 2 },
+ { 'c', "cross-ref", 2|8, "cross-reference erzeugen\n" },
+ { 'm', "my-option", 1|8 },
+ { 500, "a-long-option", 0 },
+ {0} };
+ ARGPARSE_ARGS pargs = { &argc, &argv, 2|4|32 };
+ int i;
+
+ while( ArgParse( &pargs, opts) ) {
+ switch( pargs.r_opt ) {
+ case -1 : printf( "arg=`%s'\n", pargs.r.ret_str); break;
+ case 'v': opt.verbose++; break;
+ case 'e': opt.echo++; break;
+ case 'd': opt.debug++; break;
+ case 'o': opt.outfile = pargs.r.ret_str; break;
+ case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break;
+ case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break;
+ case 500: opt.a_long_one++; break;
+ default : pargs.err = 1; break; /* force warning output */
+ }
+ }
+ for(i=0; i < argc; i++ )
+ printf("%3d -> (%s)\n", i, argv[i] );
+ puts("Options:");
+ if( opt.verbose )
+ printf(" verbose=%d\n", opt.verbose );
+ if( opt.debug )
+ printf(" debug=%d\n", opt.debug );
+ if( opt.outfile )
+ printf(" outfile=`%s'\n", opt.outfile );
+ if( opt.crf )
+ printf(" crffile=`%s'\n", opt.crf );
+ if( opt.myopt )
+ printf(" myopt=%d\n", opt.myopt );
+ if( opt.a_long_one )
+ printf(" a-long-one=%d\n", opt.a_long_one );
+ if( opt.echo )
+ printf(" echo=%d\n", opt.echo );
+ return 0;
+}
+#endif
+
+/**** bottom of file ****/
diff --git a/util/assuan-buffer.c b/util/assuan-buffer.c
new file mode 100644
index 0000000..3c01086
--- /dev/null
+++ b/util/assuan-buffer.c
@@ -0,0 +1,485 @@
+/* assuan-buffer.c - read and send data
+ * Copyright (C) 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+ *
+ * This file is part of Assuan.
+ *
+ * Assuan is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Assuan is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+/* Please note that this is a stripped down and modified version of
+ the orginal Assuan code from libassuan. */
+
+
+#include <config.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+#ifdef HAVE_W32_SYSTEM
+#include <process.h>
+#endif
+#include "assuan-defs.h"
+
+#ifndef HAVE_MEMRCHR
+void *memrchr(const void *s, int c, size_t n);
+#endif
+
+static int
+writen (assuan_context_t ctx, const char *buffer, size_t length)
+{
+ while (length)
+ {
+ ssize_t nwritten = ctx->io->writefnc (ctx, buffer, length);
+
+ if (nwritten < 0)
+ {
+ if (errno == EINTR)
+ continue;
+ return -1; /* write error */
+ }
+ length -= nwritten;
+ buffer += nwritten;
+ }
+ return 0; /* okay */
+}
+
+/* Read an entire line. */
+static int
+readaline (assuan_context_t ctx, char *buf, size_t buflen,
+ int *r_nread, int *r_eof)
+{
+ size_t nleft = buflen;
+ char *p;
+
+ *r_eof = 0;
+ *r_nread = 0;
+ while (nleft > 0)
+ {
+ ssize_t n = ctx->io->readfnc (ctx, buf, nleft);
+
+ if (n < 0)
+ {
+ if (errno == EINTR)
+ continue;
+ return -1; /* read error */
+ }
+ else if (!n)
+ {
+ *r_eof = 1;
+ break; /* allow incomplete lines */
+ }
+ p = buf;
+ nleft -= n;
+ buf += n;
+ *r_nread += n;
+
+ p = memrchr (p, '\n', n);
+ if (p)
+ break; /* at least one full line available - that's enough for now */
+ }
+
+ return 0;
+}
+
+
+int
+_assuan_read_line (assuan_context_t ctx)
+{
+ char *line = ctx->inbound.line;
+ int nread, atticlen;
+ int rc;
+ char *endp = 0;
+
+ if (ctx->inbound.eof)
+ return -1;
+
+ atticlen = ctx->inbound.attic.linelen;
+ if (atticlen)
+ {
+ memcpy (line, ctx->inbound.attic.line, atticlen);
+ ctx->inbound.attic.linelen = 0;
+
+ endp = memchr (line, '\n', atticlen);
+ if (endp)
+ /* Found another line in the attic. */
+ {
+ rc = 0;
+ nread = atticlen;
+ atticlen = 0;
+ }
+ else
+ /* There is pending data but not a full line. */
+ {
+ assert (atticlen < LINELENGTH);
+ rc = readaline (ctx, line + atticlen,
+ LINELENGTH - atticlen, &nread, &ctx->inbound.eof);
+ }
+ }
+ else
+ /* No pending data. */
+ rc = readaline (ctx, line, LINELENGTH,
+ &nread, &ctx->inbound.eof);
+ if (rc)
+ {
+ if (ctx->log_fp)
+ fprintf (ctx->log_fp, "%s[%u.%p] DBG: <- [Error: %s]\n",
+ assuan_get_assuan_log_prefix (),
+ (unsigned int)getpid (), (void *)ctx, strerror (errno));
+ return ASSUAN_Read_Error;
+ }
+ if (!nread)
+ {
+ assert (ctx->inbound.eof);
+ if (ctx->log_fp)
+ fprintf (ctx->log_fp, "%s[%u.%p] DBG: <- [EOF]\n",
+ assuan_get_assuan_log_prefix (),
+ (unsigned int)getpid (), (void *)ctx);
+ return -1;
+ }
+
+ ctx->inbound.attic.pending = 0;
+ nread += atticlen;
+
+ if (! endp)
+ endp = memchr (line, '\n', nread);
+
+ if (endp)
+ {
+ int n = endp - line + 1;
+ if (n < nread)
+ /* LINE contains more than one line. We copy it to the attic
+ now as handlers are allowed to modify the passed
+ buffer. */
+ {
+ int len = nread - n;
+ memcpy (ctx->inbound.attic.line, endp + 1, len);
+ ctx->inbound.attic.pending = memrchr (endp + 1, '\n', len) ? 1 : 0;
+ ctx->inbound.attic.linelen = len;
+ }
+
+ if (endp != line && endp[-1] == '\r')
+ endp --;
+ *endp = 0;
+
+ ctx->inbound.linelen = endp - line;
+ if (ctx->log_fp)
+ {
+ fprintf (ctx->log_fp, "%s[%u.%p] DBG: <- ",
+ assuan_get_assuan_log_prefix (),
+ (unsigned int)getpid (), (void *)ctx);
+ if (ctx->confidential)
+ fputs ("[Confidential data not shown]", ctx->log_fp);
+ else
+ _assuan_log_print_buffer (ctx->log_fp,
+ ctx->inbound.line,
+ ctx->inbound.linelen);
+ putc ('\n', ctx->log_fp);
+ }
+ return 0;
+ }
+ else
+ {
+ if (ctx->log_fp)
+ fprintf (ctx->log_fp, "%s[%u.%p] DBG: <- [Invalid line]\n",
+ assuan_get_assuan_log_prefix (),
+ (unsigned int)getpid (), (void *)ctx);
+ *line = 0;
+ ctx->inbound.linelen = 0;
+ return ctx->inbound.eof ? ASSUAN_Line_Not_Terminated
+ : ASSUAN_Line_Too_Long;
+ }
+}
+
+
+/* Read the next line from the client or server and return a pointer
+ in *LINE to a buffer holding the line. LINELEN is the length of
+ *LINE. The buffer is valid until the next read operation on it.
+ The caller may modify the buffer. The buffer is invalid (i.e. must
+ not be used) if an error is returned.
+
+ Returns 0 on success or an assuan error code.
+ See also: assuan_pending_line().
+*/
+assuan_error_t
+assuan_read_line (assuan_context_t ctx, char **line, size_t *linelen)
+{
+ assuan_error_t err;
+
+ if (!ctx)
+ return ASSUAN_Invalid_Value;
+
+ err = _assuan_read_line (ctx);
+ *line = ctx->inbound.line;
+ *linelen = ctx->inbound.linelen;
+ return err;
+}
+
+
+/* Return true if a full line is buffered (i.e. an entire line may be
+ read without any I/O). */
+int
+assuan_pending_line (assuan_context_t ctx)
+{
+ return ctx && ctx->inbound.attic.pending;
+}
+
+
+assuan_error_t
+_assuan_write_line (assuan_context_t ctx, const char *prefix,
+ const char *line, size_t len)
+{
+ int rc = 0;
+ size_t prefixlen = prefix? strlen (prefix):0;
+
+ /* Make sure that the line is short enough. */
+ if (len + prefixlen + 2 > ASSUAN_LINELENGTH)
+ {
+ if (ctx->log_fp)
+ fprintf (ctx->log_fp, "%s[%u.%p] DBG: -> "
+ "[supplied line too long -truncated]\n",
+ assuan_get_assuan_log_prefix (),
+ (unsigned int)getpid (), (void *)ctx);
+ if (prefixlen > 5)
+ prefixlen = 5;
+ if (len > ASSUAN_LINELENGTH - prefixlen - 2)
+ len = ASSUAN_LINELENGTH - prefixlen - 2 - 1;
+ }
+
+ /* Fixme: we should do some kind of line buffering. */
+ if (ctx->log_fp)
+ {
+ fprintf (ctx->log_fp, "%s[%u.%p] DBG: -> ",
+ assuan_get_assuan_log_prefix (),
+ (unsigned int)getpid (), (void *)ctx);
+ if (ctx->confidential)
+ fputs ("[Confidential data not shown]", ctx->log_fp);
+ else
+ _assuan_log_print_buffer (ctx->log_fp, line, len);
+ putc ('\n', ctx->log_fp);
+ }
+
+ if (prefixlen)
+ {
+ rc = writen (ctx, prefix, prefixlen);
+ if (rc)
+ rc = ASSUAN_Write_Error;
+ }
+ if (!rc)
+ {
+ rc = writen (ctx, line, len);
+ if (rc)
+ rc = ASSUAN_Write_Error;
+ if (!rc)
+ {
+ rc = writen (ctx, "\n", 1);
+ if (rc)
+ rc = ASSUAN_Write_Error;
+ }
+ }
+ return rc;
+}
+
+
+assuan_error_t
+assuan_write_line (assuan_context_t ctx, const char *line)
+{
+ size_t len;
+ const char *s;
+
+ if (!ctx)
+ return ASSUAN_Invalid_Value;
+
+ /* Make sure that we never take a LF from the user - this might
+ violate the protocol. */
+ s = strchr (line, '\n');
+ len = s? (s-line) : strlen (line);
+
+ if (ctx->log_fp && s)
+ fprintf (ctx->log_fp, "%s[%u.%p] DBG: -> "
+ "[supplied line contained a LF -truncated]\n",
+ assuan_get_assuan_log_prefix (),
+ (unsigned int)getpid (), (void *)ctx);
+
+ return _assuan_write_line (ctx, NULL, line, len);
+}
+
+
+
+/* Write out the data in buffer as datalines with line wrapping and
+ percent escaping. This function is used for GNU's custom streams */
+int
+_assuan_cookie_write_data (void *cookie, const char *buffer, size_t orig_size)
+{
+ assuan_context_t ctx = cookie;
+ size_t size = orig_size;
+ char *line;
+ size_t linelen;
+
+ if (ctx->outbound.data.error)
+ return 0;
+
+ line = ctx->outbound.data.line;
+ linelen = ctx->outbound.data.linelen;
+ line += linelen;
+ while (size)
+ {
+ /* insert data line header */
+ if (!linelen)
+ {
+ *line++ = 'D';
+ *line++ = ' ';
+ linelen += 2;
+ }
+
+ /* copy data, keep some space for the CRLF and to escape one character */
+ while (size && linelen < LINELENGTH-2-2)
+ {
+ if (*buffer == '%' || *buffer == '\r' || *buffer == '\n')
+ {
+ sprintf (line, "%%%02X", *(unsigned char*)buffer);
+ line += 3;
+ linelen += 3;
+ buffer++;
+ }
+ else
+ {
+ *line++ = *buffer++;
+ linelen++;
+ }
+ size--;
+ }
+
+ if (linelen >= LINELENGTH-2-2)
+ {
+ if (ctx->log_fp)
+ {
+ fprintf (ctx->log_fp, "%s[%u.%p] DBG: -> ",
+ assuan_get_assuan_log_prefix (),
+ (unsigned int)getpid (), (void *)ctx);
+
+ if (ctx->confidential)
+ fputs ("[Confidential data not shown]", ctx->log_fp);
+ else
+ _assuan_log_print_buffer (ctx->log_fp,
+ ctx->outbound.data.line,
+ linelen);
+ putc ('\n', ctx->log_fp);
+ }
+ *line++ = '\n';
+ linelen++;
+ if (writen (ctx, ctx->outbound.data.line, linelen))
+ {
+ ctx->outbound.data.error = ASSUAN_Write_Error;
+ return 0;
+ }
+ line = ctx->outbound.data.line;
+ linelen = 0;
+ }
+ }
+
+ ctx->outbound.data.linelen = linelen;
+ return (int)orig_size;
+}
+
+
+/* Write out any buffered data
+ This function is used for GNU's custom streams */
+int
+_assuan_cookie_write_flush (void *cookie)
+{
+ assuan_context_t ctx = cookie;
+ char *line;
+ size_t linelen;
+
+ if (ctx->outbound.data.error)
+ return 0;
+
+ line = ctx->outbound.data.line;
+ linelen = ctx->outbound.data.linelen;
+ line += linelen;
+ if (linelen)
+ {
+ if (ctx->log_fp)
+ {
+ fprintf (ctx->log_fp, "%s[%u.%p] DBG: -> ",
+ assuan_get_assuan_log_prefix (),
+ (unsigned int)getpid (), (void *)ctx);
+ if (ctx->confidential)
+ fputs ("[Confidential data not shown]", ctx->log_fp);
+ else
+ _assuan_log_print_buffer (ctx->log_fp,
+ ctx->outbound.data.line, linelen);
+ putc ('\n', ctx->log_fp);
+ }
+ *line++ = '\n';
+ linelen++;
+ if (writen (ctx, ctx->outbound.data.line, linelen))
+ {
+ ctx->outbound.data.error = ASSUAN_Write_Error;
+ return 0;
+ }
+ ctx->outbound.data.linelen = 0;
+ }
+ return 0;
+}
+
+
+/**
+ * assuan_send_data:
+ * @ctx: An assuan context
+ * @buffer: Data to send or NULL to flush
+ * @length: length of the data to send/
+ *
+ * This function may be used by the server or the client to send data
+ * lines. The data will be escaped as required by the Assuan protocol
+ * and may get buffered until a line is full. To force sending the
+ * data out @buffer may be passed as NULL (in which case @length must
+ * also be 0); however when used by a client this flush operation does
+ * also send the terminating "END" command to terminate the reponse on
+ * a INQUIRE response. However, when assuan_transact() is used, this
+ * function takes care of sending END itself.
+ *
+ * Return value: 0 on success or an error code
+ **/
+
+assuan_error_t
+assuan_send_data (assuan_context_t ctx, const void *buffer, size_t length)
+{
+ if (!ctx)
+ return ASSUAN_Invalid_Value;
+ if (!buffer && length)
+ return ASSUAN_Invalid_Value;
+
+ if (!buffer)
+ { /* flush what we have */
+ _assuan_cookie_write_flush (ctx);
+ if (ctx->outbound.data.error)
+ return ctx->outbound.data.error;
+ if (!ctx->is_server)
+ return assuan_write_line (ctx, "END");
+ }
+ else
+ {
+ _assuan_cookie_write_data (ctx, buffer, length);
+ if (ctx->outbound.data.error)
+ return ctx->outbound.data.error;
+ }
+
+ return 0;
+}
+
diff --git a/util/assuan-client.c b/util/assuan-client.c
new file mode 100644
index 0000000..0467f6d
--- /dev/null
+++ b/util/assuan-client.c
@@ -0,0 +1,282 @@
+/* assuan-client.c - client functions
+ * Copyright (C) 2001, 2002 Free Software Foundation, Inc.
+ * Copyright (C) 2005 Free Software Foundation, Inc.
+ *
+ * This file is part of Assuan.
+ *
+ * Assuan is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Assuan is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+/* Please note that this is a stripped down and modified version of
+ the orginal Assuan code from libassuan. */
+
+#include <config.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+#include <string.h>
+
+#include "assuan-defs.h"
+
+#define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \
+ *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
+#define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1))
+
+
+assuan_error_t
+_assuan_read_from_server (assuan_context_t ctx, int *okay, int *off)
+{
+ char *line;
+ int linelen;
+ assuan_error_t rc;
+
+ *okay = 0;
+ *off = 0;
+ do
+ {
+ rc = _assuan_read_line (ctx);
+ if (rc)
+ return rc;
+ line = ctx->inbound.line;
+ linelen = ctx->inbound.linelen;
+ }
+ while (*line == '#' || !linelen);
+
+ if (linelen >= 1
+ && line[0] == 'D' && line[1] == ' ')
+ {
+ *okay = 2; /* data line */
+ *off = 2;
+ }
+ else if (linelen >= 1
+ && line[0] == 'S'
+ && (line[1] == '\0' || line[1] == ' '))
+ {
+ *okay = 4;
+ *off = 1;
+ while (line[*off] == ' ')
+ ++*off;
+ }
+ else if (linelen >= 2
+ && line[0] == 'O' && line[1] == 'K'
+ && (line[2] == '\0' || line[2] == ' '))
+ {
+ *okay = 1;
+ *off = 2;
+ while (line[*off] == ' ')
+ ++*off;
+ }
+ else if (linelen >= 3
+ && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
+ && (line[3] == '\0' || line[3] == ' '))
+ {
+ *okay = 0;
+ *off = 3;
+ while (line[*off] == ' ')
+ ++*off;
+ }
+ else if (linelen >= 7
+ && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
+ && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
+ && line[6] == 'E'
+ && (line[7] == '\0' || line[7] == ' '))
+ {
+ *okay = 3;
+ *off = 7;
+ while (line[*off] == ' ')
+ ++*off;
+ }
+ else if (linelen >= 3
+ && line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
+ && (line[3] == '\0' || line[3] == ' '))
+ {
+ *okay = 5; /* end line */
+ *off = 3;
+ }
+ else
+ rc = ASSUAN_Invalid_Response;
+ return rc;
+}
+
+
+
+assuan_error_t
+assuan_transact (assuan_context_t ctx,
+ const char *command,
+ assuan_error_t (*data_cb)(void *, const void *, size_t),
+ void *data_cb_arg,
+ assuan_error_t (*inquire_cb)(void*, const char *),
+ void *inquire_cb_arg,
+ assuan_error_t (*status_cb)(void*, const char *),
+ void *status_cb_arg)
+{
+ return assuan_transact2 (ctx, command,
+ data_cb, data_cb_arg,
+ inquire_cb, inquire_cb_arg,
+ status_cb, status_cb_arg,
+ NULL, NULL);
+}
+
+
+/**
+ * assuan_transact2:
+ * @ctx: The Assuan context
+ * @command: Coimmand line to be send to server
+ * @data_cb: Callback function for data lines
+ * @data_cb_arg: first argument passed to @data_cb
+ * @inquire_cb: Callback function for a inquire response
+ * @inquire_cb_arg: first argument passed to @inquire_cb
+ * @status_cb: Callback function for a status response
+ * @status_cb_arg: first argument passed to @status_cb
+ * @okay_cb: Callback function for the final OK response
+ * @okay_cb_arg: first argument passed to @okay_cb
+ *
+ * FIXME: Write documentation
+ *
+ * Return value: 0 on success or error code. The error code may be
+ * the one one returned by the server in error lines or from the
+ * callback functions.
+ **/
+assuan_error_t
+assuan_transact2 (assuan_context_t ctx,
+ const char *command,
+ assuan_error_t (*data_cb)(void *, const void *, size_t),
+ void *data_cb_arg,
+ assuan_error_t (*inquire_cb)(void*, const char *),
+ void *inquire_cb_arg,
+ assuan_error_t (*status_cb)(void*, const char *),
+ void *status_cb_arg,
+ assuan_error_t (*okay_cb)(void*, const char *),
+ void *okay_cb_arg)
+{
+ int rc, okay, off;
+ unsigned char *line;
+ int linelen;
+
+ rc = assuan_write_line (ctx, command);
+ if (rc)
+ return rc;
+
+ if (*command == '#' || !*command)
+ return 0; /* Don't expect a response for a comment line. */
+
+ again:
+ rc = _assuan_read_from_server (ctx, &okay, &off);
+ if (rc)
+ return rc; /* error reading from server */
+
+ line = ctx->inbound.line + off;
+ linelen = ctx->inbound.linelen - off;
+
+ if (!okay)
+ {
+ rc = atoi (line);
+ if (rc < 100)
+ rc = ASSUAN_Server_Fault;
+ }
+ else if (okay == 1) /* Received OK. */
+ {
+ if (okay_cb)
+ {
+ rc = okay_cb (okay_cb_arg, line);
+ /* We better wipe out the buffer after processing it. This
+ is no real guarantee that it won't get swapped out but at
+ least for the standard cases we can make sure that a
+ passphrase returned with the OK line is rendered
+ unreadable. In fact the current Assuan interface suffers
+ from the problem that it is not possible to do assuan I/O
+ through secure memory. There is no easy solution given
+ the current implementation but we need to address it
+ sooner or later. The problem was introduced with
+ gpg-agent's GET_PASPHRASE command but it might also make
+ sense to have a way to convey sessions keys through
+ secured memory. Note that the old implementation in gpg
+ for accessing the passphrase in fact used secure memory
+ but had the drawback of using a limited and not fully
+ conforming Assuan implementation - given that pinentry
+ and gpg-agent neither use secured memory for Assuan I/O,
+ it is negligible to drop the old implementation in gpg's
+ passphrase.c and use the wipememory workaround here. */
+ memset (line, 0, strlen (line));
+ }
+ }
+ else if (okay == 2)
+ {
+ if (!data_cb)
+ rc = ASSUAN_No_Data_Callback;
+ else
+ {
+ unsigned char *s, *d;
+
+ for (s=d=line; linelen; linelen--)
+ {
+ if (*s == '%' && linelen > 2)
+ { /* handle escaping */
+ s++;
+ *d++ = xtoi_2 (s);
+ s += 2;
+ linelen -= 2;
+ }
+ else
+ *d++ = *s++;
+ }
+ *d = 0; /* add a hidden string terminator */
+ rc = data_cb (data_cb_arg, line, d - line);
+ if (!rc)
+ goto again;
+ }
+ }
+ else if (okay == 3)
+ {
+ if (!inquire_cb)
+ {
+ assuan_write_line (ctx, "END"); /* get out of inquire mode */
+ _assuan_read_from_server (ctx, &okay, &off); /* dummy read */
+ rc = ASSUAN_No_Inquire_Callback;
+ }
+ else
+ {
+ rc = inquire_cb (inquire_cb_arg, line);
+ if (!rc)
+ rc = assuan_send_data (ctx, NULL, 0); /* flush and send END */
+ if (!rc)
+ goto again;
+ }
+ }
+ else if (okay == 4)
+ {
+ if (status_cb)
+ rc = status_cb (status_cb_arg, line);
+ if (!rc)
+ goto again;
+ }
+ else if (okay == 5)
+ {
+ if (!data_cb)
+ rc = ASSUAN_No_Data_Callback;
+ else
+ {
+ rc = data_cb (data_cb_arg, NULL, 0);
+ if (!rc)
+ goto again;
+ }
+ }
+
+ return rc;
+}
+
diff --git a/util/assuan-connect.c b/util/assuan-connect.c
new file mode 100644
index 0000000..9952b44
--- /dev/null
+++ b/util/assuan-connect.c
@@ -0,0 +1,96 @@
+/* assuan-connect.c - Establish a connection (client)
+ * Copyright (C) 2001, 2002 Free Software Foundation, Inc.
+ *
+ * This file is part of Assuan.
+ *
+ * Assuan is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Assuan is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+/* Please note that this is a stripped down and modified version of
+ the orginal Assuan code from libassuan. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#ifndef HAVE_W32_SYSTEM
+#include <sys/wait.h>
+#endif
+
+#include "assuan-defs.h"
+
+/* Create a new context. */
+int
+_assuan_new_context (assuan_context_t *r_ctx)
+{
+ assuan_context_t ctx;
+
+ *r_ctx = NULL;
+ ctx = xcalloc (1, sizeof *ctx);
+
+ ctx->input_fd = -1;
+ ctx->output_fd = -1;
+
+ ctx->inbound.fd = -1;
+ ctx->outbound.fd = -1;
+ ctx->io = NULL;
+
+ ctx->listen_fd = -1;
+ *r_ctx = ctx;
+ return 0;
+}
+
+
+void
+_assuan_release_context (assuan_context_t ctx)
+{
+ if (ctx)
+ {
+ xfree (ctx->hello_line);
+ xfree (ctx->okay_line);
+ xfree (ctx);
+ }
+}
+
+
+/* Disconnect and release the context CTX. */
+void
+assuan_disconnect (assuan_context_t ctx)
+{
+ if (ctx)
+ {
+ assuan_write_line (ctx, "BYE");
+ ctx->finish_handler (ctx);
+ ctx->deinit_handler (ctx);
+ ctx->deinit_handler = NULL;
+ _assuan_release_context (ctx);
+ }
+}
+
+/* Return the PID of the peer or -1 if not known. */
+pid_t
+assuan_get_pid (assuan_context_t ctx)
+{
+ return (ctx && ctx->pid)? ctx->pid : -1;
+}
+
diff --git a/util/assuan-defs.h b/util/assuan-defs.h
new file mode 100644
index 0000000..67c4e13
--- /dev/null
+++ b/util/assuan-defs.h
@@ -0,0 +1,244 @@
+/* assuan-defs.c - Internal definitions to Assuan
+ * Copyright (C) 2001, 2002, 2004 Free Software Foundation, Inc.
+ * Copyright (C) 2005 Free Software Foundation, Inc.
+ *
+ * This file is part of Assuan.
+ *
+ * Assuan is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Assuan is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+/* Please note that this is a stripped down and modified version of
+ the orginal Assuan code from libassuan. */
+
+
+#ifndef ASSUAN_DEFS_H
+#define ASSUAN_DEFS_H
+
+#include <sys/types.h>
+#ifndef HAVE_W32_SYSTEM
+#include <sys/socket.h>
+#include <sys/un.h>
+#else
+#include <windows.h>
+#endif
+#include <unistd.h>
+
+#include "assuan.h"
+#include "memory.h"
+
+#ifndef HAVE_W32_SYSTEM
+#define DIRSEP_C '/'
+#else
+#define DIRSEP_C '\\'
+#endif
+
+#ifdef HAVE_W32_SYSTEM
+#define AF_LOCAL AF_UNIX
+/* We need to prefix the structure with a sockaddr_in header so we can
+ use it later for sendto and recvfrom. */
+struct sockaddr_un
+{
+ short sun_family;
+ unsigned short sun_port;
+ struct in_addr sun_addr;
+ char sun_path[108-2-4]; /* Path name. */
+};
+
+/* Not needed anymore because the current mingw32 defines this in
+ sys/types.h */
+/* typedef int ssize_t; */
+
+/* Missing W32 functions */
+int putc_unlocked (int c, FILE *stream);
+void * memrchr (const void *block, int c, size_t size);
+char * stpcpy (char *dest, const char *src);
+#endif
+
+#define LINELENGTH ASSUAN_LINELENGTH
+
+struct cmdtbl_s
+{
+ const char *name;
+ int (*handler)(assuan_context_t, char *line);
+};
+
+struct assuan_io
+{
+ /* Routine to read from input_fd. */
+ ssize_t (*readfnc) (assuan_context_t, void *, size_t);
+ /* Routine to write to output_fd. */
+ ssize_t (*writefnc) (assuan_context_t, const void *, size_t);
+ /* Send a file descriptor. */
+ assuan_error_t (*sendfd) (assuan_context_t, int);
+ /* Receive a file descriptor. */
+ assuan_error_t (*receivefd) (assuan_context_t, int *);
+};
+
+struct assuan_context_s
+{
+ assuan_error_t err_no;
+ const char *err_str;
+ int os_errno; /* last system error number used with certain error codes*/
+
+ int confidential;
+ int is_server; /* set if this is context belongs to a server */
+ int in_inquire;
+ char *hello_line;
+ char *okay_line; /* see assan_set_okay_line() */
+
+ void *user_pointer; /* for assuan_[gs]et_pointer () */
+
+ FILE *log_fp;
+
+ struct {
+ int fd;
+ int eof;
+ char line[LINELENGTH];
+ int linelen; /* w/o CR, LF - might not be the same as
+ strlen(line) due to embedded nuls. However a nul
+ is always written at this pos */
+ struct {
+ char line[LINELENGTH];
+ int linelen ;
+ int pending; /* i.e. at least one line is available in the attic */
+ } attic;
+ } inbound;
+
+ struct {
+ int fd;
+ struct {
+ FILE *fp;
+ char line[LINELENGTH];
+ int linelen;
+ int error;
+ } data;
+ } outbound;
+
+ int pipe_mode; /* We are in pipe mode, i.e. we can handle just one
+ connection and must terminate then */
+ pid_t pid; /* The the pid of the peer. */
+ int listen_fd; /* The fd we are listening on (used by socket servers) */
+ int connected_fd; /* helper */
+
+ /* Used for Unix domain sockets. */
+ struct sockaddr_un myaddr;
+ struct sockaddr_un serveraddr;
+ /* When reading from datagram sockets, we must read an entire
+ message at a time. This means that we have to do our own
+ buffering to be able to get the semantics of read. */
+ void *domainbuffer;
+ /* Offset of start of buffer. */
+ int domainbufferoffset;
+ /* Bytes buffered. */
+ int domainbuffersize;
+ /* Memory allocated. */
+ int domainbufferallocated;
+
+ int *pendingfds;
+ int pendingfdscount;
+
+ void (*deinit_handler)(assuan_context_t);
+ int (*accept_handler)(assuan_context_t);
+ int (*finish_handler)(assuan_context_t);
+
+ struct cmdtbl_s *cmdtbl;
+ size_t cmdtbl_used; /* used entries */
+ size_t cmdtbl_size; /* allocated size of table */
+
+ void (*bye_notify_fnc)(assuan_context_t);
+ void (*reset_notify_fnc)(assuan_context_t);
+ void (*cancel_notify_fnc)(assuan_context_t);
+ int (*option_handler_fnc)(assuan_context_t,const char*, const char*);
+ void (*input_notify_fnc)(assuan_context_t, const char *);
+ void (*output_notify_fnc)(assuan_context_t, const char *);
+
+ int input_fd; /* set by INPUT command */
+ int output_fd; /* set by OUTPUT command */
+
+ /* io routines. */
+ struct assuan_io *io;
+};
+
+/*-- assuan-pipe-server.c --*/
+int _assuan_new_context (assuan_context_t *r_ctx);
+void _assuan_release_context (assuan_context_t ctx);
+
+/*-- assuan-domain-connect.c --*/
+/* Make a connection to the Unix domain socket NAME and return a new
+ Assuan context in CTX. SERVER_PID is currently not used but may
+ become handy in the future. */
+assuan_error_t _assuan_domain_init (assuan_context_t *r_ctx,
+ int rendezvousfd,
+ pid_t peer);
+
+/*-- assuan-handler.c --*/
+int _assuan_register_std_commands (assuan_context_t ctx);
+
+/*-- assuan-buffer.c --*/
+int _assuan_read_line (assuan_context_t ctx);
+int _assuan_cookie_write_data (void *cookie, const char *buffer, size_t size);
+int _assuan_cookie_write_flush (void *cookie);
+assuan_error_t _assuan_write_line (assuan_context_t ctx, const char *prefix,
+ const char *line, size_t len);
+
+/*-- assuan-client.c --*/
+assuan_error_t _assuan_read_from_server (assuan_context_t ctx, int *okay, int *off);
+
+
+/*-- assuan-util.c --*/
+
+#define set_error(c,e,t) assuan_set_error ((c), ASSUAN_ ## e, (t))
+
+void _assuan_log_print_buffer (FILE *fp, const void *buffer, size_t length);
+void _assuan_log_sanitized_string (const char *string);
+
+#ifdef HAVE_W32_SYSTEM
+const char *_assuan_w32_strerror (int ec);
+#define w32_strerror(e) _assuan_w32_strerror ((e))
+#endif /*HAVE_W32_SYSTEM*/
+
+
+/*-- assuan-logging.c --*/
+void _assuan_set_default_log_stream (FILE *fp);
+
+void _assuan_log_printf (const char *format, ...)
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5 )
+ __attribute__ ((format (printf,1,2)))
+#endif
+ ;
+
+/*-- assuan-io.c --*/
+ssize_t _assuan_simple_read (assuan_context_t ctx, void *buffer, size_t size);
+ssize_t _assuan_simple_write (assuan_context_t ctx, const void *buffer,
+ size_t size);
+
+/*-- assuan-socket.c --*/
+int _assuan_close (int fd);
+int _assuan_sock_new (int domain, int type, int proto);
+int _assuan_sock_connect (int sockfd, struct sockaddr *addr, int addrlen);
+
+#ifdef HAVE_FOPENCOOKIE
+/* We have to implement funopen in terms of glibc's fopencookie. */
+FILE *_assuan_funopen(void *cookie,
+ cookie_read_function_t *readfn,
+ cookie_write_function_t *writefn,
+ cookie_seek_function_t *seekfn,
+ cookie_close_function_t *closefn);
+#define funopen(a,r,w,s,c) _assuan_funopen ((a), (r), (w), (s), (c))
+#endif /*HAVE_FOPENCOOKIE*/
+
+#endif /*ASSUAN_DEFS_H*/
+
diff --git a/util/assuan-errors.c b/util/assuan-errors.c
new file mode 100644
index 0000000..c3a8770
--- /dev/null
+++ b/util/assuan-errors.c
@@ -0,0 +1,105 @@
+/* assuan-errors.c - error codes
+ * Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc.
+ * Copyright (C) 2005 Free Software Foundation, Inc.
+ *
+ * This file is part of Assuan.
+ *
+ * Assuan is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Assuan is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+/* Please note that this is a stripped down and modified version of
+ the orginal Assuan code from libassuan. */
+
+#include <stdio.h>
+#include "assuan.h"
+
+/* This function returns a textual representaion of the given error
+ code. If this is an unknown value, a string with the value is
+ returned (Beware: it is hold in a static buffer). Return value:
+ String with the error description.
+ */
+const char *
+assuan_strerror (assuan_error_t err)
+{
+ const char *s;
+ static char buf[50];
+
+ switch (err)
+ {
+ case ASSUAN_No_Error: s="no error"; break;
+ case ASSUAN_General_Error: s="general error"; break;
+ case ASSUAN_Out_Of_Core: s="out of core"; break;
+ case ASSUAN_Invalid_Value: s="invalid value"; break;
+ case ASSUAN_Timeout: s="timeout"; break;
+ case ASSUAN_Read_Error: s="read error"; break;
+ case ASSUAN_Write_Error: s="write error"; break;
+ case ASSUAN_Problem_Starting_Server: s="problem starting server"; break;
+ case ASSUAN_Not_A_Server: s="not a server"; break;
+ case ASSUAN_Not_A_Client: s="not a client"; break;
+ case ASSUAN_Nested_Commands: s="nested commands"; break;
+ case ASSUAN_Invalid_Response: s="invalid response"; break;
+ case ASSUAN_No_Data_Callback: s="no data callback"; break;
+ case ASSUAN_No_Inquire_Callback: s="no inquire callback"; break;
+ case ASSUAN_Connect_Failed: s="connect failed"; break;
+ case ASSUAN_Accept_Failed: s="accept failed"; break;
+ case ASSUAN_Not_Implemented: s="not implemented"; break;
+ case ASSUAN_Server_Fault: s="server fault"; break;
+ case ASSUAN_Invalid_Command: s="invalid command"; break;
+ case ASSUAN_Unknown_Command: s="unknown command"; break;
+ case ASSUAN_Syntax_Error: s="syntax error"; break;
+ case ASSUAN_Parameter_Error: s="parameter error"; break;
+ case ASSUAN_Parameter_Conflict: s="parameter conflict"; break;
+ case ASSUAN_Line_Too_Long: s="line too long"; break;
+ case ASSUAN_Line_Not_Terminated: s="line not terminated"; break;
+ case ASSUAN_No_Input: s="no input"; break;
+ case ASSUAN_No_Output: s="no output"; break;
+ case ASSUAN_Canceled: s="canceled"; break;
+ case ASSUAN_Unsupported_Algorithm: s="unsupported algorithm"; break;
+ case ASSUAN_Server_Resource_Problem: s="server resource problem"; break;
+ case ASSUAN_Server_IO_Error: s="server io error"; break;
+ case ASSUAN_Server_Bug: s="server bug"; break;
+ case ASSUAN_No_Data_Available: s="no data available"; break;
+ case ASSUAN_Invalid_Data: s="invalid data"; break;
+ case ASSUAN_Unexpected_Command: s="unexpected command"; break;
+ case ASSUAN_Too_Much_Data: s="too much data"; break;
+ case ASSUAN_Inquire_Unknown: s="inquire unknown"; break;
+ case ASSUAN_Inquire_Error: s="inquire error"; break;
+ case ASSUAN_Invalid_Option: s="invalid option"; break;
+ case ASSUAN_Invalid_Index: s="invalid index"; break;
+ case ASSUAN_Unexpected_Status: s="unexpected status"; break;
+ case ASSUAN_Unexpected_Data: s="unexpected data"; break;
+ case ASSUAN_Invalid_Status: s="invalid status"; break;
+ case ASSUAN_Locale_Problem: s="locale problem"; break;
+ case ASSUAN_Not_Confirmed: s="not confirmed"; break;
+ case ASSUAN_USER_ERROR_FIRST: s="user error first"; break;
+ case ASSUAN_USER_ERROR_LAST: s="user error last"; break;
+ default:
+ {
+ unsigned int source, code;
+
+ source = ((err >> 24) & 0xff);
+ code = (err & 0x00ffffff);
+ if (source) /* Assume this is an libgpg-error. */
+ sprintf (buf, "ec=%u.%u", source, code );
+ else
+ sprintf (buf, "ec=%d", err );
+ s=buf; break;
+ }
+ }
+
+ return s;
+}
+
diff --git a/util/assuan-logging.c b/util/assuan-logging.c
new file mode 100644
index 0000000..f2d6c87
--- /dev/null
+++ b/util/assuan-logging.c
@@ -0,0 +1,116 @@
+/* assuan-logging.c - Default logging function.
+ * Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc.
+ *
+ * This file is part of Assuan.
+ *
+ * Assuan is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Assuan is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+/* Please note that this is a stripped down and modified version of
+ the orginal Assuan code from libassuan. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#ifdef HAVE_W32_SYSTEM
+#include <windows.h>
+#endif /*HAVE_W32_SYSTEM*/
+
+#include "assuan-defs.h"
+
+static char prefix_buffer[80];
+static FILE *_assuan_log;
+
+void
+_assuan_set_default_log_stream (FILE *fp)
+{
+ if (!_assuan_log)
+ _assuan_log = fp;
+}
+
+void
+assuan_set_assuan_log_stream (FILE *fp)
+{
+ _assuan_log = fp;
+}
+
+FILE *
+assuan_get_assuan_log_stream (void)
+{
+ return _assuan_log ? _assuan_log : stderr;
+}
+
+
+/* Set the prefix to be used for logging to TEXT or
+ resets it to the default if TEXT is NULL. */
+void
+assuan_set_assuan_log_prefix (const char *text)
+{
+ if (text)
+ {
+ strncpy (prefix_buffer, text, sizeof (prefix_buffer)-1);
+ prefix_buffer[sizeof (prefix_buffer)-1] = 0;
+ }
+ else
+ *prefix_buffer = 0;
+}
+
+const char *
+assuan_get_assuan_log_prefix (void)
+{
+ return prefix_buffer;
+}
+
+
+void
+_assuan_log_printf (const char *format, ...)
+{
+ va_list arg_ptr;
+ FILE *fp;
+ const char *prf;
+
+ fp = assuan_get_assuan_log_stream ();
+ prf = assuan_get_assuan_log_prefix ();
+ if (*prf)
+ {
+ fputs (prf, fp);
+ fputs (": ", fp);
+ }
+
+ va_start (arg_ptr, format);
+ vfprintf (fp, format, arg_ptr );
+ va_end (arg_ptr);
+}
+
+
+
+#ifdef HAVE_W32_SYSTEM
+const char *
+_assuan_w32_strerror (int ec)
+{
+ static char strerr[256];
+
+ if (ec == -1)
+ ec = (int)GetLastError ();
+ FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, ec,
+ MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
+ strerr, sizeof (strerr)-1, NULL);
+ return strerr;
+}
+#endif /*HAVE_W32_SYSTEM*/
diff --git a/util/assuan-socket-connect.c b/util/assuan-socket-connect.c
new file mode 100644
index 0000000..0c580cd
--- /dev/null
+++ b/util/assuan-socket-connect.c
@@ -0,0 +1,191 @@
+/* assuan-socket-connect.c - Assuan socket based client
+ * Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc.
+ *
+ * This file is part of Assuan.
+ *
+ * Assuan is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Assuan is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+/* Please note that this is a stripped down and modified version of
+ the orginal Assuan code from libassuan. */
+
+#include <config.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#ifndef HAVE_W32_SYSTEM
+#include <sys/socket.h>
+#include <sys/un.h>
+#else
+#include <windows.h>
+#endif
+
+#include "assuan-defs.h"
+
+/* Hacks for Slowaris. */
+#ifndef PF_LOCAL
+# ifdef PF_UNIX
+# define PF_LOCAL PF_UNIX
+# else
+# define PF_LOCAL AF_UNIX
+# endif
+#endif
+#ifndef AF_LOCAL
+# define AF_LOCAL AF_UNIX
+#endif
+
+#ifndef SUN_LEN
+# define SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path) \
+ + strlen ((ptr)->sun_path))
+#endif
+
+
+static int
+do_finish (assuan_context_t ctx)
+{
+ if (ctx->inbound.fd != -1)
+ {
+ _assuan_close (ctx->inbound.fd);
+ }
+ ctx->inbound.fd = -1;
+ ctx->outbound.fd = -1;
+ return 0;
+}
+
+static void
+do_deinit (assuan_context_t ctx)
+{
+ do_finish (ctx);
+}
+
+
+static ssize_t
+simple_read (assuan_context_t ctx, void *buffer, size_t size)
+{
+#ifndef HAVE_W32_SYSTEM
+ return read (ctx->inbound.fd, buffer, size);
+#else
+ return recv (ctx->inbound.fd, buffer, size, 0);
+#endif
+}
+
+static ssize_t
+simple_write (assuan_context_t ctx, const void *buffer, size_t size)
+{
+#ifndef HAVE_W32_SYSTEM
+ return write (ctx->outbound.fd, buffer, size);
+#else
+ return send (ctx->outbound.fd, buffer, size, 0);
+#endif
+}
+
+
+/* Make a connection to the Unix domain socket NAME and return a new
+ Assuan context in CTX. SERVER_PID is currently not used but may
+ become handy in the future. */
+assuan_error_t
+assuan_socket_connect (assuan_context_t *r_ctx,
+ const char *name, pid_t server_pid)
+{
+ static struct assuan_io io = { simple_read, simple_write };
+
+ assuan_error_t err;
+ assuan_context_t ctx;
+ int fd;
+ struct sockaddr_un srvr_addr;
+ size_t len;
+ const char *s;
+
+ if (!r_ctx || !name)
+ return ASSUAN_Invalid_Value;
+ *r_ctx = NULL;
+
+ /* We require that the name starts with a slash, so that we can
+ alter reuse this function for other socket types. To make things
+ easier we allow an optional dirver prefix. */
+ s = name;
+ if (*s && s[1] == ':')
+ s += 2;
+ if (*s != DIRSEP_C && *s != '/')
+ return ASSUAN_Invalid_Value;
+
+ if (strlen (name)+1 >= sizeof srvr_addr.sun_path)
+ return ASSUAN_Invalid_Value;
+
+ err = _assuan_new_context (&ctx);
+ if (err)
+ return err;
+ ctx->deinit_handler = do_deinit;
+ ctx->finish_handler = do_finish;
+
+
+ fd = _assuan_sock_new (PF_LOCAL, SOCK_STREAM, 0);
+ if (fd == -1)
+ {
+ _assuan_log_printf ("can't create socket: %s\n", strerror (errno));
+ _assuan_release_context (ctx);
+ return ASSUAN_General_Error;
+ }
+
+ memset (&srvr_addr, 0, sizeof srvr_addr);
+ srvr_addr.sun_family = AF_LOCAL;
+ strncpy (srvr_addr.sun_path, name, sizeof (srvr_addr.sun_path) - 1);
+ srvr_addr.sun_path[sizeof (srvr_addr.sun_path) - 1] = 0;
+ len = SUN_LEN (&srvr_addr);
+
+
+ if (_assuan_sock_connect (fd, (struct sockaddr *) &srvr_addr, len) == -1)
+ {
+ _assuan_log_printf ("can't connect to `%s': %s\n",
+ name, strerror (errno));
+ _assuan_release_context (ctx);
+ _assuan_close (fd);
+ return ASSUAN_Connect_Failed;
+ }
+
+ ctx->inbound.fd = fd;
+ ctx->outbound.fd = fd;
+ ctx->io = &io;
+
+ /* initial handshake */
+ {
+ int okay, off;
+
+ err = _assuan_read_from_server (ctx, &okay, &off);
+ if (err)
+ _assuan_log_printf ("can't connect to server: %s\n",
+ assuan_strerror (err));
+ else if (okay != 1)
+ {
+ /*LOG ("can't connect to server: `");*/
+ _assuan_log_sanitized_string (ctx->inbound.line);
+ fprintf (assuan_get_assuan_log_stream (), "'\n");
+ err = ASSUAN_Connect_Failed;
+ }
+ }
+
+ if (err)
+ {
+ assuan_disconnect (ctx);
+ }
+ else
+ *r_ctx = ctx;
+ return 0;
+}
diff --git a/util/assuan-socket.c b/util/assuan-socket.c
new file mode 100644
index 0000000..4d79792
--- /dev/null
+++ b/util/assuan-socket.c
@@ -0,0 +1,97 @@
+/* assuan-socket.c
+ * Copyright (C) 2004 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+/* Please note that this is a stripped down and modified version of
+ the orginal Assuan code from libassuan. */
+
+#include <config.h>
+#include <stdio.h>
+#ifdef HAVE_W32_SYSTEM
+#include <windows.h>
+#include <io.h>
+#else
+#include <sys/types.h>
+#include <sys/socket.h>
+#endif
+#include "assuan-defs.h"
+
+int
+_assuan_close (int fd)
+{
+#ifndef HAVE_W32_SYSTEM
+ return close (fd);
+#else
+ int rc = closesocket (fd);
+ if (rc && WSAGetLastError () == WSAENOTSOCK)
+ rc = close (fd);
+ return rc;
+#endif
+}
+
+
+int
+_assuan_sock_new (int domain, int type, int proto)
+{
+#ifndef HAVE_W32_SYSTEM
+ return socket (domain, type, proto);
+#else
+ if (domain == AF_UNIX || domain == AF_LOCAL)
+ domain = AF_INET;
+ return socket (domain, type, proto);
+#endif
+}
+
+
+int
+_assuan_sock_connect (int sockfd, struct sockaddr * addr, int addrlen)
+{
+#ifndef HAVE_W32_SYSTEM
+ return connect (sockfd, addr, addrlen);
+#else
+ struct sockaddr_in myaddr;
+ struct sockaddr_un * unaddr;
+ FILE * fp;
+ int port = 0;
+
+ unaddr = (struct sockaddr_un *)addr;
+ fp = fopen (unaddr->sun_path, "rb");
+ if (!fp)
+ return -1;
+ fscanf (fp, "%d", &port);
+ fclose (fp);
+ /* XXX: set errno in this case */
+ if (port < 0 || port > 65535)
+ return -1;
+
+ myaddr.sin_family = AF_INET;
+ myaddr.sin_port = port;
+ myaddr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
+
+ /* we need this later. */
+ unaddr->sun_family = myaddr.sin_family;
+ unaddr->sun_port = myaddr.sin_port;
+ unaddr->sun_addr.s_addr = myaddr.sin_addr.s_addr;
+
+ return connect (sockfd, (struct sockaddr *)&myaddr, sizeof myaddr);
+#endif
+}
+
+
diff --git a/util/assuan-util.c b/util/assuan-util.c
new file mode 100644
index 0000000..db3cb72
--- /dev/null
+++ b/util/assuan-util.c
@@ -0,0 +1,171 @@
+/* assuan-util.c - Utility functions for Assuan
+ * Copyright (C) 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+ * Copyright (C) 2005 Free Software Foundation, Inc.
+ *
+ * This file is part of Assuan.
+ *
+ * Assuan is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Assuan is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+/* Please note that this is a stripped down and modified version of
+ the orginal Assuan code from libassuan. */
+
+#include <config.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "assuan-defs.h"
+
+
+
+/* Store the error in the context so that the error sending function
+ can take out a descriptive text. Inside the assuan code, use the
+ macro set_error instead of this function. */
+int
+assuan_set_error (assuan_context_t ctx, int err, const char *text)
+{
+ ctx->err_no = err;
+ ctx->err_str = text;
+ return err;
+}
+
+void
+assuan_set_pointer (assuan_context_t ctx, void *pointer)
+{
+ if (ctx)
+ ctx->user_pointer = pointer;
+}
+
+void *
+assuan_get_pointer (assuan_context_t ctx)
+{
+ return ctx? ctx->user_pointer : NULL;
+}
+
+
+void
+assuan_set_log_stream (assuan_context_t ctx, FILE *fp)
+{
+ if (ctx)
+ {
+ if (ctx->log_fp)
+ fflush (ctx->log_fp);
+ ctx->log_fp = fp;
+ _assuan_set_default_log_stream (fp);
+ }
+}
+
+
+void
+assuan_begin_confidential (assuan_context_t ctx)
+{
+ if (ctx)
+ {
+ ctx->confidential = 1;
+ }
+}
+
+void
+assuan_end_confidential (assuan_context_t ctx)
+{
+ if (ctx)
+ {
+ ctx->confidential = 0;
+ }
+}
+
+/* Dump a possibly binary string (used for debugging). Distinguish
+ ascii text from binary and print it accordingly. */
+void
+_assuan_log_print_buffer (FILE *fp, const void *buffer, size_t length)
+{
+ const unsigned char *s;
+ int n;
+
+ for (n=length,s=buffer; n; n--, s++)
+ if ((!isascii (*s) || iscntrl (*s) || !isprint (*s)) && !(*s >= 0x80))
+ break;
+
+ s = buffer;
+ if (!n && *s != '[')
+ fwrite (buffer, length, 1, fp);
+ else
+ {
+ putc ('[', fp);
+ for (n=0; n < length; n++, s++)
+ fprintf (fp, " %02x", *s);
+ putc (' ', fp);
+ putc (']', fp);
+ }
+}
+
+/* Log a user supplied string. Escapes non-printable before
+ printing. */
+void
+_assuan_log_sanitized_string (const char *string)
+{
+ const unsigned char *s = string;
+ FILE *fp = assuan_get_assuan_log_stream ();
+
+ if (! *s)
+ return;
+
+ for (; *s; s++)
+ {
+ int c = 0;
+
+ switch (*s)
+ {
+ case '\r':
+ c = 'r';
+ break;
+
+ case '\n':
+ c = 'n';
+ break;
+
+ case '\f':
+ c = 'f';
+ break;
+
+ case '\v':
+ c = 'v';
+ break;
+
+ case '\b':
+ c = 'b';
+ break;
+
+ default:
+ if ((isascii (*s) && isprint (*s)) || (*s >= 0x80))
+ putc (*s, fp);
+ else
+ {
+ putc ('\\', fp);
+ fprintf (fp, "x%02x", *s);
+ }
+ }
+
+ if (c)
+ {
+ putc ('\\', fp);
+ putc (c, fp);
+ }
+ }
+}
+
diff --git a/util/cert.c b/util/cert.c
new file mode 100644
index 0000000..1b3390b
--- /dev/null
+++ b/util/cert.c
@@ -0,0 +1,251 @@
+/* cert.c - DNS CERT code
+ * Copyright (C) 2005, 2006 Free Software Foundation, Inc.
+ *
+ * This file is part of GNUPG.
+ *
+ * GNUPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GNUPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <sys/types.h>
+#ifdef USE_DNS_CERT
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+#endif
+#include <string.h>
+#include "memory.h"
+#endif
+#include "iobuf.h"
+#include "util.h"
+
+/* Not every installation has gotten around to supporting CERTs
+ yet... */
+#ifndef T_CERT
+#define T_CERT 37
+#endif
+
+#ifdef USE_DNS_CERT
+
+/* Returns -1 on error, 0 for no answer, 1 for PGP provided and 2 for
+ IPGP provided. */
+int
+get_cert(const char *name,size_t max_size,IOBUF *iobuf,
+ unsigned char **fpr,size_t *fpr_len,char **url)
+{
+ unsigned char *answer;
+ int r,ret=-1;
+ u16 count;
+
+ if(fpr)
+ *fpr=NULL;
+
+ if(url)
+ *url=NULL;
+
+ answer=xmalloc(max_size);
+
+ r=res_query(name,C_IN,T_CERT,answer,max_size);
+ /* Not too big, not too small, no errors and at least 1 answer. */
+ if(r>=sizeof(HEADER) && r<=max_size
+ && (((HEADER *)answer)->rcode)==NOERROR
+ && (count=ntohs(((HEADER *)answer)->ancount)))
+ {
+ int rc;
+ unsigned char *pt,*emsg;
+
+ emsg=&answer[r];
+
+ pt=&answer[sizeof(HEADER)];
+
+ /* Skip over the query */
+
+ rc=dn_skipname(pt,emsg);
+ if(rc==-1)
+ goto fail;
+
+ pt+=rc+QFIXEDSZ;
+
+ /* There are several possible response types for a CERT request.
+ We're interested in the PGP (a key) and IPGP (a URI) types.
+ Skip all others. TODO: A key is better than a URI since
+ we've gone through all this bother to fetch it, so favor that
+ if we have both PGP and IPGP? */
+
+ while(count-->0 && pt<emsg)
+ {
+ u16 type,class,dlen,ctype;
+
+ rc=dn_skipname(pt,emsg); /* the name we just queried for */
+ if(rc==-1)
+ break;
+
+ pt+=rc;
+
+ /* Truncated message? 15 bytes takes us to the point where
+ we start looking at the ctype. */
+ if((emsg-pt)<15)
+ break;
+
+ type=*pt++ << 8;
+ type|=*pt++;
+
+ class=*pt++ << 8;
+ class|=*pt++;
+ /* We asked for IN and got something else !? */
+ if(class!=C_IN)
+ break;
+
+ /* ttl */
+ pt+=4;
+
+ /* data length */
+ dlen=*pt++ << 8;
+ dlen|=*pt++;
+
+ /* We asked for CERT and got something else - might be a
+ CNAME, so loop around again. */
+ if(type!=T_CERT)
+ {
+ pt+=dlen;
+ continue;
+ }
+
+ /* The CERT type */
+ ctype=*pt++ << 8;
+ ctype|=*pt++;
+
+ /* Skip the CERT key tag and algo which we don't need. */
+ pt+=3;
+
+ dlen-=5;
+
+ /* 15 bytes takes us to here */
+
+ if(ctype==3 && iobuf && dlen)
+ {
+ /* PGP type */
+ *iobuf=iobuf_temp_with_content((char *)pt,dlen);
+ ret=1;
+ break;
+ }
+ else if(ctype==6 && dlen && dlen<1023 && dlen>=pt[0]+1
+ && fpr && fpr_len && url)
+ {
+ /* IPGP type */
+ *fpr_len=pt[0];
+
+ if(*fpr_len)
+ {
+ *fpr=xmalloc(*fpr_len);
+ memcpy(*fpr,&pt[1],*fpr_len);
+ }
+ else
+ *fpr=NULL;
+
+ if(dlen>*fpr_len+1)
+ {
+ *url=xmalloc(dlen-(*fpr_len+1)+1);
+ memcpy(*url,&pt[*fpr_len+1],dlen-(*fpr_len+1));
+ (*url)[dlen-(*fpr_len+1)]='\0';
+ }
+ else
+ *url=NULL;
+
+ ret=2;
+ break;
+ }
+
+ /* Neither type matches, so go around to the next answer. */
+ pt+=dlen;
+ }
+ }
+
+ fail:
+ xfree(answer);
+
+ return ret;
+}
+
+#else /* !USE_DNS_CERT */
+
+int
+get_cert(const char *name,size_t max_size,IOBUF *iobuf,
+ unsigned char **fpr,size_t *fpr_len,char **url)
+{
+ return -1;
+}
+
+#endif
+
+/* Test with simon.josefsson.org */
+
+#ifdef TEST
+int
+main(int argc,char *argv[])
+{
+ unsigned char *fpr;
+ size_t fpr_len;
+ char *url;
+ int rc;
+ IOBUF iobuf;
+
+ if(argc!=2)
+ {
+ printf("cert-test [name]\n");
+ return 1;
+ }
+
+ printf("CERT lookup on %s\n",argv[1]);
+
+ rc=get_cert(argv[1],16384,&iobuf,&fpr,&fpr_len,&url);
+ if(rc==-1)
+ printf("error\n");
+ else if(rc==0)
+ printf("no answer\n");
+ else if(rc==1)
+ {
+ printf("key found: %d bytes\n",(int)iobuf_get_temp_length(iobuf));
+ iobuf_close(iobuf);
+ }
+ else if(rc==2)
+ {
+ if(fpr)
+ {
+ size_t i;
+ printf("Fingerprint found (%d bytes): ",(int)fpr_len);
+ for(i=0;i<fpr_len;i++)
+ printf("%02X",fpr[i]);
+ printf("\n");
+ }
+ else
+ printf("No fingerprint found\n");
+
+ if(url)
+ printf("URL found: %s\n",url);
+ else
+ printf("No URL found\n");
+
+ xfree(fpr);
+ xfree(url);
+ }
+
+ return 0;
+}
+#endif /* TEST */
diff --git a/util/compat.c b/util/compat.c
new file mode 100644
index 0000000..aca558a
--- /dev/null
+++ b/util/compat.c
@@ -0,0 +1,24 @@
+int
+hextobyte (const char *s)
+{
+ int c;
+
+ if ( *s >= '0' && *s <= '9' )
+ c = 16 * (*s - '0');
+ else if ( *s >= 'A' && *s <= 'F' )
+ c = 16 * (10 + *s - 'A');
+ else if ( *s >= 'a' && *s <= 'f' )
+ c = 16 * (10 + *s - 'a');
+ else
+ return -1;
+ s++;
+ if ( *s >= '0' && *s <= '9' )
+ c += *s - '0';
+ else if ( *s >= 'A' && *s <= 'F' )
+ c += 10 + *s - 'A';
+ else if ( *s >= 'a' && *s <= 'f' )
+ c += 10 + *s - 'a';
+ else
+ return -1;
+ return c;
+}
diff --git a/util/dotlock.c b/util/dotlock.c
new file mode 100644
index 0000000..9edac57
--- /dev/null
+++ b/util/dotlock.c
@@ -0,0 +1,454 @@
+/* dotlock.c - dotfile locking
+ * Copyright (C) 1998, 1999, 2000, 2001, 2004,
+ * 2005 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <errno.h>
+#include <unistd.h>
+#if !defined (HAVE_DOSISH_SYSTEM)
+#include <sys/utsname.h>
+#endif
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/time.h>
+#endif
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <signal.h>
+#include "types.h"
+#include "util.h"
+#include "memory.h"
+
+struct dotlock_handle {
+ struct dotlock_handle *next;
+ char *tname; /* name of lockfile template */
+ char *lockname; /* name of the real lockfile */
+ int locked; /* lock status */
+ int disable; /* locking */
+};
+
+
+static volatile DOTLOCK all_lockfiles;
+static int never_lock;
+
+void
+disable_dotlock(void)
+{
+ never_lock = 1;
+}
+
+/****************
+ * Create a lockfile with the given name and return an object of
+ * type DOTLOCK which may be used later to actually do the lock.
+ * A cleanup routine gets installed to cleanup left over locks
+ * or other files used together with the lockmechanism.
+ * Althoug the function is called dotlock, this does not necessarily
+ * mean that real lockfiles are used - the function may decide to
+ * use fcntl locking. Calling the function with NULL only install
+ * the atexit handler and maybe used to assure that the cleanup
+ * is called after all other atexit handlers.
+ *
+ * Notes: This function creates a lock file in the same directory
+ * as file_to_lock with the name "file_to_lock.lock"
+ * A temporary file ".#lk.<hostname>.pid[.threadid] is used.
+ * This function does nothing for Windoze.
+ */
+DOTLOCK
+create_dotlock( const char *file_to_lock )
+{
+ static int initialized;
+ DOTLOCK h;
+#if !defined (HAVE_DOSISH_SYSTEM)
+ int fd = -1;
+ char pidstr[16];
+ struct utsname utsbuf;
+ const char *nodename;
+ const char *dirpart;
+ int dirpartlen;
+#endif
+
+ if( !initialized ) {
+ atexit( remove_lockfiles );
+ initialized = 1;
+ }
+ if( !file_to_lock )
+ return NULL;
+
+ h = xmalloc_clear( sizeof *h );
+ if( never_lock ) {
+ h->disable = 1;
+#ifdef _REENTRANT
+ /* fixme: aquire mutex on all_lockfiles */
+#endif
+ h->next = all_lockfiles;
+ all_lockfiles = h;
+ return h;
+ }
+
+
+#if !defined (HAVE_DOSISH_SYSTEM)
+ sprintf( pidstr, "%10d\n", (int)getpid() );
+ /* fixme: add the hostname to the second line (FQDN or IP addr?) */
+
+ /* create a temporary file */
+ if( uname( &utsbuf ) )
+ nodename = "unknown";
+ else
+ nodename = utsbuf.nodename;
+
+#ifdef __riscos__
+ {
+ char *iter = (char *) nodename;
+ for (; iter[0]; iter++)
+ if (iter[0] == '.')
+ iter[0] = '/';
+ }
+#endif /* __riscos__ */
+
+ if( !(dirpart = strrchr( file_to_lock, DIRSEP_C )) ) {
+ dirpart = EXTSEP_S;
+ dirpartlen = 1;
+ }
+ else {
+ dirpartlen = dirpart - file_to_lock;
+ dirpart = file_to_lock;
+ }
+
+#ifdef _REENTRANT
+ /* fixme: aquire mutex on all_lockfiles */
+#endif
+ h->next = all_lockfiles;
+ all_lockfiles = h;
+
+ h->tname = xmalloc( dirpartlen + 6+30+ strlen(nodename) + 11 );
+#ifndef __riscos__
+ sprintf( h->tname, "%.*s/.#lk%p.%s.%d",
+ dirpartlen, dirpart, (void *)h, nodename, (int)getpid() );
+#else /* __riscos__ */
+ sprintf( h->tname, "%.*s.lk%p/%s/%d",
+ dirpartlen, dirpart, (void *)h, nodename, (int)getpid() );
+#endif /* __riscos__ */
+
+ do {
+ errno = 0;
+ fd = open( h->tname, O_WRONLY|O_CREAT|O_EXCL,
+ S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR );
+ } while( fd == -1 && errno == EINTR );
+ if( fd == -1 ) {
+ all_lockfiles = h->next;
+ log_error( "failed to create temporary file `%s': %s\n",
+ h->tname, strerror(errno));
+ xfree(h->tname);
+ xfree(h);
+ return NULL;
+ }
+ if( write(fd, pidstr, 11 ) != 11 ) {
+ all_lockfiles = h->next;
+#ifdef _REENTRANT
+ /* release mutex */
+#endif
+ log_fatal( "error writing to `%s': %s\n", h->tname, strerror(errno) );
+ close(fd);
+ unlink(h->tname);
+ xfree(h->tname);
+ xfree(h);
+ return NULL;
+ }
+ if( close(fd) ) {
+ all_lockfiles = h->next;
+#ifdef _REENTRANT
+ /* release mutex */
+#endif
+ log_error( "error closing `%s': %s\n", h->tname, strerror(errno));
+ unlink(h->tname);
+ xfree(h->tname);
+ xfree(h);
+ return NULL;
+ }
+
+#ifdef _REENTRANT
+ /* release mutex */
+#endif
+#endif
+ h->lockname = xmalloc( strlen(file_to_lock) + 6 );
+ strcpy(stpcpy(h->lockname, file_to_lock), EXTSEP_S "lock");
+ return h;
+}
+
+
+void
+destroy_dotlock ( DOTLOCK h )
+{
+#if !defined (HAVE_DOSISH_SYSTEM)
+ if ( h )
+ {
+ DOTLOCK hprev, htmp;
+
+ /* First remove the handle from our global list of all locks. */
+ for (hprev=NULL, htmp=all_lockfiles; htmp; hprev=htmp, htmp=htmp->next)
+ if (htmp == h)
+ {
+ if (hprev)
+ hprev->next = htmp->next;
+ else
+ all_lockfiles = htmp->next;
+ h->next = NULL;
+ break;
+ }
+
+ /* Second destroy the lock. */
+ if (!h->disable)
+ {
+ if (h->locked && h->lockname)
+ unlink (h->lockname);
+ if (h->tname)
+ unlink (h->tname);
+ xfree (h->tname);
+ xfree (h->lockname);
+ }
+ xfree(h);
+
+ }
+#endif
+}
+
+#ifndef HAVE_DOSISH_SYSTEM
+
+static int
+maybe_deadlock( DOTLOCK h )
+{
+ DOTLOCK r;
+
+ for( r=all_lockfiles; r; r = r->next ) {
+ if( r != h && r->locked )
+ return 1;
+ }
+ return 0;
+}
+
+/****************
+ * Read the lock file and return the pid, returns -1 on error.
+ */
+static int
+read_lockfile( const char *name )
+{
+ int fd, pid;
+ char pidstr[16];
+
+ if( (fd = open(name, O_RDONLY)) == -1 ) {
+ int e = errno;
+ log_debug("error opening lockfile `%s': %s\n", name, strerror(errno) );
+ errno = e;
+ return -1;
+ }
+ if( read(fd, pidstr, 10 ) != 10 ) { /* Read 10 digits w/o newline */
+ log_debug("error reading lockfile `%s'", name );
+ close(fd);
+ errno = 0;
+ return -1;
+ }
+ pidstr[10] = 0; /* terminate pid string */
+ close(fd);
+ pid = atoi(pidstr);
+#ifndef __riscos__
+ if( !pid || pid == -1 ) {
+#else /* __riscos__ */
+ if( (!pid && riscos_getpid()) || pid == -1 ) {
+#endif /* __riscos__ */
+ log_error("invalid pid %d in lockfile `%s'", pid, name );
+ errno = 0;
+ return -1;
+ }
+ return pid;
+}
+#endif /* !HAVE_DOSISH_SYSTEM */
+
+/****************
+ * Do a lock on H. A TIMEOUT of 0 returns immediately,
+ * -1 waits forever (hopefully not), other
+ * values are timeouts in milliseconds.
+ * Returns: 0 on success
+ */
+int
+make_dotlock( DOTLOCK h, long timeout )
+{
+#if defined (HAVE_DOSISH_SYSTEM)
+ return 0;
+#else
+ int pid;
+ const char *maybe_dead="";
+ int backoff=0;
+
+ if( h->disable ) {
+ return 0;
+ }
+
+ if( h->locked ) {
+#ifndef __riscos__
+ log_debug("oops, `%s' is already locked\n", h->lockname );
+#endif /* !__riscos__ */
+ return 0;
+ }
+
+ for(;;) {
+#ifndef __riscos__
+ if( !link(h->tname, h->lockname) ) {
+ /* fixme: better use stat to check the link count */
+ h->locked = 1;
+ return 0; /* okay */
+ }
+ if( errno != EEXIST ) {
+ log_error( "lock not made: link() failed: %s\n", strerror(errno) );
+ return -1;
+ }
+#else /* __riscos__ */
+ if( !riscos_renamefile(h->tname, h->lockname) ) {
+ h->locked = 1;
+ return 0; /* okay */
+ }
+ if( errno != EEXIST ) {
+ log_error( "lock not made: rename() failed: %s\n", strerror(errno) );
+ return -1;
+ }
+#endif /* __riscos__ */
+ if( (pid = read_lockfile(h->lockname)) == -1 ) {
+ if( errno != ENOENT ) {
+ log_info("cannot read lockfile\n");
+ return -1;
+ }
+ log_info( "lockfile disappeared\n");
+ continue;
+ }
+ else if( pid == getpid() ) {
+ log_info( "Oops: lock already held by us\n");
+ h->locked = 1;
+ return 0; /* okay */
+ }
+ else if( kill(pid, 0) && errno == ESRCH ) {
+#ifndef __riscos__
+ maybe_dead = " - probably dead";
+#if 0 /* we should not do this without checking the permissions */
+ /* and the hostname */
+ log_info( "removing stale lockfile (created by %d)", pid );
+#endif
+#else /* __riscos__ */
+ /* we are *pretty* sure that the other task is dead and therefore
+ we remove the other lock file */
+ maybe_dead = " - probably dead - removing lock";
+ unlink(h->lockname);
+#endif /* __riscos__ */
+ }
+ if( timeout == -1 ) {
+ struct timeval tv;
+ log_info( "waiting for lock (held by %d%s) %s...\n",
+ pid, maybe_dead, maybe_deadlock(h)? "(deadlock?) ":"");
+
+
+ /* can't use sleep, cause signals may be blocked */
+ tv.tv_sec = 1 + backoff;
+ tv.tv_usec = 0;
+ select(0, NULL, NULL, NULL, &tv);
+ if( backoff < 10 )
+ backoff++ ;
+ }
+ else
+ return -1;
+ }
+ /*not reached */
+#endif
+}
+
+
+/****************
+ * release a lock
+ * Returns: 0 := success
+ */
+int
+release_dotlock( DOTLOCK h )
+{
+#if defined (HAVE_DOSISH_SYSTEM)
+ return 0;
+#else
+ int pid;
+
+ /* To avoid atexit race conditions we first check whether there
+ are any locks left. It might happen that another atexit
+ handler tries to release the lock while the atexit handler of
+ this module already ran and thus H is undefined. */
+ if(!all_lockfiles)
+ return 0;
+
+ if( h->disable )
+ return 0;
+
+ if( !h->locked ) {
+ log_debug("oops, `%s' is not locked\n", h->lockname );
+ return 0;
+ }
+
+ pid = read_lockfile( h->lockname );
+ if( pid == -1 ) {
+ log_error( "release_dotlock: lockfile error\n");
+ return -1;
+ }
+ if( pid != getpid() ) {
+ log_error( "release_dotlock: not our lock (pid=%d)\n", pid);
+ return -1;
+ }
+#ifndef __riscos__
+ if( unlink( h->lockname ) ) {
+ log_error( "release_dotlock: error removing lockfile `%s'",
+ h->lockname);
+ return -1;
+ }
+#else /* __riscos__ */
+ if( riscos_renamefile(h->lockname, h->tname) ) {
+ log_error( "release_dotlock: error renaming lockfile `%s' to `%s'",
+ h->lockname, h->tname);
+ return -1;
+ }
+#endif /* __riscos__ */
+ /* fixme: check that the link count is now 1 */
+ h->locked = 0;
+ return 0;
+#endif
+}
+
+void
+remove_lockfiles()
+{
+#if !defined (HAVE_DOSISH_SYSTEM)
+ DOTLOCK h, h2;
+
+ h = all_lockfiles;
+ all_lockfiles = NULL;
+
+ while( h ) {
+ h2 = h->next;
+ destroy_dotlock (h);
+ h = h2;
+ }
+#endif
+}
diff --git a/util/errors.c b/util/errors.c
new file mode 100644
index 0000000..75498f8
--- /dev/null
+++ b/util/errors.c
@@ -0,0 +1,116 @@
+/* errors.c - error strings
+ * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include "errors.h"
+#include "i18n.h"
+
+#ifndef HAVE_STRERROR
+char *
+strerror( int n )
+{
+ extern char *sys_errlist[];
+ extern int sys_nerr;
+ static char buf[15];
+
+ if( n >= 0 && n < sys_nerr )
+ return sys_errlist[n];
+ strcpy( buf, "Unknown error" );
+ return buf;
+}
+#endif /* !HAVE_STRERROR */
+
+const char *
+g10_errstr( int err )
+{
+ static char buf[50];
+ const char *p;
+
+#define X(n,s) case G10ERR_##n : p = s; break;
+ switch( err ) {
+ case -1: p = "eof"; break;
+ case 0: p = "okay"; break;
+ X(GENERAL, N_("general error"))
+ X(UNKNOWN_PACKET, N_("unknown packet type"))
+ X(UNKNOWN_VERSION,N_("unknown version"))
+ X(PUBKEY_ALGO ,N_("unknown pubkey algorithm"))
+ X(DIGEST_ALGO ,N_("unknown digest algorithm"))
+ X(BAD_PUBKEY ,N_("bad public key"))
+ X(BAD_SECKEY ,N_("bad secret key"))
+ X(BAD_SIGN ,N_("bad signature"))
+ X(CHECKSUM , N_("checksum error"))
+ X(BAD_PASS , N_("bad passphrase"))
+ X(NO_PUBKEY ,N_("public key not found"))
+ X(CIPHER_ALGO ,N_("unknown cipher algorithm"))
+ X(KEYRING_OPEN ,N_("can't open the keyring"))
+ X(INVALID_PACKET ,N_("invalid packet"))
+ X(INVALID_ARMOR ,N_("invalid armor"))
+ X(NO_USER_ID ,N_("no such user id"))
+ X(NO_SECKEY ,N_("secret key not available"))
+ X(WRONG_SECKEY ,N_("wrong secret key used"))
+ X(UNSUPPORTED ,N_("not supported"))
+ X(BAD_KEY ,N_("bad key"))
+ X(READ_FILE ,N_("file read error"))
+ X(WRITE_FILE ,N_("file write error"))
+ X(COMPR_ALGO ,N_("unknown compress algorithm"))
+ X(OPEN_FILE ,N_("file open error"))
+ X(CREATE_FILE ,N_("file create error"))
+ X(PASSPHRASE ,N_("invalid passphrase"))
+ X(NI_PUBKEY ,N_("unimplemented pubkey algorithm"))
+ X(NI_CIPHER ,N_("unimplemented cipher algorithm"))
+ X(SIG_CLASS ,N_("unknown signature class"))
+ X(TRUSTDB ,N_("trust database error"))
+ X(BAD_MPI ,N_("bad MPI"))
+ X(RESOURCE_LIMIT ,N_("resource limit"))
+ X(INV_KEYRING ,N_("invalid keyring"))
+ X(BAD_CERT ,N_("bad certificate"))
+ X(INV_USER_ID ,N_("malformed user id"))
+ X(CLOSE_FILE ,N_("file close error"))
+ X(RENAME_FILE ,N_("file rename error"))
+ X(DELETE_FILE ,N_("file delete error"))
+ X(UNEXPECTED ,N_("unexpected data"))
+ X(TIME_CONFLICT ,N_("timestamp conflict"))
+ X(WR_PUBKEY_ALGO ,N_("unusable pubkey algorithm"))
+ X(FILE_EXISTS ,N_("file exists"))
+ X(WEAK_KEY ,N_("weak key"))
+ X(INV_ARG ,N_("invalid argument"))
+ X(BAD_URI ,N_("bad URI"))
+ X(INVALID_URI ,N_("unsupported URI"))
+ X(NETWORK ,N_("network error"))
+ X(SELFTEST_FAILED,"selftest failed")
+ X(NOT_ENCRYPTED ,N_("not encrypted"))
+ X(NOT_PROCESSED ,N_("not processed"))
+ /* the key cannot be used for a specific usage */
+ X(UNU_PUBKEY ,N_("unusable public key"))
+ X(UNU_SECKEY ,N_("unusable secret key"))
+ X(KEYSERVER ,N_("keyserver error"))
+ X(CANCELED ,N_("canceled"))
+ X(NO_CARD ,N_("no card"))
+ X(NO_DATA ,N_("no data"))
+ default: p = buf; sprintf(buf, "g10err=%d", err); break;
+ }
+#undef X
+ return _(p);
+}
diff --git a/util/fileutil.c b/util/fileutil.c
new file mode 100644
index 0000000..5834e3d
--- /dev/null
+++ b/util/fileutil.c
@@ -0,0 +1,297 @@
+/* fileutil.c - file utilities
+ * Copyright (C) 1998, 2003, 2005 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/types.h>
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+#include "util.h"
+#include "memory.h"
+#include "ttyio.h"
+
+
+/***************
+ * Extract from a given path the filename component.
+ *
+ */
+char *
+make_basename(const char *filepath, const char *inputpath)
+{
+#ifdef __riscos__
+ return riscos_make_basename(filepath, inputpath);
+#endif
+
+ char *p;
+
+ if ( !(p=strrchr(filepath, DIRSEP_C)) )
+#ifdef HAVE_DRIVE_LETTERS
+ if ( !(p=strrchr(filepath, '\\')) )
+ if ( !(p=strrchr(filepath, ':')) )
+#endif
+ {
+ return xstrdup(filepath);
+ }
+
+ return xstrdup(p+1);
+}
+
+
+
+/***************
+ * Extract from a given filename the path prepended to it.
+ * If their isn't a path prepended to the filename, a dot
+ * is returned ('.').
+ *
+ */
+char *
+make_dirname(const char *filepath)
+{
+ char *dirname;
+ int dirname_length;
+ char *p;
+
+ if ( !(p=strrchr(filepath, DIRSEP_C)) )
+#ifdef HAVE_DRIVE_LETTERS
+ if ( !(p=strrchr(filepath, '\\')) )
+ if ( !(p=strrchr(filepath, ':')) )
+#endif
+ {
+ return xstrdup(EXTSEP_S);
+ }
+
+ dirname_length = p-filepath;
+ dirname = xmalloc(dirname_length+1);
+ strncpy(dirname, filepath, dirname_length);
+ dirname[dirname_length] = 0;
+
+ return dirname;
+}
+
+/* Expand tildes. Handles both the ~/foo and ~username/foo cases.
+ Returns what the tilde expands to. *name is advanced to be past
+ the tilde expansion. */
+static char *
+untilde(const char **name)
+{
+ char *home=NULL;
+
+ assert((*name)[0]=='~');
+
+ if((*name)[1]==DIRSEP_C || (*name)[1]=='\0')
+ {
+ /* This is the "~/foo" or "~" case. */
+ char *tmp=getenv("HOME");
+ if(tmp)
+ home=xstrdup(tmp);
+
+#ifdef HAVE_GETPWUID
+ if(!home)
+ {
+ struct passwd *pwd;
+
+ pwd=getpwuid(getuid());
+ if(pwd)
+ home=xstrdup(pwd->pw_dir);
+ }
+#endif
+ if(home)
+ (*name)++;
+ }
+#ifdef HAVE_GETPWNAM
+ else
+ {
+ /* This is the "~username" case. */
+ char *user,*sep;
+ struct passwd *pwd;
+
+ user=xstrdup((*name)+1);
+
+ sep=strchr(user,DIRSEP_C);
+ if(sep)
+ *sep='\0';
+
+ pwd=getpwnam(user);
+ if(pwd)
+ {
+ home=xstrdup(pwd->pw_dir);
+ (*name)+=1+strlen(user);
+ }
+
+ xfree(user);
+ }
+#endif
+
+ return home;
+}
+
+/*
+ Construct a filename from the NULL terminated list of parts. Tilde
+ expansion is done here. Note that FIRST_PART must never be NULL and
+ that this function is guaranteed to return an allocated string. */
+char *
+make_filename( const char *first_part, ... )
+{
+ va_list arg_ptr ;
+ size_t n;
+ const char *s;
+ char *name, *p, *home=NULL;
+
+ va_start( arg_ptr, first_part ) ;
+ n = strlen(first_part)+1;
+ while( (s=va_arg(arg_ptr, const char *)) )
+ n += strlen(s) + 1;
+ va_end(arg_ptr);
+
+#ifndef __riscos__
+ if(*first_part=='~')
+ {
+ home=untilde(&first_part);
+ if(home)
+ n+=strlen(home);
+ }
+#endif
+ name = xmalloc(n);
+ p = home ? stpcpy(stpcpy(name,home), first_part)
+ : stpcpy(name, first_part);
+ va_start( arg_ptr, first_part ) ;
+ while( (s=va_arg(arg_ptr, const char *)) )
+ p = stpcpy(stpcpy(p, DIRSEP_S), s);
+ va_end(arg_ptr);
+ xfree(home);
+
+#ifndef __riscos__
+ return name;
+#else /* __riscos__ */
+ p = riscos_gstrans(name);
+ xfree(name);
+ return p;
+#endif /* __riscos__ */
+}
+
+
+int
+compare_filenames( const char *a, const char *b )
+{
+ /* ? check whether this is an absolute filename and
+ * resolve symlinks?
+ */
+#ifndef __riscos__
+#ifdef HAVE_DRIVE_LETTERS
+ return ascii_strcasecmp(a,b);
+#else
+ return strcmp(a,b);
+#endif
+#else /* __riscos__ */
+ int c = 0;
+ char *abuf, *bbuf;
+
+ abuf = riscos_gstrans(a);
+ bbuf = riscos_gstrans(b);
+
+ c = ascii_strcasecmp (abuf, bbuf);
+
+ xfree(abuf);
+ xfree(bbuf);
+
+ return c;
+#endif /* __riscos__ */
+}
+
+
+/****************
+ * A simple function to decide whether the filename is stdout
+ * or a real filename.
+ */
+const char *
+print_fname_stdout( const char *s )
+{
+ if( !s || (*s == '-' && !s[1]) )
+ return "[stdout]";
+ return s;
+}
+
+
+const char *
+print_fname_stdin( const char *s )
+{
+ if( !s || (*s == '-' && !s[1]) )
+ return "[stdin]";
+ return s;
+}
+
+/****************
+ * Check if the file is compressed.
+ **/
+int
+is_file_compressed( const char *s, int *ret_rc )
+{
+ IOBUF a;
+ byte buf[4];
+ int i, rc = 0;
+ int overflow;
+
+ struct magic_compress_s {
+ size_t len;
+ byte magic[4];
+ } magic[] = {
+ { 3, { 0x42, 0x5a, 0x68, 0x00 } }, /* bzip2 */
+ { 3, { 0x1f, 0x8b, 0x08, 0x00 } }, /* gzip */
+ { 4, { 0x50, 0x4b, 0x03, 0x04 } }, /* (pk)zip */
+ };
+
+ if ( iobuf_is_pipe_filename (s) || !ret_rc )
+ return 0; /* We can't check stdin or no file was given */
+
+ a = iobuf_open( s );
+ if ( a == NULL ) {
+ *ret_rc = G10ERR_OPEN_FILE;
+ return 0;
+ }
+
+ if ( iobuf_get_filelength( a, &overflow ) < 4 && !overflow) {
+ *ret_rc = 0;
+ goto leave;
+ }
+
+ if ( iobuf_read( a, buf, 4 ) == -1 ) {
+ *ret_rc = G10ERR_READ_FILE;
+ goto leave;
+ }
+
+ for ( i = 0; i < DIM( magic ); i++ ) {
+ if ( !memcmp( buf, magic[i].magic, magic[i].len ) ) {
+ *ret_rc = 0;
+ rc = 1;
+ break;
+ }
+ }
+
+leave:
+ iobuf_close( a );
+ return rc;
+}
diff --git a/util/http.c b/util/http.c
new file mode 100644
index 0000000..4100002
--- /dev/null
+++ b/util/http.c
@@ -0,0 +1,1076 @@
+/* http.c - HTTP protocol handler
+ * Copyright (C) 1999, 2001, 2002, 2003, 2004,
+ * 2005 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <time.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#endif
+
+#include "util.h"
+#include "iobuf.h"
+#include "i18n.h"
+#include "http.h"
+#include "srv.h"
+
+#ifdef _WIN32
+#define sock_close(a) closesocket(a)
+#else
+#define sock_close(a) close(a)
+#endif
+
+#define MAX_LINELEN 20000 /* max. length of a HTTP line */
+#define VALID_URI_CHARS "abcdefghijklmnopqrstuvwxyz" \
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
+ "01234567890@" \
+ "!\"#$%&'()*+,-./:;<=>?[\\]^_{|}~"
+
+#ifndef EAGAIN
+#define EAGAIN EWOULDBLOCK
+#endif
+
+static int parse_uri( PARSED_URI *ret_uri, const char *uri );
+static void release_parsed_uri( PARSED_URI uri );
+static int do_parse_uri( PARSED_URI uri, int only_local_part );
+static int remove_escapes( byte *string );
+static int insert_escapes( byte *buffer, const byte *string,
+ const byte *special );
+static URI_TUPLE parse_tuple( byte *string );
+static int send_request( HTTP_HD hd, const char *auth, const char *proxy );
+static byte *build_rel_path( PARSED_URI uri );
+static int parse_response( HTTP_HD hd );
+
+static int connect_server( const char *server, ushort port, unsigned int flags,
+ const char *srvtag );
+static int write_server( int sock, const char *data, size_t length );
+
+#ifdef _WIN32
+static void
+deinit_sockets (void)
+{
+ WSACleanup();
+}
+
+static void
+init_sockets (void)
+{
+ static int initialized;
+ static WSADATA wsdata;
+
+ if (initialized)
+ return;
+
+ if( WSAStartup( 0x0101, &wsdata ) ) {
+ log_error ("error initializing socket library: ec=%d\n",
+ (int)WSAGetLastError () );
+ return;
+ }
+ if( wsdata.wVersion < 0x0001 ) {
+ log_error ("socket library version is %x.%x - but 1.1 needed\n",
+ LOBYTE(wsdata.wVersion), HIBYTE(wsdata.wVersion));
+ WSACleanup();
+ return;
+ }
+ atexit ( deinit_sockets );
+ initialized = 1;
+}
+#endif /*_WIN32*/
+
+static byte bintoasc[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789+/";
+
+/****************
+ * create a radix64 encoded string.
+ */
+
+/* TODO: This is a duplicate of code in g10/armor.c modified to do the
+ "=" padding. Better to use a single copy in strgutil.c ? */
+static char *
+make_radix64_string( const byte *data, size_t len )
+{
+ char *buffer, *p;
+
+ buffer = p = xmalloc( (len+2)/3*4 + 1 );
+ for( ; len >= 3 ; len -= 3, data += 3 ) {
+ *p++ = bintoasc[(data[0] >> 2) & 077];
+ *p++ = bintoasc[(((data[0] <<4)&060)|((data[1] >> 4)&017))&077];
+ *p++ = bintoasc[(((data[1]<<2)&074)|((data[2]>>6)&03))&077];
+ *p++ = bintoasc[data[2]&077];
+ }
+ if( len == 2 ) {
+ *p++ = bintoasc[(data[0] >> 2) & 077];
+ *p++ = bintoasc[(((data[0] <<4)&060)|((data[1] >> 4)&017))&077];
+ *p++ = bintoasc[((data[1]<<2)&074)];
+ *p++ = '=';
+ }
+ else if( len == 1 ) {
+ *p++ = bintoasc[(data[0] >> 2) & 077];
+ *p++ = bintoasc[(data[0] <<4)&060];
+ *p++ = '=';
+ *p++ = '=';
+ }
+ *p = 0;
+ return buffer;
+}
+
+int
+http_open( HTTP_HD hd, HTTP_REQ_TYPE reqtype, const char *url,
+ char *auth, unsigned int flags, const char *proxy )
+{
+ int rc;
+
+ if( !(reqtype == HTTP_REQ_GET || reqtype == HTTP_REQ_POST) )
+ return G10ERR_INV_ARG;
+
+ /* initialize the handle */
+ memset( hd, 0, sizeof *hd );
+ hd->sock = -1;
+ hd->initialized = 1;
+ hd->req_type = reqtype;
+ hd->flags = flags;
+
+ rc = parse_uri( &hd->uri, url );
+ if( !rc ) {
+ rc = send_request( hd, auth, proxy );
+ if( !rc ) {
+ hd->fp_write = iobuf_sockopen( hd->sock , "w" );
+ if( hd->fp_write )
+ return 0;
+ rc = G10ERR_GENERAL;
+ }
+ }
+
+ if( !hd->fp_read && !hd->fp_write && hd->sock != -1 )
+ sock_close( hd->sock );
+ iobuf_close( hd->fp_read );
+ iobuf_close( hd->fp_write);
+ release_parsed_uri( hd->uri );
+ hd->initialized = 0;
+
+ return rc;
+}
+
+
+void
+http_start_data( HTTP_HD hd )
+{
+ iobuf_flush ( hd->fp_write );
+ if( !hd->in_data ) {
+ write_server (hd->sock, "\r\n", 2);
+ hd->in_data = 1;
+ }
+}
+
+
+int
+http_wait_response( HTTP_HD hd, unsigned int *ret_status )
+{
+ int rc;
+
+ http_start_data( hd ); /* make sure that we are in the data */
+
+#if 0
+ hd->sock = dup( hd->sock );
+ if( hd->sock == -1 )
+ return G10ERR_GENERAL;
+#endif
+ iobuf_ioctl (hd->fp_write, 1, 1, NULL); /* keep the socket open */
+ iobuf_close (hd->fp_write);
+ hd->fp_write = NULL;
+ if ( !(hd->flags & HTTP_FLAG_NO_SHUTDOWN) )
+ shutdown( hd->sock, 1 );
+ hd->in_data = 0;
+
+ hd->fp_read = iobuf_sockopen( hd->sock , "r" );
+ if( !hd->fp_read )
+ return G10ERR_GENERAL;
+
+ rc = parse_response( hd );
+ if( !rc && ret_status )
+ *ret_status = hd->status_code;
+
+ return rc;
+}
+
+
+int
+http_open_document( HTTP_HD hd, const char *document, char *auth,
+ unsigned int flags, const char *proxy )
+{
+ int rc;
+
+ rc = http_open(hd, HTTP_REQ_GET, document, auth, flags, proxy );
+ if( rc )
+ return rc;
+
+ rc = http_wait_response( hd, NULL );
+ if( rc )
+ http_close( hd );
+
+ return rc;
+}
+
+
+void
+http_close( HTTP_HD hd )
+{
+ if( !hd || !hd->initialized )
+ return;
+ if( !hd->fp_read && !hd->fp_write && hd->sock != -1 )
+ sock_close( hd->sock );
+ iobuf_close( hd->fp_read );
+ iobuf_close( hd->fp_write );
+ release_parsed_uri( hd->uri );
+ xfree( hd->buffer );
+ hd->initialized = 0;
+}
+
+
+
+/****************
+ * Parse an URI and put the result into the newly allocated ret_uri.
+ * The caller must always use release_parsed_uri to releases the
+ * resources (even on an error).
+ */
+static int
+parse_uri( PARSED_URI *ret_uri, const char *uri )
+{
+ *ret_uri = xmalloc_clear( sizeof(**ret_uri) + strlen(uri) );
+ strcpy( (*ret_uri)->buffer, uri );
+ return do_parse_uri( *ret_uri, 0 );
+}
+
+static void
+release_parsed_uri( PARSED_URI uri )
+{
+ if( uri )
+ {
+ URI_TUPLE r, r2;
+
+ for( r = uri->query; r; r = r2 ) {
+ r2 = r->next;
+ xfree( r );
+ }
+ xfree( uri );
+ }
+}
+
+static int
+do_parse_uri( PARSED_URI uri, int only_local_part )
+{
+ URI_TUPLE *tail;
+ char *p, *p2, *p3;
+ int n;
+
+ p = uri->buffer;
+ n = strlen( uri->buffer );
+ /* initialize all fields to an empty string or an empty list */
+ uri->scheme = uri->host = uri->path = p + n;
+ uri->port = 0;
+ uri->params = uri->query = NULL;
+
+ /* a quick validity check */
+ if( strspn( p, VALID_URI_CHARS) != n )
+ return G10ERR_BAD_URI; /* invalid characters found */
+
+ if( !only_local_part ) {
+ /* find the scheme */
+ if( !(p2 = strchr( p, ':' ) ) || p2 == p )
+ return G10ERR_BAD_URI; /* No scheme */
+ *p2++ = 0;
+ strlwr( p );
+ uri->scheme = p;
+ if(strcmp(uri->scheme,"http")==0)
+ uri->port = 80;
+ else if(strcmp(uri->scheme,"hkp")==0)
+ uri->port = 11371;
+ else
+ return G10ERR_INVALID_URI; /* Unsupported scheme */
+
+ p = p2;
+
+ /* find the hostname */
+ if( *p != '/' )
+ return G10ERR_INVALID_URI; /* does not start with a slash */
+
+ p++;
+ if( *p == '/' ) { /* there seems to be a hostname */
+ p++;
+ if( (p2 = strchr(p, '/')) )
+ *p2++ = 0;
+
+ /* Check for username/password encoding */
+ if((p3=strchr(p,'@')))
+ {
+ uri->auth=p;
+ *p3++='\0';
+ p=p3;
+ }
+
+ strlwr( p );
+ uri->host = p;
+ if( (p3=strchr( p, ':' )) ) {
+ *p3++ = 0;
+ uri->port = atoi( p3 );
+ }
+
+ uri->host = p;
+ if( (n = remove_escapes( uri->host )) < 0 )
+ return G10ERR_BAD_URI;
+ if( n != strlen( p ) )
+ return G10ERR_BAD_URI; /* hostname with a Nul in it */
+ p = p2 ? p2 : NULL;
+ }
+ } /* end global URI part */
+
+ /* parse the pathname part */
+ if( !p || !*p ) /* we don't have a path */
+ return 0; /* and this is okay */
+
+ /* todo: here we have to check params */
+
+ /* do we have a query part */
+ if( (p2 = strchr( p, '?' )) )
+ *p2++ = 0;
+
+ uri->path = p;
+ if( (n = remove_escapes( p )) < 0 )
+ return G10ERR_BAD_URI;
+ if( n != strlen( p ) )
+ return G10ERR_BAD_URI; /* path with a Nul in it */
+ p = p2 ? p2 : NULL;
+
+ if( !p || !*p ) /* we don't have a query string */
+ return 0; /* okay */
+
+ /* now parse the query string */
+ tail = &uri->query;
+ for(;;) {
+ URI_TUPLE elem;
+
+ if( (p2 = strchr( p, '&' )) )
+ *p2++ = 0;
+ if( !(elem = parse_tuple( p )) )
+ return G10ERR_BAD_URI;
+ *tail = elem;
+ tail = &elem->next;
+
+ if( !p2 )
+ break; /* ready */
+ p = p2;
+ }
+
+ return 0;
+}
+
+
+
+/****************
+ * Remove all %xx escapes; this is done inplace.
+ * Returns: new length of the string.
+ */
+static int
+remove_escapes( byte *string )
+{
+ int n = 0;
+ byte *p, *s;
+
+ for(p=s=string; *s ; s++ ) {
+ if( *s == '%' ) {
+ if( s[1] && s[2] && isxdigit(s[1]) && isxdigit(s[2]) ) {
+ s++;
+ *p = *s >= '0' && *s <= '9' ? *s - '0' :
+ *s >= 'A' && *s <= 'F' ? *s - 'A' + 10 : *s - 'a' + 10 ;
+ *p <<= 4;
+ s++;
+ *p |= *s >= '0' && *s <= '9' ? *s - '0' :
+ *s >= 'A' && *s <= 'F' ? *s - 'A' + 10 : *s - 'a' + 10 ;
+ p++;
+ n++;
+ }
+ else {
+ *p++ = *s++;
+ if( *s )
+ *p++ = *s++;
+ if( *s )
+ *p++ = *s++;
+ if( *s )
+ *p = 0;
+ return -1; /* bad URI */
+ }
+ }
+ else
+ {
+ *p++ = *s;
+ n++;
+ }
+ }
+ *p = 0; /* always keep a string terminator */
+ return n;
+}
+
+
+static int
+insert_escapes( byte *buffer, const byte *string, const byte *special )
+{
+ int n = 0;
+
+ for( ; *string; string++ ) {
+ if( strchr( VALID_URI_CHARS, *string )
+ && !strchr( special, *string ) ) {
+ if( buffer )
+ *buffer++ = *string;
+ n++;
+ }
+ else {
+ if( buffer ) {
+ sprintf( buffer, "%%%02X", *string );
+ buffer += 3;
+ }
+ n += 3;
+ }
+ }
+ return n;
+}
+
+
+static URI_TUPLE
+parse_tuple( byte *string )
+{
+ byte *p = string;
+ byte *p2;
+ int n;
+ URI_TUPLE tuple;
+
+ if( (p2 = strchr( p, '=' )) )
+ *p2++ = 0;
+ if( (n = remove_escapes( p )) < 0 )
+ return NULL; /* bad URI */
+ if( n != strlen( p ) )
+ return NULL; /* name with a Nul in it */
+ tuple = xmalloc_clear( sizeof *tuple );
+ tuple->name = p;
+ if( !p2 ) {
+ /* we have only the name, so we assume an empty value string */
+ tuple->value = p + strlen(p);
+ tuple->valuelen = 0;
+ }
+ else { /* name and value */
+ if( (n = remove_escapes( p2 )) < 0 ) {
+ xfree( tuple );
+ return NULL; /* bad URI */
+ }
+ tuple->value = p2;
+ tuple->valuelen = n;
+ }
+ return tuple;
+}
+
+
+/****************
+ * Send a HTTP request to the server
+ * Returns 0 if the request was successful
+ */
+static int
+send_request( HTTP_HD hd, const char *auth, const char *proxy )
+{
+ const byte *server;
+ byte *request, *p;
+ ushort port;
+ int rc;
+ char *proxy_authstr=NULL,*authstr=NULL;
+
+ server = *hd->uri->host? hd->uri->host : "localhost";
+ port = hd->uri->port? hd->uri->port : 80;
+
+ if(proxy && *proxy)
+ {
+ PARSED_URI uri;
+
+ rc = parse_uri( &uri, proxy );
+ if (rc)
+ {
+ log_error("invalid HTTP proxy (%s): %s\n",proxy,g10_errstr(rc));
+ release_parsed_uri( uri );
+ return G10ERR_NETWORK;
+ }
+ hd->sock = connect_server( *uri->host? uri->host : "localhost",
+ uri->port? uri->port : 80, 0, NULL );
+ if(uri->auth)
+ {
+ char *x;
+ remove_escapes(uri->auth);
+ x=make_radix64_string(uri->auth,strlen(uri->auth));
+ proxy_authstr=xmalloc(52+strlen(x));
+ sprintf(proxy_authstr,"Proxy-Authorization: Basic %s\r\n",x);
+ xfree(x);
+ }
+
+ release_parsed_uri( uri );
+ }
+ else
+ hd->sock = connect_server( server, port, hd->flags, hd->uri->scheme );
+
+ if(auth || hd->uri->auth)
+ {
+ char *x,*tempauth=NULL;
+
+ if(auth)
+ {
+ tempauth=xstrdup(auth);
+ remove_escapes(tempauth);
+ }
+ else if(hd->uri->auth)
+ remove_escapes(hd->uri->auth);
+
+ x=make_radix64_string(tempauth?tempauth:hd->uri->auth,
+ strlen(tempauth?tempauth:hd->uri->auth));
+ authstr=xmalloc(52+strlen(x));
+ sprintf(authstr,"Authorization: Basic %s\r\n",x);
+ xfree(x);
+ xfree(tempauth);
+ }
+
+ if( hd->sock == -1 )
+ return G10ERR_NETWORK;
+
+ p = build_rel_path( hd->uri );
+
+ request=xmalloc(strlen(server)*2 + strlen(p)
+ + (authstr?strlen(authstr):0)
+ + (proxy_authstr?strlen(proxy_authstr):0) + 65);
+ if( proxy && *proxy )
+ sprintf( request, "%s http://%s:%hu%s%s HTTP/1.0\r\n%s%s",
+ hd->req_type == HTTP_REQ_GET ? "GET" :
+ hd->req_type == HTTP_REQ_HEAD? "HEAD":
+ hd->req_type == HTTP_REQ_POST? "POST": "OOPS",
+ server, port, *p == '/'? "":"/", p,
+ authstr?authstr:"",proxy_authstr?proxy_authstr:"" );
+ else
+ {
+ char portstr[15];
+
+ if(port!=80)
+ sprintf(portstr,":%u",port);
+
+ sprintf( request, "%s %s%s HTTP/1.0\r\nHost: %s%s\r\n%s",
+ hd->req_type == HTTP_REQ_GET ? "GET" :
+ hd->req_type == HTTP_REQ_HEAD? "HEAD":
+ hd->req_type == HTTP_REQ_POST? "POST": "OOPS",
+ *p == '/'? "":"/", p, server, (port!=80)?portstr:"",
+ authstr?authstr:"");
+ }
+
+ xfree(p);
+
+ rc = write_server( hd->sock, request, strlen(request) );
+ xfree( request );
+ xfree(proxy_authstr);
+ xfree(authstr);
+
+ return rc;
+}
+
+
+/****************
+ * Build the relative path from the parsed URI.
+ * Minimal implementation.
+ */
+static byte*
+build_rel_path( PARSED_URI uri )
+{
+ URI_TUPLE r;
+ byte *rel_path, *p;
+ int n;
+
+ /* count the needed space */
+ n = insert_escapes( NULL, uri->path, "%;?&" );
+ /* todo: build params */
+ for( r=uri->query; r; r = r->next ) {
+ n++; /* '?'/'&' */
+ n += insert_escapes( NULL, r->name, "%;?&=" );
+ n++; /* '='*/
+ n += insert_escapes( NULL, r->value, "%;?&=" );
+ }
+ n++;
+
+ /* now allocate and copy */
+ p = rel_path = xmalloc( n );
+ n = insert_escapes( p, uri->path, "%;?&" );
+ p += n;
+ /* todo: add params */
+ for( r=uri->query; r; r = r->next ) {
+ *p++ = r == uri->query? '?':'&';
+ n = insert_escapes( p, r->name, "%;?&=" );
+ p += n;
+ *p++ = '=';
+ /* todo: use valuelen */
+ n = insert_escapes( p, r->value, "%;?&=" );
+ p += n;
+ }
+ *p = 0;
+ return rel_path;
+}
+
+
+
+/***********************
+ * Parse the response from a server.
+ * Returns: errorcode and sets some fileds in the handle
+ */
+static int
+parse_response( HTTP_HD hd )
+{
+ byte *line, *p, *p2;
+ unsigned maxlen, len;
+
+ /* Wait for the status line */
+ do {
+ maxlen = MAX_LINELEN;
+ len = iobuf_read_line( hd->fp_read, &hd->buffer,
+ &hd->buffer_size, &maxlen );
+ line = hd->buffer;
+ if( !maxlen )
+ return -1; /* line has been truncated */
+ if( !len )
+ return -1; /* eof */
+ } while( !*line );
+
+ if( (p = strchr( line, '/')) )
+ *p++ = 0;
+ if( !p || strcmp( line, "HTTP" ) )
+ return 0; /* assume http 0.9 */
+
+ if( (p2 = strpbrk( p, " \t" ) ) ) {
+ *p2++ = 0;
+ p2 += strspn( p2, " \t" );
+ }
+ if( !p2 )
+ return 0; /* assume http 0.9 */
+ p = p2;
+ /* todo: add HTTP version number check here */
+ if( (p2 = strpbrk( p, " \t" ) ) )
+ *p2++ = 0;
+ if( !isdigit(p[0]) || !isdigit(p[1]) || !isdigit(p[2]) || p[3] ) {
+ /* malformed HTTP statuscode - assume HTTP 0.9 */
+ hd->is_http_0_9 = 1;
+ hd->status_code = 200;
+ return 0;
+ }
+ hd->status_code = atoi( p );
+
+ /* skip all the header lines and wait for the empty line */
+ do {
+ maxlen = MAX_LINELEN;
+ len = iobuf_read_line( hd->fp_read, &hd->buffer,
+ &hd->buffer_size, &maxlen );
+ line = hd->buffer;
+ /* we ignore truncated lines */
+ if( !len )
+ return -1; /* eof */
+ /* time lineendings */
+ if( (*line == '\r' && line[1] == '\n') || *line == '\n' )
+ *line = 0;
+ } while( len && *line );
+
+ return 0;
+}
+
+#ifdef TEST
+static int
+start_server()
+{
+ struct sockaddr_in mya;
+ struct sockaddr_in peer;
+ int fd, client;
+ fd_set rfds;
+ int addrlen;
+ int i;
+
+ if( (fd=socket(AF_INET,SOCK_STREAM, 0)) == -1 ) {
+ log_error("socket() failed: %s\n", strerror(errno));
+ return -1;
+ }
+ i = 1;
+ if( setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, (byte*)&i, sizeof(i) ) )
+ log_info("setsockopt(SO_REUSEADDR) failed: %s\n", strerror(errno) );
+
+ mya.sin_family=AF_INET;
+ memset(&mya.sin_addr, 0, sizeof(mya.sin_addr));
+ mya.sin_port=htons(11371);
+
+ if( bind( fd, (struct sockaddr *)&mya, sizeof(mya)) ) {
+ log_error("bind to port 11371 failed: %s\n", strerror(errno) );
+ sock_close( fd );
+ return -1;
+ }
+
+ if( listen( fd, 5 ) ) {
+ log_error("listen failed: %s\n", strerror(errno) );
+ sock_close( fd );
+ return -1;
+ }
+
+ for(;;) {
+ FD_ZERO(&rfds);
+ FD_SET( fd, &rfds );
+
+ if( select( fd+1, &rfds, NULL, NULL, NULL) <= 0 )
+ continue; /* ignore any errors */
+
+ if( !FD_ISSET( fd, &rfds ) )
+ continue;
+
+ addrlen = sizeof peer;
+ client = accept( fd, (struct sockaddr *)&peer, &addrlen);
+ if( client == -1 )
+ continue; /* oops */
+
+ log_info("connect from %s\n", inet_ntoa( peer.sin_addr ) );
+
+ fflush(stdout);
+ fflush(stderr);
+ if( !fork() ) {
+ int c;
+ FILE *fp;
+
+ fp = fdopen( client , "r" );
+ while( (c=getc(fp)) != EOF )
+ putchar(c);
+ fclose(fp);
+ exit(0);
+ }
+ sock_close( client );
+ }
+
+
+ return 0;
+}
+#endif
+
+
+static int
+connect_server( const char *server, ushort port, unsigned int flags,
+ const char *srvtag )
+{
+ int sock=-1,srv,srvcount=0,connected=0,hostfound=0;
+ struct srventry *srvlist=NULL;
+
+#ifdef _WIN32
+ unsigned long inaddr;
+
+ init_sockets();
+ /* Win32 gethostbyname doesn't handle IP addresses internally, so we
+ try inet_addr first on that platform only. */
+ if((inaddr=inet_addr(server))!=INADDR_NONE)
+ {
+ struct sockaddr_in addr;
+
+ memset(&addr,0,sizeof(addr));
+
+ if((sock=socket(AF_INET,SOCK_STREAM,0))==INVALID_SOCKET)
+ {
+ log_error("error creating socket: ec=%d\n",(int)WSAGetLastError());
+ return -1;
+ }
+
+ addr.sin_family=AF_INET;
+ addr.sin_port=htons(port);
+ memcpy(&addr.sin_addr,&inaddr,sizeof(inaddr));
+
+ if(connect(sock,(struct sockaddr *)&addr,sizeof(addr))==0)
+ return sock;
+ else
+ {
+ sock_close(sock);
+ return -1;
+ }
+ }
+#endif
+
+#ifdef USE_DNS_SRV
+ /* Do the SRV thing */
+ if(flags&HTTP_FLAG_TRY_SRV && srvtag)
+ {
+ /* We're using SRV, so append the tags */
+ if(1+strlen(srvtag)+6+strlen(server)+1<=MAXDNAME)
+ {
+ char srvname[MAXDNAME];
+
+ strcpy(srvname,"_");
+ strcat(srvname,srvtag);
+ strcat(srvname,"._tcp.");
+ strcat(srvname,server);
+ srvcount=getsrv(srvname,&srvlist);
+ }
+ }
+#endif
+
+ if(srvlist==NULL)
+ {
+ /* Either we're not using SRV, or the SRV lookup failed. Make
+ up a fake SRV record. */
+ srvlist=xmalloc_clear(sizeof(struct srventry));
+ srvlist->port=port;
+ strncpy(srvlist->target,server,MAXDNAME);
+ srvlist->target[MAXDNAME-1]='\0';
+ srvcount=1;
+ }
+
+#ifdef HAVE_GETADDRINFO
+
+ for(srv=0;srv<srvcount;srv++)
+ {
+ struct addrinfo hints,*res,*ai;
+ char portstr[6];
+
+ sprintf(portstr,"%u",srvlist[srv].port);
+ memset(&hints,0,sizeof(hints));
+ hints.ai_socktype=SOCK_STREAM;
+ if(getaddrinfo(srvlist[srv].target,portstr,&hints,&res)==0)
+ hostfound=1;
+ else
+ continue;
+
+ for(ai=res;ai;ai=ai->ai_next)
+ {
+ if((sock=socket(ai->ai_family,ai->ai_socktype,ai->ai_protocol))==-1)
+ {
+ log_error("error creating socket: %s\n",strerror(errno));
+ freeaddrinfo(res);
+ return -1;
+ }
+
+ if(connect(sock,ai->ai_addr,ai->ai_addrlen)==0)
+ {
+ connected=1;
+ break;
+ }
+
+ sock_close(sock);
+ }
+
+ freeaddrinfo(res);
+
+ if(ai)
+ break;
+ }
+
+#else /* !HAVE_GETADDRINFO */
+
+ for(srv=0;srv<srvcount;srv++)
+ {
+ int i=0;
+ struct hostent *host=NULL;
+ struct sockaddr_in addr;
+
+ memset(&addr,0,sizeof(addr));
+
+ if((host=gethostbyname(srvlist[srv].target))==NULL)
+ continue;
+
+ hostfound=1;
+
+ if((sock=socket(host->h_addrtype,SOCK_STREAM,0))==-1)
+ {
+ log_error("error creating socket: %s\n",strerror(errno));
+ return -1;
+ }
+
+ addr.sin_family=host->h_addrtype;
+ if(addr.sin_family!=AF_INET)
+ {
+ log_error("%s: unknown address family\n",srvlist[srv].target);
+ return -1;
+ }
+
+ addr.sin_port=htons(srvlist[srv].port);
+
+ /* Try all A records until one responds. */
+ while(host->h_addr_list[i])
+ {
+ if(host->h_length!=4)
+ {
+ log_error("%s: illegal address length\n",srvlist[srv].target);
+ return -1;
+ }
+
+ memcpy(&addr.sin_addr,host->h_addr_list[i],host->h_length);
+
+ if(connect(sock,(struct sockaddr *)&addr,sizeof(addr))==0)
+ {
+ connected=1;
+ break;
+ }
+
+ i++;
+ }
+
+ if(host->h_addr_list[i])
+ break;
+
+ sock_close(sock);
+ }
+#endif /* !HAVE_GETADDRINFO */
+
+ xfree(srvlist);
+
+ if(!connected)
+ {
+ int err=errno;
+#ifdef _WIN32
+ if(hostfound)
+ log_error("%s: Unable to connect: ec=%d\n",server,(int)WSAGetLastError());
+ else
+ log_error("%s: Host not found: ec=%d\n",server,(int)WSAGetLastError());
+#else
+ if(hostfound)
+ log_error("%s: %s\n",server,strerror(err));
+ else
+ log_error("%s: Host not found\n",server);
+#endif
+ if(sock!=-1)
+ sock_close(sock);
+ errno=err;
+ return -1;
+ }
+
+ return sock;
+}
+
+
+static int
+write_server( int sock, const char *data, size_t length )
+{
+ int nleft;
+
+ nleft = length;
+ while( nleft > 0 ) {
+#ifdef _WIN32
+ int nwritten;
+
+ nwritten = send (sock, data, nleft, 0);
+ if ( nwritten == SOCKET_ERROR ) {
+ log_info ("write failed: ec=%d\n", (int)WSAGetLastError ());
+ return G10ERR_NETWORK;
+ }
+#else
+ int nwritten = write( sock, data, nleft );
+ if( nwritten == -1 ) {
+ if( errno == EINTR )
+ continue;
+ if( errno == EAGAIN ) {
+ struct timeval tv;
+
+ tv.tv_sec = 0;
+ tv.tv_usec = 50000;
+ select(0, NULL, NULL, NULL, &tv);
+ continue;
+ }
+ log_info("write failed: %s\n", strerror(errno));
+ return G10ERR_NETWORK;
+ }
+#endif
+ nleft -=nwritten;
+ data += nwritten;
+ }
+
+ return 0;
+}
+
+/**** Test code ****/
+#ifdef TEST
+
+int
+main(int argc, char **argv)
+{
+ int rc;
+ PARSED_URI uri;
+ URI_TUPLE r;
+ struct http_context hd;
+ int c;
+
+ log_set_name("http-test");
+ if( argc == 1 ) {
+ start_server();
+ return 0;
+ }
+
+ if( argc != 2 ) {
+ fprintf(stderr,"usage: http-test uri\n");
+ return 1;
+ }
+ argc--; argv++;
+
+ rc = parse_uri( &uri, *argv );
+ if( rc ) {
+ log_error("`%s': %s\n", *argv, g10_errstr(rc));
+ release_parsed_uri( uri );
+ return 1;
+ }
+
+ printf("Scheme: %s\n", uri->scheme );
+ printf("Host : %s\n", uri->host );
+ printf("Port : %u\n", uri->port );
+ printf("Path : %s\n", uri->path );
+ for( r=uri->params; r; r = r->next ) {
+ printf("Params: %s=%s", r->name, r->value );
+ if( strlen( r->value ) != r->valuelen )
+ printf(" [real length=%d]", (int)r->valuelen );
+ putchar('\n');
+ }
+ for( r=uri->query; r; r = r->next ) {
+ printf("Query : %s=%s", r->name, r->value );
+ if( strlen( r->value ) != r->valuelen )
+ printf(" [real length=%d]", (int)r->valuelen );
+ putchar('\n');
+ }
+ release_parsed_uri( uri ); uri = NULL;
+
+ rc = http_open_document( &hd, *argv, 0, NULL );
+ if( rc ) {
+ log_error("can't get `%s': %s\n", *argv, g10_errstr(rc));
+ return 1;
+ }
+ log_info("open_http_document succeeded; status=%u\n", hd.status_code );
+ while( (c=iobuf_get( hd.fp_read)) != -1 )
+ putchar(c);
+ http_close( &hd );
+ return 0;
+}
+#endif /*TEST*/
diff --git a/util/iobuf.c b/util/iobuf.c
new file mode 100644
index 0000000..f6e817c
--- /dev/null
+++ b/util/iobuf.c
@@ -0,0 +1,2276 @@
+/* iobuf.c - file handling
+ * Copyright (C) 1998, 1999, 2000, 2001, 2003,
+ * 2004 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#ifdef HAVE_DOSISH_SYSTEM
+#include <windows.h>
+#endif
+#ifdef __riscos__
+#include <kernel.h>
+#include <swis.h>
+#endif /* __riscos__ */
+
+#include "memory.h"
+#include "util.h"
+#include "dynload.h"
+#include "iobuf.h"
+
+/* The size of the internal buffers.
+ NOTE: If you change this value you MUST also adjust the regression
+ test "armored_key_8192" in armor.test! */
+#define IOBUF_BUFFER_SIZE 8192
+
+
+#undef FILE_FILTER_USES_STDIO
+
+#ifdef HAVE_DOSISH_SYSTEM
+#define USE_SETMODE 1
+#endif
+
+#ifdef FILE_FILTER_USES_STDIO
+#define my_fileno(a) fileno ((a))
+#define my_fopen_ro(a,b) fopen ((a),(b))
+#define my_fopen(a,b) fopen ((a),(b))
+typedef FILE *FILEP_OR_FD;
+#define INVALID_FP NULL
+#define FILEP_OR_FD_FOR_STDIN (stdin)
+#define FILEP_OR_FD_FOR_STDOUT (stdout)
+typedef struct {
+ FILE *fp; /* open file handle */
+ int keep_open;
+ int no_cache;
+ int print_only_name; /* flags indicating that fname is not a real file*/
+ char fname[1]; /* name of the file */
+ } file_filter_ctx_t ;
+#else
+#define my_fileno(a) (a)
+#define my_fopen_ro(a,b) fd_cache_open ((a),(b))
+#define my_fopen(a,b) direct_open ((a),(b))
+#ifdef HAVE_DOSISH_SYSTEM
+typedef HANDLE FILEP_OR_FD;
+#define INVALID_FP ((HANDLE)-1)
+#define FILEP_OR_FD_FOR_STDIN (GetStdHandle (STD_INPUT_HANDLE))
+#define FILEP_OR_FD_FOR_STDOUT (GetStdHandle (STD_OUTPUT_HANDLE))
+#undef USE_SETMODE
+#else
+typedef int FILEP_OR_FD;
+#define INVALID_FP (-1)
+#define FILEP_OR_FD_FOR_STDIN (0)
+#define FILEP_OR_FD_FOR_STDOUT (1)
+#endif
+typedef struct {
+ FILEP_OR_FD fp; /* open file handle */
+ int keep_open;
+ int no_cache;
+ int eof_seen;
+ int print_only_name; /* flags indicating that fname is not a real file*/
+ char fname[1]; /* name of the file */
+ } file_filter_ctx_t ;
+
+ struct close_cache_s {
+ struct close_cache_s *next;
+ FILEP_OR_FD fp;
+ char fname[1];
+ };
+ typedef struct close_cache_s *CLOSE_CACHE;
+ static CLOSE_CACHE close_cache;
+#endif
+
+#ifdef _WIN32
+typedef struct {
+ int sock;
+ int keep_open;
+ int no_cache;
+ int eof_seen;
+ int print_only_name; /* flags indicating that fname is not a real file*/
+ char fname[1]; /* name of the file */
+} sock_filter_ctx_t ;
+#endif /*_WIN32*/
+
+/* The first partial length header block must be of size 512
+ * to make it easier (and efficienter) we use a min. block size of 512
+ * for all chunks (but the last one) */
+#define OP_MIN_PARTIAL_CHUNK 512
+#define OP_MIN_PARTIAL_CHUNK_2POW 9
+
+typedef struct {
+ int use;
+ size_t size;
+ size_t count;
+ int partial; /* 1 = partial header, 2 in last partial packet */
+ char *buffer; /* used for partial header */
+ size_t buflen; /* used size of buffer */
+ int first_c; /* of partial header (which is > 0)*/
+ int eof;
+} block_filter_ctx_t;
+
+static int special_names_enabled;
+
+static int underflow(IOBUF a);
+static int translate_file_handle ( int fd, int for_write );
+
+
+
+#ifndef FILE_FILTER_USES_STDIO
+
+/* This is a replacement for strcmp. Under W32 it does not
+ distinguish between backslash and slash. */
+static int
+fd_cache_strcmp (const char *a, const char *b)
+{
+#ifdef HAVE_DOSISH_SYSTEM
+ for (; *a && *b; a++, b++)
+ {
+ if (*a != *b && !((*a == '/' && *b == '\\')
+ || (*a == '\\' && *b == '/')) )
+ break;
+ }
+ return *(const unsigned char *)a - *(const unsigned char *)b;
+#else
+ return strcmp (a, b);
+#endif
+}
+
+/*
+ * Invalidate (i.e. close) a cached iobuf or all iobufs if NULL is
+ * used for FNAME.
+ */
+static void
+fd_cache_invalidate (const char *fname)
+{
+ CLOSE_CACHE cc;
+
+ if (!fname) {
+ if( DBG_IOBUF )
+ log_debug ("fd_cache_invalidate (all)\n");
+
+ for (cc=close_cache; cc; cc = cc->next ) {
+ if ( cc->fp != INVALID_FP ) {
+#ifdef HAVE_DOSISH_SYSTEM
+ CloseHandle (cc->fp);
+#else
+ close(cc->fp);
+#endif
+ cc->fp = INVALID_FP;
+ }
+ }
+ return;
+ }
+
+ if( DBG_IOBUF )
+ log_debug ("fd_cache_invalidate (%s)\n", fname);
+
+ for (cc=close_cache; cc; cc = cc->next ) {
+ if ( cc->fp != INVALID_FP && !fd_cache_strcmp (cc->fname, fname) ) {
+ if( DBG_IOBUF )
+ log_debug (" did (%s)\n", cc->fname);
+#ifdef HAVE_DOSISH_SYSTEM
+ CloseHandle (cc->fp);
+#else
+ close(cc->fp);
+#endif
+ cc->fp = INVALID_FP;
+ }
+ }
+}
+
+
+
+static FILEP_OR_FD
+direct_open (const char *fname, const char *mode)
+{
+#ifdef HAVE_DOSISH_SYSTEM
+ unsigned long da, cd, sm;
+ HANDLE hfile;
+
+ /* Note, that we do not handle all mode combinations */
+
+ /* According to the ReactOS source it seems that open() of the
+ * standard MSW32 crt does open the file in share mode which is
+ * something new for MS applications ;-)
+ */
+ if ( strchr (mode, '+') ) {
+ fd_cache_invalidate (fname);
+ da = GENERIC_READ|GENERIC_WRITE;
+ cd = OPEN_EXISTING;
+ sm = FILE_SHARE_READ | FILE_SHARE_WRITE;
+ }
+ else if ( strchr (mode, 'w') ) {
+ fd_cache_invalidate (fname);
+ da = GENERIC_WRITE;
+ cd = CREATE_ALWAYS;
+ sm = FILE_SHARE_WRITE;
+ }
+ else {
+ da = GENERIC_READ;
+ cd = OPEN_EXISTING;
+ sm = FILE_SHARE_READ;
+ }
+
+ hfile = CreateFile (fname, da, sm, NULL, cd, FILE_ATTRIBUTE_NORMAL, NULL);
+ return hfile;
+#else
+ int oflag;
+ int cflag = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
+
+ /* Note, that we do not handle all mode combinations */
+ if ( strchr (mode, '+') ) {
+ fd_cache_invalidate (fname);
+ oflag = O_RDWR;
+ }
+ else if ( strchr (mode, 'w') ) {
+ fd_cache_invalidate (fname);
+ oflag = O_WRONLY | O_CREAT | O_TRUNC;
+ }
+ else {
+ oflag = O_RDONLY;
+ }
+#ifdef O_BINARY
+ if (strchr (mode, 'b'))
+ oflag |= O_BINARY;
+#endif
+#ifndef __riscos__
+ return open (fname, oflag, cflag );
+#else
+ {
+ struct stat buf;
+ int rc = stat( fname, &buf );
+
+ /* Don't allow iobufs on directories */
+ if( !rc && S_ISDIR(buf.st_mode) && !S_ISREG(buf.st_mode) )
+ return __set_errno( EISDIR );
+ else
+ return open( fname, oflag, cflag );
+ }
+#endif
+#endif
+}
+
+
+/*
+ * Instead of closing an FD we keep it open and cache it for later reuse
+ * Note that this caching strategy only works if the process does not chdir.
+ */
+static void
+fd_cache_close (const char *fname, FILEP_OR_FD fp)
+{
+ CLOSE_CACHE cc;
+
+ assert (fp);
+ if ( !fname || !*fname ) {
+#ifdef HAVE_DOSISH_SYSTEM
+ CloseHandle (fp);
+#else
+ close(fp);
+#endif
+ if( DBG_IOBUF )
+ log_debug ("fd_cache_close (%p) real\n", (void*)fp);
+ return;
+ }
+ /* try to reuse a slot */
+ for (cc=close_cache; cc; cc = cc->next ) {
+ if ( cc->fp == INVALID_FP && !fd_cache_strcmp (cc->fname, fname) ) {
+ cc->fp = fp;
+ if( DBG_IOBUF )
+ log_debug ("fd_cache_close (%s) used existing slot\n", fname);
+ return;
+ }
+ }
+ /* add a new one */
+ if( DBG_IOBUF )
+ log_debug ("fd_cache_close (%s) new slot created\n", fname);
+ cc = xmalloc_clear (sizeof *cc + strlen (fname));
+ strcpy (cc->fname, fname);
+ cc->fp = fp;
+ cc->next = close_cache;
+ close_cache = cc;
+}
+
+/*
+ * Do an direct_open on FNAME but first try to reuse one from the fd_cache
+ */
+static FILEP_OR_FD
+fd_cache_open (const char *fname, const char *mode)
+{
+ CLOSE_CACHE cc;
+
+ assert (fname);
+ for (cc=close_cache; cc; cc = cc->next ) {
+ if ( cc->fp != INVALID_FP && !fd_cache_strcmp (cc->fname, fname) ) {
+ FILEP_OR_FD fp = cc->fp;
+ cc->fp = INVALID_FP;
+ if( DBG_IOBUF )
+ log_debug ("fd_cache_open (%s) using cached fp\n", fname);
+#ifdef HAVE_DOSISH_SYSTEM
+ if (SetFilePointer (fp, 0, NULL, FILE_BEGIN) == 0xffffffff ) {
+ log_error ("rewind file failed on handle %p: %s\n",
+ fp, w32_strerror (errno));
+ fp = INVALID_FP;
+ }
+#else
+ if ( lseek (fp, 0, SEEK_SET) == (off_t)-1 ) {
+ log_error("can't rewind fd %d: %s\n", fp, strerror(errno) );
+ fp = INVALID_FP;
+ }
+#endif
+ return fp;
+ }
+ }
+ if( DBG_IOBUF )
+ log_debug ("fd_cache_open (%s) not cached\n", fname);
+ return direct_open (fname, mode);
+}
+
+
+#endif /*FILE_FILTER_USES_STDIO*/
+
+
+/****************
+ * Read data from a file into buf which has an allocated length of *LEN.
+ * return the number of read bytes in *LEN. OPAQUE is the FILE * of
+ * the stream. A is not used.
+ * control may be:
+ * IOBUFCTRL_INIT: called just before the function is linked into the
+ * list of function. This can be used to prepare internal
+ * data structures of the function.
+ * IOBUFCTRL_FREE: called just before the function is removed from the
+ * list of functions and can be used to release internal
+ * data structures or close a file etc.
+ * IOBUFCTRL_UNDERFLOW: called by iobuf_underflow to fill the buffer
+ * with new stuff. *RET_LEN is the available size of the
+ * buffer, and should be set to the number of bytes
+ * which were put into the buffer. The function
+ * returns 0 to indicate success, -1 on EOF and
+ * G10ERR_xxxxx for other errors.
+ *
+ * IOBUFCTRL_FLUSH: called by iobuf_flush() to write out the collected stuff.
+ * *RET_LAN is the number of bytes in BUF.
+ *
+ * IOBUFCTRL_CANCEL: send to all filters on behalf of iobuf_cancel. The
+ * filter may take appropriate action on this message.
+ */
+static int
+file_filter(void *opaque, int control, IOBUF chain, byte *buf, size_t *ret_len)
+{
+ file_filter_ctx_t *a = opaque;
+ FILEP_OR_FD f = a->fp;
+ size_t size = *ret_len;
+ size_t nbytes = 0;
+ int rc = 0;
+
+#ifdef FILE_FILTER_USES_STDIO
+ if( control == IOBUFCTRL_UNDERFLOW ) {
+ assert( size ); /* need a buffer */
+ if ( feof(f)) { /* On terminals you could easiely read as many EOFs as you call */
+ rc = -1; /* fread() or fgetc() repeatly. Every call will block until you press */
+ *ret_len = 0; /* CTRL-D. So we catch this case before we call fread() again. */
+ }
+ else {
+ clearerr( f );
+ nbytes = fread( buf, 1, size, f );
+ if( feof(f) && !nbytes ) {
+ rc = -1; /* okay: we can return EOF now. */
+ }
+ else if( ferror(f) && errno != EPIPE ) {
+ log_error("%s: read error: %s\n",
+ a->fname, strerror(errno));
+ rc = G10ERR_READ_FILE;
+ }
+ *ret_len = nbytes;
+ }
+ }
+ else if( control == IOBUFCTRL_FLUSH ) {
+ if( size ) {
+ clearerr( f );
+ nbytes = fwrite( buf, 1, size, f );
+ if( ferror(f) ) {
+ log_error("%s: write error: %s\n", a->fname, strerror(errno));
+ rc = G10ERR_WRITE_FILE;
+ }
+ }
+ *ret_len = nbytes;
+ }
+ else if( control == IOBUFCTRL_INIT ) {
+ a->keep_open = a->no_cache = 0;
+ }
+ else if( control == IOBUFCTRL_DESC ) {
+ *(char**)buf = "file_filter";
+ }
+ else if( control == IOBUFCTRL_FREE ) {
+ if( f != stdin && f != stdout ) {
+ if( DBG_IOBUF )
+ log_debug("%s: close fd %d\n", a->fname, fileno(f) );
+ if (!a->keep_open)
+ fclose(f);
+ }
+ f = NULL;
+ xfree(a); /* we can free our context now */
+ }
+#else /* !stdio implementation */
+
+ if( control == IOBUFCTRL_UNDERFLOW ) {
+ assert( size ); /* need a buffer */
+ if ( a->eof_seen) {
+ rc = -1;
+ *ret_len = 0;
+ }
+ else {
+#ifdef HAVE_DOSISH_SYSTEM
+ unsigned long nread;
+
+ nbytes = 0;
+ if ( !ReadFile ( f, buf, size, &nread, NULL ) ) {
+ if ((int)GetLastError () != ERROR_BROKEN_PIPE) {
+ log_error ("%s: read error: %s\n", a->fname,
+ w32_strerror (0));
+ rc = G10ERR_READ_FILE;
+ }
+ }
+ else if ( !nread ) {
+ a->eof_seen = 1;
+ rc = -1;
+ }
+ else {
+ nbytes = nread;
+ }
+
+#else
+
+ int n;
+
+ nbytes = 0;
+ do {
+ n = read ( f, buf, size );
+ } while (n == -1 && errno == EINTR );
+ if ( n == -1 ) { /* error */
+ if (errno != EPIPE) {
+ log_error("%s: read error: %s\n",
+ a->fname, strerror(errno));
+ rc = G10ERR_READ_FILE;
+ }
+ }
+ else if ( !n ) { /* eof */
+ a->eof_seen = 1;
+ rc = -1;
+ }
+ else {
+ nbytes = n;
+ }
+#endif
+ *ret_len = nbytes;
+ }
+ }
+ else if( control == IOBUFCTRL_FLUSH ) {
+ if( size ) {
+#ifdef HAVE_DOSISH_SYSTEM
+ byte *p = buf;
+ unsigned long n;
+
+ nbytes = size;
+ do {
+ if (size && !WriteFile (f, p, nbytes, &n, NULL)) {
+ log_error ("%s: write error: %s\n", a->fname,
+ w32_strerror (0));
+ rc = G10ERR_WRITE_FILE;
+ break;
+ }
+ p += n;
+ nbytes -= n;
+ } while ( nbytes );
+ nbytes = p - buf;
+#else
+ byte *p = buf;
+ int n;
+
+ nbytes = size;
+ do {
+ do {
+ n = write ( f, p, nbytes );
+ } while ( n == -1 && errno == EINTR );
+ if ( n > 0 ) {
+ p += n;
+ nbytes -= n;
+ }
+ } while ( n != -1 && nbytes );
+ if( n == -1 ) {
+ log_error("%s: write error: %s\n", a->fname, strerror(errno));
+ rc = G10ERR_WRITE_FILE;
+ }
+ nbytes = p - buf;
+#endif
+ }
+ *ret_len = nbytes;
+ }
+ else if ( control == IOBUFCTRL_INIT ) {
+ a->eof_seen = 0;
+ a->keep_open = 0;
+ a->no_cache = 0;
+ }
+ else if ( control == IOBUFCTRL_DESC ) {
+ *(char**)buf = "file_filter(fd)";
+ }
+ else if ( control == IOBUFCTRL_FREE ) {
+#ifdef HAVE_DOSISH_SYSTEM
+ if ( f != FILEP_OR_FD_FOR_STDIN && f != FILEP_OR_FD_FOR_STDOUT ) {
+ if( DBG_IOBUF )
+ log_debug("%s: close handle %p\n", a->fname, f );
+ if (!a->keep_open)
+ fd_cache_close (a->no_cache?NULL:a->fname, f);
+ }
+#else
+ if ( (int)f != 0 && (int)f != 1 ) {
+ if( DBG_IOBUF )
+ log_debug("%s: close fd %d\n", a->fname, f );
+ if (!a->keep_open)
+ fd_cache_close (a->no_cache?NULL:a->fname, f);
+ }
+ f = INVALID_FP;
+#endif
+ xfree (a); /* we can free our context now */
+ }
+#endif /* !stdio implementation */
+ return rc;
+}
+
+#ifdef _WIN32
+/* Becuase sockets are an special object under Lose32 we have to
+ * use a special filter */
+static int
+sock_filter (void *opaque, int control, IOBUF chain, byte *buf, size_t *ret_len)
+{
+ sock_filter_ctx_t *a = opaque;
+ size_t size = *ret_len;
+ size_t nbytes = 0;
+ int rc = 0;
+
+ if( control == IOBUFCTRL_UNDERFLOW ) {
+ assert( size ); /* need a buffer */
+ if ( a->eof_seen) {
+ rc = -1;
+ *ret_len = 0;
+ }
+ else {
+ int nread;
+
+ nread = recv ( a->sock, buf, size, 0 );
+ if ( nread == SOCKET_ERROR ) {
+ int ec = (int)WSAGetLastError ();
+ log_error("socket read error: ec=%d\n", ec);
+ rc = G10ERR_READ_FILE;
+ }
+ else if ( !nread ) {
+ a->eof_seen = 1;
+ rc = -1;
+ }
+ else {
+ nbytes = nread;
+ }
+ *ret_len = nbytes;
+ }
+ }
+ else if( control == IOBUFCTRL_FLUSH ) {
+ if( size ) {
+ byte *p = buf;
+ int n;
+
+ nbytes = size;
+ do {
+ n = send (a->sock, p, nbytes, 0);
+ if ( n == SOCKET_ERROR ) {
+ int ec = (int)WSAGetLastError ();
+ log_error("socket write error: ec=%d\n", ec);
+ rc = G10ERR_WRITE_FILE;
+ break;
+ }
+ p += n;
+ nbytes -= n;
+ } while ( nbytes );
+ nbytes = p - buf;
+ }
+ *ret_len = nbytes;
+ }
+ else if ( control == IOBUFCTRL_INIT ) {
+ a->eof_seen = 0;
+ a->keep_open = 0;
+ a->no_cache = 0;
+ }
+ else if ( control == IOBUFCTRL_DESC ) {
+ *(char**)buf = "sock_filter";
+ }
+ else if ( control == IOBUFCTRL_FREE ) {
+ if (!a->keep_open)
+ closesocket (a->sock);
+ xfree (a); /* we can free our context now */
+ }
+ return rc;
+}
+#endif /*_WIN32*/
+
+/****************
+ * This is used to implement the block write mode.
+ * Block reading is done on a byte by byte basis in readbyte(),
+ * without a filter
+ */
+static int
+block_filter(void *opaque, int control, IOBUF chain, byte *buf, size_t *ret_len)
+{
+ block_filter_ctx_t *a = opaque;
+ size_t size = *ret_len;
+ int c, needed, rc = 0;
+ char *p;
+
+ if( control == IOBUFCTRL_UNDERFLOW ) {
+ size_t n=0;
+
+ p = buf;
+ assert( size ); /* need a buffer */
+ if( a->eof ) /* don't read any further */
+ rc = -1;
+ while( !rc && size ) {
+ if( !a->size ) { /* get the length bytes */
+ if( a->partial == 2 ) {
+ a->eof = 1;
+ if( !n )
+ rc = -1;
+ break;
+ }
+ else if( a->partial ) {
+ /* These OpenPGP introduced huffman like encoded length
+ * bytes are really a mess :-( */
+ if( a->first_c ) {
+ c = a->first_c;
+ a->first_c = 0;
+ }
+ else if( (c = iobuf_get(chain)) == -1 ) {
+ log_error("block_filter: 1st length byte missing\n");
+ rc = G10ERR_READ_FILE;
+ break;
+ }
+ if( c < 192 ) {
+ a->size = c;
+ a->partial = 2;
+ if( !a->size ) {
+ a->eof = 1;
+ if( !n )
+ rc = -1;
+ break;
+ }
+ }
+ else if( c < 224 ) {
+ a->size = (c - 192) * 256;
+ if( (c = iobuf_get(chain)) == -1 ) {
+ log_error("block_filter: 2nd length byte missing\n");
+ rc = G10ERR_READ_FILE;
+ break;
+ }
+ a->size += c + 192;
+ a->partial = 2;
+ if( !a->size ) {
+ a->eof = 1;
+ if( !n )
+ rc = -1;
+ break;
+ }
+ }
+ else if( c == 255 ) {
+ a->size = iobuf_get(chain) << 24;
+ a->size |= iobuf_get(chain) << 16;
+ a->size |= iobuf_get(chain) << 8;
+ if( (c = iobuf_get(chain)) == -1 ) {
+ log_error("block_filter: invalid 4 byte length\n");
+ rc = G10ERR_READ_FILE;
+ break;
+ }
+ a->size |= c;
+ a->partial = 2;
+ if( !a->size ) {
+ a->eof = 1;
+ if( !n )
+ rc = -1;
+ break;
+ }
+ }
+ else { /* next partial body length */
+ a->size = 1 << (c & 0x1f);
+ }
+ /* log_debug("partial: ctx=%p c=%02x size=%u\n", a, c, a->size);*/
+ }
+ else
+ BUG();
+ }
+
+ while( !rc && size && a->size ) {
+ needed = size < a->size ? size : a->size;
+ c = iobuf_read( chain, p, needed );
+ if( c < needed ) {
+ if( c == -1 ) c = 0;
+ log_error("block_filter %p: read error (size=%lu,a->size=%lu)\n",
+ a, (ulong)size+c, (ulong)a->size+c);
+ rc = G10ERR_READ_FILE;
+ }
+ else {
+ size -= c;
+ a->size -= c;
+ p += c;
+ n += c;
+ }
+ }
+ }
+ *ret_len = n;
+ }
+ else if( control == IOBUFCTRL_FLUSH ) {
+ if( a->partial ) { /* the complicated openpgp scheme */
+ size_t blen, n, nbytes = size + a->buflen;
+
+ assert( a->buflen <= OP_MIN_PARTIAL_CHUNK );
+ if( nbytes < OP_MIN_PARTIAL_CHUNK ) {
+ /* not enough to write a partial block out; so we store it*/
+ if( !a->buffer )
+ a->buffer = xmalloc( OP_MIN_PARTIAL_CHUNK );
+ memcpy( a->buffer + a->buflen, buf, size );
+ a->buflen += size;
+ }
+ else { /* okay, we can write out something */
+ /* do this in a loop to use the most efficient block lengths */
+ p = buf;
+ do {
+ /* find the best matching block length - this is limited
+ * by the size of the internal buffering */
+ for( blen=OP_MIN_PARTIAL_CHUNK*2,
+ c=OP_MIN_PARTIAL_CHUNK_2POW+1; blen <= nbytes;
+ blen *=2, c++ )
+ ;
+ blen /= 2; c--;
+ /* write the partial length header */
+ assert( c <= 0x1f ); /*;-)*/
+ c |= 0xe0;
+ iobuf_put( chain, c );
+ if( (n=a->buflen) ) { /* write stuff from the buffer */
+ assert( n == OP_MIN_PARTIAL_CHUNK);
+ if( iobuf_write(chain, a->buffer, n ) )
+ rc = G10ERR_WRITE_FILE;
+ a->buflen = 0;
+ nbytes -= n;
+ }
+ if( (n = nbytes) > blen )
+ n = blen;
+ if( n && iobuf_write(chain, p, n ) )
+ rc = G10ERR_WRITE_FILE;
+ p += n;
+ nbytes -= n;
+ } while( !rc && nbytes >= OP_MIN_PARTIAL_CHUNK );
+ /* store the rest in the buffer */
+ if( !rc && nbytes ) {
+ assert( !a->buflen );
+ assert( nbytes < OP_MIN_PARTIAL_CHUNK );
+ if( !a->buffer )
+ a->buffer = xmalloc( OP_MIN_PARTIAL_CHUNK );
+ memcpy( a->buffer, p, nbytes );
+ a->buflen = nbytes;
+ }
+ }
+ }
+ else
+ BUG();
+ }
+ else if( control == IOBUFCTRL_INIT ) {
+ if( DBG_IOBUF )
+ log_debug("init block_filter %p\n", a );
+ if( a->partial )
+ a->count = 0;
+ else if( a->use == 1 )
+ a->count = a->size = 0;
+ else
+ a->count = a->size; /* force first length bytes */
+ a->eof = 0;
+ a->buffer = NULL;
+ a->buflen = 0;
+ }
+ else if( control == IOBUFCTRL_DESC ) {
+ *(char**)buf = "block_filter";
+ }
+ else if( control == IOBUFCTRL_FREE ) {
+ if( a->use == 2 ) { /* write the end markers */
+ if( a->partial ) {
+ u32 len;
+ /* write out the remaining bytes without a partial header
+ * the length of this header may be 0 - but if it is
+ * the first block we are not allowed to use a partial header
+ * and frankly we can't do so, because this length must be
+ * a power of 2. This is _really_ complicated because we
+ * have to check the possible length of a packet prior
+ * to it's creation: a chain of filters becomes complicated
+ * and we need a lot of code to handle compressed packets etc.
+ * :-(((((((
+ */
+ /* construct header */
+ len = a->buflen;
+ /*log_debug("partial: remaining length=%u\n", len );*/
+ if( len < 192 )
+ rc = iobuf_put(chain, len );
+ else if( len < 8384 ) {
+ if( !(rc=iobuf_put( chain, ((len-192) / 256) + 192)) )
+ rc = iobuf_put( chain, ((len-192) % 256));
+ }
+ else { /* use a 4 byte header */
+ if( !(rc=iobuf_put( chain, 0xff )) )
+ if( !(rc=iobuf_put( chain, (len >> 24)&0xff )) )
+ if( !(rc=iobuf_put( chain, (len >> 16)&0xff )) )
+ if( !(rc=iobuf_put( chain, (len >> 8)&0xff )))
+ rc=iobuf_put( chain, len & 0xff );
+ }
+ if( !rc && len )
+ rc = iobuf_write(chain, a->buffer, len );
+ if( rc ) {
+ log_error("block_filter: write error: %s\n",strerror(errno));
+ rc = G10ERR_WRITE_FILE;
+ }
+ xfree( a->buffer ); a->buffer = NULL; a->buflen = 0;
+ }
+ else
+ BUG();
+ }
+ else if( a->size ) {
+ log_error("block_filter: pending bytes!\n");
+ }
+ if( DBG_IOBUF )
+ log_debug("free block_filter %p\n", a );
+ xfree(a); /* we can free our context now */
+ }
+
+ return rc;
+}
+
+
+static void
+print_chain( IOBUF a )
+{
+ if( !DBG_IOBUF )
+ return;
+ for(; a; a = a->chain ) {
+ size_t dummy_len = 0;
+ const char *desc = "[none]";
+
+ if( a->filter )
+ a->filter( a->filter_ov, IOBUFCTRL_DESC, NULL,
+ (byte*)&desc, &dummy_len );
+
+ log_debug("iobuf chain: %d.%d `%s' filter_eof=%d start=%d len=%d\n",
+ a->no, a->subno, desc, a->filter_eof,
+ (int)a->d.start, (int)a->d.len );
+ }
+}
+
+int
+iobuf_print_chain( IOBUF a )
+{
+ print_chain(a);
+ return 0;
+}
+
+/****************
+ * Allocate a new io buffer, with no function assigned.
+ * Use is the desired usage: 1 for input, 2 for output, 3 for temp buffer
+ * BUFSIZE is a suggested buffer size.
+ */
+IOBUF
+iobuf_alloc(int use, size_t bufsize)
+{
+ IOBUF a;
+ static int number=0;
+
+ a = xmalloc_clear(sizeof *a);
+ a->use = use;
+ a->d.buf = xmalloc( bufsize );
+ a->d.size = bufsize;
+ a->no = ++number;
+ a->subno = 0;
+ a->opaque = NULL;
+ a->real_fname = NULL;
+ return a;
+}
+
+int
+iobuf_close ( IOBUF a )
+{
+ IOBUF a2;
+ size_t dummy_len=0;
+ int rc=0;
+
+ if( a && a->directfp ) {
+ fclose( a->directfp );
+ xfree( a->real_fname );
+ if( DBG_IOBUF )
+ log_debug("iobuf_close -> %p\n", a->directfp );
+ return 0;
+ }
+
+ for( ; a && !rc ; a = a2 ) {
+ a2 = a->chain;
+ if( a->use == 2 && (rc=iobuf_flush(a)) )
+ log_error("iobuf_flush failed on close: %s\n", g10_errstr(rc));
+
+ if( DBG_IOBUF )
+ log_debug("iobuf-%d.%d: close `%s'\n", a->no, a->subno, a->desc );
+ if( a->filter && (rc = a->filter(a->filter_ov, IOBUFCTRL_FREE,
+ a->chain, NULL, &dummy_len)) )
+ log_error("IOBUFCTRL_FREE failed on close: %s\n", g10_errstr(rc) );
+ xfree(a->real_fname);
+ if (a->d.buf) {
+ memset (a->d.buf, 0, a->d.size); /* erase the buffer */
+ xfree(a->d.buf);
+ }
+ xfree(a);
+ }
+ return rc;
+}
+
+int
+iobuf_cancel( IOBUF a )
+{
+ const char *s;
+ IOBUF a2;
+ int rc;
+#if defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__)
+ char *remove_name = NULL;
+#endif
+
+ if( a && a->use == 2 ) {
+ s = iobuf_get_real_fname(a);
+ if( s && *s ) {
+#if defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__)
+ remove_name = xstrdup ( s );
+#else
+ remove(s);
+#endif
+ }
+ }
+
+ /* send a cancel message to all filters */
+ for( a2 = a; a2 ; a2 = a2->chain ) {
+ size_t dummy;
+ if( a2->filter )
+ a2->filter( a2->filter_ov, IOBUFCTRL_CANCEL, a2->chain,
+ NULL, &dummy );
+ }
+
+ rc = iobuf_close(a);
+#if defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__)
+ if ( remove_name ) {
+ /* Argg, MSDOS does not allow to remove open files. So
+ * we have to do it here */
+ remove ( remove_name );
+ xfree ( remove_name );
+ }
+#endif
+ return rc;
+}
+
+
+/****************
+ * create a temporary iobuf, which can be used to collect stuff
+ * in an iobuf and later be written by iobuf_write_temp() to another
+ * iobuf.
+ */
+IOBUF
+iobuf_temp()
+{
+ IOBUF a;
+
+ a = iobuf_alloc(3, IOBUF_BUFFER_SIZE );
+
+ return a;
+}
+
+IOBUF
+iobuf_temp_with_content( const char *buffer, size_t length )
+{
+ IOBUF a;
+
+ a = iobuf_alloc(3, length );
+ memcpy( a->d.buf, buffer, length );
+ a->d.len = length;
+
+ return a;
+}
+
+void
+iobuf_enable_special_filenames ( int yes )
+{
+ special_names_enabled = yes;
+}
+
+/*
+ * see whether the filename has the for "-&nnnn", where n is a
+ * non-zero number.
+ * Returns this number or -1 if it is not the case.
+ */
+static int
+check_special_filename ( const char *fname )
+{
+ if ( special_names_enabled
+ && fname && *fname == '-' && fname[1] == '&' ) {
+ int i;
+
+ fname += 2;
+ for (i=0; digitp (fname+i); i++ )
+ ;
+ if ( !fname[i] )
+ return atoi (fname);
+ }
+ return -1;
+}
+
+/* This fucntion returns true if FNAME indicates a PIPE (stdout or
+ stderr) or a special file name if those are enabled. */
+int
+iobuf_is_pipe_filename (const char *fname)
+{
+ if (!fname || (*fname=='-' && !fname[1]) )
+ return 1;
+ return check_special_filename (fname) != -1;
+}
+
+/****************
+ * Create a head iobuf for reading from a file
+ * returns: NULL if an error occures and sets errno
+ */
+IOBUF
+iobuf_open( const char *fname )
+{
+ IOBUF a;
+ FILEP_OR_FD fp;
+ file_filter_ctx_t *fcx;
+ size_t len;
+ int print_only = 0;
+ int fd;
+
+ if( !fname || (*fname=='-' && !fname[1]) ) {
+ fp = FILEP_OR_FD_FOR_STDIN;
+#ifdef USE_SETMODE
+ setmode ( my_fileno(fp) , O_BINARY );
+#endif
+ fname = "[stdin]";
+ print_only = 1;
+ }
+ else if ( (fd = check_special_filename ( fname )) != -1 )
+ return iobuf_fdopen ( translate_file_handle (fd,0), "rb" );
+ else if( (fp = my_fopen_ro(fname, "rb")) == INVALID_FP )
+ return NULL;
+ a = iobuf_alloc(1, IOBUF_BUFFER_SIZE );
+ fcx = xmalloc( sizeof *fcx + strlen(fname) );
+ fcx->fp = fp;
+ fcx->print_only_name = print_only;
+ strcpy(fcx->fname, fname );
+ if( !print_only )
+ a->real_fname = xstrdup( fname );
+ a->filter = file_filter;
+ a->filter_ov = fcx;
+ file_filter( fcx, IOBUFCTRL_DESC, NULL, (byte*)&a->desc, &len );
+ file_filter( fcx, IOBUFCTRL_INIT, NULL, NULL, &len );
+ if( DBG_IOBUF )
+ log_debug("iobuf-%d.%d: open `%s' fd=%d\n",
+ a->no, a->subno, fname, (int)my_fileno(fcx->fp) );
+
+ return a;
+}
+
+/****************
+ * Create a head iobuf for reading from a file
+ * returns: NULL if an error occures and sets errno
+ */
+IOBUF
+iobuf_fdopen( int fd, const char *mode )
+{
+ IOBUF a;
+ FILEP_OR_FD fp;
+ file_filter_ctx_t *fcx;
+ size_t len;
+
+#ifdef FILE_FILTER_USES_STDIO
+ if( !(fp = fdopen(fd, mode)) )
+ return NULL;
+#else
+ fp = (FILEP_OR_FD)fd;
+#endif
+ a = iobuf_alloc( strchr( mode, 'w')? 2:1, IOBUF_BUFFER_SIZE );
+ fcx = xmalloc( sizeof *fcx + 20 );
+ fcx->fp = fp;
+ fcx->print_only_name = 1;
+ sprintf(fcx->fname, "[fd %d]", fd );
+ a->filter = file_filter;
+ a->filter_ov = fcx;
+ file_filter( fcx, IOBUFCTRL_DESC, NULL, (byte*)&a->desc, &len );
+ file_filter( fcx, IOBUFCTRL_INIT, NULL, NULL, &len );
+ if( DBG_IOBUF )
+ log_debug("iobuf-%d.%d: fdopen `%s'\n", a->no, a->subno, fcx->fname );
+ iobuf_ioctl (a,3,1,NULL); /* disable fd caching */
+ return a;
+}
+
+
+IOBUF
+iobuf_sockopen ( int fd, const char *mode )
+{
+ IOBUF a;
+#ifdef _WIN32
+ sock_filter_ctx_t *scx;
+ size_t len;
+
+ a = iobuf_alloc( strchr( mode, 'w')? 2:1, IOBUF_BUFFER_SIZE );
+ scx = xmalloc( sizeof *scx + 25 );
+ scx->sock = fd;
+ scx->print_only_name = 1;
+ sprintf(scx->fname, "[sock %d]", fd );
+ a->filter = sock_filter;
+ a->filter_ov = scx;
+ sock_filter( scx, IOBUFCTRL_DESC, NULL, (byte*)&a->desc, &len );
+ sock_filter( scx, IOBUFCTRL_INIT, NULL, NULL, &len );
+ if( DBG_IOBUF )
+ log_debug("iobuf-%d.%d: sockopen `%s'\n", a->no, a->subno, scx->fname);
+ iobuf_ioctl (a,3,1,NULL); /* disable fd caching */
+#else
+ a = iobuf_fdopen (fd, mode);
+#endif
+ return a;
+}
+
+/****************
+ * create an iobuf for writing to a file; the file will be created.
+ */
+IOBUF
+iobuf_create( const char *fname )
+{
+ IOBUF a;
+ FILEP_OR_FD fp;
+ file_filter_ctx_t *fcx;
+ size_t len;
+ int print_only = 0;
+ int fd;
+
+ if( !fname || (*fname=='-' && !fname[1]) ) {
+ fp = FILEP_OR_FD_FOR_STDOUT;
+#ifdef USE_SETMODE
+ setmode ( my_fileno(fp) , O_BINARY );
+#endif
+ fname = "[stdout]";
+ print_only = 1;
+ }
+ else if ( (fd = check_special_filename ( fname )) != -1 )
+ return iobuf_fdopen ( translate_file_handle (fd, 1), "wb" );
+ else if( (fp = my_fopen(fname, "wb")) == INVALID_FP )
+ return NULL;
+ a = iobuf_alloc(2, IOBUF_BUFFER_SIZE );
+ fcx = xmalloc( sizeof *fcx + strlen(fname) );
+ fcx->fp = fp;
+ fcx->print_only_name = print_only;
+ strcpy(fcx->fname, fname );
+ if( !print_only )
+ a->real_fname = xstrdup( fname );
+ a->filter = file_filter;
+ a->filter_ov = fcx;
+ file_filter( fcx, IOBUFCTRL_DESC, NULL, (byte*)&a->desc, &len );
+ file_filter( fcx, IOBUFCTRL_INIT, NULL, NULL, &len );
+ if( DBG_IOBUF )
+ log_debug("iobuf-%d.%d: create `%s'\n", a->no, a->subno, a->desc );
+
+ return a;
+}
+
+/****************
+ * append to an iobuf; if the file does not exist, create it.
+ * cannot be used for stdout.
+ * Note: This is not used.
+ */
+#if 0 /* not used */
+IOBUF
+iobuf_append( const char *fname )
+{
+ IOBUF a;
+ FILE *fp;
+ file_filter_ctx_t *fcx;
+ size_t len;
+
+ if( !fname )
+ return NULL;
+ else if( !(fp = my_fopen(fname, "ab")) )
+ return NULL;
+ a = iobuf_alloc(2, IOBUF_BUFFER_SIZE );
+ fcx = xmalloc( sizeof *fcx + strlen(fname) );
+ fcx->fp = fp;
+ strcpy(fcx->fname, fname );
+ a->real_fname = xstrdup( fname );
+ a->filter = file_filter;
+ a->filter_ov = fcx;
+ file_filter( fcx, IOBUFCTRL_DESC, NULL, (byte*)&a->desc, &len );
+ file_filter( fcx, IOBUFCTRL_INIT, NULL, NULL, &len );
+ if( DBG_IOBUF )
+ log_debug("iobuf-%d.%d: append `%s'\n", a->no, a->subno, a->desc );
+
+ return a;
+}
+#endif
+
+IOBUF
+iobuf_openrw( const char *fname )
+{
+ IOBUF a;
+ FILEP_OR_FD fp;
+ file_filter_ctx_t *fcx;
+ size_t len;
+
+ if( !fname )
+ return NULL;
+ else if( (fp = my_fopen(fname, "r+b")) == INVALID_FP )
+ return NULL;
+ a = iobuf_alloc(2, IOBUF_BUFFER_SIZE );
+ fcx = xmalloc( sizeof *fcx + strlen(fname) );
+ fcx->fp = fp;
+ strcpy(fcx->fname, fname );
+ a->real_fname = xstrdup( fname );
+ a->filter = file_filter;
+ a->filter_ov = fcx;
+ file_filter( fcx, IOBUFCTRL_DESC, NULL, (byte*)&a->desc, &len );
+ file_filter( fcx, IOBUFCTRL_INIT, NULL, NULL, &len );
+ if( DBG_IOBUF )
+ log_debug("iobuf-%d.%d: openrw `%s'\n", a->no, a->subno, a->desc );
+
+ return a;
+}
+
+
+int
+iobuf_ioctl ( IOBUF a, int cmd, int intval, void *ptrval )
+{
+ if ( cmd == 1 ) { /* keep system filepointer/descriptor open */
+ if( DBG_IOBUF )
+ log_debug("iobuf-%d.%d: ioctl `%s' keep=%d\n",
+ a? a->no:-1, a?a->subno:-1, a?a->desc:"?", intval );
+ for( ; a; a = a->chain )
+ if( !a->chain && a->filter == file_filter ) {
+ file_filter_ctx_t *b = a->filter_ov;
+ b->keep_open = intval;
+ return 0;
+ }
+#ifdef _WIN32
+ else if( !a->chain && a->filter == sock_filter ) {
+ sock_filter_ctx_t *b = a->filter_ov;
+ b->keep_open = intval;
+ return 0;
+ }
+#endif
+ }
+ else if ( cmd == 2 ) { /* invalidate cache */
+ if( DBG_IOBUF )
+ log_debug("iobuf-*.*: ioctl `%s' invalidate\n",
+ ptrval? (char*)ptrval:"[all]");
+ if ( !a && !intval ) {
+#ifndef FILE_FILTER_USES_STDIO
+ fd_cache_invalidate (ptrval);
+#endif
+ return 0;
+ }
+ }
+ else if ( cmd == 3 ) { /* disallow/allow caching */
+ if( DBG_IOBUF )
+ log_debug("iobuf-%d.%d: ioctl `%s' no_cache=%d\n",
+ a? a->no:-1, a?a->subno:-1, a?a->desc:"?", intval );
+ for( ; a; a = a->chain )
+ if( !a->chain && a->filter == file_filter ) {
+ file_filter_ctx_t *b = a->filter_ov;
+ b->no_cache = intval;
+ return 0;
+ }
+#ifdef _WIN32
+ else if( !a->chain && a->filter == sock_filter ) {
+ sock_filter_ctx_t *b = a->filter_ov;
+ b->no_cache = intval;
+ return 0;
+ }
+#endif
+ }
+
+ return -1;
+}
+
+
+/****************
+ * Register an i/o filter.
+ */
+int
+iobuf_push_filter( IOBUF a,
+ int (*f)(void *opaque, int control,
+ IOBUF chain, byte *buf, size_t *len), void *ov )
+{
+ return iobuf_push_filter2( a, f, ov, 0 );
+}
+
+int
+iobuf_push_filter2( IOBUF a,
+ int (*f)(void *opaque, int control,
+ IOBUF chain, byte *buf, size_t *len),
+ void *ov, int rel_ov )
+{
+ IOBUF b;
+ size_t dummy_len=0;
+ int rc=0;
+
+ if( a->directfp )
+ BUG();
+
+ if( a->use == 2 && (rc=iobuf_flush(a)) )
+ return rc;
+ /* make a copy of the current stream, so that
+ * A is the new stream and B the original one.
+ * The contents of the buffers are transferred to the
+ * new stream.
+ */
+ b = xmalloc(sizeof *b);
+ memcpy(b, a, sizeof *b );
+ /* fixme: it is stupid to keep a copy of the name at every level
+ * but we need the name somewhere because the name known by file_filter
+ * may have been released when we need the name of the file */
+ b->real_fname = a->real_fname? xstrdup(a->real_fname):NULL;
+ /* remove the filter stuff from the new stream */
+ a->filter = NULL;
+ a->filter_ov = NULL;
+ a->filter_ov_owner = 0;
+ a->filter_eof = 0;
+ if( a->use == 3 )
+ a->use = 2; /* make a write stream from a temp stream */
+
+ if( a->use == 2 ) { /* allocate a fresh buffer for the original stream */
+ b->d.buf = xmalloc( a->d.size );
+ b->d.len = 0;
+ b->d.start = 0;
+ }
+ else { /* allocate a fresh buffer for the new stream */
+ a->d.buf = xmalloc( a->d.size );
+ a->d.len = 0;
+ a->d.start = 0;
+ }
+ /* disable nlimit for the new stream */
+ a->ntotal = b->ntotal + b->nbytes;
+ a->nlimit = a->nbytes = 0;
+ a->nofast &= ~1;
+ /* make a link from the new stream to the original stream */
+ a->chain = b;
+ a->opaque = b->opaque;
+
+ /* setup the function on the new stream */
+ a->filter = f;
+ a->filter_ov = ov;
+ a->filter_ov_owner = rel_ov;
+
+ a->subno = b->subno + 1;
+ f( ov, IOBUFCTRL_DESC, NULL, (byte*)&a->desc, &dummy_len );
+
+ if( DBG_IOBUF ) {
+ log_debug("iobuf-%d.%d: push `%s'\n", a->no, a->subno, a->desc );
+ print_chain( a );
+ }
+
+ /* now we can initialize the new function if we have one */
+ if( a->filter && (rc = a->filter(a->filter_ov, IOBUFCTRL_INIT, a->chain,
+ NULL, &dummy_len)) )
+ log_error("IOBUFCTRL_INIT failed: %s\n", g10_errstr(rc) );
+ return rc;
+}
+
+/****************
+ * Remove an i/o filter.
+ */
+static int
+pop_filter( IOBUF a, int (*f)(void *opaque, int control,
+ IOBUF chain, byte *buf, size_t *len), void *ov )
+{
+ IOBUF b;
+ size_t dummy_len=0;
+ int rc=0;
+
+ if( a->directfp )
+ BUG();
+
+ if( DBG_IOBUF )
+ log_debug("iobuf-%d.%d: pop `%s'\n", a->no, a->subno, a->desc );
+ if( !a->filter ) { /* this is simple */
+ b = a->chain;
+ assert(b);
+ xfree(a->d.buf);
+ xfree(a->real_fname);
+ memcpy(a,b, sizeof *a);
+ xfree(b);
+ return 0;
+ }
+ for(b=a ; b; b = b->chain )
+ if( b->filter == f && (!ov || b->filter_ov == ov) )
+ break;
+ if( !b )
+ log_bug("pop_filter(): filter function not found\n");
+
+ /* flush this stream if it is an output stream */
+ if( a->use == 2 && (rc=iobuf_flush(b)) ) {
+ log_error("iobuf_flush failed in pop_filter: %s\n", g10_errstr(rc));
+ return rc;
+ }
+ /* and tell the filter to free it self */
+ if( b->filter && (rc = b->filter(b->filter_ov, IOBUFCTRL_FREE, b->chain,
+ NULL, &dummy_len)) ) {
+ log_error("IOBUFCTRL_FREE failed: %s\n", g10_errstr(rc) );
+ return rc;
+ }
+ if( b->filter_ov && b->filter_ov_owner ) {
+ xfree( b->filter_ov );
+ b->filter_ov = NULL;
+ }
+
+
+ /* and see how to remove it */
+ if( a == b && !b->chain )
+ log_bug("can't remove the last filter from the chain\n");
+ else if( a == b ) { /* remove the first iobuf from the chain */
+ /* everything from b is copied to a. This is save because
+ * a flush has been done on the to be removed entry
+ */
+ b = a->chain;
+ xfree(a->d.buf);
+ xfree(a->real_fname);
+ memcpy(a,b, sizeof *a);
+ xfree(b);
+ if( DBG_IOBUF )
+ log_debug("iobuf-%d.%d: popped filter\n", a->no, a->subno );
+ }
+ else if( !b->chain ) { /* remove the last iobuf from the chain */
+ log_bug("Ohh jeee, trying to remove a head filter\n");
+ }
+ else { /* remove an intermediate iobuf from the chain */
+ log_bug("Ohh jeee, trying to remove an intermediate filter\n");
+ }
+
+ return rc;
+}
+
+
+/****************
+ * read underflow: read more bytes into the buffer and return
+ * the first byte or -1 on EOF.
+ */
+static int
+underflow(IOBUF a)
+{
+ size_t len;
+ int rc;
+
+ assert( a->d.start == a->d.len );
+ if( a->use == 3 )
+ return -1; /* EOF because a temp buffer can't do an underflow */
+
+ if( a->filter_eof ) {
+ if( a->chain ) {
+ IOBUF b = a->chain;
+ if( DBG_IOBUF )
+ log_debug("iobuf-%d.%d: pop `%s' in underflow\n",
+ a->no, a->subno, a->desc );
+ xfree(a->d.buf);
+ xfree(a->real_fname);
+ memcpy(a, b, sizeof *a);
+ xfree(b);
+ print_chain(a);
+ }
+ else
+ a->filter_eof = 0; /* for the top level filter */
+ if( DBG_IOBUF )
+ log_debug("iobuf-%d.%d: underflow: eof (due to filter eof)\n",
+ a->no, a->subno );
+ return -1; /* return one(!) EOF */
+ }
+ if( a->error ) {
+ if( DBG_IOBUF )
+ log_debug("iobuf-%d.%d: error\n", a->no, a->subno );
+ return -1;
+ }
+
+ if( a->directfp ) {
+ FILE *fp = a->directfp;
+
+ len = fread( a->d.buf, 1, a->d.size, fp);
+ if( len < a->d.size ) {
+ if( ferror(fp) )
+ a->error = 1;
+ }
+ a->d.len = len;
+ a->d.start = 0;
+ return len? a->d.buf[a->d.start++] : -1;
+ }
+
+
+ if( a->filter ) {
+ len = a->d.size;
+ if( DBG_IOBUF )
+ log_debug("iobuf-%d.%d: underflow: req=%lu\n",
+ a->no, a->subno, (ulong)len );
+ rc = a->filter( a->filter_ov, IOBUFCTRL_UNDERFLOW, a->chain,
+ a->d.buf, &len );
+ if( DBG_IOBUF ) {
+ log_debug("iobuf-%d.%d: underflow: got=%lu rc=%d\n",
+ a->no, a->subno, (ulong)len, rc );
+/* if( a->no == 1 ) */
+/* log_hexdump (" data:", a->d.buf, len); */
+ }
+ if( a->use == 1 && rc == -1 ) { /* EOF: we can remove the filter */
+ size_t dummy_len=0;
+
+ /* and tell the filter to free itself */
+ if( (rc = a->filter(a->filter_ov, IOBUFCTRL_FREE, a->chain,
+ NULL, &dummy_len)) )
+ log_error("IOBUFCTRL_FREE failed: %s\n", g10_errstr(rc) );
+ if( a->filter_ov && a->filter_ov_owner ) {
+ xfree( a->filter_ov );
+ a->filter_ov = NULL;
+ }
+ a->filter = NULL;
+ a->desc = NULL;
+ a->filter_ov = NULL;
+ a->filter_eof = 1;
+ if( !len && a->chain ) {
+ IOBUF b = a->chain;
+ if( DBG_IOBUF )
+ log_debug("iobuf-%d.%d: pop `%s' in underflow (!len)\n",
+ a->no, a->subno, a->desc );
+ xfree(a->d.buf);
+ xfree(a->real_fname);
+ memcpy(a,b, sizeof *a);
+ xfree(b);
+ print_chain(a);
+ }
+ }
+ else if( rc )
+ a->error = 1;
+
+ if( !len ) {
+ if( DBG_IOBUF )
+ log_debug("iobuf-%d.%d: underflow: eof\n", a->no, a->subno );
+ return -1;
+ }
+ a->d.len = len;
+ a->d.start = 0;
+ return a->d.buf[a->d.start++];
+ }
+ else {
+ if( DBG_IOBUF )
+ log_debug("iobuf-%d.%d: underflow: eof (no filter)\n",
+ a->no, a->subno );
+ return -1; /* no filter; return EOF */
+ }
+}
+
+
+int
+iobuf_flush(IOBUF a)
+{
+ size_t len;
+ int rc;
+
+ if( a->directfp )
+ return 0;
+
+ if( a->use == 3 ) { /* increase the temp buffer */
+ char *newbuf;
+ size_t newsize = a->d.size + IOBUF_BUFFER_SIZE;
+
+ if( DBG_IOBUF )
+ log_debug("increasing temp iobuf from %lu to %lu\n",
+ (ulong)a->d.size, (ulong)newsize );
+ newbuf = xmalloc( newsize );
+ memcpy( newbuf, a->d.buf, a->d.len );
+ xfree(a->d.buf);
+ a->d.buf = newbuf;
+ a->d.size = newsize;
+ return 0;
+ }
+ else if( a->use != 2 )
+ log_bug("flush on non-output iobuf\n");
+ else if( !a->filter )
+ log_bug("iobuf_flush: no filter\n");
+ len = a->d.len;
+ rc = a->filter( a->filter_ov, IOBUFCTRL_FLUSH, a->chain, a->d.buf, &len );
+ if( !rc && len != a->d.len ) {
+ log_info("iobuf_flush did not write all!\n");
+ rc = G10ERR_WRITE_FILE;
+ }
+ else if( rc )
+ a->error = 1;
+ a->d.len = 0;
+
+ return rc;
+}
+
+
+/****************
+ * Read a byte from the iobuf; returns -1 on EOF
+ */
+int
+iobuf_readbyte(IOBUF a)
+{
+ int c;
+
+ /* nlimit does not work together with unget */
+ /* nbytes is also not valid! */
+ if( a->unget.buf ) {
+ if( a->unget.start < a->unget.len )
+ return a->unget.buf[a->unget.start++];
+ xfree(a->unget.buf);
+ a->unget.buf = NULL;
+ a->nofast &= ~2;
+ }
+
+ if( a->nlimit && a->nbytes >= a->nlimit )
+ return -1; /* forced EOF */
+
+ if( a->d.start < a->d.len ) {
+ c = a->d.buf[a->d.start++];
+ }
+ else if( (c=underflow(a)) == -1 )
+ return -1; /* EOF */
+
+ a->nbytes++;
+ return c;
+}
+
+
+int
+iobuf_read(IOBUF a, byte *buf, unsigned buflen )
+{
+ int c, n;
+
+ if( a->unget.buf || a->nlimit ) {
+ /* handle special cases */
+ for(n=0 ; n < buflen; n++ ) {
+ if( (c = iobuf_readbyte(a)) == -1 ) {
+ if( !n )
+ return -1; /* eof */
+ break;
+ }
+ else
+ if( buf ) *buf = c;
+ if( buf ) buf++;
+ }
+ return n;
+ }
+
+ n = 0;
+ do {
+ if( n < buflen && a->d.start < a->d.len ) {
+ unsigned size = a->d.len - a->d.start;
+ if( size > buflen - n )
+ size = buflen - n;
+ if( buf )
+ memcpy( buf, a->d.buf + a->d.start, size );
+ n += size;
+ a->d.start += size;
+ if( buf )
+ buf += size;
+ }
+ if( n < buflen ) {
+ if( (c=underflow(a)) == -1 ) {
+ a->nbytes += n;
+ return n? n : -1/*EOF*/;
+ }
+ if( buf )
+ *buf++ = c;
+ n++;
+ }
+ } while( n < buflen );
+ a->nbytes += n;
+ return n;
+}
+
+
+/****************
+ * Have a look at the iobuf.
+ * NOTE: This only works in special cases.
+ */
+int
+iobuf_peek(IOBUF a, byte *buf, unsigned buflen )
+{
+ int n=0;
+
+ if( a->filter_eof )
+ return -1;
+
+ if( !(a->d.start < a->d.len) ) {
+ if( underflow(a) == -1 )
+ return -1;
+ /* and unget this character */
+ assert(a->d.start == 1);
+ a->d.start = 0;
+ }
+
+ for(n=0 ; n < buflen && (a->d.start+n) < a->d.len ; n++, buf++ )
+ *buf = a->d.buf[n];
+ return n;
+}
+
+
+
+
+int
+iobuf_writebyte(IOBUF a, unsigned c)
+{
+
+ if( a->directfp )
+ BUG();
+
+ if( a->d.len == a->d.size )
+ if( iobuf_flush(a) )
+ return -1;
+
+ assert( a->d.len < a->d.size );
+ a->d.buf[a->d.len++] = c;
+ return 0;
+}
+
+
+int
+iobuf_write(IOBUF a, byte *buf, unsigned buflen )
+{
+
+ if( a->directfp )
+ BUG();
+
+ do {
+ if( buflen && a->d.len < a->d.size ) {
+ unsigned size = a->d.size - a->d.len;
+ if( size > buflen ) size = buflen;
+ memcpy( a->d.buf + a->d.len, buf, size );
+ buflen -= size;
+ buf += size;
+ a->d.len += size;
+ }
+ if( buflen ) {
+ if( iobuf_flush(a) )
+ return -1;
+ }
+ } while( buflen );
+ return 0;
+}
+
+
+int
+iobuf_writestr(IOBUF a, const char *buf )
+{
+ for( ; *buf; buf++ )
+ if( iobuf_writebyte(a, *buf) )
+ return -1;
+ return 0;
+}
+
+
+
+/****************
+ * copy the contents of TEMP to A.
+ */
+int
+iobuf_write_temp( IOBUF a, IOBUF temp )
+{
+ while( temp->chain )
+ pop_filter( temp, temp->filter, NULL );
+ return iobuf_write(a, temp->d.buf, temp->d.len );
+}
+
+/****************
+ * copy the contents of the temp io stream to BUFFER.
+ */
+size_t
+iobuf_temp_to_buffer( IOBUF a, byte *buffer, size_t buflen )
+{
+ size_t n = a->d.len;
+
+ if( n > buflen )
+ n = buflen;
+ memcpy( buffer, a->d.buf, n );
+ return n;
+}
+
+
+/****************
+ * Call this function to terminate processing of the temp stream
+ * without closing it. This removes all filters from the stream
+ * makes sure that iobuf_get_temp_{buffer,length}() returns correct
+ * values.
+ */
+void
+iobuf_flush_temp( IOBUF temp )
+{
+ while( temp->chain )
+ pop_filter( temp, temp->filter, NULL );
+}
+
+
+/****************
+ * Set a limit on how many bytes may be read from the input stream A.
+ * Setting the limit to 0 disables this feature.
+ */
+void
+iobuf_set_limit( IOBUF a, off_t nlimit )
+{
+ if( nlimit )
+ a->nofast |= 1;
+ else
+ a->nofast &= ~1;
+ a->nlimit = nlimit;
+ a->ntotal += a->nbytes;
+ a->nbytes = 0;
+}
+
+
+
+/* Return the length of an open file A. IF OVERFLOW is not NULL it
+ will be set to true if the file is larger than what off_t can cope
+ with. The function return 0 on error or on overflow condition. */
+off_t
+iobuf_get_filelength (IOBUF a, int *overflow )
+{
+ struct stat st;
+
+ if (overflow)
+ *overflow = 0;
+
+ if( a->directfp ) {
+ FILE *fp = a->directfp;
+
+ if( !fstat(fileno(fp), &st) )
+ return st.st_size;
+ log_error("fstat() failed: %s\n", strerror(errno) );
+ return 0;
+ }
+
+ /* Hmmm: file_filter may have already been removed */
+ for( ; a; a = a->chain )
+ if( !a->chain && a->filter == file_filter ) {
+ file_filter_ctx_t *b = a->filter_ov;
+ FILEP_OR_FD fp = b->fp;
+
+#if defined(HAVE_DOSISH_SYSTEM) && !defined(FILE_FILTER_USES_STDIO)
+ ulong size;
+ static int (* __stdcall get_file_size_ex)
+ (void *handle, LARGE_INTEGER *size);
+ static int get_file_size_ex_initialized;
+
+ if (!get_file_size_ex_initialized)
+ {
+ void *handle;
+
+ handle = dlopen ("kernel32.dll", RTLD_LAZY);
+ if (handle)
+ {
+ get_file_size_ex = dlsym (handle, "GetFileSizeEx");
+ if (!get_file_size_ex)
+ dlclose (handle);
+ }
+ get_file_size_ex_initialized = 1;
+ }
+
+ if (get_file_size_ex)
+ {
+ /* This is a newer system with GetFileSizeEx; we use
+ this then becuase it seem that GetFileSize won't
+ return a proper error in case a file is larger than
+ 4GB. */
+ LARGE_INTEGER size;
+
+ if (get_file_size_ex (fp, &size))
+ {
+ if (!size.u.HighPart)
+ return size.u.LowPart;
+ if (overflow)
+ *overflow = 1;
+ return 0;
+ }
+ }
+ else
+ {
+ if ((size=GetFileSize (fp, NULL)) != 0xffffffff)
+ return size;
+ }
+ log_error ("GetFileSize for handle %p failed: %s\n",
+ fp, w32_strerror (0));
+#else
+ if( !fstat(my_fileno(fp), &st) )
+ return st.st_size;
+ log_error("fstat() failed: %s\n", strerror(errno) );
+#endif
+ break;
+ }
+
+ return 0;
+}
+
+
+/* Return the file descriptor of the underlying file or -1 if it is
+ not available. */
+int
+iobuf_get_fd (IOBUF a)
+{
+ if (a->directfp)
+ return fileno ( (FILE*)a->directfp );
+
+ for ( ; a; a = a->chain )
+ if (!a->chain && a->filter == file_filter)
+ {
+ file_filter_ctx_t *b = a->filter_ov;
+ FILEP_OR_FD fp = b->fp;
+
+ return my_fileno (fp);
+ }
+
+ return -1;
+}
+
+
+/****************
+ * Tell the file position, where the next read will take place
+ */
+off_t
+iobuf_tell( IOBUF a )
+{
+ return a->ntotal + a->nbytes;
+}
+
+
+#if !defined(HAVE_FSEEKO) && !defined(fseeko)
+
+#ifdef HAVE_LIMITS_H
+# include <limits.h>
+#endif
+#ifndef LONG_MAX
+# define LONG_MAX ((long) ((unsigned long) -1 >> 1))
+#endif
+#ifndef LONG_MIN
+# define LONG_MIN (-1 - LONG_MAX)
+#endif
+
+/****************
+ * A substitute for fseeko, for hosts that don't have it.
+ */
+static int
+fseeko( FILE *stream, off_t newpos, int whence )
+{
+ while( newpos != (long) newpos ) {
+ long pos = newpos < 0 ? LONG_MIN : LONG_MAX;
+ if( fseek( stream, pos, whence ) != 0 )
+ return -1;
+ newpos -= pos;
+ whence = SEEK_CUR;
+ }
+ return fseek( stream, (long)newpos, whence );
+}
+#endif
+
+/****************
+ * This is a very limited implementation. It simply discards all internal
+ * buffering and removes all filters but the first one.
+ */
+int
+iobuf_seek( IOBUF a, off_t newpos )
+{
+ file_filter_ctx_t *b = NULL;
+
+ if( a->directfp ) {
+ FILE *fp = a->directfp;
+ if( fseeko( fp, newpos, SEEK_SET ) ) {
+ log_error("can't seek: %s\n", strerror(errno) );
+ return -1;
+ }
+ clearerr(fp);
+ }
+ else {
+ for( ; a; a = a->chain ) {
+ if( !a->chain && a->filter == file_filter ) {
+ b = a->filter_ov;
+ break;
+ }
+ }
+ if( !a )
+ return -1;
+#ifdef FILE_FILTER_USES_STDIO
+ if( fseeko( b->fp, newpos, SEEK_SET ) ) {
+ log_error("can't fseek: %s\n", strerror(errno) );
+ return -1;
+ }
+#else
+#ifdef HAVE_DOSISH_SYSTEM
+ if (SetFilePointer (b->fp, newpos, NULL, FILE_BEGIN) == 0xffffffff ) {
+ log_error ("SetFilePointer failed on handle %p: %s\n",
+ b->fp, w32_strerror (0));
+ return -1;
+ }
+#else
+ if ( lseek (b->fp, newpos, SEEK_SET) == (off_t)-1 ) {
+ log_error("can't lseek: %s\n", strerror(errno) );
+ return -1;
+ }
+#endif
+#endif
+ }
+ a->d.len = 0; /* discard buffer */
+ a->d.start = 0;
+ a->nbytes = 0;
+ a->nlimit = 0;
+ a->nofast &= ~1;
+ a->ntotal = newpos;
+ a->error = 0;
+ /* remove filters, but the last */
+ if( a->chain )
+ log_debug("pop_filter called in iobuf_seek - please report\n");
+ while( a->chain )
+ pop_filter( a, a->filter, NULL );
+
+ return 0;
+}
+
+
+
+
+
+
+/****************
+ * Retrieve the real filename
+ */
+const char *
+iobuf_get_real_fname( IOBUF a )
+{
+ if( a->real_fname )
+ return a->real_fname;
+
+ /* the old solution */
+ for( ; a; a = a->chain )
+ if( !a->chain && a->filter == file_filter ) {
+ file_filter_ctx_t *b = a->filter_ov;
+ return b->print_only_name? NULL : b->fname;
+ }
+
+ return NULL;
+}
+
+
+/****************
+ * Retrieve the filename
+ */
+const char *
+iobuf_get_fname( IOBUF a )
+{
+ for( ; a; a = a->chain )
+ if( !a->chain && a->filter == file_filter ) {
+ file_filter_ctx_t *b = a->filter_ov;
+ return b->fname;
+ }
+
+ return NULL;
+}
+
+
+/****************
+ * enable partial block mode as described in the OpenPGP draft.
+ * LEN is the first length byte on read, but ignored on writes.
+ */
+void
+iobuf_set_partial_block_mode( IOBUF a, size_t len )
+{
+ block_filter_ctx_t *ctx = xmalloc_clear( sizeof *ctx );
+
+ assert( a->use == 1 || a->use == 2 );
+ ctx->use = a->use;
+ if( !len ) {
+ if( a->use == 1 )
+ log_debug("pop_filter called in set_partial_block_mode"
+ " - please report\n");
+ pop_filter(a, block_filter, NULL );
+ }
+ else {
+ ctx->partial = 1;
+ ctx->size = 0;
+ ctx->first_c = len;
+ iobuf_push_filter(a, block_filter, ctx );
+ }
+}
+
+
+/****************
+ * Same as fgets() but if the buffer is too short a larger one will
+ * be allocated up to some limit *max_length.
+ * A line is considered a byte stream ending in a LF.
+ * Returns the length of the line. EOF is indicated by a line of
+ * length zero. The last LF may be missing due to an EOF.
+ * is max_length is zero on return, the line has been truncated.
+ *
+ * Note: The buffer is allocated with enough space to append a CR,LF,EOL
+ */
+unsigned
+iobuf_read_line( IOBUF a, byte **addr_of_buffer,
+ unsigned *length_of_buffer, unsigned *max_length )
+{
+ int c;
+ char *buffer = *addr_of_buffer;
+ unsigned length = *length_of_buffer;
+ unsigned nbytes = 0;
+ unsigned maxlen = *max_length;
+ char *p;
+
+ if( !buffer ) { /* must allocate a new buffer */
+ length = 256;
+ buffer = xmalloc( length );
+ *addr_of_buffer = buffer;
+ *length_of_buffer = length;
+ }
+
+ length -= 3; /* reserve 3 bytes (cr,lf,eol) */
+ p = buffer;
+ while( (c=iobuf_get(a)) != -1 ) {
+ if( nbytes == length ) { /* increase the buffer */
+ if( length > maxlen ) { /* this is out limit */
+ /* skip the rest of the line */
+ while( c != '\n' && (c=iobuf_get(a)) != -1 )
+ ;
+ *p++ = '\n'; /* always append a LF (we have reserved space) */
+ nbytes++;
+ *max_length = 0; /* indicate truncation */
+ break;
+ }
+ length += 3; /* correct for the reserved byte */
+ length += length < 1024? 256 : 1024;
+ buffer = xrealloc( buffer, length );
+ *addr_of_buffer = buffer;
+ *length_of_buffer = length;
+ length -= 3; /* and reserve again */
+ p = buffer + nbytes;
+ }
+ *p++ = c;
+ nbytes++;
+ if( c == '\n' )
+ break;
+ }
+ *p = 0; /* make sure the line is a string */
+
+ return nbytes;
+}
+
+/* This is the non iobuf specific function */
+int
+iobuf_translate_file_handle ( int fd, int for_write )
+{
+#ifdef _WIN32
+ {
+ int x;
+
+ if ( fd <= 2 )
+ return fd; /* do not do this for error, stdin, stdout, stderr */
+
+ x = _open_osfhandle ( fd, for_write? 1:0 );
+ if (x==-1 )
+ log_error ("failed to translate osfhandle %p\n", (void*)fd );
+ else {
+ /*log_info ("_open_osfhandle %p yields %d%s\n",
+ (void*)fd, x, for_write? " for writing":"" );*/
+ fd = x;
+ }
+ }
+#endif
+ return fd;
+}
+
+static int
+translate_file_handle ( int fd, int for_write )
+{
+#ifdef _WIN32
+#ifdef FILE_FILTER_USES_STDIO
+ fd = iobuf_translate_file_handle (fd, for_write);
+#else
+ {
+ int x;
+
+ if ( fd == 0 )
+ x = (int)GetStdHandle (STD_INPUT_HANDLE);
+ else if (fd == 1)
+ x = (int)GetStdHandle (STD_OUTPUT_HANDLE);
+ else if (fd == 2)
+ x = (int)GetStdHandle (STD_ERROR_HANDLE);
+ else
+ x = fd;
+
+ if (x == -1)
+ log_debug ("GetStdHandle(%d) failed: %s\n",
+ fd, w32_strerror (0));
+
+ fd = x;
+ }
+#endif
+#endif
+ return fd;
+}
+
+
+void
+iobuf_skip_rest(IOBUF a, unsigned long n, int partial)
+{
+ if ( partial ) {
+ for (;;) {
+ if (a->nofast || a->d.start >= a->d.len) {
+ if (iobuf_readbyte (a) == -1) {
+ break;
+ }
+ } else {
+ unsigned long count = a->d.len - a->d.start;
+ a->nbytes += count;
+ a->d.start = a->d.len;
+ }
+ }
+ } else {
+ unsigned long remaining = n;
+ while (remaining > 0) {
+ if (a->nofast || a->d.start >= a->d.len) {
+ if (iobuf_readbyte (a) == -1) {
+ break;
+ }
+ --remaining;
+ } else {
+ unsigned long count = a->d.len - a->d.start;
+ if (count > remaining) {
+ count = remaining;
+ }
+ a->nbytes += count;
+ a->d.start += count;
+ remaining -= count;
+ }
+ }
+ }
+}
diff --git a/util/isascii.c b/util/isascii.c
new file mode 100644
index 0000000..b71febe
--- /dev/null
+++ b/util/isascii.c
@@ -0,0 +1,30 @@
+/* isascii.c - Replacement for isascii.
+ * Copyright (C) 2002 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+int
+isascii (int c)
+{
+ return (((c) & ~0x7f) == 0);
+}
diff --git a/util/logger.c b/util/logger.c
new file mode 100644
index 0000000..857436c
--- /dev/null
+++ b/util/logger.c
@@ -0,0 +1,265 @@
+/* logger.c - log functions
+ * Copyright (C) 1998, 1999 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+
+#include "util.h"
+#include "i18n.h"
+
+static char pidstring[15];
+static char *pgm_name;
+static int errorcount;
+static int strict;
+static FILE *logfp;
+
+/****************
+ * Set the logfile to use (not yet implemneted) or, if logfile is NULL,
+ * the Fd where logoutputs should go.
+ */
+void
+log_set_logfile( const char *name, int fd )
+{
+ if( name )
+ BUG();
+
+ if( logfp && logfp != stderr && logfp != stdout )
+ fclose( logfp );
+ if( fd == 1 )
+ logfp = stdout;
+ else if( fd == 2 )
+ logfp = stderr;
+ else
+ logfp = fdopen( fd, "a" );
+ if( !logfp ) {
+ logfp = stderr;
+ log_fatal("can't open fd %d for logging: %s\n", fd, strerror(errno));
+ }
+}
+
+FILE *
+log_stream()
+{
+ if( !logfp )
+ logfp = stderr;
+ return logfp;
+}
+
+
+void
+log_set_name( const char *name )
+{
+ xfree(pgm_name);
+ if( name )
+ pgm_name = xstrdup(name);
+ else
+ pgm_name = NULL;
+}
+
+const char *
+log_get_name(void)
+{
+ return pgm_name? pgm_name : "";
+}
+
+
+void
+log_set_pid( int pid )
+{
+ if( pid )
+ sprintf(pidstring,"[%u]", (unsigned)pid );
+ else
+ *pidstring = 0;
+}
+
+int
+log_get_errorcount( int clear)
+{
+ int n = errorcount;
+ if( clear )
+ errorcount = 0;
+ return n;
+}
+
+void
+log_inc_errorcount()
+{
+ errorcount++;
+}
+
+int
+log_set_strict(int val)
+{
+ int old=strict;
+ strict=val;
+ return old;
+}
+
+void
+g10_log_print_prefix(const char *text)
+{
+ if( !logfp )
+ logfp = stderr;
+ if( pgm_name )
+ fprintf(logfp, "%s%s: %s", pgm_name, pidstring, text );
+ else
+ fprintf(logfp, "?%s: %s", pidstring, text );
+#ifdef __riscos__
+ fflush( logfp );
+#endif /* __riscos__ */
+}
+
+
+void
+g10_log_info( const char *fmt, ... )
+{
+ va_list arg_ptr ;
+
+ g10_log_print_prefix("");
+ va_start( arg_ptr, fmt ) ;
+ vfprintf(logfp,fmt,arg_ptr) ;
+ va_end(arg_ptr);
+#ifdef __riscos__
+ fflush( logfp );
+#endif /* __riscos__ */
+}
+
+
+void
+g10_log_warning( const char *fmt, ... )
+{
+ va_list arg_ptr ;
+
+ if(strict)
+ {
+ errorcount++;
+ g10_log_print_prefix(_("ERROR: "));
+ }
+ else
+ g10_log_print_prefix(_("WARNING: "));
+
+ va_start( arg_ptr, fmt ) ;
+ vfprintf(logfp,fmt,arg_ptr) ;
+ va_end(arg_ptr);
+#ifdef __riscos__
+ fflush( logfp );
+#endif /* __riscos__ */
+}
+
+
+void
+g10_log_error( const char *fmt, ... )
+{
+ va_list arg_ptr ;
+
+ g10_log_print_prefix("");
+ va_start( arg_ptr, fmt ) ;
+ vfprintf(logfp,fmt,arg_ptr) ;
+ va_end(arg_ptr);
+ errorcount++;
+#ifdef __riscos__
+ fflush( logfp );
+#endif /* __riscos__ */
+}
+
+
+void
+g10_log_fatal( const char *fmt, ... )
+{
+ va_list arg_ptr ;
+
+ g10_log_print_prefix("fatal: ");
+ va_start( arg_ptr, fmt ) ;
+ vfprintf(logfp,fmt,arg_ptr) ;
+ va_end(arg_ptr);
+ secmem_dump_stats();
+#ifdef __riscos__
+ fflush( logfp );
+#endif /* __riscos__ */
+ exit(2);
+}
+
+void
+g10_log_bug( const char *fmt, ... )
+{
+ va_list arg_ptr ;
+
+ putc('\n', stderr );
+ g10_log_print_prefix("Ohhhh jeeee: ");
+ va_start( arg_ptr, fmt ) ;
+ vfprintf(stderr,fmt,arg_ptr) ;
+ va_end(arg_ptr);
+ fflush(stderr);
+ secmem_dump_stats();
+ abort();
+}
+
+#if defined (__riscos__) \
+ || ( __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5 ))
+void
+g10_log_bug0( const char *file, int line, const char *func )
+{
+ log_bug(_("... this is a bug (%s:%d:%s)\n"), file, line, func );
+}
+#else
+void
+g10_log_bug0( const char *file, int line )
+{
+ log_bug(_("you found a bug ... (%s:%d)\n"), file, line);
+}
+#endif
+
+void
+g10_log_debug( const char *fmt, ... )
+{
+ va_list arg_ptr ;
+
+ g10_log_print_prefix("DBG: ");
+ va_start( arg_ptr, fmt ) ;
+ vfprintf(logfp,fmt,arg_ptr) ;
+ va_end(arg_ptr);
+#ifdef __riscos__
+ fflush( logfp );
+#endif /* __riscos__ */
+}
+
+
+
+void
+g10_log_hexdump( const char *text, const char *buf, size_t len )
+{
+ int i;
+
+ g10_log_print_prefix(text);
+ for(i=0; i < len; i++ )
+ fprintf(logfp, " %02X", ((const byte*)buf)[i] );
+ fputc('\n', logfp);
+#ifdef __riscos__
+ fflush( logfp );
+#endif /* __riscos__ */
+}
+
+
+
diff --git a/util/membuf.c b/util/membuf.c
new file mode 100644
index 0000000..44347fa
--- /dev/null
+++ b/util/membuf.c
@@ -0,0 +1,84 @@
+/* membuf.c - A simple implementation of a dynamic buffer
+ * Copyright (C) 2001, 2003 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+
+#include "util.h"
+
+
+/* A simple implementation of a dynamic buffer. Use init_membuf() to
+ create a buffer, put_membuf to append bytes and get_membuf to
+ release and return the buffer. Allocation errors are detected but
+ only returned at the final get_membuf(), this helps not to clutter
+ the code with out of core checks. */
+
+void
+init_membuf (membuf_t *mb, int initiallen)
+{
+ mb->len = 0;
+ mb->size = initiallen;
+ mb->out_of_core = 0;
+ mb->buf = xmalloc (initiallen);
+ if (!mb->buf)
+ mb->out_of_core = errno;
+}
+
+
+void
+put_membuf (membuf_t *mb, const void *buf, size_t len)
+{
+ if (mb->out_of_core)
+ return;
+
+ if (mb->len + len >= mb->size)
+ {
+ char *p;
+
+ mb->size += len + 1024;
+ p = xrealloc (mb->buf, mb->size);
+ mb->buf = p;
+ }
+ memcpy (mb->buf + mb->len, buf, len);
+ mb->len += len;
+}
+
+
+void *
+get_membuf (membuf_t *mb, size_t *len)
+{
+ char *p;
+
+ if (mb->out_of_core)
+ {
+ xfree (mb->buf);
+ mb->buf = NULL;
+ return NULL;
+ }
+
+ p = mb->buf;
+ *len = mb->len;
+ mb->buf = NULL;
+ mb->out_of_core = ENOMEM; /* hack to make sure it won't get reused. */
+ return p;
+}
diff --git a/util/memory.c b/util/memory.c
new file mode 100644
index 0000000..e16c3d8
--- /dev/null
+++ b/util/memory.c
@@ -0,0 +1,647 @@
+/* memory.c - memory allocation
+ * Copyright (C) 1998, 1999, 2001, 2005 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ *
+ *
+ * We use our own memory allocation functions instead of plain malloc(),
+ * so that we can provide some special enhancements:
+ * a) functions to provide memory from a secure memory.
+ * b) by looking at the requested allocation size we
+ * can reuse memory very quickly (e.g. MPI storage)
+ * (really needed?)
+ * c) memory usage reporting if compiled with M_DEBUG
+ * d) memory checking if compiled with M_GUARD
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include "types.h"
+#include "memory.h"
+#include "util.h"
+
+
+#define MAGIC_NOR_BYTE 0x55
+#define MAGIC_SEC_BYTE 0xcc
+#define MAGIC_END_BYTE 0xaa
+
+/* This is a very crude alignment check which does not work on all CPUs
+ * IIRC, I once introduced it for testing on an Alpha. We should better
+ * replace this guard stuff with one provided by a modern malloc library
+ */
+#if SIZEOF_UNSIGNED_LONG == 8
+#define EXTRA_ALIGN 4
+#else
+#define EXTRA_ALIGN 0
+#endif
+
+#if defined(M_DEBUG) || defined(M_GUARD)
+ static void membug( const char *fmt, ... );
+#endif
+
+#ifdef M_DEBUG
+
+#ifndef M_GUARD
+#define M_GUARD 1
+#endif
+#undef xmalloc
+#undef xmalloc_clear
+#undef xmalloc_secure
+#undef xmalloc_secure_clear
+#undef xrealloc
+#undef xfree
+#undef m_check
+#undef xstrdup
+#define FNAME(a) m_debug_ ##a
+#define FNAMEX(a) m_debug_ ##a
+#define FNAMEXM(a) m_debug_ ##a
+#define FNAMEPRT , const char *info
+#define FNAMEARG , info
+#ifndef __riscos__
+#define store_len(p,n,m) do { add_entry(p,n,m, \
+ info, __FUNCTION__); } while(0)
+#else
+#define store_len(p,n,m) do { add_entry(p,n,m, \
+ info, __func__ ); } while(0)
+#endif
+#else
+#define FNAME(a) m_ ##a
+#define FNAMEX(a) x ##a
+#define FNAMEXM(a) xm ##a
+#define FNAMEPRT
+#define FNAMEARG
+#define store_len(p,n,m) do { ((byte*)p)[EXTRA_ALIGN+0] = n; \
+ ((byte*)p)[EXTRA_ALIGN+1] = n >> 8 ; \
+ ((byte*)p)[EXTRA_ALIGN+2] = n >> 16 ; \
+ ((byte*)p)[EXTRA_ALIGN+3] = m? MAGIC_SEC_BYTE \
+ : MAGIC_NOR_BYTE; \
+ } while(0)
+#endif
+
+
+#ifdef M_GUARD
+static long used_memory;
+#endif
+
+#ifdef M_DEBUG /* stuff used for memory debuging */
+
+struct info_entry {
+ struct info_entry *next;
+ unsigned count; /* call count */
+ const char *info; /* the reference to the info string */
+};
+
+struct memtbl_entry {
+ const void *user_p; /* for reference: the pointer given to the user */
+ size_t user_n; /* length requested by the user */
+ struct memtbl_entry *next; /* to build a list of unused entries */
+ const struct info_entry *info; /* points into the table with */
+ /* the info strings */
+ unsigned inuse:1; /* this entry is in use */
+ unsigned count:31;
+};
+
+
+#define INFO_BUCKETS 53
+#define info_hash(p) ( *(u32*)((p)) % INFO_BUCKETS )
+static struct info_entry *info_strings[INFO_BUCKETS]; /* hash table */
+
+static struct memtbl_entry *memtbl; /* the table with the memory info */
+static unsigned memtbl_size; /* number of allocated entries */
+static unsigned memtbl_len; /* number of used entries */
+static struct memtbl_entry *memtbl_unused;/* to keep track of unused entries */
+
+static void dump_table_at_exit(void);
+static void dump_table(void);
+static void check_allmem( const char *info );
+
+/****************
+ * Put the new P into the debug table and return a pointer to the table entry.
+ * mode is true for security. BY is the name of the function which called us.
+ */
+static void
+add_entry( byte *p, unsigned n, int mode, const char *info, const char *by )
+{
+ unsigned index;
+ struct memtbl_entry *e;
+ struct info_entry *ie;
+
+ if( memtbl_len < memtbl_size )
+ index = memtbl_len++;
+ else {
+ struct memtbl_entry *e;
+ /* look for a used entry in the table. We take the first one,
+ * so that freed entries remain as long as possible in the table
+ * (free appends a new one)
+ */
+ if( (e = memtbl_unused) ) {
+ index = e - memtbl;
+ memtbl_unused = e->next;
+ e->next = NULL;
+ }
+ else { /* no free entries in the table: extend the table */
+ if( !memtbl_size ) { /* first time */
+ memtbl_size = 100;
+ if( !(memtbl = calloc( memtbl_size, sizeof *memtbl )) )
+ membug("memory debug table malloc failed\n");
+ index = 0;
+ memtbl_len = 1;
+ atexit( dump_table_at_exit );
+ }
+ else { /* realloc */
+ unsigned n = memtbl_size / 4; /* enlarge by 25% */
+ if(!(memtbl = realloc(memtbl, (memtbl_size+n)*sizeof *memtbl)))
+ membug("memory debug table realloc failed\n");
+ memset(memtbl+memtbl_size, 0, n*sizeof *memtbl );
+ memtbl_size += n;
+ index = memtbl_len++;
+ }
+ }
+ }
+ e = memtbl+index;
+ if( e->inuse )
+ membug("Ooops: entry %u is flagged as in use\n", index);
+ e->user_p = p + EXTRA_ALIGN + 4;
+ e->user_n = n;
+ e->count++;
+ if( e->next )
+ membug("Ooops: entry is in free entry list\n");
+ /* do we already have this info string */
+ for( ie = info_strings[info_hash(info)]; ie; ie = ie->next )
+ if( ie->info == info )
+ break;
+ if( !ie ) { /* no: make a new entry */
+ if( !(ie = malloc( sizeof *ie )) )
+ membug("can't allocate info entry\n");
+ ie->next = info_strings[info_hash(info)];
+ info_strings[info_hash(info)] = ie;
+ ie->info = info;
+ ie->count = 0;
+ }
+ ie->count++;
+ e->info = ie;
+ e->inuse = 1;
+
+ /* put the index at the start of the memory */
+ p[EXTRA_ALIGN+0] = index;
+ p[EXTRA_ALIGN+1] = index >> 8 ;
+ p[EXTRA_ALIGN+2] = index >> 16 ;
+ p[EXTRA_ALIGN+3] = mode? MAGIC_SEC_BYTE : MAGIC_NOR_BYTE ;
+ if( DBG_MEMORY )
+ log_debug( "%s allocates %u bytes using %s\n", info, e->user_n, by );
+}
+
+
+
+/****************
+ * Check that the memory block is correct. The magic byte has already been
+ * checked. Checks which are done here:
+ * - see whether the index points into our memory table
+ * - see whether P is the same as the one stored in the table
+ * - see whether we have already freed this block.
+ */
+struct memtbl_entry *
+check_mem( const byte *p, const char *info )
+{
+ unsigned n;
+ struct memtbl_entry *e;
+
+ n = p[EXTRA_ALIGN+0];
+ n |= p[EXTRA_ALIGN+1] << 8;
+ n |= p[EXTRA_ALIGN+2] << 16;
+
+ if( n >= memtbl_len )
+ membug("memory at %p corrupted: index=%u table_len=%u (%s)\n",
+ p+EXTRA_ALIGN+4, n, memtbl_len, info );
+ e = memtbl+n;
+
+ if( e->user_p != p+EXTRA_ALIGN+4 )
+ membug("memory at %p corrupted: reference mismatch (%s)\n",
+ p+EXTRA_ALIGN+4, info );
+ if( !e->inuse )
+ membug("memory at %p corrupted: marked as free (%s)\n",
+ p+EXTRA_ALIGN+4, info );
+
+ if( !(p[EXTRA_ALIGN+3] == MAGIC_NOR_BYTE
+ || p[EXTRA_ALIGN+3] == MAGIC_SEC_BYTE) )
+ membug("memory at %p corrupted: underflow=%02x (%s)\n",
+ p+EXTRA_ALIGN+4, p[EXTRA_ALIGN+3], info );
+ if( p[EXTRA_ALIGN+4+e->user_n] != MAGIC_END_BYTE )
+ membug("memory at %p corrupted: overflow=%02x (%s)\n",
+ p+EXTRA_ALIGN+4, p[EXTRA_ALIGN+4+e->user_n], info );
+ return e;
+}
+
+
+/****************
+ * free the entry and the memory (replaces free)
+ */
+static void
+free_entry( byte *p, const char *info )
+{
+ struct memtbl_entry *e, *e2;
+
+ check_allmem("add_entry");
+
+ e = check_mem(p, info);
+ if( DBG_MEMORY )
+ log_debug( "%s frees %u bytes alloced by %s\n",
+ info, e->user_n, e->info->info );
+ if( !e->inuse ) {
+ if( e->user_p == p + EXTRA_ALIGN+ 4 )
+ membug("freeing an already freed pointer at %p\n", p+EXTRA_ALIGN+4 );
+ else
+ membug("freeing pointer %p which is flagged as freed\n", p+EXTRA_ALIGN+4 );
+ }
+
+ e->inuse = 0;
+ e->next = NULL;
+ if( !memtbl_unused )
+ memtbl_unused = e;
+ else {
+ for(e2=memtbl_unused; e2->next; e2 = e2->next )
+ ;
+ e2->next = e;
+ }
+ if( m_is_secure(p+EXTRA_ALIGN+4) )
+ secmem_free(p);
+ else {
+ memset(p,'f', e->user_n+5);
+ free(p);
+ }
+}
+
+static void
+dump_entry(struct memtbl_entry *e )
+{
+ unsigned n = e - memtbl;
+
+ fprintf(stderr, "mem %4u%c %5u %p %5u %s (%u)\n",
+ n, e->inuse?'a':'u', e->count, e->user_p, e->user_n,
+ e->info->info, e->info->count );
+
+
+}
+
+
+static void
+dump_table_at_exit( void)
+{
+ if( DBG_MEMSTAT )
+ dump_table();
+}
+
+static void
+dump_table( void)
+{
+ unsigned n;
+ struct memtbl_entry *e;
+ ulong sum = 0, chunks =0;
+
+ for( e = memtbl, n = 0; n < memtbl_len; n++, e++ ) {
+ if(e->inuse) {
+ dump_entry(e);
+ sum += e->user_n;
+ chunks++;
+ }
+ }
+ fprintf(stderr, " memory used: %8lu bytes in %ld chunks\n",
+ sum, chunks );
+}
+
+
+static void
+check_allmem( const char *info )
+{
+ unsigned n;
+ struct memtbl_entry *e;
+
+ for( e = memtbl, n = 0; n < memtbl_len; n++, e++ ) {
+ if( e->inuse ) {
+#ifndef __riscos__
+ check_mem(e->user_p-4-EXTRA_ALIGN, info);
+#else
+ check_mem((const byte *) e->user_p-4-EXTRA_ALIGN, info);
+#endif
+ }
+ }
+}
+
+#endif /* M_DEBUG */
+
+#if defined(M_DEBUG) || defined(M_GUARD)
+static void
+membug( const char *fmt, ... )
+{
+ va_list arg_ptr ;
+
+ fprintf(stderr, "\nMemory Error: " ) ;
+ va_start( arg_ptr, fmt ) ;
+ vfprintf(stderr,fmt,arg_ptr) ;
+ va_end(arg_ptr);
+ fflush(stderr);
+#ifdef M_DEBUG
+ if( DBG_MEMSTAT )
+ dump_table();
+#endif
+ abort();
+}
+#endif
+
+void
+m_print_stats( const char *prefix )
+{
+#ifdef M_DEBUG
+ unsigned n;
+ struct memtbl_entry *e;
+ ulong sum = 0, chunks =0;
+
+ for( e = memtbl, n = 0; n < memtbl_len; n++, e++ ) {
+ if(e->inuse) {
+ sum += e->user_n;
+ chunks++;
+ }
+ }
+
+ log_debug( "%s%smemstat: %8lu bytes in %ld chunks used\n",
+ prefix? prefix:"", prefix? ": ":"", sum, chunks );
+#elif defined(M_GUARD)
+ log_debug( "%s%smemstat: %8ld bytes\n",
+ prefix? prefix:"", prefix? ": ":"", used_memory );
+#endif
+}
+
+void
+m_dump_table( const char *prefix )
+{
+#ifdef M_DEBUG
+ fprintf(stderr,"Memory-Table-Dump: %s\n", prefix);
+ dump_table();
+#endif
+ m_print_stats( prefix );
+}
+
+
+static void
+out_of_core(size_t n, int secure)
+{
+ log_error ("out of %s memory while allocating %u bytes\n",
+ secure? "secure":"" ,(unsigned)n );
+ if (secure) {
+ /*secmem_dump_stats ();*/
+ log_info ("(this may be caused by too many secret keys used "
+ "simultaneously or due to excessive large key sizes)\n");
+ }
+#if defined(M_GUARD) && defined(__riscos__)
+ abort();
+#endif
+ exit (2);
+}
+
+/****************
+ * Allocate memory of size n.
+ * This function gives up if we do not have enough memory
+ */
+void *
+FNAMEXM(alloc)( size_t n FNAMEPRT )
+{
+ char *p;
+
+#ifdef M_GUARD
+ if(!n)
+ out_of_core(n,0); /* should never happen */
+ if( !(p = malloc( n + EXTRA_ALIGN+5 )) )
+ out_of_core(n,0);
+ store_len(p,n,0);
+ used_memory += n;
+ p[4+EXTRA_ALIGN+n] = MAGIC_END_BYTE;
+ return p+EXTRA_ALIGN+4;
+#else
+ /* mallocing zero bytes is undefined by ISO-C, so we better make
+ sure that it won't happen */
+ if (!n)
+ n = 1;
+ if( !(p = malloc( n )) )
+ out_of_core(n,0);
+ return p;
+#endif
+}
+
+/****************
+ * Allocate memory of size n from the secure memory pool.
+ * This function gives up if we do not have enough memory
+ */
+void *
+FNAMEXM(alloc_secure)( size_t n FNAMEPRT )
+{
+ char *p;
+
+#ifdef M_GUARD
+ if(!n)
+ out_of_core(n,1); /* should never happen */
+ if( !(p = secmem_malloc( n +EXTRA_ALIGN+ 5 )) )
+ out_of_core(n,1);
+ store_len(p,n,1);
+ p[4+EXTRA_ALIGN+n] = MAGIC_END_BYTE;
+ return p+EXTRA_ALIGN+4;
+#else
+ /* mallocing zero bytes is undefined by ISO-C, so we better make
+ sure that it won't happen */
+ if (!n)
+ n = 1;
+ if( !(p = secmem_malloc( n )) )
+ out_of_core(n,1);
+ return p;
+#endif
+}
+
+void *
+FNAMEXM(alloc_clear)( size_t n FNAMEPRT )
+{
+ void *p;
+ p = FNAMEXM(alloc)( n FNAMEARG );
+ memset(p, 0, n );
+ return p;
+}
+
+void *
+FNAMEXM(alloc_secure_clear)( size_t n FNAMEPRT)
+{
+ void *p;
+ p = FNAMEXM(alloc_secure)( n FNAMEARG );
+ memset(p, 0, n );
+ return p;
+}
+
+
+/****************
+ * realloc and clear the old space
+ */
+void *
+FNAMEX(realloc)( void *a, size_t n FNAMEPRT )
+{
+ void *b;
+
+#ifdef M_GUARD
+ if( a ) {
+#error "--enable-m-guard does not currently work"
+ unsigned char *p = a;
+ size_t len = m_size(a);
+
+ if( len >= n ) /* we don't shrink for now */
+ return a;
+ if( p[-1] == MAGIC_SEC_BYTE )
+ b = FNAME(alloc_secure_clear)(n FNAMEARG);
+ else
+ b = FNAME(alloc_clear)(n FNAMEARG);
+ FNAME(check)(NULL FNAMEARG);
+ memcpy(b, a, len );
+ FNAME(free)(p FNAMEARG);
+ }
+ else
+ b = FNAME(alloc)(n FNAMEARG);
+#else
+ if( m_is_secure(a) ) {
+ if( !(b = secmexrealloc( a, n )) )
+ out_of_core(n,1);
+ }
+ else {
+ if( !(b = realloc( a, n )) )
+ out_of_core(n,0);
+ }
+#endif
+
+ return b;
+}
+
+
+
+/****************
+ * Free a pointer
+ */
+void
+FNAMEX(free)( void *a FNAMEPRT )
+{
+ byte *p = a;
+
+ if( !p )
+ return;
+#ifdef M_DEBUG
+ free_entry(p-EXTRA_ALIGN-4, info);
+#elif defined M_GUARD
+ m_check(p);
+ if( m_is_secure(a) )
+ secmem_free(p-EXTRA_ALIGN-4);
+ else {
+ used_memory -= m_size(a);
+ free(p-EXTRA_ALIGN-4);
+ }
+#else
+ if( m_is_secure(a) )
+ secmem_free(p);
+ else
+ free(p);
+#endif
+}
+
+
+void
+FNAME(check)( const void *a FNAMEPRT )
+{
+#ifdef M_GUARD
+ const byte *p = a;
+
+#ifdef M_DEBUG
+ if( p )
+ check_mem(p-EXTRA_ALIGN-4, info);
+ else
+ check_allmem(info);
+#else
+ if( !p )
+ return;
+ if( !(p[-1] == MAGIC_NOR_BYTE || p[-1] == MAGIC_SEC_BYTE) )
+ membug("memory at %p corrupted (underflow=%02x)\n", p, p[-1] );
+ else if( p[m_size(p)] != MAGIC_END_BYTE )
+ membug("memory at %p corrupted (overflow=%02x)\n", p, p[-1] );
+#endif
+#endif
+}
+
+
+size_t
+m_size( const void *a )
+{
+#ifndef M_GUARD
+ log_debug("dummy m_size called\n");
+ return 0;
+#else
+ const byte *p = a;
+ size_t n;
+
+#ifdef M_DEBUG
+ n = check_mem(p-EXTRA_ALIGN-4, "m_size")->user_n;
+#else
+ n = ((byte*)p)[-4];
+ n |= ((byte*)p)[-3] << 8;
+ n |= ((byte*)p)[-2] << 16;
+#endif
+ return n;
+#endif
+}
+
+
+char *
+FNAMEX(strdup)( const char *a FNAMEPRT )
+{
+ size_t n = strlen(a);
+ char *p = FNAMEXM(alloc)(n+1 FNAMEARG);
+ strcpy(p, a);
+ return p;
+}
+
+
+/* Wrapper around xmalloc_clear to take the usual 2 arguments of a
+ calloc style function. */
+void *
+xcalloc (size_t n, size_t m)
+{
+ size_t nbytes;
+
+ nbytes = n * m;
+ if (m && nbytes / m != n)
+ out_of_core (nbytes, 0);
+ return xmalloc_clear (nbytes);
+}
+
+/* Wrapper around xmalloc_csecure_lear to take the usual 2 arguments
+ of a calloc style function. */
+void *
+xcalloc_secure (size_t n, size_t m)
+{
+ size_t nbytes;
+
+ nbytes = n * m;
+ if (m && nbytes / m != n)
+ out_of_core (nbytes, 1);
+ return xmalloc_secure_clear (nbytes);
+}
+
diff --git a/util/memrchr.c b/util/memrchr.c
new file mode 100644
index 0000000..5621f73
--- /dev/null
+++ b/util/memrchr.c
@@ -0,0 +1,49 @@
+/* memrchr.c - libc replacement function
+ * Copyright (C) 2005 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+/*
+ memrchr() is a GNU function that might not be available everywhere.
+ It's basically the inverse of memchr() - search backwards in a
+ memory block for a particular character.
+*/
+
+#include <config.h>
+#include <string.h>
+
+/* There are many ways to optimize this, but this is a simple
+ unoptimized implementation. */
+void *
+memrchr(const void *s, int c, size_t n)
+{
+ const unsigned char *start=s,*end=s;
+
+ end+=n-1;
+
+ while(end>=start)
+ {
+ if(*end==c)
+ return (void *)end;
+ else
+ end--;
+ }
+
+ return NULL;
+}
diff --git a/util/miscutil.c b/util/miscutil.c
new file mode 100644
index 0000000..01241bc
--- /dev/null
+++ b/util/miscutil.c
@@ -0,0 +1,455 @@
+/* miscutil.c - miscellaneous utilities
+ * Copyright (C) 1998, 1999, 2000, 2001, 2003,
+ * 2004, 2005 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <ctype.h>
+#ifdef HAVE_LANGINFO_H
+#include <langinfo.h>
+#endif
+#include "types.h"
+#include "util.h"
+#include "i18n.h"
+
+/****************
+ * I know that the OpenPGP protocol has a Y2106 problem ;-)
+ */
+u32
+make_timestamp()
+{
+ return time(NULL);
+}
+
+/****************
+ * Scan a date string and return a timestamp.
+ * The only supported format is "yyyy-mm-dd"
+ * Returns 0 for an invalid date.
+ */
+u32
+scan_isodatestr( const char *string )
+{
+ int year, month, day;
+ struct tm tmbuf;
+ time_t stamp;
+ int i;
+
+ if( strlen(string) != 10 || string[4] != '-' || string[7] != '-' )
+ return 0;
+ for( i=0; i < 4; i++ )
+ if( !digitp(string+i) )
+ return 0;
+ if( !digitp(string+5) || !digitp(string+6) )
+ return 0;
+ if( !digitp(string+8) || !digitp(string+9) )
+ return 0;
+ year = atoi(string);
+ month = atoi(string+5);
+ day = atoi(string+8);
+ /* some basic checks */
+ if( year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 )
+ return 0;
+ memset( &tmbuf, 0, sizeof tmbuf );
+ tmbuf.tm_mday = day;
+ tmbuf.tm_mon = month-1;
+ tmbuf.tm_year = year - 1900;
+ tmbuf.tm_isdst = -1;
+ stamp = mktime( &tmbuf );
+ if( stamp == (time_t)-1 )
+ return 0;
+ return stamp;
+}
+
+
+/****************
+ * Return a string with a time value in the form: x Y, n D, n H
+ */
+
+const char *
+strtimevalue( u32 value )
+{
+ static char buffer[30];
+ unsigned int years, days, hours, minutes;
+
+ value /= 60;
+ minutes = value % 60;
+ value /= 60;
+ hours = value % 24;
+ value /= 24;
+ days = value % 365;
+ value /= 365;
+ years = value;
+
+ sprintf(buffer,"%uy%ud%uh%um", years, days, hours, minutes );
+ if( years )
+ return buffer;
+ if( days )
+ return strchr( buffer, 'y' ) + 1;
+ return strchr( buffer, 'd' ) + 1;
+}
+
+
+/****************
+ * Note: this function returns GMT
+ */
+const char *
+strtimestamp( u32 stamp )
+{
+ static char buffer[11+5];
+ struct tm *tp;
+ time_t atime = stamp;
+
+ if (atime < 0) {
+ strcpy (buffer, "????" "-??" "-??");
+ }
+ else {
+ tp = gmtime( &atime );
+ sprintf(buffer,"%04d-%02d-%02d",
+ 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday );
+ }
+ return buffer;
+}
+
+
+/****************
+ * Note: this function returns GMT
+ */
+const char *
+isotimestamp (u32 stamp)
+{
+ static char buffer[25+5];
+ struct tm *tp;
+ time_t atime = stamp;
+
+ if (atime < 0) {
+ strcpy (buffer, "????" "-??" "-??" " " "??" ":" "??" ":" "??");
+ }
+ else {
+ tp = gmtime( &atime );
+ sprintf(buffer,"%04d-%02d-%02d %02d:%02d:%02d",
+ 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday,
+ tp->tm_hour, tp->tm_min, tp->tm_sec);
+ }
+ return buffer;
+}
+
+/****************
+ * Note: this function returns local time
+ */
+const char *
+asctimestamp( u32 stamp )
+{
+ static char buffer[50];
+#if defined (HAVE_STRFTIME) && defined (HAVE_NL_LANGINFO)
+ static char fmt[50];
+#endif
+ struct tm *tp;
+ time_t atime = stamp;
+
+ if (atime < 0) {
+ strcpy (buffer, "????" "-??" "-??");
+ return buffer;
+ }
+
+ tp = localtime( &atime );
+#ifdef HAVE_STRFTIME
+#if defined(HAVE_NL_LANGINFO)
+ mem2str( fmt, nl_langinfo(D_T_FMT), DIM(fmt)-3 );
+ if( strstr( fmt, "%Z" ) == NULL )
+ strcat( fmt, " %Z");
+ strftime( buffer, DIM(buffer)-1, fmt, tp );
+#else
+ /* fixme: we should check whether the locale appends a " %Z"
+ * These locales from glibc don't put the " %Z":
+ * fi_FI hr_HR ja_JP lt_LT lv_LV POSIX ru_RU ru_SU sv_FI sv_SE zh_CN
+ */
+ strftime( buffer, DIM(buffer)-1,
+#ifdef HAVE_W32_SYSTEM
+ "%c"
+#else
+ "%c %Z"
+#endif
+ , tp );
+#endif
+ buffer[DIM(buffer)-1] = 0;
+#else
+ mem2str( buffer, asctime(tp), DIM(buffer) );
+#endif
+ return buffer;
+}
+
+
+/****************
+ * Print a string to FP, but filter all control characters out.
+ */
+void
+print_string2( FILE *fp, const byte *p, size_t n, int delim, int delim2 )
+{
+ for( ; n; n--, p++ )
+ if( *p < 0x20 || (*p >= 0x7f && *p < 0xa0)
+ || *p == delim || *p == delim2
+ || ((delim || delim2) && *p=='\\'))
+ {
+ putc('\\', fp);
+ if( *p == '\n' )
+ putc('n', fp);
+ else if( *p == '\r' )
+ putc('r', fp);
+ else if( *p == '\f' )
+ putc('f', fp);
+ else if( *p == '\v' )
+ putc('v', fp);
+ else if( *p == '\b' )
+ putc('b', fp);
+ else if( !*p )
+ putc('0', fp);
+ else
+ fprintf(fp, "x%02x", *p );
+ }
+ else
+ putc(*p, fp);
+}
+
+void
+print_string( FILE *fp, const byte *p, size_t n, int delim )
+{
+ print_string2(fp,p,n,delim,0);
+}
+
+/****************
+ * Print an UTF8 string to FP and filter all control characters out.
+ */
+void
+print_utf8_string2 ( FILE *fp, const byte *p, size_t n, int delim )
+{
+ size_t i;
+ char *buf;
+
+ /* we can handle plain ascii simpler, so check for it first */
+ for(i=0; i < n; i++ ) {
+ if( p[i] & 0x80 )
+ break;
+ }
+ if( i < n ) {
+ buf = utf8_to_native ( p, n, delim );
+ /*(utf8 conversion already does the control character quoting)*/
+ fputs( buf, fp );
+ xfree( buf );
+ }
+ else
+ print_string( fp, p, n, delim );
+}
+
+void
+print_utf8_string( FILE *fp, const byte *p, size_t n )
+{
+ print_utf8_string2 (fp, p, n, 0);
+}
+
+/****************
+ * This function returns a string which is suitable for printing
+ * Caller must release it with xfree()
+ */
+char *
+make_printable_string( const byte *p, size_t n, int delim )
+{
+ size_t save_n, buflen;
+ const byte *save_p;
+ char *buffer, *d;
+
+ /* first count length */
+ for(save_n = n, save_p = p, buflen=1 ; n; n--, p++ ) {
+ if( *p < 0x20 || (*p >= 0x7f && *p < 0xa0) || *p == delim ||
+ (delim && *p=='\\')) {
+ if( *p=='\n' || *p=='\r' || *p=='\f'
+ || *p=='\v' || *p=='\b' || !*p )
+ buflen += 2;
+ else
+ buflen += 4;
+ }
+ else
+ buflen++;
+ }
+ p = save_p;
+ n = save_n;
+ /* and now make the string */
+ d = buffer = xmalloc( buflen );
+ for( ; n; n--, p++ ) {
+ if( *p < 0x20 || (*p >= 0x7f && *p < 0xa0) || *p == delim ||
+ (delim && *p=='\\')) {
+ *d++ = '\\';
+ if( *p == '\n' )
+ *d++ = 'n';
+ else if( *p == '\r' )
+ *d++ = 'r';
+ else if( *p == '\f' )
+ *d++ = 'f';
+ else if( *p == '\v' )
+ *d++ = 'v';
+ else if( *p == '\b' )
+ *d++ = 'b';
+ else if( !*p )
+ *d++ = '0';
+ else {
+ sprintf(d, "x%02x", *p );
+ d += 3;
+ }
+ }
+ else
+ *d++ = *p;
+ }
+ *d = 0;
+ return buffer;
+}
+
+int
+answer_is_yes_no_default( const char *s, int def_answer )
+{
+ /* TRANSLATORS: See doc/TRANSLATE about this string. */
+ const char *long_yes = _("yes");
+ const char *short_yes = _("yY");
+ /* TRANSLATORS: See doc/TRANSLATE about this string. */
+ const char *long_no = _("no");
+ const char *short_no = _("nN");
+
+ /* Note: we have to use the local dependent strcasecmp here */
+ if( match_multistr(long_yes,s) )
+ return 1;
+ if( *s && strchr( short_yes, *s ) && !s[1] )
+ return 1;
+ /* test for no strings to catch ambiguities for the next test */
+ if( match_multistr(long_no,s) )
+ return 0;
+ if( *s && strchr( short_no, *s ) && !s[1] )
+ return 0;
+ /* test for the english version (for those who are used to type yes) */
+ if( !ascii_strcasecmp(s, "yes" ) )
+ return 1;
+ if( *s && strchr( "yY", *s ) && !s[1] )
+ return 1;
+ return def_answer;
+}
+
+int
+answer_is_yes( const char *s )
+{
+ return answer_is_yes_no_default(s,0);
+}
+
+/****************
+ * Return 1 for yes, -1 for quit, or 0 for no
+ */
+int
+answer_is_yes_no_quit( const char *s )
+{
+ /* TRANSLATORS: See doc/TRANSLATE about this string. */
+ const char *long_yes = _("yes");
+ /* TRANSLATORS: See doc/TRANSLATE about this string. */
+ const char *long_no = _("no");
+ /* TRANSLATORS: See doc/TRANSLATE about this string. */
+ const char *long_quit = _("quit");
+ const char *short_yes = _("yY");
+ const char *short_no = _("nN");
+ const char *short_quit = _("qQ");
+
+ if( match_multistr(long_no,s) )
+ return 0;
+ if( match_multistr(long_yes,s) )
+ return 1;
+ if( match_multistr(long_quit,s) )
+ return -1;
+ if( *s && strchr( short_no, *s ) && !s[1] )
+ return 0;
+ if( *s && strchr( short_yes, *s ) && !s[1] )
+ return 1;
+ if( *s && strchr( short_quit, *s ) && !s[1] )
+ return -1;
+ /* but not here */
+ if( !ascii_strcasecmp(s, "yes" ) )
+ return 1;
+ if( !ascii_strcasecmp(s, "quit" ) )
+ return -1;
+ if( *s && strchr( "yY", *s ) && !s[1] )
+ return 1;
+ if( *s && strchr( "qQ", *s ) && !s[1] )
+ return -1;
+ return 0;
+}
+
+/*
+ Return 1 for okay, 0 for for cancel or DEF_ANSWER for default.
+ */
+int
+answer_is_okay_cancel (const char *s, int def_answer)
+{
+ /* TRANSLATORS: See doc/TRANSLATE about this string. */
+ const char *long_okay = _("okay|okay");
+ /* TRANSLATORS: See doc/TRANSLATE about this string. */
+ const char *long_cancel = _("cancel|cancel");
+ const char *short_okay = _("oO");
+ const char *short_cancel = _("cC");
+
+ /* Note: We have to use the locale dependent strcasecmp */
+ if ( match_multistr(long_okay,s) )
+ return 1;
+ if ( match_multistr(long_cancel,s) )
+ return 0;
+ if ( *s && strchr( short_okay, *s ) && !s[1] )
+ return 1;
+ if ( *s && strchr( short_cancel, *s ) && !s[1] )
+ return 0;
+ /* Always test for the English values (not locale here) */
+ if ( !ascii_strcasecmp(s, "okay" ) )
+ return 1;
+ if ( !ascii_strcasecmp(s, "ok" ) )
+ return 1;
+ if ( !ascii_strcasecmp(s, "cancel" ) )
+ return 0;
+ if ( *s && strchr( "oO", *s ) && !s[1] )
+ return 1;
+ if ( *s && strchr( "cC", *s ) && !s[1] )
+ return 0;
+ return def_answer;
+}
+
+/* Try match against each substring of multistr, delimited by | */
+int
+match_multistr(const char *multistr,const char *match)
+{
+ do
+ {
+ size_t seglen=strcspn(multistr,"|");
+ if(!seglen)
+ break;
+ /* Using the localized strncasecmp */
+ if(strncasecmp(multistr,match,seglen)==0)
+ return 1;
+ multistr+=seglen;
+ if(*multistr=='|')
+ multistr++;
+ }
+ while(*multistr);
+
+ return 0;
+}
diff --git a/util/mkdtemp.c b/util/mkdtemp.c
new file mode 100644
index 0000000..c0ee0bb
--- /dev/null
+++ b/util/mkdtemp.c
@@ -0,0 +1,99 @@
+/* mkdtemp.c - libc replacement function
+ * Copyright (C) 2001 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+/* This is a replacement function for mkdtemp in case the platform
+ we're building on (like mine!) doesn't have it. */
+
+#include <config.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include "types.h"
+#include "cipher.h"
+
+#ifdef MKDIR_TAKES_ONE_ARG
+# undef mkdir
+# define mkdir(a,b) mkdir(a)
+#endif
+
+char *mkdtemp(char *template)
+{
+ unsigned int attempts,idx,count=0;
+ char *ch;
+
+ idx=strlen(template);
+
+ /* Walk backwards to count all the Xes */
+ while(idx>0 && template[idx-1]=='X')
+ {
+ count++;
+ idx--;
+ }
+
+ if(count==0)
+ {
+ errno=EINVAL;
+ return NULL;
+ }
+
+ ch=&template[idx];
+
+ /* Try 4 times to make the temp directory */
+ for(attempts=0;attempts<4;attempts++)
+ {
+ unsigned int remaining=count;
+ char *marker=ch;
+ byte *randombits;
+
+ idx=0;
+
+ /* Using really random bits is probably overkill here. The
+ worst thing that can happen with a directory name collision
+ is that the function will return an error. */
+
+ randombits=get_random_bits(4*remaining,0,0);
+
+ while(remaining>1)
+ {
+ sprintf(marker,"%02X",randombits[idx++]);
+ marker+=2;
+ remaining-=2;
+ }
+
+ /* Any leftover Xes? get_random_bits rounds up to full bytes,
+ so this is safe. */
+ if(remaining>0)
+ sprintf(marker,"%X",randombits[idx]&0xF);
+
+ xfree(randombits);
+
+ if(mkdir(template,0700)==0)
+ break;
+ }
+
+ if(attempts==4)
+ return NULL; /* keeps the errno from mkdir, whatever it is */
+
+ return template;
+}
diff --git a/util/pka.c b/util/pka.c
new file mode 100644
index 0000000..3c9e5c4
--- /dev/null
+++ b/util/pka.c
@@ -0,0 +1,254 @@
+/* pka.c - DNS Public Key Association RR access
+ * Copyright (C) 2005 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef USE_DNS_PKA
+#include <sys/types.h>
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+#endif
+#endif /* USE_DNS_PKA */
+
+#include "memory.h"
+#include "types.h"
+#include "util.h"
+
+
+#ifdef USE_DNS_PKA
+/* Parse the TXT resource record. Format is:
+
+ v=pka1;fpr=a4d94e92b0986ab5ee9dcd755de249965b0358a2;uri=string
+
+ For simplicity white spaces are not allowed. Because we expect to
+ use a new RRTYPE for this in the future we define the TXT really
+ strict for simplicity: No white spaces, case sensitivity of the
+ names, order must be as given above. Only URI is optional.
+
+ This function modifies BUFFER. On success 0 is returned, the 20
+ byte fingerprint stored at FPR and BUFFER contains the URI or an
+ empty string.
+*/
+static int
+parse_txt_record (char *buffer, unsigned char *fpr)
+{
+ char *p, *pend;
+ int i;
+
+ p = buffer;
+ pend = strchr (p, ';');
+ if (!pend)
+ return -1;
+ *pend++ = 0;
+ if (strcmp (p, "v=pka1"))
+ return -1; /* Wrong or missing version. */
+
+ p = pend;
+ pend = strchr (p, ';');
+ if (pend)
+ *pend++ = 0;
+ if (strncmp (p, "fpr=", 4))
+ return -1; /* Missing fingerprint part. */
+ p += 4;
+ for (i=0; i < 20 && hexdigitp (p) && hexdigitp (p+1); i++, p += 2)
+ fpr[i] = xtoi_2 (p);
+ if (i != 20)
+ return -1; /* Fingerprint consists not of exactly 40 hexbytes. */
+
+ p = pend;
+ if (!p || !*p)
+ {
+ *buffer = 0;
+ return 0; /* Success (no URI given). */
+ }
+ if (strncmp (p, "uri=", 4))
+ return -1; /* Unknown part. */
+ p += 4;
+ /* There is an URI, copy it to the start of the buffer. */
+ while (*p)
+ *buffer++ = *p++;
+ *buffer = 0;
+ return 0;
+}
+
+
+/* For the given email ADDRESS lookup the PKA information in the DNS.
+
+ On success the 20 byte SHA-1 fingerprint is stored at FPR and the
+ URI will be returned in an allocated buffer. Note that the URI
+ might be an zero length string as this information is optiobnal.
+ Caller must xfree the returned string.
+
+ On error NULL is returned and the 20 bytes at FPR are not
+ defined. */
+char *
+get_pka_info (const char *address, unsigned char *fpr)
+{
+ unsigned char answer[PACKETSZ];
+ int anslen;
+ int qdcount, ancount, nscount, arcount;
+ int rc;
+ unsigned char *p, *pend;
+ const char *domain;
+ char *name;
+
+
+ domain = strrchr (address, '@');
+ if (!domain || domain == address || !domain[1])
+ return NULL; /* invalid mail address given. */
+
+ name = malloc (strlen (address) + 5 + 1);
+ memcpy (name, address, domain - address);
+ strcpy (stpcpy (name + (domain-address), "._pka."), domain+1);
+
+ anslen = res_query (name, C_IN, T_TXT, answer, PACKETSZ);
+ xfree (name);
+ if (anslen < sizeof(HEADER))
+ return NULL; /* DNS resolver returned a too short answer. */
+ if ( (rc=((HEADER*)answer)->rcode) != NOERROR )
+ return NULL; /* DNS resolver returned an error. */
+
+ /* We assume that PACKETSZ is large enough and don't do dynmically
+ expansion of the buffer. */
+ if (anslen > PACKETSZ)
+ return NULL; /* DNS resolver returned a too long answer */
+
+ qdcount = ntohs (((HEADER*)answer)->qdcount);
+ ancount = ntohs (((HEADER*)answer)->ancount);
+ nscount = ntohs (((HEADER*)answer)->nscount);
+ arcount = ntohs (((HEADER*)answer)->arcount);
+
+ if (!ancount)
+ return NULL; /* Got no answer. */
+
+ p = answer + sizeof (HEADER);
+ pend = answer + anslen; /* Actually points directly behind the buffer. */
+
+ while (qdcount-- && p < pend)
+ {
+ rc = dn_skipname (p, pend);
+ if (rc == -1)
+ return NULL;
+ p += rc + QFIXEDSZ;
+ }
+
+ if (ancount > 1)
+ return NULL; /* more than one possible gpg trustdns record - none used. */
+
+ while (ancount-- && p <= pend)
+ {
+ unsigned int type, class, txtlen, n;
+ char *buffer, *bufp;
+
+ rc = dn_skipname (p, pend);
+ if (rc == -1)
+ return NULL;
+ p += rc;
+ if (p >= pend - 10)
+ return NULL; /* RR too short. */
+
+ type = *p++ << 8;
+ type |= *p++;
+ class = *p++ << 8;
+ class |= *p++;
+ p += 4;
+ txtlen = *p++ << 8;
+ txtlen |= *p++;
+ if (type != T_TXT || class != C_IN)
+ return NULL; /* Answer does not match the query. */
+
+ buffer = bufp = xmalloc (txtlen + 1);
+ while (txtlen && p < pend)
+ {
+ for (n = *p++, txtlen--; txtlen && n && p < pend; txtlen--, n--)
+ *bufp++ = *p++;
+ }
+ *bufp = 0;
+ if (parse_txt_record (buffer, fpr))
+ {
+ xfree (buffer);
+ return NULL; /* Not a valid gpg trustdns RR. */
+ }
+ return buffer;
+ }
+
+ return NULL;
+}
+#else /* !USE_DNS_PKA */
+
+/* Dummy version of the function if we can't use the resolver
+ functions. */
+char *
+get_pka_info (const char *address, unsigned char *fpr)
+{
+ return NULL;
+}
+#endif /* !USE_DNS_PKA */
+
+
+#ifdef TEST
+int
+main(int argc,char *argv[])
+{
+ unsigned char fpr[20];
+ char *uri;
+ int i;
+
+ if (argc < 2)
+ {
+ fprintf (stderr, "usage: pka mail-addresses\n");
+ return 1;
+ }
+ argc--;
+ argv++;
+
+ for (; argc; argc--, argv++)
+ {
+ uri = get_pka_info ( *argv, fpr );
+ printf ("%s", *argv);
+ if (uri)
+ {
+ putchar (' ');
+ for (i=0; i < 20; i++)
+ printf ("%02X", fpr[i]);
+ if (*uri)
+ printf (" %s", uri);
+ xfree (uri);
+ }
+ putchar ('\n');
+ }
+ return 0;
+}
+#endif /* TEST */
+
+/*
+Local Variables:
+compile-command: "cc -DUSE_DNS_PKA -DTEST -I.. -I../include -Wall -g -o pka pka.c -lresolv libutil.a"
+End:
+*/
diff --git a/util/regcomp.c b/util/regcomp.c
new file mode 100644
index 0000000..7663399
--- /dev/null
+++ b/util/regcomp.c
@@ -0,0 +1,3495 @@
+/* Extended regular expression matching and search library.
+ Copyright (C) 2002 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02110-1301 USA. */
+
+#include <assert.h>
+#include <ctype.h>
+#include <limits.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if defined(_WIN32) && !defined (MB_CUR_MAX)
+#define MB_CUR_MAX 2
+#endif
+
+#if defined HAVE_WCHAR_H || defined _LIBC
+# include <wchar.h>
+#endif /* HAVE_WCHAR_H || _LIBC */
+#if defined HAVE_WCTYPE_H || defined _LIBC
+# include <wctype.h>
+#endif /* HAVE_WCTYPE_H || _LIBC */
+
+/* In case that the system doesn't have isblank(). */
+#if !defined _LIBC && !defined HAVE_ISBLANK && !defined isblank
+# define isblank(ch) ((ch) == ' ' || (ch) == '\t')
+#endif
+
+#ifdef _LIBC
+# ifndef _RE_DEFINE_LOCALE_FUNCTIONS
+# define _RE_DEFINE_LOCALE_FUNCTIONS 1
+# include <locale/localeinfo.h>
+# include <locale/elem-hash.h>
+# include <locale/coll-lookup.h>
+# endif
+#endif
+
+/* This is for other GNU distributions with internationalized messages. */
+#if HAVE_LIBINTL_H || defined _LIBC
+# include <libintl.h>
+# ifdef _LIBC
+# undef gettext
+# define gettext(msgid) \
+ INTUSE(__dcgettext) (INTUSE(_libc_intl_domainname), msgid, LC_MESSAGES)
+# endif
+#else
+# define gettext(msgid) (msgid)
+#endif
+
+#ifndef gettext_noop
+/* This define is so xgettext can find the internationalizable
+ strings. */
+# define gettext_noop(String) String
+#endif
+
+#include "_regex.h" /* gnupg */
+#include "regex_internal.h"
+
+static reg_errcode_t re_compile_internal (regex_t *preg, const char * pattern,
+ int length, reg_syntax_t syntax);
+static void re_compile_fastmap_iter (regex_t *bufp,
+ const re_dfastate_t *init_state,
+ char *fastmap);
+static reg_errcode_t init_dfa (re_dfa_t *dfa, int pat_len);
+static reg_errcode_t init_word_char (re_dfa_t *dfa);
+#ifdef RE_ENABLE_I18N
+static void free_charset (re_charset_t *cset);
+#endif /* RE_ENABLE_I18N */
+static void free_workarea_compile (regex_t *preg);
+static reg_errcode_t create_initial_state (re_dfa_t *dfa);
+static reg_errcode_t analyze (re_dfa_t *dfa);
+static reg_errcode_t analyze_tree (re_dfa_t *dfa, bin_tree_t *node);
+static void calc_first (re_dfa_t *dfa, bin_tree_t *node);
+static void calc_next (re_dfa_t *dfa, bin_tree_t *node);
+static void calc_epsdest (re_dfa_t *dfa, bin_tree_t *node);
+static reg_errcode_t duplicate_node (int *new_idx, re_dfa_t *dfa, int org_idx,
+ unsigned int constraint);
+static reg_errcode_t calc_eclosure (re_dfa_t *dfa);
+static reg_errcode_t calc_eclosure_iter (re_node_set *new_set, re_dfa_t *dfa,
+ int node, int root);
+static void calc_inveclosure (re_dfa_t *dfa);
+static int fetch_number (re_string_t *input, re_token_t *token,
+ reg_syntax_t syntax);
+static re_token_t fetch_token (re_string_t *input, reg_syntax_t syntax);
+static int peek_token (re_token_t *token, re_string_t *input,
+ reg_syntax_t syntax);
+static int peek_token_bracket (re_token_t *token, re_string_t *input,
+ reg_syntax_t syntax);
+static bin_tree_t *parse (re_string_t *regexp, regex_t *preg,
+ reg_syntax_t syntax, reg_errcode_t *err);
+static bin_tree_t *parse_reg_exp (re_string_t *regexp, regex_t *preg,
+ re_token_t *token, reg_syntax_t syntax,
+ int nest, reg_errcode_t *err);
+static bin_tree_t *parse_branch (re_string_t *regexp, regex_t *preg,
+ re_token_t *token, reg_syntax_t syntax,
+ int nest, reg_errcode_t *err);
+static bin_tree_t *parse_expression (re_string_t *regexp, regex_t *preg,
+ re_token_t *token, reg_syntax_t syntax,
+ int nest, reg_errcode_t *err);
+static bin_tree_t *parse_sub_exp (re_string_t *regexp, regex_t *preg,
+ re_token_t *token, reg_syntax_t syntax,
+ int nest, reg_errcode_t *err);
+static bin_tree_t *parse_dup_op (bin_tree_t *dup_elem, re_string_t *regexp,
+ re_dfa_t *dfa, re_token_t *token,
+ reg_syntax_t syntax, reg_errcode_t *err);
+static bin_tree_t *parse_bracket_exp (re_string_t *regexp, re_dfa_t *dfa,
+ re_token_t *token, reg_syntax_t syntax,
+ reg_errcode_t *err);
+static reg_errcode_t parse_bracket_element (bracket_elem_t *elem,
+ re_string_t *regexp,
+ re_token_t *token, int token_len,
+ re_dfa_t *dfa,
+ reg_syntax_t syntax);
+static reg_errcode_t parse_bracket_symbol (bracket_elem_t *elem,
+ re_string_t *regexp,
+ re_token_t *token);
+#ifndef _LIBC
+# ifdef RE_ENABLE_I18N
+static reg_errcode_t build_range_exp (re_bitset_ptr_t sbcset,
+ re_charset_t *mbcset, int *range_alloc,
+ bracket_elem_t *start_elem,
+ bracket_elem_t *end_elem);
+static reg_errcode_t build_collating_symbol (re_bitset_ptr_t sbcset,
+ re_charset_t *mbcset,
+ int *coll_syxmalloc,
+ const unsigned char *name);
+# else /* not RE_ENABLE_I18N */
+static reg_errcode_t build_range_exp (re_bitset_ptr_t sbcset,
+ bracket_elem_t *start_elem,
+ bracket_elem_t *end_elem);
+static reg_errcode_t build_collating_symbol (re_bitset_ptr_t sbcset,
+ const unsigned char *name);
+# endif /* not RE_ENABLE_I18N */
+#endif /* not _LIBC */
+#ifdef RE_ENABLE_I18N
+static reg_errcode_t build_equiv_class (re_bitset_ptr_t sbcset,
+ re_charset_t *mbcset,
+ int *equiv_class_alloc,
+ const unsigned char *name);
+static reg_errcode_t build_charclass (re_bitset_ptr_t sbcset,
+ re_charset_t *mbcset,
+ int *char_class_alloc,
+ const unsigned char *class_name,
+ reg_syntax_t syntax);
+#else /* not RE_ENABLE_I18N */
+static reg_errcode_t build_equiv_class (re_bitset_ptr_t sbcset,
+ const unsigned char *name);
+static reg_errcode_t build_charclass (re_bitset_ptr_t sbcset,
+ const unsigned char *class_name,
+ reg_syntax_t syntax);
+#endif /* not RE_ENABLE_I18N */
+static bin_tree_t *build_word_op (re_dfa_t *dfa, int not, reg_errcode_t *err);
+static void free_bin_tree (bin_tree_t *tree);
+static bin_tree_t *create_tree (bin_tree_t *left, bin_tree_t *right,
+ re_token_type_t type, int index);
+static bin_tree_t *duplicate_tree (const bin_tree_t *src, re_dfa_t *dfa);
+
+/* This table gives an error message for each of the error codes listed
+ in regex.h. Obviously the order here has to be same as there.
+ POSIX doesn't require that we do anything for REG_NOERROR,
+ but why not be nice? */
+
+const char __re_error_msgid[] attribute_hidden =
+ {
+#define REG_NOERROR_IDX 0
+ gettext_noop ("Success") /* REG_NOERROR */
+ "\0"
+#define REG_NOMATCH_IDX (REG_NOERROR_IDX + sizeof "Success")
+ gettext_noop ("No match") /* REG_NOMATCH */
+ "\0"
+#define REG_BADPAT_IDX (REG_NOMATCH_IDX + sizeof "No match")
+ gettext_noop ("Invalid regular expression") /* REG_BADPAT */
+ "\0"
+#define REG_ECOLLATE_IDX (REG_BADPAT_IDX + sizeof "Invalid regular expression")
+ gettext_noop ("Invalid collation character") /* REG_ECOLLATE */
+ "\0"
+#define REG_ECTYPE_IDX (REG_ECOLLATE_IDX + sizeof "Invalid collation character")
+ gettext_noop ("Invalid character class name") /* REG_ECTYPE */
+ "\0"
+#define REG_EESCAPE_IDX (REG_ECTYPE_IDX + sizeof "Invalid character class name")
+ gettext_noop ("Trailing backslash") /* REG_EESCAPE */
+ "\0"
+#define REG_ESUBREG_IDX (REG_EESCAPE_IDX + sizeof "Trailing backslash")
+ gettext_noop ("Invalid back reference") /* REG_ESUBREG */
+ "\0"
+#define REG_EBRACK_IDX (REG_ESUBREG_IDX + sizeof "Invalid back reference")
+ gettext_noop ("Unmatched [ or [^") /* REG_EBRACK */
+ "\0"
+#define REG_EPAREN_IDX (REG_EBRACK_IDX + sizeof "Unmatched [ or [^")
+ gettext_noop ("Unmatched ( or \\(") /* REG_EPAREN */
+ "\0"
+#define REG_EBRACE_IDX (REG_EPAREN_IDX + sizeof "Unmatched ( or \\(")
+ gettext_noop ("Unmatched \\{") /* REG_EBRACE */
+ "\0"
+#define REG_BADBR_IDX (REG_EBRACE_IDX + sizeof "Unmatched \\{")
+ gettext_noop ("Invalid content of \\{\\}") /* REG_BADBR */
+ "\0"
+#define REG_ERANGE_IDX (REG_BADBR_IDX + sizeof "Invalid content of \\{\\}")
+ gettext_noop ("Invalid range end") /* REG_ERANGE */
+ "\0"
+#define REG_ESPACE_IDX (REG_ERANGE_IDX + sizeof "Invalid range end")
+ gettext_noop ("Memory exhausted") /* REG_ESPACE */
+ "\0"
+#define REG_BADRPT_IDX (REG_ESPACE_IDX + sizeof "Memory exhausted")
+ gettext_noop ("Invalid preceding regular expression") /* REG_BADRPT */
+ "\0"
+#define REG_EEND_IDX (REG_BADRPT_IDX + sizeof "Invalid preceding regular expression")
+ gettext_noop ("Premature end of regular expression") /* REG_EEND */
+ "\0"
+#define REG_ESIZE_IDX (REG_EEND_IDX + sizeof "Premature end of regular expression")
+ gettext_noop ("Regular expression too big") /* REG_ESIZE */
+ "\0"
+#define REG_ERPAREN_IDX (REG_ESIZE_IDX + sizeof "Regular expression too big")
+ gettext_noop ("Unmatched ) or \\)") /* REG_ERPAREN */
+ };
+
+const size_t __re_error_msgid_idx[] attribute_hidden =
+ {
+ REG_NOERROR_IDX,
+ REG_NOMATCH_IDX,
+ REG_BADPAT_IDX,
+ REG_ECOLLATE_IDX,
+ REG_ECTYPE_IDX,
+ REG_EESCAPE_IDX,
+ REG_ESUBREG_IDX,
+ REG_EBRACK_IDX,
+ REG_EPAREN_IDX,
+ REG_EBRACE_IDX,
+ REG_BADBR_IDX,
+ REG_ERANGE_IDX,
+ REG_ESPACE_IDX,
+ REG_BADRPT_IDX,
+ REG_EEND_IDX,
+ REG_ESIZE_IDX,
+ REG_ERPAREN_IDX
+ };
+
+/* Entry points for GNU code. */
+
+/* re_compile_pattern is the GNU regular expression compiler: it
+ compiles PATTERN (of length LENGTH) and puts the result in BUFP.
+ Returns 0 if the pattern was valid, otherwise an error string.
+
+ Assumes the `allocated' (and perhaps `buffer') and `translate' fields
+ are set in BUFP on entry. */
+
+const char *
+re_compile_pattern (pattern, length, bufp)
+ const char *pattern;
+ size_t length;
+ struct re_pattern_buffer *bufp;
+{
+ reg_errcode_t ret;
+
+ /* GNU code is written to assume at least RE_NREGS registers will be set
+ (and at least one extra will be -1). */
+ bufp->regs_allocated = REGS_UNALLOCATED;
+
+ /* And GNU code determines whether or not to get register information
+ by passing null for the REGS argument to re_match, etc., not by
+ setting no_sub. */
+ bufp->no_sub = 0;
+
+ /* Match anchors at newline. */
+ bufp->newline_anchor = 1;
+
+ ret = re_compile_internal (bufp, pattern, length, re_syntax_options);
+
+ if (!ret)
+ return NULL;
+ return gettext (__re_error_msgid + __re_error_msgid_idx[(int) ret]);
+}
+#ifdef _LIBC
+weak_alias (__re_compile_pattern, re_compile_pattern)
+#endif
+
+/* Set by `re_set_syntax' to the current regexp syntax to recognize. Can
+ also be assigned to arbitrarily: each pattern buffer stores its own
+ syntax, so it can be changed between regex compilations. */
+/* This has no initializer because initialized variables in Emacs
+ become read-only after dumping. */
+reg_syntax_t re_syntax_options;
+
+
+/* Specify the precise syntax of regexps for compilation. This provides
+ for compatibility for various utilities which historically have
+ different, incompatible syntaxes.
+
+ The argument SYNTAX is a bit mask comprised of the various bits
+ defined in regex.h. We return the old syntax. */
+
+reg_syntax_t
+re_set_syntax (syntax)
+ reg_syntax_t syntax;
+{
+ reg_syntax_t ret = re_syntax_options;
+
+ re_syntax_options = syntax;
+ return ret;
+}
+#ifdef _LIBC
+weak_alias (__re_set_syntax, re_set_syntax)
+#endif
+
+int
+re_compile_fastmap (bufp)
+ struct re_pattern_buffer *bufp;
+{
+ re_dfa_t *dfa = (re_dfa_t *) bufp->buffer;
+ char *fastmap = bufp->fastmap;
+
+ memset (fastmap, '\0', sizeof (char) * SBC_MAX);
+ re_compile_fastmap_iter (bufp, dfa->init_state, fastmap);
+ if (dfa->init_state != dfa->init_state_word)
+ re_compile_fastmap_iter (bufp, dfa->init_state_word, fastmap);
+ if (dfa->init_state != dfa->init_state_nl)
+ re_compile_fastmap_iter (bufp, dfa->init_state_nl, fastmap);
+ if (dfa->init_state != dfa->init_state_begbuf)
+ re_compile_fastmap_iter (bufp, dfa->init_state_begbuf, fastmap);
+ bufp->fastmap_accurate = 1;
+ return 0;
+}
+#ifdef _LIBC
+weak_alias (__re_compile_fastmap, re_compile_fastmap)
+#endif
+
+/* Helper function for re_compile_fastmap.
+ Compile fastmap for the initial_state INIT_STATE. */
+
+static void
+re_compile_fastmap_iter (bufp, init_state, fastmap)
+ regex_t *bufp;
+ const re_dfastate_t *init_state;
+ char *fastmap;
+{
+ re_dfa_t *dfa = (re_dfa_t *) bufp->buffer;
+ int node_cnt;
+ for (node_cnt = 0; node_cnt < init_state->nodes.nelem; ++node_cnt)
+ {
+ int node = init_state->nodes.elems[node_cnt];
+ re_token_type_t type = dfa->nodes[node].type;
+ if (type == OP_CONTEXT_NODE)
+ {
+ node = dfa->nodes[node].opr.ctx_info->entity;
+ type = dfa->nodes[node].type;
+ }
+
+ if (type == CHARACTER)
+ fastmap[dfa->nodes[node].opr.c] = 1;
+ else if (type == SIMPLE_BRACKET)
+ {
+ int i, j, ch;
+ for (i = 0, ch = 0; i < BITSET_UINTS; ++i)
+ for (j = 0; j < UINT_BITS; ++j, ++ch)
+ if (dfa->nodes[node].opr.sbcset[i] & (1 << j))
+ fastmap[ch] = 1;
+ }
+#ifdef RE_ENABLE_I18N
+ else if (type == COMPLEX_BRACKET)
+ {
+ int i;
+ re_charset_t *cset = dfa->nodes[node].opr.mbcset;
+ if (cset->non_match || cset->ncoll_syms || cset->nequiv_classes
+ || cset->nranges || cset->nchar_classes)
+ {
+# ifdef _LIBC
+ if (_NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES) != 0)
+ {
+ /* In this case we want to catch the bytes which are
+ the first byte of any collation elements.
+ e.g. In da_DK, we want to catch 'a' since "aa"
+ is a valid collation element, and don't catch
+ 'b' since 'b' is the only collation element
+ which starts from 'b'. */
+ int j, ch;
+ const int32_t *table = (const int32_t *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB);
+ for (i = 0, ch = 0; i < BITSET_UINTS; ++i)
+ for (j = 0; j < UINT_BITS; ++j, ++ch)
+ if (table[ch] < 0)
+ fastmap[ch] = 1;
+ }
+# else
+ if (MB_CUR_MAX > 1)
+ for (i = 0; i < SBC_MAX; ++i)
+ if (__btowc (i) == WEOF)
+ fastmap[i] = 1;
+# endif /* not _LIBC */
+ }
+ for (i = 0; i < cset->nmbchars; ++i)
+ {
+ char buf[256];
+ wctomb (buf, cset->mbchars[i]);
+ fastmap[*(unsigned char *) buf] = 1;
+ }
+ }
+#endif /* RE_ENABLE_I18N */
+ else if (type == END_OF_RE || type == OP_PERIOD
+#ifdef RE_ENABLE_I18N
+ || type == COMPLEX_BRACKET
+#endif /* RE_ENABLE_I18N */
+ )
+ {
+ memset (fastmap, '\1', sizeof (char) * SBC_MAX);
+ if (type == END_OF_RE)
+ bufp->can_be_null = 1;
+ return;
+ }
+ }
+}
+
+/* Entry point for POSIX code. */
+/* regcomp takes a regular expression as a string and compiles it.
+
+ PREG is a regex_t *. We do not expect any fields to be initialized,
+ since POSIX says we shouldn't. Thus, we set
+
+ `buffer' to the compiled pattern;
+ `used' to the length of the compiled pattern;
+ `syntax' to RE_SYNTAX_POSIX_EXTENDED if the
+ REG_EXTENDED bit in CFLAGS is set; otherwise, to
+ RE_SYNTAX_POSIX_BASIC;
+ `newline_anchor' to REG_NEWLINE being set in CFLAGS;
+ `fastmap' to an allocated space for the fastmap;
+ `fastmap_accurate' to zero;
+ `re_nsub' to the number of subexpressions in PATTERN.
+
+ PATTERN is the address of the pattern string.
+
+ CFLAGS is a series of bits which affect compilation.
+
+ If REG_EXTENDED is set, we use POSIX extended syntax; otherwise, we
+ use POSIX basic syntax.
+
+ If REG_NEWLINE is set, then . and [^...] don't match newline.
+ Also, regexec will try a match beginning after every newline.
+
+ If REG_ICASE is set, then we considers upper- and lowercase
+ versions of letters to be equivalent when matching.
+
+ If REG_NOSUB is set, then when PREG is passed to regexec, that
+ routine will report only success or failure, and nothing about the
+ registers.
+
+ It returns 0 if it succeeds, nonzero if it doesn't. (See regex.h for
+ the return codes and their meanings.) */
+
+int
+regcomp (preg, pattern, cflags)
+ regex_t *__restrict preg;
+ const char *__restrict pattern;
+ int cflags;
+{
+ reg_errcode_t ret;
+ reg_syntax_t syntax = ((cflags & REG_EXTENDED) ? RE_SYNTAX_POSIX_EXTENDED
+ : RE_SYNTAX_POSIX_BASIC);
+
+ preg->buffer = NULL;
+ preg->allocated = 0;
+ preg->used = 0;
+
+ /* Try to allocate space for the fastmap. */
+ preg->fastmap = re_malloc (char, SBC_MAX);
+ if (BE (preg->fastmap == NULL, 0))
+ return REG_ESPACE;
+
+ syntax |= (cflags & REG_ICASE) ? RE_ICASE : 0;
+
+ /* If REG_NEWLINE is set, newlines are treated differently. */
+ if (cflags & REG_NEWLINE)
+ { /* REG_NEWLINE implies neither . nor [^...] match newline. */
+ syntax &= ~RE_DOT_NEWLINE;
+ syntax |= RE_HAT_LISTS_NOT_NEWLINE;
+ /* It also changes the matching behavior. */
+ preg->newline_anchor = 1;
+ }
+ else
+ preg->newline_anchor = 0;
+ preg->no_sub = !!(cflags & REG_NOSUB);
+ preg->translate = NULL;
+
+ ret = re_compile_internal (preg, pattern, strlen (pattern), syntax);
+
+ /* POSIX doesn't distinguish between an unmatched open-group and an
+ unmatched close-group: both are REG_EPAREN. */
+ if (ret == REG_ERPAREN)
+ ret = REG_EPAREN;
+
+ /* We have already checked preg->fastmap != NULL. */
+ if (BE (ret == REG_NOERROR, 1))
+ {
+ /* Compute the fastmap now, since regexec cannot modify the pattern
+ buffer. */
+ if (BE (re_compile_fastmap (preg) == -2, 0))
+ {
+ /* Some error occurred while computing the fastmap, just forget
+ about it. */
+ re_free (preg->fastmap);
+ preg->fastmap = NULL;
+ }
+ }
+
+ return (int) ret;
+}
+#ifdef _LIBC
+weak_alias (__regcomp, regcomp)
+#endif
+
+/* Returns a message corresponding to an error code, ERRCODE, returned
+ from either regcomp or regexec. We don't use PREG here. */
+
+size_t
+regerror (errcode, preg, errbuf, errbuf_size)
+ int errcode;
+ const regex_t *preg;
+ char *errbuf;
+ size_t errbuf_size;
+{
+ const char *msg;
+ size_t msg_size;
+
+ if (BE (errcode < 0
+ || errcode >= (int) (sizeof (__re_error_msgid_idx)
+ / sizeof (__re_error_msgid_idx[0])), 0))
+ /* Only error codes returned by the rest of the code should be passed
+ to this routine. If we are given anything else, or if other regex
+ code generates an invalid error code, then the program has a bug.
+ Dump core so we can fix it. */
+ abort ();
+
+ msg = gettext (__re_error_msgid + __re_error_msgid_idx[errcode]);
+
+ msg_size = strlen (msg) + 1; /* Includes the null. */
+
+ if (BE (errbuf_size != 0, 1))
+ {
+ if (BE (msg_size > errbuf_size, 0))
+ {
+#if defined HAVE_MEMPCPY || defined _LIBC
+ *((char *) __mempcpy (errbuf, msg, errbuf_size - 1)) = '\0';
+#else
+ memcpy (errbuf, msg, errbuf_size - 1);
+ errbuf[errbuf_size - 1] = 0;
+#endif
+ }
+ else
+ memcpy (errbuf, msg, msg_size);
+ }
+
+ return msg_size;
+}
+#ifdef _LIBC
+weak_alias (__regerror, regerror)
+#endif
+
+/* Free dynamically allocated space used by PREG. */
+
+void
+regfree (preg)
+ regex_t *preg;
+{
+ int i, j;
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ if (BE (dfa != NULL, 1))
+ {
+ re_free (dfa->subexps);
+
+ for (i = 0; i < dfa->nodes_len; ++i)
+ {
+ re_token_t *node = dfa->nodes + i;
+#ifdef RE_ENABLE_I18N
+ if (node->type == COMPLEX_BRACKET && node->duplicated == 0)
+ free_charset (node->opr.mbcset);
+ else
+#endif /* RE_ENABLE_I18N */
+ if (node->type == SIMPLE_BRACKET && node->duplicated == 0)
+ re_free (node->opr.sbcset);
+ else if (node->type == OP_CONTEXT_NODE)
+ {
+ if (dfa->nodes[node->opr.ctx_info->entity].type == OP_BACK_REF)
+ {
+ if (node->opr.ctx_info->bkref_eclosure != NULL)
+ re_node_set_free (node->opr.ctx_info->bkref_eclosure);
+ re_free (node->opr.ctx_info->bkref_eclosure);
+ }
+ re_free (node->opr.ctx_info);
+ }
+ }
+ re_free (dfa->firsts);
+ re_free (dfa->nexts);
+ for (i = 0; i < dfa->nodes_len; ++i)
+ {
+ if (dfa->eclosures != NULL)
+ re_node_set_free (dfa->eclosures + i);
+ if (dfa->inveclosures != NULL)
+ re_node_set_free (dfa->inveclosures + i);
+ if (dfa->edests != NULL)
+ re_node_set_free (dfa->edests + i);
+ }
+ re_free (dfa->edests);
+ re_free (dfa->eclosures);
+ re_free (dfa->inveclosures);
+ re_free (dfa->nodes);
+
+ for (i = 0; i <= dfa->state_hash_mask; ++i)
+ {
+ struct re_state_table_entry *entry = dfa->state_table + i;
+ for (j = 0; j < entry->num; ++j)
+ {
+ re_dfastate_t *state = entry->array[j];
+ if (state->entrance_nodes != &state->nodes)
+ {
+ re_node_set_free (state->entrance_nodes);
+ re_free (state->entrance_nodes);
+ }
+ re_node_set_free (&state->nodes);
+ re_free (state->trtable);
+ re_free (state->trtable_search);
+ re_free (state);
+ }
+ re_free (entry->array);
+ }
+ re_free (dfa->state_table);
+
+ if (dfa->word_char != NULL)
+ re_free (dfa->word_char);
+#ifdef DEBUG
+ re_free (dfa->re_str);
+#endif
+ re_free (dfa);
+ }
+ re_free (preg->fastmap);
+}
+#ifdef _LIBC
+weak_alias (__regfree, regfree)
+#endif
+
+/* Entry points compatible with 4.2 BSD regex library. We don't define
+ them unless specifically requested. */
+
+#if defined _REGEX_RE_COMP || defined _LIBC
+
+/* BSD has one and only one pattern buffer. */
+static struct re_pattern_buffer re_comp_buf;
+
+char *
+# ifdef _LIBC
+/* Make these definitions weak in libc, so POSIX programs can redefine
+ these names if they don't use our functions, and still use
+ regcomp/regexec above without link errors. */
+weak_function
+# endif
+re_comp (s)
+ const char *s;
+{
+ reg_errcode_t ret;
+
+ if (!s)
+ {
+ if (!re_comp_buf.buffer)
+ return gettext ("No previous regular expression");
+ return 0;
+ }
+
+ if (!re_comp_buf.buffer)
+ {
+ re_comp_buf.fastmap = (char *) malloc (SBC_MAX);
+ if (re_comp_buf.fastmap == NULL)
+ return (char *) gettext (__re_error_msgid
+ + __re_error_msgid_idx[(int) REG_ESPACE]);
+ }
+
+ /* Since `re_exec' always passes NULL for the `regs' argument, we
+ don't need to initialize the pattern buffer fields which affect it. */
+
+ /* Match anchors at newlines. */
+ re_comp_buf.newline_anchor = 1;
+
+ ret = re_compile_internal (&re_comp_buf, s, strlen (s), re_syntax_options);
+
+ if (!ret)
+ return NULL;
+
+ /* Yes, we're discarding `const' here if !HAVE_LIBINTL. */
+ return (char *) gettext (__re_error_msgid + __re_error_msgid_idx[(int) ret]);
+}
+#endif /* _REGEX_RE_COMP */
+
+/* Internal entry point.
+ Compile the regular expression PATTERN, whose length is LENGTH.
+ SYNTAX indicate regular expression's syntax. */
+
+static reg_errcode_t
+re_compile_internal (preg, pattern, length, syntax)
+ regex_t *preg;
+ const char * pattern;
+ int length;
+ reg_syntax_t syntax;
+{
+ reg_errcode_t err = REG_NOERROR;
+ re_dfa_t *dfa;
+ re_string_t regexp;
+
+ /* Initialize the pattern buffer. */
+ preg->fastmap_accurate = 0;
+ preg->syntax = syntax;
+ preg->not_bol = preg->not_eol = 0;
+ preg->used = 0;
+ preg->re_nsub = 0;
+
+ /* Initialize the dfa. */
+ dfa = (re_dfa_t *) preg->buffer;
+ if (preg->allocated < sizeof (re_dfa_t))
+ {
+ /* If zero allocated, but buffer is non-null, try to realloc
+ enough space. This loses if buffer's address is bogus, but
+ that is the user's responsibility. If ->buffer is NULL this
+ is a simple allocation. */
+ dfa = re_realloc (preg->buffer, re_dfa_t, 1);
+ if (dfa == NULL)
+ return REG_ESPACE;
+ preg->allocated = sizeof (re_dfa_t);
+ }
+ preg->buffer = (unsigned char *) dfa;
+ preg->used = sizeof (re_dfa_t);
+
+ err = init_dfa (dfa, length);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ re_free (dfa);
+ preg->buffer = NULL;
+ return err;
+ }
+#ifdef DEBUG
+ dfa->re_str = re_malloc (char, length + 1);
+ strncpy (dfa->re_str, pattern, length + 1);
+#endif
+
+ err = re_string_construct (&regexp, pattern, length, preg->translate,
+ syntax & RE_ICASE);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ re_free (dfa);
+ preg->buffer = NULL;
+ return err;
+ }
+
+ /* Parse the regular expression, and build a structure tree. */
+ preg->re_nsub = 0;
+ dfa->str_tree = parse (&regexp, preg, syntax, &err);
+ if (BE (dfa->str_tree == NULL, 0))
+ goto re_compile_internal_free_return;
+
+ /* Analyze the tree and collect information which is necessary to
+ create the dfa. */
+ err = analyze (dfa);
+ if (BE (err != REG_NOERROR, 0))
+ goto re_compile_internal_free_return;
+
+ /* Then create the initial state of the dfa. */
+ err = create_initial_state (dfa);
+ if (BE (err != REG_NOERROR, 0))
+ goto re_compile_internal_free_return;
+
+ re_compile_internal_free_return:
+ /* Release work areas. */
+ free_workarea_compile (preg);
+ re_string_destruct (&regexp);
+
+ return err;
+}
+
+/* Initialize DFA. We use the length of the regular expression PAT_LEN
+ as the initial length of some arrays. */
+
+static reg_errcode_t
+init_dfa (dfa, pat_len)
+ re_dfa_t *dfa;
+ int pat_len;
+{
+ int table_size;
+
+ memset (dfa, '\0', sizeof (re_dfa_t));
+
+ dfa->nodes_alloc = pat_len + 1;
+ dfa->nodes = re_malloc (re_token_t, dfa->nodes_alloc);
+
+ dfa->states_alloc = pat_len + 1;
+
+ /* table_size = 2 ^ ceil(log pat_len) */
+ for (table_size = 1; table_size > 0; table_size <<= 1)
+ if (table_size > pat_len)
+ break;
+
+ dfa->state_table = calloc (sizeof (struct re_state_table_entry), table_size);
+ dfa->state_hash_mask = table_size - 1;
+
+ dfa->subexps_alloc = 1;
+ dfa->subexps = re_malloc (re_subexp_t, dfa->subexps_alloc);
+ dfa->word_char = NULL;
+
+ if (BE (dfa->nodes == NULL || dfa->state_table == NULL
+ || dfa->subexps == NULL, 0))
+ {
+ /* We don't bother to free anything which was allocated. Very
+ soon the process will go down anyway. */
+ dfa->subexps = NULL;
+ dfa->state_table = NULL;
+ dfa->nodes = NULL;
+ return REG_ESPACE;
+ }
+ return REG_NOERROR;
+}
+
+/* Initialize WORD_CHAR table, which indicate which character is
+ "word". In this case "word" means that it is the word construction
+ character used by some operators like "\<", "\>", etc. */
+
+static reg_errcode_t
+init_word_char (dfa)
+ re_dfa_t *dfa;
+{
+ int i, j, ch;
+ dfa->word_char = (re_bitset_ptr_t) calloc (sizeof (bitset), 1);
+ if (BE (dfa->word_char == NULL, 0))
+ return REG_ESPACE;
+ for (i = 0, ch = 0; i < BITSET_UINTS; ++i)
+ for (j = 0; j < UINT_BITS; ++j, ++ch)
+ if (isalnum (ch) || ch == '_')
+ dfa->word_char[i] |= 1 << j;
+ return REG_NOERROR;
+}
+
+/* Free the work area which are only used while compiling. */
+
+static void
+free_workarea_compile (preg)
+ regex_t *preg;
+{
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ free_bin_tree (dfa->str_tree);
+ dfa->str_tree = NULL;
+}
+
+/* Create initial states for all contexts. */
+
+static reg_errcode_t
+create_initial_state (dfa)
+ re_dfa_t *dfa;
+{
+ int first, i;
+ reg_errcode_t err;
+ re_node_set init_nodes;
+
+ /* Initial states have the epsilon closure of the node which is
+ the first node of the regular expression. */
+ first = dfa->str_tree->first;
+ dfa->init_node = first;
+ err = re_node_set_init_copy (&init_nodes, dfa->eclosures + first);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+
+ /* The back-references which are in initial states can epsilon transit,
+ since in this case all of the subexpressions can be null.
+ Then we add epsilon closures of the nodes which are the next nodes of
+ the back-references. */
+ if (dfa->nbackref > 0)
+ for (i = 0; i < init_nodes.nelem; ++i)
+ {
+ int node_idx = init_nodes.elems[i];
+ re_token_type_t type = dfa->nodes[node_idx].type;
+
+ int clexp_idx;
+ int entity = (type != OP_CONTEXT_NODE ? node_idx
+ : dfa->nodes[node_idx].opr.ctx_info->entity);
+ if ((type != OP_CONTEXT_NODE
+ || (dfa->nodes[entity].type != OP_BACK_REF))
+ && (type != OP_BACK_REF))
+ continue;
+ for (clexp_idx = 0; clexp_idx < init_nodes.nelem; ++clexp_idx)
+ {
+ re_token_t *clexp_node;
+ clexp_node = dfa->nodes + init_nodes.elems[clexp_idx];
+ if (clexp_node->type == OP_CLOSE_SUBEXP
+ && clexp_node->opr.idx + 1 == dfa->nodes[entity].opr.idx)
+ break;
+ }
+ if (clexp_idx == init_nodes.nelem)
+ continue;
+
+ if (type == OP_CONTEXT_NODE
+ && (dfa->nodes[dfa->nodes[node_idx].opr.ctx_info->entity].type
+ == OP_BACK_REF))
+ {
+ int prev_nelem = init_nodes.nelem;
+ re_node_set_merge (&init_nodes,
+ dfa->nodes[node_idx].opr.ctx_info->bkref_eclosure);
+ if (prev_nelem < init_nodes.nelem)
+ i = 0;
+ }
+ else if (type == OP_BACK_REF)
+ {
+ int next_idx = dfa->nexts[node_idx];
+ if (!re_node_set_contains (&init_nodes, next_idx))
+ {
+ re_node_set_merge (&init_nodes, dfa->eclosures + next_idx);
+ i = 0;
+ }
+ }
+ }
+
+ /* It must be the first time to invoke acquire_state. */
+ dfa->init_state = re_acquire_state_context (&err, dfa, &init_nodes, 0);
+ /* We don't check ERR here, since the initial state must not be NULL. */
+ if (BE (dfa->init_state == NULL, 0))
+ return err;
+ if (dfa->init_state->has_constraint)
+ {
+ dfa->init_state_word = re_acquire_state_context (&err, dfa, &init_nodes,
+ CONTEXT_WORD);
+ dfa->init_state_nl = re_acquire_state_context (&err, dfa, &init_nodes,
+ CONTEXT_NEWLINE);
+ dfa->init_state_begbuf = re_acquire_state_context (&err, dfa,
+ &init_nodes,
+ CONTEXT_NEWLINE
+ | CONTEXT_BEGBUF);
+ if (BE (dfa->init_state_word == NULL || dfa->init_state_nl == NULL
+ || dfa->init_state_begbuf == NULL, 0))
+ return err;
+ }
+ else
+ dfa->init_state_word = dfa->init_state_nl
+ = dfa->init_state_begbuf = dfa->init_state;
+
+ re_node_set_free (&init_nodes);
+ return REG_NOERROR;
+}
+
+/* Analyze the structure tree, and calculate "first", "next", "edest",
+ "eclosure", and "inveclosure". */
+
+static reg_errcode_t
+analyze (dfa)
+ re_dfa_t *dfa;
+{
+ int i;
+ reg_errcode_t ret;
+
+ /* Allocate arrays. */
+ dfa->firsts = re_malloc (int, dfa->nodes_alloc);
+ dfa->nexts = re_malloc (int, dfa->nodes_alloc);
+ dfa->edests = re_malloc (re_node_set, dfa->nodes_alloc);
+ dfa->eclosures = re_malloc (re_node_set, dfa->nodes_alloc);
+ dfa->inveclosures = re_malloc (re_node_set, dfa->nodes_alloc);
+ if (BE (dfa->firsts == NULL || dfa->nexts == NULL || dfa->edests == NULL
+ || dfa->eclosures == NULL || dfa->inveclosures == NULL, 0))
+ return REG_ESPACE;
+ /* Initialize them. */
+ for (i = 0; i < dfa->nodes_len; ++i)
+ {
+ dfa->firsts[i] = -1;
+ dfa->nexts[i] = -1;
+ re_node_set_init_empty (dfa->edests + i);
+ re_node_set_init_empty (dfa->eclosures + i);
+ re_node_set_init_empty (dfa->inveclosures + i);
+ }
+
+ ret = analyze_tree (dfa, dfa->str_tree);
+ if (BE (ret == REG_NOERROR, 1))
+ {
+ ret = calc_eclosure (dfa);
+ if (ret == REG_NOERROR)
+ calc_inveclosure (dfa);
+ }
+ return ret;
+}
+
+/* Helper functions for analyze.
+ This function calculate "first", "next", and "edest" for the subtree
+ whose root is NODE. */
+
+static reg_errcode_t
+analyze_tree (dfa, node)
+ re_dfa_t *dfa;
+ bin_tree_t *node;
+{
+ reg_errcode_t ret;
+ if (node->first == -1)
+ calc_first (dfa, node);
+ if (node->next == -1)
+ calc_next (dfa, node);
+ if (node->eclosure.nelem == 0)
+ calc_epsdest (dfa, node);
+ /* Calculate "first" etc. for the left child. */
+ if (node->left != NULL)
+ {
+ ret = analyze_tree (dfa, node->left);
+ if (BE (ret != REG_NOERROR, 0))
+ return ret;
+ }
+ /* Calculate "first" etc. for the right child. */
+ if (node->right != NULL)
+ {
+ ret = analyze_tree (dfa, node->right);
+ if (BE (ret != REG_NOERROR, 0))
+ return ret;
+ }
+ return REG_NOERROR;
+}
+
+/* Calculate "first" for the node NODE. */
+static void
+calc_first (dfa, node)
+ re_dfa_t *dfa;
+ bin_tree_t *node;
+{
+ int idx, type;
+ idx = node->node_idx;
+ type = (node->type == 0) ? dfa->nodes[idx].type : node->type;
+
+ switch (type)
+ {
+#ifdef DEBUG
+ case OP_OPEN_BRACKET:
+ case OP_CLOSE_BRACKET:
+ case OP_OPEN_DUP_NUM:
+ case OP_CLOSE_DUP_NUM:
+ case OP_NON_MATCH_LIST:
+ case OP_OPEN_COLL_ELEM:
+ case OP_CLOSE_COLL_ELEM:
+ case OP_OPEN_EQUIV_CLASS:
+ case OP_CLOSE_EQUIV_CLASS:
+ case OP_OPEN_CHAR_CLASS:
+ case OP_CLOSE_CHAR_CLASS:
+ /* These must not be appeared here. */
+ assert (0);
+#endif
+ case END_OF_RE:
+ case CHARACTER:
+ case OP_PERIOD:
+ case OP_DUP_ASTERISK:
+ case OP_DUP_QUESTION:
+#ifdef RE_ENABLE_I18N
+ case COMPLEX_BRACKET:
+#endif /* RE_ENABLE_I18N */
+ case SIMPLE_BRACKET:
+ case OP_BACK_REF:
+ case ANCHOR:
+ case OP_OPEN_SUBEXP:
+ case OP_CLOSE_SUBEXP:
+ node->first = idx;
+ break;
+ case OP_DUP_PLUS:
+#ifdef DEBUG
+ assert (node->left != NULL);
+#endif
+ if (node->left->first == -1)
+ calc_first (dfa, node->left);
+ node->first = node->left->first;
+ break;
+ case OP_ALT:
+ node->first = idx;
+ break;
+ /* else fall through */
+ default:
+#ifdef DEBUG
+ assert (node->left != NULL);
+#endif
+ if (node->left->first == -1)
+ calc_first (dfa, node->left);
+ node->first = node->left->first;
+ break;
+ }
+ if (node->type == 0)
+ dfa->firsts[idx] = node->first;
+}
+
+/* Calculate "next" for the node NODE. */
+
+static void
+calc_next (dfa, node)
+ re_dfa_t *dfa;
+ bin_tree_t *node;
+{
+ int idx, type;
+ bin_tree_t *parent = node->parent;
+ if (parent == NULL)
+ {
+ node->next = -1;
+ idx = node->node_idx;
+ if (node->type == 0)
+ dfa->nexts[idx] = node->next;
+ return;
+ }
+
+ idx = parent->node_idx;
+ type = (parent->type == 0) ? dfa->nodes[idx].type : parent->type;
+
+ switch (type)
+ {
+ case OP_DUP_ASTERISK:
+ case OP_DUP_PLUS:
+ node->next = idx;
+ break;
+ case CONCAT:
+ if (parent->left == node)
+ {
+ if (parent->right->first == -1)
+ calc_first (dfa, parent->right);
+ node->next = parent->right->first;
+ break;
+ }
+ /* else fall through */
+ default:
+ if (parent->next == -1)
+ calc_next (dfa, parent);
+ node->next = parent->next;
+ break;
+ }
+ idx = node->node_idx;
+ if (node->type == 0)
+ dfa->nexts[idx] = node->next;
+}
+
+/* Calculate "edest" for the node NODE. */
+
+static void
+calc_epsdest (dfa, node)
+ re_dfa_t *dfa;
+ bin_tree_t *node;
+{
+ int idx;
+ idx = node->node_idx;
+ if (node->type == 0)
+ {
+ if (dfa->nodes[idx].type == OP_DUP_ASTERISK
+ || dfa->nodes[idx].type == OP_DUP_PLUS
+ || dfa->nodes[idx].type == OP_DUP_QUESTION)
+ {
+ if (node->left->first == -1)
+ calc_first (dfa, node->left);
+ if (node->next == -1)
+ calc_next (dfa, node);
+ re_node_set_init_2 (dfa->edests + idx, node->left->first,
+ node->next);
+ }
+ else if (dfa->nodes[idx].type == OP_ALT)
+ {
+ int left, right;
+ if (node->left != NULL)
+ {
+ if (node->left->first == -1)
+ calc_first (dfa, node->left);
+ left = node->left->first;
+ }
+ else
+ {
+ if (node->next == -1)
+ calc_next (dfa, node);
+ left = node->next;
+ }
+ if (node->right != NULL)
+ {
+ if (node->right->first == -1)
+ calc_first (dfa, node->right);
+ right = node->right->first;
+ }
+ else
+ {
+ if (node->next == -1)
+ calc_next (dfa, node);
+ right = node->next;
+ }
+ re_node_set_init_2 (dfa->edests + idx, left, right);
+ }
+ else if (dfa->nodes[idx].type == ANCHOR
+ || dfa->nodes[idx].type == OP_OPEN_SUBEXP
+ || dfa->nodes[idx].type == OP_CLOSE_SUBEXP)
+ re_node_set_init_1 (dfa->edests + idx, node->next);
+ }
+}
+
+/* Duplicate the node whose index is ORG_IDX and set the constraint CONSTRAINT.
+ The new index will be stored in NEW_IDX and return REG_NOERROR if succeeded,
+ otherwise return the error code. */
+
+static reg_errcode_t
+duplicate_node (new_idx, dfa, org_idx, constraint)
+ re_dfa_t *dfa;
+ int *new_idx, org_idx;
+ unsigned int constraint;
+{
+ re_token_t dup;
+ int dup_idx;
+ reg_errcode_t err;
+
+ dup.type = OP_CONTEXT_NODE;
+ if (dfa->nodes[org_idx].type == OP_CONTEXT_NODE)
+ {
+ /* If the node whose index is ORG_IDX is the same as the intended
+ node, use it. */
+ if (dfa->nodes[org_idx].constraint == constraint)
+ {
+ *new_idx = org_idx;
+ return REG_NOERROR;
+ }
+ dup.constraint = constraint |
+ dfa->nodes[org_idx].constraint;
+ }
+ else
+ dup.constraint = constraint;
+
+ /* In case that `entity' points OP_CONTEXT_NODE,
+ we correct `entity' to real entity in calc_inveclosures(). */
+ dup.opr.ctx_info = malloc (sizeof (*dup.opr.ctx_info));
+ dup_idx = re_dfa_add_node (dfa, dup, 1);
+ if (BE (dup.opr.ctx_info == NULL || dup_idx == -1, 0))
+ return REG_ESPACE;
+ dup.opr.ctx_info->entity = org_idx;
+ dup.opr.ctx_info->bkref_eclosure = NULL;
+
+ dfa->nodes[dup_idx].duplicated = 1;
+ dfa->firsts[dup_idx] = dfa->firsts[org_idx];
+ dfa->nexts[dup_idx] = dfa->nexts[org_idx];
+ err = re_node_set_init_copy (dfa->edests + dup_idx, dfa->edests + org_idx);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ /* Since we don't duplicate epsilon nodes, epsilon closure have
+ only itself. */
+ err = re_node_set_init_1 (dfa->eclosures + dup_idx, dup_idx);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ err = re_node_set_init_1 (dfa->inveclosures + dup_idx, dup_idx);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ /* Then we must update inveclosure for this node.
+ We process them at last part of calc_eclosure(),
+ since we don't complete to calculate them here. */
+
+ *new_idx = dup_idx;
+ return REG_NOERROR;
+}
+
+static void
+calc_inveclosure (dfa)
+ re_dfa_t *dfa;
+{
+ int src, idx, dest, entity;
+ for (src = 0; src < dfa->nodes_len; ++src)
+ {
+ for (idx = 0; idx < dfa->eclosures[src].nelem; ++idx)
+ {
+ dest = dfa->eclosures[src].elems[idx];
+ re_node_set_insert (dfa->inveclosures + dest, src);
+ }
+
+ entity = src;
+ while (dfa->nodes[entity].type == OP_CONTEXT_NODE)
+ {
+ entity = dfa->nodes[entity].opr.ctx_info->entity;
+ re_node_set_merge (dfa->inveclosures + src,
+ dfa->inveclosures + entity);
+ dfa->nodes[src].opr.ctx_info->entity = entity;
+ }
+ }
+}
+
+/* Calculate "eclosure" for all the node in DFA. */
+
+static reg_errcode_t
+calc_eclosure (dfa)
+ re_dfa_t *dfa;
+{
+ int idx, node_idx, max, incomplete = 0;
+#ifdef DEBUG
+ assert (dfa->nodes_len > 0);
+#endif
+ /* For each nodes, calculate epsilon closure. */
+ for (node_idx = 0, max = dfa->nodes_len; ; ++node_idx)
+ {
+ reg_errcode_t err;
+ re_node_set eclosure_elem;
+ if (node_idx == max)
+ {
+ if (!incomplete)
+ break;
+ incomplete = 0;
+ node_idx = 0;
+ }
+
+#ifdef DEBUG
+ assert (dfa->nodes[node_idx].type != OP_CONTEXT_NODE);
+ assert (dfa->eclosures[node_idx].nelem != -1);
+#endif
+ /* If we have already calculated, skip it. */
+ if (dfa->eclosures[node_idx].nelem != 0)
+ continue;
+ /* Calculate epsilon closure of `node_idx'. */
+ err = calc_eclosure_iter (&eclosure_elem, dfa, node_idx, 1);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+
+ if (dfa->eclosures[node_idx].nelem == 0)
+ {
+ incomplete = 1;
+ re_node_set_free (&eclosure_elem);
+ }
+ }
+
+ /* for duplicated nodes. */
+ for (idx = max; idx < dfa->nodes_len; ++idx)
+ {
+ int entity, i, constraint;
+ re_node_set *bkref_eclosure;
+ entity = dfa->nodes[idx].opr.ctx_info->entity;
+ re_node_set_merge (dfa->inveclosures + idx, dfa->inveclosures + entity);
+ if (dfa->nodes[entity].type != OP_BACK_REF)
+ continue;
+
+ /* If the node is backreference, duplicate the epsilon closure of
+ the next node. Since it may epsilon transit. */
+ /* Note: duplicate_node() may realloc dfa->eclosures, etc. */
+ bkref_eclosure = re_malloc (re_node_set, 1);
+ if (BE (bkref_eclosure == NULL, 0))
+ return REG_ESPACE;
+ re_node_set_init_empty (bkref_eclosure);
+ constraint = dfa->nodes[idx].constraint;
+ for (i = 0; i < dfa->eclosures[dfa->nexts[idx]].nelem; ++i)
+ {
+ int dest_node_idx = dfa->eclosures[dfa->nexts[idx]].elems[i];
+ if (!IS_EPSILON_NODE (dfa->nodes[dest_node_idx].type))
+ {
+ reg_errcode_t err;
+ err = duplicate_node (&dest_node_idx, dfa, dest_node_idx,
+ constraint);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ re_node_set_insert (bkref_eclosure, dest_node_idx);
+ }
+ dfa->nodes[idx].opr.ctx_info->bkref_eclosure = bkref_eclosure;
+ }
+
+ return REG_NOERROR;
+}
+
+/* Calculate epsilon closure of NODE. */
+
+static reg_errcode_t
+calc_eclosure_iter (new_set, dfa, node, root)
+ re_node_set *new_set;
+ re_dfa_t *dfa;
+ int node, root;
+{
+ reg_errcode_t err;
+ unsigned int constraint;
+ int i, max, incomplete = 0;
+ re_node_set eclosure;
+ err = re_node_set_alloc (&eclosure, dfa->edests[node].nelem + 1);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+
+ /* This indicates that we are calculating this node now.
+ We reference this value to avoid infinite loop. */
+ dfa->eclosures[node].nelem = -1;
+
+ constraint = ((dfa->nodes[node].type == ANCHOR)
+ ? dfa->nodes[node].opr.ctx_type : 0);
+
+ /* Expand each epsilon destination nodes. */
+ if (dfa->edests[node].nelem != 0)
+ for (i = 0; i < dfa->edests[node].nelem; ++i)
+ {
+ re_node_set eclosure_elem;
+ int edest = dfa->edests[node].elems[i];
+ /* If calculating the epsilon closure of `edest' is in progress,
+ return intermediate result. */
+ if (dfa->eclosures[edest].nelem == -1)
+ {
+ incomplete = 1;
+ continue;
+ }
+ /* If we haven't calculated the epsilon closure of `edest' yet,
+ calculate now. Otherwise use calculated epsilon closure. */
+ if (dfa->eclosures[edest].nelem == 0)
+ {
+ err = calc_eclosure_iter (&eclosure_elem, dfa, edest, 0);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ else
+ eclosure_elem = dfa->eclosures[edest];
+ /* Merge the epsilon closure of `edest'. */
+ re_node_set_merge (&eclosure, &eclosure_elem);
+ /* If the epsilon closure of `edest' is incomplete,
+ the epsilon closure of this node is also incomplete. */
+ if (dfa->eclosures[edest].nelem == 0)
+ {
+ incomplete = 1;
+ re_node_set_free (&eclosure_elem);
+ }
+ }
+
+ /* If the current node has constraints, duplicate all non-epsilon nodes.
+ Since they must inherit the constraints. */
+ if (constraint)
+ for (i = 0, max = eclosure.nelem; i < max; ++i)
+ {
+ int dest = eclosure.elems[i];
+ if (!IS_EPSILON_NODE (dfa->nodes[dest].type))
+ {
+ int dup_dest;
+ reg_errcode_t err;
+ err = duplicate_node (&dup_dest, dfa, dest, constraint);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ if (dest != dup_dest)
+ {
+ re_node_set_remove_at (&eclosure, i--);
+ re_node_set_insert (&eclosure, dup_dest);
+ --max;
+ }
+ }
+ }
+
+ /* Epsilon closures include itself. */
+ re_node_set_insert (&eclosure, node);
+ if (incomplete && !root)
+ dfa->eclosures[node].nelem = 0;
+ else
+ dfa->eclosures[node] = eclosure;
+ *new_set = eclosure;
+ return REG_NOERROR;
+}
+
+/* Functions for token which are used in the parser. */
+
+/* Fetch a token from INPUT.
+ We must not use this function inside bracket expressions. */
+
+static re_token_t
+fetch_token (input, syntax)
+ re_string_t *input;
+ reg_syntax_t syntax;
+{
+ re_token_t token;
+ int consumed_byte;
+ consumed_byte = peek_token (&token, input, syntax);
+ re_string_skip_bytes (input, consumed_byte);
+ return token;
+}
+
+/* Peek a token from INPUT, and return the length of the token.
+ We must not use this function inside bracket expressions. */
+
+static int
+peek_token (token, input, syntax)
+ re_token_t *token;
+ re_string_t *input;
+ reg_syntax_t syntax;
+{
+ unsigned char c;
+
+ if (re_string_eoi (input))
+ {
+ token->type = END_OF_RE;
+ return 0;
+ }
+
+ c = re_string_peek_byte (input, 0);
+ token->opr.c = c;
+
+#ifdef RE_ENABLE_I18N
+ token->mb_partial = 0;
+ if (MB_CUR_MAX > 1 &&
+ !re_string_first_byte (input, re_string_cur_idx (input)))
+ {
+ token->type = CHARACTER;
+ token->mb_partial = 1;
+ return 1;
+ }
+#endif
+ if (c == '\\')
+ {
+ unsigned char c2;
+ if (re_string_cur_idx (input) + 1 >= re_string_length (input))
+ {
+ token->type = BACK_SLASH;
+ return 1;
+ }
+
+ c2 = re_string_peek_byte_case (input, 1);
+ token->opr.c = c2;
+ token->type = CHARACTER;
+ switch (c2)
+ {
+ case '|':
+ if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_NO_BK_VBAR))
+ token->type = OP_ALT;
+ break;
+ case '1': case '2': case '3': case '4': case '5':
+ case '6': case '7': case '8': case '9':
+ if (!(syntax & RE_NO_BK_REFS))
+ {
+ token->type = OP_BACK_REF;
+ token->opr.idx = c2 - '0';
+ }
+ break;
+ case '<':
+ if (!(syntax & RE_NO_GNU_OPS))
+ {
+ token->type = ANCHOR;
+ token->opr.idx = WORD_FIRST;
+ }
+ break;
+ case '>':
+ if (!(syntax & RE_NO_GNU_OPS))
+ {
+ token->type = ANCHOR;
+ token->opr.idx = WORD_LAST;
+ }
+ break;
+ case 'b':
+ if (!(syntax & RE_NO_GNU_OPS))
+ {
+ token->type = ANCHOR;
+ token->opr.idx = WORD_DELIM;
+ }
+ break;
+ case 'B':
+ if (!(syntax & RE_NO_GNU_OPS))
+ {
+ token->type = ANCHOR;
+ token->opr.idx = INSIDE_WORD;
+ }
+ break;
+ case 'w':
+ if (!(syntax & RE_NO_GNU_OPS))
+ token->type = OP_WORD;
+ break;
+ case 'W':
+ if (!(syntax & RE_NO_GNU_OPS))
+ token->type = OP_NOTWORD;
+ break;
+ case '`':
+ if (!(syntax & RE_NO_GNU_OPS))
+ {
+ token->type = ANCHOR;
+ token->opr.idx = BUF_FIRST;
+ }
+ break;
+ case '\'':
+ if (!(syntax & RE_NO_GNU_OPS))
+ {
+ token->type = ANCHOR;
+ token->opr.idx = BUF_LAST;
+ }
+ break;
+ case '(':
+ if (!(syntax & RE_NO_BK_PARENS))
+ token->type = OP_OPEN_SUBEXP;
+ break;
+ case ')':
+ if (!(syntax & RE_NO_BK_PARENS))
+ token->type = OP_CLOSE_SUBEXP;
+ break;
+ case '+':
+ if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_BK_PLUS_QM))
+ token->type = OP_DUP_PLUS;
+ break;
+ case '?':
+ if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_BK_PLUS_QM))
+ token->type = OP_DUP_QUESTION;
+ break;
+ case '{':
+ if ((syntax & RE_INTERVALS) && (!(syntax & RE_NO_BK_BRACES)))
+ token->type = OP_OPEN_DUP_NUM;
+ break;
+ case '}':
+ if ((syntax & RE_INTERVALS) && (!(syntax & RE_NO_BK_BRACES)))
+ token->type = OP_CLOSE_DUP_NUM;
+ break;
+ default:
+ break;
+ }
+ return 2;
+ }
+
+ token->type = CHARACTER;
+ switch (c)
+ {
+ case '\n':
+ if (syntax & RE_NEWLINE_ALT)
+ token->type = OP_ALT;
+ break;
+ case '|':
+ if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_NO_BK_VBAR))
+ token->type = OP_ALT;
+ break;
+ case '*':
+ token->type = OP_DUP_ASTERISK;
+ break;
+ case '+':
+ if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_BK_PLUS_QM))
+ token->type = OP_DUP_PLUS;
+ break;
+ case '?':
+ if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_BK_PLUS_QM))
+ token->type = OP_DUP_QUESTION;
+ break;
+ case '{':
+ if ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES))
+ token->type = OP_OPEN_DUP_NUM;
+ break;
+ case '}':
+ if ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES))
+ token->type = OP_CLOSE_DUP_NUM;
+ break;
+ case '(':
+ if (syntax & RE_NO_BK_PARENS)
+ token->type = OP_OPEN_SUBEXP;
+ break;
+ case ')':
+ if (syntax & RE_NO_BK_PARENS)
+ token->type = OP_CLOSE_SUBEXP;
+ break;
+ case '[':
+ token->type = OP_OPEN_BRACKET;
+ break;
+ case '.':
+ token->type = OP_PERIOD;
+ break;
+ case '^':
+ if (!(syntax & RE_CONTEXT_INDEP_ANCHORS) &&
+ re_string_cur_idx (input) != 0)
+ {
+ char prev = re_string_peek_byte (input, -1);
+ if (prev != '|' && prev != '(' &&
+ (!(syntax & RE_NEWLINE_ALT) || prev != '\n'))
+ break;
+ }
+ token->type = ANCHOR;
+ token->opr.idx = LINE_FIRST;
+ break;
+ case '$':
+ if (!(syntax & RE_CONTEXT_INDEP_ANCHORS) &&
+ re_string_cur_idx (input) + 1 != re_string_length (input))
+ {
+ re_token_t next;
+ re_string_skip_bytes (input, 1);
+ peek_token (&next, input, syntax);
+ re_string_skip_bytes (input, -1);
+ if (next.type != OP_ALT && next.type != OP_CLOSE_SUBEXP)
+ break;
+ }
+ token->type = ANCHOR;
+ token->opr.idx = LINE_LAST;
+ break;
+ default:
+ break;
+ }
+ return 1;
+}
+
+/* Peek a token from INPUT, and return the length of the token.
+ We must not use this function out of bracket expressions. */
+
+static int
+peek_token_bracket (token, input, syntax)
+ re_token_t *token;
+ re_string_t *input;
+ reg_syntax_t syntax;
+{
+ unsigned char c;
+ if (re_string_eoi (input))
+ {
+ token->type = END_OF_RE;
+ return 0;
+ }
+ c = re_string_peek_byte (input, 0);
+ token->opr.c = c;
+
+#ifdef RE_ENABLE_I18N
+ if (MB_CUR_MAX > 1 &&
+ !re_string_first_byte (input, re_string_cur_idx (input)))
+ {
+ token->type = CHARACTER;
+ return 1;
+ }
+#endif /* RE_ENABLE_I18N */
+
+ if (c == '\\' && (syntax & RE_BACKSLASH_ESCAPE_IN_LISTS))
+ {
+ /* In this case, '\' escape a character. */
+ unsigned char c2;
+ c2 = re_string_peek_byte (input, 1);
+ token->opr.c = c2;
+ token->type = CHARACTER;
+ return 1;
+ }
+ if (c == '[') /* '[' is a special char in a bracket exps. */
+ {
+ unsigned char c2;
+ int token_len;
+ c2 = re_string_peek_byte (input, 1);
+ token->opr.c = c2;
+ token_len = 2;
+ switch (c2)
+ {
+ case '.':
+ token->type = OP_OPEN_COLL_ELEM;
+ break;
+ case '=':
+ token->type = OP_OPEN_EQUIV_CLASS;
+ break;
+ case ':':
+ if (syntax & RE_CHAR_CLASSES)
+ {
+ token->type = OP_OPEN_CHAR_CLASS;
+ break;
+ }
+ /* else fall through. */
+ default:
+ token->type = CHARACTER;
+ token->opr.c = c;
+ token_len = 1;
+ break;
+ }
+ return token_len;
+ }
+ switch (c)
+ {
+ case '-':
+ token->type = OP_CHARSET_RANGE;
+ break;
+ case ']':
+ token->type = OP_CLOSE_BRACKET;
+ break;
+ case '^':
+ token->type = OP_NON_MATCH_LIST;
+ break;
+ default:
+ token->type = CHARACTER;
+ }
+ return 1;
+}
+
+/* Functions for parser. */
+
+/* Entry point of the parser.
+ Parse the regular expression REGEXP and return the structure tree.
+ If an error is occured, ERR is set by error code, and return NULL.
+ This function build the following tree, from regular expression <reg_exp>:
+ CAT
+ / \
+ / \
+ <reg_exp> EOR
+
+ CAT means concatenation.
+ EOR means end of regular expression. */
+
+static bin_tree_t *
+parse (regexp, preg, syntax, err)
+ re_string_t *regexp;
+ regex_t *preg;
+ reg_syntax_t syntax;
+ reg_errcode_t *err;
+{
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ bin_tree_t *tree, *eor, *root;
+ re_token_t current_token;
+ int new_idx;
+ current_token = fetch_token (regexp, syntax);
+ tree = parse_reg_exp (regexp, preg, &current_token, syntax, 0, err);
+ if (BE (*err != REG_NOERROR && tree == NULL, 0))
+ return NULL;
+ new_idx = re_dfa_add_node (dfa, current_token, 0);
+ eor = create_tree (NULL, NULL, 0, new_idx);
+ if (tree != NULL)
+ root = create_tree (tree, eor, CONCAT, 0);
+ else
+ root = eor;
+ if (BE (new_idx == -1 || eor == NULL || root == NULL, 0))
+ return *err = REG_ESPACE, NULL;
+ return root;
+}
+
+/* This function build the following tree, from regular expression
+ <branch1>|<branch2>:
+ ALT
+ / \
+ / \
+ <branch1> <branch2>
+
+ ALT means alternative, which represents the operator `|'. */
+
+static bin_tree_t *
+parse_reg_exp (regexp, preg, token, syntax, nest, err)
+ re_string_t *regexp;
+ regex_t *preg;
+ re_token_t *token;
+ reg_syntax_t syntax;
+ int nest;
+ reg_errcode_t *err;
+{
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ bin_tree_t *tree, *branch = NULL;
+ int new_idx;
+ tree = parse_branch (regexp, preg, token, syntax, nest, err);
+ if (BE (*err != REG_NOERROR && tree == NULL, 0))
+ return NULL;
+
+ while (token->type == OP_ALT)
+ {
+ re_token_t alt_token = *token;
+ new_idx = re_dfa_add_node (dfa, alt_token, 0);
+ *token = fetch_token (regexp, syntax);
+ if (token->type != OP_ALT && token->type != END_OF_RE
+ && (nest == 0 || token->type != OP_CLOSE_SUBEXP))
+ {
+ branch = parse_branch (regexp, preg, token, syntax, nest, err);
+ if (BE (*err != REG_NOERROR && branch == NULL, 0))
+ {
+ free_bin_tree (tree);
+ return NULL;
+ }
+ }
+ else
+ branch = NULL;
+ tree = create_tree (tree, branch, 0, new_idx);
+ if (BE (new_idx == -1 || tree == NULL, 0))
+ return *err = REG_ESPACE, NULL;
+ dfa->has_plural_match = 1;
+ }
+ return tree;
+}
+
+/* This function build the following tree, from regular expression
+ <exp1><exp2>:
+ CAT
+ / \
+ / \
+ <exp1> <exp2>
+
+ CAT means concatenation. */
+
+static bin_tree_t *
+parse_branch (regexp, preg, token, syntax, nest, err)
+ re_string_t *regexp;
+ regex_t *preg;
+ re_token_t *token;
+ reg_syntax_t syntax;
+ int nest;
+ reg_errcode_t *err;
+{
+ bin_tree_t *tree, *exp;
+ tree = parse_expression (regexp, preg, token, syntax, nest, err);
+ if (BE (*err != REG_NOERROR && tree == NULL, 0))
+ return NULL;
+
+ while (token->type != OP_ALT && token->type != END_OF_RE
+ && (nest == 0 || token->type != OP_CLOSE_SUBEXP))
+ {
+ exp = parse_expression (regexp, preg, token, syntax, nest, err);
+ if (BE (*err != REG_NOERROR && exp == NULL, 0))
+ {
+ free_bin_tree (tree);
+ return NULL;
+ }
+ if (tree != NULL && exp != NULL)
+ {
+ tree = create_tree (tree, exp, CONCAT, 0);
+ if (tree == NULL)
+ return *err = REG_ESPACE, NULL;
+ }
+ else if (tree == NULL)
+ tree = exp;
+ /* Otherwise exp == NULL, we don't need to create new tree. */
+ }
+ return tree;
+}
+
+/* This function build the following tree, from regular expression a*:
+ *
+ |
+ a
+*/
+
+static bin_tree_t *
+parse_expression (regexp, preg, token, syntax, nest, err)
+ re_string_t *regexp;
+ regex_t *preg;
+ re_token_t *token;
+ reg_syntax_t syntax;
+ int nest;
+ reg_errcode_t *err;
+{
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ bin_tree_t *tree;
+ int new_idx;
+ switch (token->type)
+ {
+ case CHARACTER:
+ new_idx = re_dfa_add_node (dfa, *token, 0);
+ tree = create_tree (NULL, NULL, 0, new_idx);
+ if (BE (new_idx == -1 || tree == NULL, 0))
+ return *err = REG_ESPACE, NULL;
+#ifdef RE_ENABLE_I18N
+ if (MB_CUR_MAX > 1)
+ {
+ while (!re_string_eoi (regexp)
+ && !re_string_first_byte (regexp, re_string_cur_idx (regexp)))
+ {
+ bin_tree_t *mbc_remain;
+ *token = fetch_token (regexp, syntax);
+ new_idx = re_dfa_add_node (dfa, *token, 0);
+ mbc_remain = create_tree (NULL, NULL, 0, new_idx);
+ tree = create_tree (tree, mbc_remain, CONCAT, 0);
+ if (BE (new_idx == -1 || mbc_remain == NULL || tree == NULL, 0))
+ return *err = REG_ESPACE, NULL;
+ }
+ }
+#endif
+ break;
+ case OP_OPEN_SUBEXP:
+ tree = parse_sub_exp (regexp, preg, token, syntax, nest + 1, err);
+ if (BE (*err != REG_NOERROR && tree == NULL, 0))
+ return NULL;
+ break;
+ case OP_OPEN_BRACKET:
+ tree = parse_bracket_exp (regexp, dfa, token, syntax, err);
+ if (BE (*err != REG_NOERROR && tree == NULL, 0))
+ return NULL;
+ break;
+ case OP_BACK_REF:
+ if (BE (preg->re_nsub < token->opr.idx
+ || dfa->subexps[token->opr.idx - 1].end == -1, 0))
+ {
+ *err = REG_ESUBREG;
+ return NULL;
+ }
+ new_idx = re_dfa_add_node (dfa, *token, 0);
+ tree = create_tree (NULL, NULL, 0, new_idx);
+ if (BE (new_idx == -1 || tree == NULL, 0))
+ return *err = REG_ESPACE, NULL;
+ ++dfa->nbackref;
+ dfa->has_mb_node = 1;
+ break;
+ case OP_DUP_ASTERISK:
+ case OP_DUP_PLUS:
+ case OP_DUP_QUESTION:
+ case OP_OPEN_DUP_NUM:
+ if (syntax & RE_CONTEXT_INVALID_OPS)
+ return *err = REG_BADRPT, NULL;
+ else if (syntax & RE_CONTEXT_INDEP_OPS)
+ {
+ *token = fetch_token (regexp, syntax);
+ return parse_expression (regexp, preg, token, syntax, nest, err);
+ }
+ /* else fall through */
+ case OP_CLOSE_SUBEXP:
+ if ((token->type == OP_CLOSE_SUBEXP) &&
+ !(syntax & RE_UNMATCHED_RIGHT_PAREN_ORD))
+ return *err = REG_ERPAREN, NULL;
+ /* else fall through */
+ case OP_CLOSE_DUP_NUM:
+ /* We treat it as a normal character. */
+
+ /* Then we can these characters as normal characters. */
+ token->type = CHARACTER;
+ new_idx = re_dfa_add_node (dfa, *token, 0);
+ tree = create_tree (NULL, NULL, 0, new_idx);
+ if (BE (new_idx == -1 || tree == NULL, 0))
+ return *err = REG_ESPACE, NULL;
+ break;
+ case ANCHOR:
+ if (dfa->word_char == NULL)
+ {
+ *err = init_word_char (dfa);
+ if (BE (*err != REG_NOERROR, 0))
+ return NULL;
+ }
+ if (token->opr.ctx_type == WORD_DELIM)
+ {
+ bin_tree_t *tree_first, *tree_last;
+ int idx_first, idx_last;
+ token->opr.ctx_type = WORD_FIRST;
+ idx_first = re_dfa_add_node (dfa, *token, 0);
+ tree_first = create_tree (NULL, NULL, 0, idx_first);
+ token->opr.ctx_type = WORD_LAST;
+ idx_last = re_dfa_add_node (dfa, *token, 0);
+ tree_last = create_tree (NULL, NULL, 0, idx_last);
+ token->type = OP_ALT;
+ new_idx = re_dfa_add_node (dfa, *token, 0);
+ tree = create_tree (tree_first, tree_last, 0, new_idx);
+ if (BE (idx_first == -1 || idx_last == -1 || new_idx == -1
+ || tree_first == NULL || tree_last == NULL
+ || tree == NULL, 0))
+ return *err = REG_ESPACE, NULL;
+ }
+ else
+ {
+ new_idx = re_dfa_add_node (dfa, *token, 0);
+ tree = create_tree (NULL, NULL, 0, new_idx);
+ if (BE (new_idx == -1 || tree == NULL, 0))
+ return *err = REG_ESPACE, NULL;
+ }
+ /* We must return here, since ANCHORs can't be followed
+ by repetition operators.
+ eg. RE"^*" is invalid or "<ANCHOR(^)><CHAR(*)>",
+ it must not be "<ANCHOR(^)><REPEAT(*)>". */
+ *token = fetch_token (regexp, syntax);
+ return tree;
+ case OP_PERIOD:
+ new_idx = re_dfa_add_node (dfa, *token, 0);
+ tree = create_tree (NULL, NULL, 0, new_idx);
+ if (BE (new_idx == -1 || tree == NULL, 0))
+ return *err = REG_ESPACE, NULL;
+ if (MB_CUR_MAX > 1)
+ dfa->has_mb_node = 1;
+ break;
+ case OP_WORD:
+ tree = build_word_op (dfa, 0, err);
+ if (BE (*err != REG_NOERROR && tree == NULL, 0))
+ return NULL;
+ break;
+ case OP_NOTWORD:
+ tree = build_word_op (dfa, 1, err);
+ if (BE (*err != REG_NOERROR && tree == NULL, 0))
+ return NULL;
+ break;
+ case OP_ALT:
+ case END_OF_RE:
+ return NULL;
+ case BACK_SLASH:
+ *err = REG_EESCAPE;
+ return NULL;
+ default:
+ /* Must not happen? */
+#ifdef DEBUG
+ assert (0);
+#endif
+ return NULL;
+ }
+ *token = fetch_token (regexp, syntax);
+
+ while (token->type == OP_DUP_ASTERISK || token->type == OP_DUP_PLUS
+ || token->type == OP_DUP_QUESTION || token->type == OP_OPEN_DUP_NUM)
+ {
+ tree = parse_dup_op (tree, regexp, dfa, token, syntax, err);
+ if (BE (*err != REG_NOERROR && tree == NULL, 0))
+ return NULL;
+ dfa->has_plural_match = 1;
+ }
+
+ return tree;
+}
+
+/* This function build the following tree, from regular expression
+ (<reg_exp>):
+ SUBEXP
+ |
+ <reg_exp>
+*/
+
+static bin_tree_t *
+parse_sub_exp (regexp, preg, token, syntax, nest, err)
+ re_string_t *regexp;
+ regex_t *preg;
+ re_token_t *token;
+ reg_syntax_t syntax;
+ int nest;
+ reg_errcode_t *err;
+{
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ bin_tree_t *tree, *left_par, *right_par;
+ size_t cur_nsub;
+ int new_idx;
+ cur_nsub = preg->re_nsub++;
+ if (dfa->subexps_alloc < preg->re_nsub)
+ {
+ re_subexp_t *new_array;
+ dfa->subexps_alloc *= 2;
+ new_array = re_realloc (dfa->subexps, re_subexp_t, dfa->subexps_alloc);
+ if (BE (new_array == NULL, 0))
+ {
+ dfa->subexps_alloc /= 2;
+ *err = REG_ESPACE;
+ return NULL;
+ }
+ dfa->subexps = new_array;
+ }
+ dfa->subexps[cur_nsub].start = dfa->nodes_len;
+ dfa->subexps[cur_nsub].end = -1;
+
+ new_idx = re_dfa_add_node (dfa, *token, 0);
+ left_par = create_tree (NULL, NULL, 0, new_idx);
+ if (BE (new_idx == -1 || left_par == NULL, 0))
+ return *err = REG_ESPACE, NULL;
+ dfa->nodes[new_idx].opr.idx = cur_nsub;
+ *token = fetch_token (regexp, syntax);
+
+ /* The subexpression may be a null string. */
+ if (token->type == OP_CLOSE_SUBEXP)
+ tree = NULL;
+ else
+ {
+ tree = parse_reg_exp (regexp, preg, token, syntax, nest, err);
+ if (BE (*err != REG_NOERROR && tree == NULL, 0))
+ return NULL;
+ }
+ if (BE (token->type != OP_CLOSE_SUBEXP, 0))
+ {
+ free_bin_tree (tree);
+ *err = REG_BADPAT;
+ return NULL;
+ }
+ new_idx = re_dfa_add_node (dfa, *token, 0);
+ dfa->subexps[cur_nsub].end = dfa->nodes_len;
+ right_par = create_tree (NULL, NULL, 0, new_idx);
+ tree = ((tree == NULL) ? right_par
+ : create_tree (tree, right_par, CONCAT, 0));
+ tree = create_tree (left_par, tree, CONCAT, 0);
+ if (BE (new_idx == -1 || right_par == NULL || tree == NULL, 0))
+ return *err = REG_ESPACE, NULL;
+ dfa->nodes[new_idx].opr.idx = cur_nsub;
+
+ return tree;
+}
+
+/* This function parse repetition operators like "*", "+", "{1,3}" etc. */
+
+static bin_tree_t *
+parse_dup_op (dup_elem, regexp, dfa, token, syntax, err)
+ bin_tree_t *dup_elem;
+ re_string_t *regexp;
+ re_dfa_t *dfa;
+ re_token_t *token;
+ reg_syntax_t syntax;
+ reg_errcode_t *err;
+{
+ re_token_t dup_token;
+ bin_tree_t *tree = dup_elem, *work_tree;
+ int new_idx, start_idx = re_string_cur_idx (regexp);
+ re_token_t start_token = *token;
+ if (token->type == OP_OPEN_DUP_NUM)
+ {
+ int i;
+ int end = 0;
+ int start = fetch_number (regexp, token, syntax);
+ bin_tree_t *elem;
+ if (start == -1)
+ {
+ if (token->type == CHARACTER && token->opr.c == ',')
+ start = 0; /* We treat "{,m}" as "{0,m}". */
+ else
+ {
+ *err = REG_BADBR; /* <re>{} is invalid. */
+ return NULL;
+ }
+ }
+ if (BE (start != -2, 1))
+ {
+ /* We treat "{n}" as "{n,n}". */
+ end = ((token->type == OP_CLOSE_DUP_NUM) ? start
+ : ((token->type == CHARACTER && token->opr.c == ',')
+ ? fetch_number (regexp, token, syntax) : -2));
+ }
+ if (BE (start == -2 || end == -2, 0))
+ {
+ /* Invalid sequence. */
+ if (token->type == OP_CLOSE_DUP_NUM)
+ goto parse_dup_op_invalid_interval;
+ else
+ goto parse_dup_op_ebrace;
+ }
+ if (BE (start == 0 && end == 0, 0))
+ {
+ /* We treat "<re>{0}" and "<re>{0,0}" as null string. */
+ *token = fetch_token (regexp, syntax);
+ free_bin_tree (dup_elem);
+ return NULL;
+ }
+
+ /* Extract "<re>{n,m}" to "<re><re>...<re><re>{0,<m-n>}". */
+ elem = tree;
+ for (i = 0; i < start; ++i)
+ if (i != 0)
+ {
+ work_tree = duplicate_tree (elem, dfa);
+ tree = create_tree (tree, work_tree, CONCAT, 0);
+ if (BE (work_tree == NULL || tree == NULL, 0))
+ goto parse_dup_op_espace;
+ }
+
+ if (end == -1)
+ {
+ /* We treat "<re>{0,}" as "<re>*". */
+ dup_token.type = OP_DUP_ASTERISK;
+ if (start > 0)
+ {
+ elem = duplicate_tree (elem, dfa);
+ new_idx = re_dfa_add_node (dfa, dup_token, 0);
+ work_tree = create_tree (elem, NULL, 0, new_idx);
+ tree = create_tree (tree, work_tree, CONCAT, 0);
+ if (BE (elem == NULL || new_idx == -1 || work_tree == NULL
+ || tree == NULL, 0))
+ goto parse_dup_op_espace;
+ }
+ else
+ {
+ new_idx = re_dfa_add_node (dfa, dup_token, 0);
+ tree = create_tree (elem, NULL, 0, new_idx);
+ if (BE (new_idx == -1 || tree == NULL, 0))
+ goto parse_dup_op_espace;
+ }
+ }
+ else if (end - start > 0)
+ {
+ /* Then extract "<re>{0,m}" to "<re>?<re>?...<re>?". */
+ dup_token.type = OP_DUP_QUESTION;
+ if (start > 0)
+ {
+ elem = duplicate_tree (elem, dfa);
+ new_idx = re_dfa_add_node (dfa, dup_token, 0);
+ elem = create_tree (elem, NULL, 0, new_idx);
+ tree = create_tree (tree, elem, CONCAT, 0);
+ if (BE (elem == NULL || new_idx == -1 || tree == NULL, 0))
+ goto parse_dup_op_espace;
+ }
+ else
+ {
+ new_idx = re_dfa_add_node (dfa, dup_token, 0);
+ tree = elem = create_tree (elem, NULL, 0, new_idx);
+ if (BE (new_idx == -1 || tree == NULL, 0))
+ goto parse_dup_op_espace;
+ }
+ for (i = 1; i < end - start; ++i)
+ {
+ work_tree = duplicate_tree (elem, dfa);
+ tree = create_tree (tree, work_tree, CONCAT, 0);
+ if (BE (work_tree == NULL || tree == NULL, 0))
+ return *err = REG_ESPACE, NULL;
+ }
+ }
+ }
+ else
+ {
+ new_idx = re_dfa_add_node (dfa, *token, 0);
+ tree = create_tree (tree, NULL, 0, new_idx);
+ if (BE (new_idx == -1 || tree == NULL, 0))
+ return *err = REG_ESPACE, NULL;
+ }
+ *token = fetch_token (regexp, syntax);
+ return tree;
+
+ parse_dup_op_espace:
+ free_bin_tree (tree);
+ *err = REG_ESPACE;
+ return NULL;
+
+ parse_dup_op_ebrace:
+ if (BE (!(syntax & RE_INVALID_INTERVAL_ORD), 0))
+ {
+ *err = REG_EBRACE;
+ return NULL;
+ }
+ goto parse_dup_op_rollback;
+ parse_dup_op_invalid_interval:
+ if (BE (!(syntax & RE_INVALID_INTERVAL_ORD), 0))
+ {
+ *err = REG_BADBR;
+ return NULL;
+ }
+ parse_dup_op_rollback:
+ re_string_set_index (regexp, start_idx);
+ *token = start_token;
+ token->type = CHARACTER;
+ return dup_elem;
+}
+
+/* Size of the names for collating symbol/equivalence_class/character_class.
+ I'm not sure, but maybe enough. */
+#define BRACKET_NAME_BUF_SIZE 32
+
+#ifndef _LIBC
+ /* Local function for parse_bracket_exp only used in case of NOT _LIBC.
+ Build the range expression which starts from START_ELEM, and ends
+ at END_ELEM. The result are written to MBCSET and SBCSET.
+ RANGE_ALLOC is the allocated size of mbcset->range_starts, and
+ mbcset->range_ends, is a pointer argument sinse we may
+ update it. */
+
+static reg_errcode_t
+# ifdef RE_ENABLE_I18N
+build_range_exp (sbcset, mbcset, range_alloc, start_elem, end_elem)
+ re_charset_t *mbcset;
+ int *range_alloc;
+# else /* not RE_ENABLE_I18N */
+build_range_exp (sbcset, start_elem, end_elem)
+# endif /* not RE_ENABLE_I18N */
+ re_bitset_ptr_t sbcset;
+ bracket_elem_t *start_elem, *end_elem;
+{
+ unsigned int start_ch, end_ch;
+ /* Equivalence Classes and Character Classes can't be a range start/end. */
+ if (BE (start_elem->type == EQUIV_CLASS || start_elem->type == CHAR_CLASS
+ || end_elem->type == EQUIV_CLASS || end_elem->type == CHAR_CLASS,
+ 0))
+ return REG_ERANGE;
+
+ /* We can handle no multi character collating elements without libc
+ support. */
+ if (BE ((start_elem->type == COLL_SYM
+ && strlen ((char *) start_elem->opr.name) > 1)
+ || (end_elem->type == COLL_SYM
+ && strlen ((char *) end_elem->opr.name) > 1), 0))
+ return REG_ECOLLATE;
+
+# ifdef RE_ENABLE_I18N
+ {
+ wchar_t wc, start_wc, end_wc;
+ wchar_t cmp_buf[6] = {L'\0', L'\0', L'\0', L'\0', L'\0', L'\0'};
+
+ start_ch = ((start_elem->type == SB_CHAR) ? start_elem->opr.ch
+ : ((start_elem->type == COLL_SYM) ? start_elem->opr.name[0]
+ : 0));
+ end_ch = ((end_elem->type == SB_CHAR) ? end_elem->opr.ch
+ : ((end_elem->type == COLL_SYM) ? end_elem->opr.name[0]
+ : 0));
+ start_wc = ((start_elem->type == SB_CHAR || start_elem->type == COLL_SYM)
+ ? __btowc (start_ch) : start_elem->opr.wch);
+ end_wc = ((end_elem->type == SB_CHAR || end_elem->type == COLL_SYM)
+ ? __btowc (end_ch) : end_elem->opr.wch);
+ cmp_buf[0] = start_wc;
+ cmp_buf[4] = end_wc;
+ if (wcscoll (cmp_buf, cmp_buf + 4) > 0)
+ return REG_ERANGE;
+
+ /* Check the space of the arrays. */
+ if (*range_alloc == mbcset->nranges)
+ {
+ /* There are not enough space, need realloc. */
+ wchar_t *new_array_start, *new_array_end;
+ int new_nranges;
+
+ /* +1 in case of mbcset->nranges is 0. */
+ new_nranges = 2 * mbcset->nranges + 1;
+ /* Use realloc since mbcset->range_starts and mbcset->range_ends
+ are NULL if *range_alloc == 0. */
+ new_array_start = re_realloc (mbcset->range_starts, wchar_t,
+ new_nranges);
+ new_array_end = re_realloc (mbcset->range_ends, wchar_t,
+ new_nranges);
+
+ if (BE (new_array_start == NULL || new_array_end == NULL, 0))
+ return REG_ESPACE;
+
+ mbcset->range_starts = new_array_start;
+ mbcset->range_ends = new_array_end;
+ *range_alloc = new_nranges;
+ }
+
+ mbcset->range_starts[mbcset->nranges] = start_wc;
+ mbcset->range_ends[mbcset->nranges++] = end_wc;
+
+ /* Build the table for single byte characters. */
+ for (wc = 0; wc <= SBC_MAX; ++wc)
+ {
+ cmp_buf[2] = wc;
+ if (wcscoll (cmp_buf, cmp_buf + 2) <= 0
+ && wcscoll (cmp_buf + 2, cmp_buf + 4) <= 0)
+ bitset_set (sbcset, wc);
+ }
+ }
+# else /* not RE_ENABLE_I18N */
+ {
+ unsigned int ch;
+ start_ch = ((start_elem->type == SB_CHAR ) ? start_elem->opr.ch
+ : ((start_elem->type == COLL_SYM) ? start_elem->opr.name[0]
+ : 0));
+ end_ch = ((end_elem->type == SB_CHAR ) ? end_elem->opr.ch
+ : ((end_elem->type == COLL_SYM) ? end_elem->opr.name[0]
+ : 0));
+ if (start_ch > end_ch)
+ return REG_ERANGE;
+ /* Build the table for single byte characters. */
+ for (ch = 0; ch <= SBC_MAX; ++ch)
+ if (start_ch <= ch && ch <= end_ch)
+ bitset_set (sbcset, ch);
+ }
+# endif /* not RE_ENABLE_I18N */
+ return REG_NOERROR;
+}
+#endif /* not _LIBC */
+
+#ifndef _LIBC
+/* Helper function for parse_bracket_exp only used in case of NOT _LIBC..
+ Build the collating element which is represented by NAME.
+ The result are written to MBCSET and SBCSET.
+ COLL_SYM_ALLOC is the allocated size of mbcset->coll_sym, is a
+ pointer argument since we may update it. */
+
+static reg_errcode_t
+# ifdef RE_ENABLE_I18N
+build_collating_symbol (sbcset, mbcset, coll_syxmalloc, name)
+ re_charset_t *mbcset;
+ int *coll_syxmalloc;
+# else /* not RE_ENABLE_I18N */
+build_collating_symbol (sbcset, name)
+# endif /* not RE_ENABLE_I18N */
+ re_bitset_ptr_t sbcset;
+ const unsigned char *name;
+{
+ size_t name_len = strlen ((const char *) name);
+ if (BE (name_len != 1, 0))
+ return REG_ECOLLATE;
+ else
+ {
+ bitset_set (sbcset, name[0]);
+ return REG_NOERROR;
+ }
+}
+#endif /* not _LIBC */
+
+/* This function parse bracket expression like "[abc]", "[a-c]",
+ "[[.a-a.]]" etc. */
+
+static bin_tree_t *
+parse_bracket_exp (regexp, dfa, token, syntax, err)
+ re_string_t *regexp;
+ re_dfa_t *dfa;
+ re_token_t *token;
+ reg_syntax_t syntax;
+ reg_errcode_t *err;
+{
+#ifdef _LIBC
+ const unsigned char *collseqmb;
+ const char *collseqwc;
+ uint32_t nrules;
+ int32_t table_size;
+ const int32_t *symb_table;
+ const unsigned char *extra;
+
+ /* Local function for parse_bracket_exp used in _LIBC environement.
+ Seek the collating symbol entry correspondings to NAME.
+ Return the index of the symbol in the SYMB_TABLE. */
+
+ static inline int32_t
+ seek_collating_symbol_entry (name, name_len)
+ const unsigned char *name;
+ size_t name_len;
+ {
+ int32_t hash = elem_hash ((const char *) name, name_len);
+ int32_t elem = hash % table_size;
+ int32_t second = hash % (table_size - 2);
+ while (symb_table[2 * elem] != 0)
+ {
+ /* First compare the hashing value. */
+ if (symb_table[2 * elem] == hash
+ /* Compare the length of the name. */
+ && name_len == extra[symb_table[2 * elem + 1]]
+ /* Compare the name. */
+ && memcmp (name, &extra[symb_table[2 * elem + 1] + 1],
+ name_len) == 0)
+ {
+ /* Yep, this is the entry. */
+ break;
+ }
+
+ /* Next entry. */
+ elem += second;
+ }
+ return elem;
+ }
+
+ /* Local function for parse_bracket_exp used in _LIBC environement.
+ Look up the collation sequence value of BR_ELEM.
+ Return the value if succeeded, UINT_MAX otherwise. */
+
+ static inline unsigned int
+ lookup_collation_sequence_value (br_elem)
+ bracket_elem_t *br_elem;
+ {
+ if (br_elem->type == SB_CHAR)
+ {
+ /*
+ if (MB_CUR_MAX == 1)
+ */
+ if (nrules == 0)
+ return collseqmb[br_elem->opr.ch];
+ else
+ {
+ wint_t wc = __btowc (br_elem->opr.ch);
+ return collseq_table_lookup (collseqwc, wc);
+ }
+ }
+ else if (br_elem->type == MB_CHAR)
+ {
+ return collseq_table_lookup (collseqwc, br_elem->opr.wch);
+ }
+ else if (br_elem->type == COLL_SYM)
+ {
+ size_t sym_name_len = strlen ((char *) br_elem->opr.name);
+ if (nrules != 0)
+ {
+ int32_t elem, idx;
+ elem = seek_collating_symbol_entry (br_elem->opr.name,
+ sym_name_len);
+ if (symb_table[2 * elem] != 0)
+ {
+ /* We found the entry. */
+ idx = symb_table[2 * elem + 1];
+ /* Skip the name of collating element name. */
+ idx += 1 + extra[idx];
+ /* Skip the byte sequence of the collating element. */
+ idx += 1 + extra[idx];
+ /* Adjust for the alignment. */
+ idx = (idx + 3) & ~3;
+ /* Skip the multibyte collation sequence value. */
+ idx += sizeof (unsigned int);
+ /* Skip the wide char sequence of the collating element. */
+ idx += sizeof (unsigned int) *
+ (1 + *(unsigned int *) (extra + idx));
+ /* Return the collation sequence value. */
+ return *(unsigned int *) (extra + idx);
+ }
+ else if (symb_table[2 * elem] == 0 && sym_name_len == 1)
+ {
+ /* No valid character. Match it as a single byte
+ character. */
+ return collseqmb[br_elem->opr.name[0]];
+ }
+ }
+ else if (sym_name_len == 1)
+ return collseqmb[br_elem->opr.name[0]];
+ }
+ return UINT_MAX;
+ }
+
+ /* Local function for parse_bracket_exp used in _LIBC environement.
+ Build the range expression which starts from START_ELEM, and ends
+ at END_ELEM. The result are written to MBCSET and SBCSET.
+ RANGE_ALLOC is the allocated size of mbcset->range_starts, and
+ mbcset->range_ends, is a pointer argument sinse we may
+ update it. */
+
+ static inline reg_errcode_t
+# ifdef RE_ENABLE_I18N
+ build_range_exp (sbcset, mbcset, range_alloc, start_elem, end_elem)
+ re_charset_t *mbcset;
+ int *range_alloc;
+# else /* not RE_ENABLE_I18N */
+ build_range_exp (sbcset, start_elem, end_elem)
+# endif /* not RE_ENABLE_I18N */
+ re_bitset_ptr_t sbcset;
+ bracket_elem_t *start_elem, *end_elem;
+ {
+ unsigned int ch;
+ uint32_t start_collseq;
+ uint32_t end_collseq;
+
+# ifdef RE_ENABLE_I18N
+ /* Check the space of the arrays. */
+ if (*range_alloc == mbcset->nranges)
+ {
+ /* There are not enough space, need realloc. */
+ uint32_t *new_array_start;
+ uint32_t *new_array_end;
+ int new_nranges;
+
+ /* +1 in case of mbcset->nranges is 0. */
+ new_nranges = 2 * mbcset->nranges + 1;
+ /* Use realloc since mbcset->range_starts and mbcset->range_ends
+ are NULL if *range_alloc == 0. */
+ new_array_start = re_realloc (mbcset->range_starts, uint32_t,
+ new_nranges);
+ new_array_end = re_realloc (mbcset->range_ends, uint32_t,
+ new_nranges);
+
+ if (BE (new_array_start == NULL || new_array_end == NULL, 0))
+ return REG_ESPACE;
+
+ mbcset->range_starts = new_array_start;
+ mbcset->range_ends = new_array_end;
+ *range_alloc = new_nranges;
+ }
+# endif /* RE_ENABLE_I18N */
+
+ /* Equivalence Classes and Character Classes can't be a range
+ start/end. */
+ if (BE (start_elem->type == EQUIV_CLASS || start_elem->type == CHAR_CLASS
+ || end_elem->type == EQUIV_CLASS || end_elem->type == CHAR_CLASS,
+ 0))
+ return REG_ERANGE;
+
+ start_collseq = lookup_collation_sequence_value (start_elem);
+ end_collseq = lookup_collation_sequence_value (end_elem);
+ /* Check start/end collation sequence values. */
+ if (BE (start_collseq == UINT_MAX || end_collseq == UINT_MAX, 0))
+ return REG_ECOLLATE;
+ if (BE ((syntax & RE_NO_EMPTY_RANGES) && start_collseq > end_collseq, 0))
+ return REG_ERANGE;
+
+# ifdef RE_ENABLE_I18N
+ /* Got valid collation sequence values, add them as a new entry. */
+ mbcset->range_starts[mbcset->nranges] = start_collseq;
+ mbcset->range_ends[mbcset->nranges++] = end_collseq;
+# endif /* RE_ENABLE_I18N */
+
+ /* Build the table for single byte characters. */
+ for (ch = 0; ch <= SBC_MAX; ch++)
+ {
+ uint32_t ch_collseq;
+ /*
+ if (MB_CUR_MAX == 1)
+ */
+ if (nrules == 0)
+ ch_collseq = collseqmb[ch];
+ else
+ ch_collseq = collseq_table_lookup (collseqwc, __btowc (ch));
+ if (start_collseq <= ch_collseq && ch_collseq <= end_collseq)
+ bitset_set (sbcset, ch);
+ }
+ return REG_NOERROR;
+ }
+
+ /* Local function for parse_bracket_exp used in _LIBC environement.
+ Build the collating element which is represented by NAME.
+ The result are written to MBCSET and SBCSET.
+ COLL_SYM_ALLOC is the allocated size of mbcset->coll_sym, is a
+ pointer argument sinse we may update it. */
+
+ static inline reg_errcode_t
+# ifdef RE_ENABLE_I18N
+ build_collating_symbol (sbcset, mbcset, coll_syxmalloc, name)
+ re_charset_t *mbcset;
+ int *coll_syxmalloc;
+# else /* not RE_ENABLE_I18N */
+ build_collating_symbol (sbcset, name)
+# endif /* not RE_ENABLE_I18N */
+ re_bitset_ptr_t sbcset;
+ const unsigned char *name;
+ {
+ int32_t elem, idx;
+ size_t name_len = strlen ((const char *) name);
+ if (nrules != 0)
+ {
+ elem = seek_collating_symbol_entry (name, name_len);
+ if (symb_table[2 * elem] != 0)
+ {
+ /* We found the entry. */
+ idx = symb_table[2 * elem + 1];
+ /* Skip the name of collating element name. */
+ idx += 1 + extra[idx];
+ }
+ else if (symb_table[2 * elem] == 0 && name_len == 1)
+ {
+ /* No valid character, treat it as a normal
+ character. */
+ bitset_set (sbcset, name[0]);
+ return REG_NOERROR;
+ }
+ else
+ return REG_ECOLLATE;
+
+# ifdef RE_ENABLE_I18N
+ /* Got valid collation sequence, add it as a new entry. */
+ /* Check the space of the arrays. */
+ if (*coll_syxmalloc == mbcset->ncoll_syms)
+ {
+ /* Not enough, realloc it. */
+ /* +1 in case of mbcset->ncoll_syms is 0. */
+ *coll_syxmalloc = 2 * mbcset->ncoll_syms + 1;
+ /* Use realloc since mbcset->coll_syms is NULL
+ if *alloc == 0. */
+ mbcset->coll_syms = re_realloc (mbcset->coll_syms, int32_t,
+ *coll_syxmalloc);
+ if (BE (mbcset->coll_syms == NULL, 0))
+ return REG_ESPACE;
+ }
+ mbcset->coll_syms[mbcset->ncoll_syms++] = idx;
+# endif /* RE_ENABLE_I18N */
+ return REG_NOERROR;
+ }
+ else
+ {
+ if (BE (name_len != 1, 0))
+ return REG_ECOLLATE;
+ else
+ {
+ bitset_set (sbcset, name[0]);
+ return REG_NOERROR;
+ }
+ }
+ }
+#endif
+
+ re_token_t br_token;
+ re_bitset_ptr_t sbcset;
+#ifdef RE_ENABLE_I18N
+ re_charset_t *mbcset;
+ int coll_syxmalloc = 0, range_alloc = 0, mbchar_alloc = 0;
+ int equiv_class_alloc = 0, char_class_alloc = 0;
+#else /* not RE_ENABLE_I18N */
+ int non_match = 0;
+#endif /* not RE_ENABLE_I18N */
+ bin_tree_t *work_tree;
+ int token_len, new_idx;
+#ifdef _LIBC
+ collseqmb = (const unsigned char *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQMB);
+ nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
+ if (nrules)
+ {
+ /*
+ if (MB_CUR_MAX > 1)
+ */
+ collseqwc = _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQWC);
+ table_size = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_SYMB_HASH_SIZEMB);
+ symb_table = (const int32_t *) _NL_CURRENT (LC_COLLATE,
+ _NL_COLLATE_SYMB_TABLEMB);
+ extra = (const unsigned char *) _NL_CURRENT (LC_COLLATE,
+ _NL_COLLATE_SYMB_EXTRAMB);
+ }
+#endif
+ sbcset = (re_bitset_ptr_t) calloc (sizeof (unsigned int), BITSET_UINTS);
+#ifdef RE_ENABLE_I18N
+ mbcset = (re_charset_t *) calloc (sizeof (re_charset_t), 1);
+#endif /* RE_ENABLE_I18N */
+#ifdef RE_ENABLE_I18N
+ if (BE (sbcset == NULL || mbcset == NULL, 0))
+#else
+ if (BE (sbcset == NULL, 0))
+#endif /* RE_ENABLE_I18N */
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+
+ token_len = peek_token_bracket (token, regexp, syntax);
+ if (BE (token->type == END_OF_RE, 0))
+ {
+ *err = REG_BADPAT;
+ goto parse_bracket_exp_free_return;
+ }
+ if (token->type == OP_NON_MATCH_LIST)
+ {
+#ifdef RE_ENABLE_I18N
+ int i;
+ mbcset->non_match = 1;
+#else /* not RE_ENABLE_I18N */
+ non_match = 1;
+#endif /* not RE_ENABLE_I18N */
+ if (syntax & RE_HAT_LISTS_NOT_NEWLINE)
+ bitset_set (sbcset, '\0');
+ re_string_skip_bytes (regexp, token_len); /* Skip a token. */
+ token_len = peek_token_bracket (token, regexp, syntax);
+ if (BE (token->type == END_OF_RE, 0))
+ {
+ *err = REG_BADPAT;
+ goto parse_bracket_exp_free_return;
+ }
+#ifdef RE_ENABLE_I18N
+ if (MB_CUR_MAX > 1)
+ for (i = 0; i < SBC_MAX; ++i)
+ if (__btowc (i) == WEOF)
+ bitset_set (sbcset, i);
+#endif /* RE_ENABLE_I18N */
+ }
+
+ /* We treat the first ']' as a normal character. */
+ if (token->type == OP_CLOSE_BRACKET)
+ token->type = CHARACTER;
+
+ while (1)
+ {
+ bracket_elem_t start_elem, end_elem;
+ unsigned char start_name_buf[BRACKET_NAME_BUF_SIZE];
+ unsigned char end_name_buf[BRACKET_NAME_BUF_SIZE];
+ reg_errcode_t ret;
+ int token_len2 = 0, is_range_exp = 0;
+ re_token_t token2;
+
+ start_elem.opr.name = start_name_buf;
+ ret = parse_bracket_element (&start_elem, regexp, token, token_len, dfa,
+ syntax);
+ if (BE (ret != REG_NOERROR, 0))
+ {
+ *err = ret;
+ goto parse_bracket_exp_free_return;
+ }
+
+ token_len = peek_token_bracket (token, regexp, syntax);
+ if (BE (token->type == END_OF_RE, 0))
+ {
+ *err = REG_BADPAT;
+ goto parse_bracket_exp_free_return;
+ }
+ if (token->type == OP_CHARSET_RANGE)
+ {
+ re_string_skip_bytes (regexp, token_len); /* Skip '-'. */
+ token_len2 = peek_token_bracket (&token2, regexp, syntax);
+ if (BE (token->type == END_OF_RE, 0))
+ {
+ *err = REG_BADPAT;
+ goto parse_bracket_exp_free_return;
+ }
+ if (token2.type == OP_CLOSE_BRACKET)
+ {
+ /* We treat the last '-' as a normal character. */
+ re_string_skip_bytes (regexp, -token_len);
+ token->type = CHARACTER;
+ }
+ else
+ is_range_exp = 1;
+ }
+
+ if (is_range_exp == 1)
+ {
+ end_elem.opr.name = end_name_buf;
+ ret = parse_bracket_element (&end_elem, regexp, &token2, token_len2,
+ dfa, syntax);
+ if (BE (ret != REG_NOERROR, 0))
+ {
+ *err = ret;
+ goto parse_bracket_exp_free_return;
+ }
+
+ token_len = peek_token_bracket (token, regexp, syntax);
+ if (BE (token->type == END_OF_RE, 0))
+ {
+ *err = REG_BADPAT;
+ goto parse_bracket_exp_free_return;
+ }
+ *err = build_range_exp (sbcset,
+#ifdef RE_ENABLE_I18N
+ mbcset, &range_alloc,
+#endif /* RE_ENABLE_I18N */
+ &start_elem, &end_elem);
+ if (BE (*err != REG_NOERROR, 0))
+ goto parse_bracket_exp_free_return;
+ }
+ else
+ {
+ switch (start_elem.type)
+ {
+ case SB_CHAR:
+ bitset_set (sbcset, start_elem.opr.ch);
+ break;
+#ifdef RE_ENABLE_I18N
+ case MB_CHAR:
+ /* Check whether the array has enough space. */
+ if (mbchar_alloc == mbcset->nmbchars)
+ {
+ /* Not enough, realloc it. */
+ /* +1 in case of mbcset->nmbchars is 0. */
+ mbchar_alloc = 2 * mbcset->nmbchars + 1;
+ /* Use realloc since array is NULL if *alloc == 0. */
+ mbcset->mbchars = re_realloc (mbcset->mbchars, wchar_t,
+ mbchar_alloc);
+ if (BE (mbcset->mbchars == NULL, 0))
+ goto parse_bracket_exp_espace;
+ }
+ mbcset->mbchars[mbcset->nmbchars++] = start_elem.opr.wch;
+ break;
+#endif /* RE_ENABLE_I18N */
+ case EQUIV_CLASS:
+ *err = build_equiv_class (sbcset,
+#ifdef RE_ENABLE_I18N
+ mbcset, &equiv_class_alloc,
+#endif /* RE_ENABLE_I18N */
+ start_elem.opr.name);
+ if (BE (*err != REG_NOERROR, 0))
+ goto parse_bracket_exp_free_return;
+ break;
+ case COLL_SYM:
+ *err = build_collating_symbol (sbcset,
+#ifdef RE_ENABLE_I18N
+ mbcset, &coll_syxmalloc,
+#endif /* RE_ENABLE_I18N */
+ start_elem.opr.name);
+ if (BE (*err != REG_NOERROR, 0))
+ goto parse_bracket_exp_free_return;
+ break;
+ case CHAR_CLASS:
+ ret = build_charclass (sbcset,
+#ifdef RE_ENABLE_I18N
+ mbcset, &char_class_alloc,
+#endif /* RE_ENABLE_I18N */
+ start_elem.opr.name, syntax);
+ if (BE (ret != REG_NOERROR, 0))
+ goto parse_bracket_exp_espace;
+ break;
+ default:
+ assert (0);
+ break;
+ }
+ }
+ if (token->type == OP_CLOSE_BRACKET)
+ break;
+ }
+
+ re_string_skip_bytes (regexp, token_len); /* Skip a token. */
+
+ /* If it is non-matching list. */
+#ifdef RE_ENABLE_I18N
+ if (mbcset->non_match)
+#else /* not RE_ENABLE_I18N */
+ if (non_match)
+#endif /* not RE_ENABLE_I18N */
+ bitset_not (sbcset);
+
+ /* Build a tree for simple bracket. */
+ br_token.type = SIMPLE_BRACKET;
+ br_token.opr.sbcset = sbcset;
+ new_idx = re_dfa_add_node (dfa, br_token, 0);
+ work_tree = create_tree (NULL, NULL, 0, new_idx);
+ if (BE (new_idx == -1 || work_tree == NULL, 0))
+ goto parse_bracket_exp_espace;
+
+#ifdef RE_ENABLE_I18N
+ if (mbcset->nmbchars || mbcset->ncoll_syms || mbcset->nequiv_classes
+ || mbcset->nranges || (MB_CUR_MAX > 1 && (mbcset->nchar_classes
+ || mbcset->non_match)))
+ {
+ re_token_t alt_token;
+ bin_tree_t *mbc_tree;
+ /* Build a tree for complex bracket. */
+ br_token.type = COMPLEX_BRACKET;
+ br_token.opr.mbcset = mbcset;
+ dfa->has_mb_node = 1;
+ new_idx = re_dfa_add_node (dfa, br_token, 0);
+ mbc_tree = create_tree (NULL, NULL, 0, new_idx);
+ if (BE (new_idx == -1 || mbc_tree == NULL, 0))
+ goto parse_bracket_exp_espace;
+ /* Then join them by ALT node. */
+ dfa->has_plural_match = 1;
+ alt_token.type = OP_ALT;
+ new_idx = re_dfa_add_node (dfa, alt_token, 0);
+ work_tree = create_tree (work_tree, mbc_tree, 0, new_idx);
+ if (BE (new_idx != -1 && mbc_tree != NULL, 1))
+ return work_tree;
+ }
+ else
+ {
+ free_charset (mbcset);
+ return work_tree;
+ }
+#else /* not RE_ENABLE_I18N */
+ return work_tree;
+#endif /* not RE_ENABLE_I18N */
+
+ parse_bracket_exp_espace:
+ *err = REG_ESPACE;
+ parse_bracket_exp_free_return:
+ re_free (sbcset);
+#ifdef RE_ENABLE_I18N
+ free_charset (mbcset);
+#endif /* RE_ENABLE_I18N */
+ return NULL;
+}
+
+/* Parse an element in the bracket expression. */
+
+static reg_errcode_t
+parse_bracket_element (elem, regexp, token, token_len, dfa, syntax)
+ bracket_elem_t *elem;
+ re_string_t *regexp;
+ re_token_t *token;
+ int token_len;
+ re_dfa_t *dfa;
+ reg_syntax_t syntax;
+{
+#ifdef RE_ENABLE_I18N
+ int cur_char_size;
+ cur_char_size = re_string_char_size_at (regexp, re_string_cur_idx (regexp));
+ if (cur_char_size > 1)
+ {
+ elem->type = MB_CHAR;
+ elem->opr.wch = re_string_wchar_at (regexp, re_string_cur_idx (regexp));
+ re_string_skip_bytes (regexp, cur_char_size);
+ return REG_NOERROR;
+ }
+#endif /* RE_ENABLE_I18N */
+ re_string_skip_bytes (regexp, token_len); /* Skip a token. */
+ if (token->type == OP_OPEN_COLL_ELEM || token->type == OP_OPEN_CHAR_CLASS
+ || token->type == OP_OPEN_EQUIV_CLASS)
+ return parse_bracket_symbol (elem, regexp, token);
+ elem->type = SB_CHAR;
+ elem->opr.ch = token->opr.c;
+ return REG_NOERROR;
+}
+
+/* Parse a bracket symbol in the bracket expression. Bracket symbols are
+ such as [:<character_class>:], [.<collating_element>.], and
+ [=<equivalent_class>=]. */
+
+static reg_errcode_t
+parse_bracket_symbol (elem, regexp, token)
+ bracket_elem_t *elem;
+ re_string_t *regexp;
+ re_token_t *token;
+{
+ unsigned char ch, delim = token->opr.c;
+ int i = 0;
+ for (;; ++i)
+ {
+ if (re_string_eoi(regexp) || i >= BRACKET_NAME_BUF_SIZE)
+ return REG_EBRACK;
+ if (token->type == OP_OPEN_CHAR_CLASS)
+ ch = re_string_fetch_byte_case (regexp);
+ else
+ ch = re_string_fetch_byte (regexp);
+ if (ch == delim && re_string_peek_byte (regexp, 0) == ']')
+ break;
+ elem->opr.name[i] = ch;
+ }
+ re_string_skip_bytes (regexp, 1);
+ elem->opr.name[i] = '\0';
+ switch (token->type)
+ {
+ case OP_OPEN_COLL_ELEM:
+ elem->type = COLL_SYM;
+ break;
+ case OP_OPEN_EQUIV_CLASS:
+ elem->type = EQUIV_CLASS;
+ break;
+ case OP_OPEN_CHAR_CLASS:
+ elem->type = CHAR_CLASS;
+ break;
+ default:
+ break;
+ }
+ return REG_NOERROR;
+}
+
+ /* Helper function for parse_bracket_exp.
+ Build the equivalence class which is represented by NAME.
+ The result are written to MBCSET and SBCSET.
+ EQUIV_CLASS_ALLOC is the allocated size of mbcset->equiv_classes,
+ is a pointer argument sinse we may update it. */
+
+static reg_errcode_t
+#ifdef RE_ENABLE_I18N
+build_equiv_class (sbcset, mbcset, equiv_class_alloc, name)
+ re_charset_t *mbcset;
+ int *equiv_class_alloc;
+#else /* not RE_ENABLE_I18N */
+build_equiv_class (sbcset, name)
+#endif /* not RE_ENABLE_I18N */
+ re_bitset_ptr_t sbcset;
+ const unsigned char *name;
+{
+#if defined _LIBC && defined RE_ENABLE_I18N
+ uint32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
+ if (nrules != 0)
+ {
+ const int32_t *table, *indirect;
+ const unsigned char *weights, *extra, *cp;
+ unsigned char char_buf[2];
+ int32_t idx1, idx2;
+ unsigned int ch;
+ size_t len;
+ /* This #include defines a local function! */
+# include <locale/weight.h>
+ /* Calculate the index for equivalence class. */
+ cp = name;
+ table = (const int32_t *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB);
+ weights = (const unsigned char *) _NL_CURRENT (LC_COLLATE,
+ _NL_COLLATE_WEIGHTMB);
+ extra = (const unsigned char *) _NL_CURRENT (LC_COLLATE,
+ _NL_COLLATE_EXTRAMB);
+ indirect = (const int32_t *) _NL_CURRENT (LC_COLLATE,
+ _NL_COLLATE_INDIRECTMB);
+ idx1 = findidx (&cp);
+ if (BE (idx1 == 0 || cp < name + strlen ((const char *) name), 0))
+ /* This isn't a valid character. */
+ return REG_ECOLLATE;
+
+ /* Build single byte matcing table for this equivalence class. */
+ char_buf[1] = (unsigned char) '\0';
+ len = weights[idx1];
+ for (ch = 0; ch < SBC_MAX; ++ch)
+ {
+ char_buf[0] = ch;
+ cp = char_buf;
+ idx2 = findidx (&cp);
+/*
+ idx2 = table[ch];
+*/
+ if (idx2 == 0)
+ /* This isn't a valid character. */
+ continue;
+ if (len == weights[idx2])
+ {
+ int cnt = 0;
+ while (cnt <= len &&
+ weights[idx1 + 1 + cnt] == weights[idx2 + 1 + cnt])
+ ++cnt;
+
+ if (cnt > len)
+ bitset_set (sbcset, ch);
+ }
+ }
+ /* Check whether the array has enough space. */
+ if (*equiv_class_alloc == mbcset->nequiv_classes)
+ {
+ /* Not enough, realloc it. */
+ /* +1 in case of mbcset->nequiv_classes is 0. */
+ *equiv_class_alloc = 2 * mbcset->nequiv_classes + 1;
+ /* Use realloc since the array is NULL if *alloc == 0. */
+ mbcset->equiv_classes = re_realloc (mbcset->equiv_classes, int32_t,
+ *equiv_class_alloc);
+ if (BE (mbcset->equiv_classes == NULL, 0))
+ return REG_ESPACE;
+ }
+ mbcset->equiv_classes[mbcset->nequiv_classes++] = idx1;
+ }
+ else
+#endif /* _LIBC && RE_ENABLE_I18N */
+ {
+ if (BE (strlen ((const char *) name) != 1, 0))
+ return REG_ECOLLATE;
+ bitset_set (sbcset, *name);
+ }
+ return REG_NOERROR;
+}
+
+ /* Helper function for parse_bracket_exp.
+ Build the character class which is represented by NAME.
+ The result are written to MBCSET and SBCSET.
+ CHAR_CLASS_ALLOC is the allocated size of mbcset->char_classes,
+ is a pointer argument sinse we may update it. */
+
+static reg_errcode_t
+#ifdef RE_ENABLE_I18N
+build_charclass (sbcset, mbcset, char_class_alloc, class_name, syntax)
+ re_charset_t *mbcset;
+ int *char_class_alloc;
+#else /* not RE_ENABLE_I18N */
+build_charclass (sbcset, class_name, syntax)
+#endif /* not RE_ENABLE_I18N */
+ re_bitset_ptr_t sbcset;
+ const unsigned char *class_name;
+ reg_syntax_t syntax;
+{
+ int i;
+ const char *name = (const char *) class_name;
+
+ /* In case of REG_ICASE "upper" and "lower" match the both of
+ upper and lower cases. */
+ if ((syntax & RE_ICASE)
+ && (strcmp (name, "upper") == 0 || strcmp (name, "lower") == 0))
+ name = "alpha";
+
+#ifdef RE_ENABLE_I18N
+ /* Check the space of the arrays. */
+ if (*char_class_alloc == mbcset->nchar_classes)
+ {
+ /* Not enough, realloc it. */
+ /* +1 in case of mbcset->nchar_classes is 0. */
+ *char_class_alloc = 2 * mbcset->nchar_classes + 1;
+ /* Use realloc since array is NULL if *alloc == 0. */
+ mbcset->char_classes = re_realloc (mbcset->char_classes, wctype_t,
+ *char_class_alloc);
+ if (BE (mbcset->char_classes == NULL, 0))
+ return REG_ESPACE;
+ }
+ mbcset->char_classes[mbcset->nchar_classes++] = __wctype (name);
+#endif /* RE_ENABLE_I18N */
+
+#define BUILD_CHARCLASS_LOOP(ctype_func)\
+ for (i = 0; i < SBC_MAX; ++i) \
+ { \
+ if (ctype_func (i)) \
+ bitset_set (sbcset, i); \
+ }
+
+ if (strcmp (name, "alnum") == 0)
+ BUILD_CHARCLASS_LOOP (isalnum)
+ else if (strcmp (name, "cntrl") == 0)
+ BUILD_CHARCLASS_LOOP (iscntrl)
+ else if (strcmp (name, "lower") == 0)
+ BUILD_CHARCLASS_LOOP (islower)
+ else if (strcmp (name, "space") == 0)
+ BUILD_CHARCLASS_LOOP (isspace)
+ else if (strcmp (name, "alpha") == 0)
+ BUILD_CHARCLASS_LOOP (isalpha)
+ else if (strcmp (name, "digit") == 0)
+ BUILD_CHARCLASS_LOOP (isdigit)
+ else if (strcmp (name, "print") == 0)
+ BUILD_CHARCLASS_LOOP (isprint)
+ else if (strcmp (name, "upper") == 0)
+ BUILD_CHARCLASS_LOOP (isupper)
+ else if (strcmp (name, "blank") == 0)
+ BUILD_CHARCLASS_LOOP (isblank)
+ else if (strcmp (name, "graph") == 0)
+ BUILD_CHARCLASS_LOOP (isgraph)
+ else if (strcmp (name, "punct") == 0)
+ BUILD_CHARCLASS_LOOP (ispunct)
+ else if (strcmp (name, "xdigit") == 0)
+ BUILD_CHARCLASS_LOOP (isxdigit)
+ else
+ return REG_ECTYPE;
+
+ return REG_NOERROR;
+}
+
+static bin_tree_t *
+build_word_op (dfa, not, err)
+ re_dfa_t *dfa;
+ int not;
+ reg_errcode_t *err;
+{
+ re_bitset_ptr_t sbcset;
+#ifdef RE_ENABLE_I18N
+ re_charset_t *mbcset;
+ int alloc = 0;
+#else /* not RE_ENABLE_I18N */
+ int non_match = 0;
+#endif /* not RE_ENABLE_I18N */
+ reg_errcode_t ret;
+ re_token_t br_token;
+ bin_tree_t *tree;
+ int new_idx;
+
+ sbcset = (re_bitset_ptr_t) calloc (sizeof (unsigned int), BITSET_UINTS);
+#ifdef RE_ENABLE_I18N
+ mbcset = (re_charset_t *) calloc (sizeof (re_charset_t), 1);
+#endif /* RE_ENABLE_I18N */
+
+#ifdef RE_ENABLE_I18N
+ if (BE (sbcset == NULL || mbcset == NULL, 0))
+#else /* not RE_ENABLE_I18N */
+ if (BE (sbcset == NULL, 0))
+#endif /* not RE_ENABLE_I18N */
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+
+ if (not)
+ {
+#ifdef RE_ENABLE_I18N
+ int i;
+ /*
+ if (syntax & RE_HAT_LISTS_NOT_NEWLINE)
+ bitset_set(cset->sbcset, '\0');
+ */
+ mbcset->non_match = 1;
+ if (MB_CUR_MAX > 1)
+ for (i = 0; i < SBC_MAX; ++i)
+ if (__btowc (i) == WEOF)
+ bitset_set (sbcset, i);
+#else /* not RE_ENABLE_I18N */
+ non_match = 1;
+#endif /* not RE_ENABLE_I18N */
+ }
+
+ /* We don't care the syntax in this case. */
+ ret = build_charclass (sbcset,
+#ifdef RE_ENABLE_I18N
+ mbcset, &alloc,
+#endif /* RE_ENABLE_I18N */
+ (const unsigned char *) "alpha", 0);
+
+ if (BE (ret != REG_NOERROR, 0))
+ {
+ re_free (sbcset);
+#ifdef RE_ENABLE_I18N
+ free_charset (mbcset);
+#endif /* RE_ENABLE_I18N */
+ *err = REG_ESPACE;
+ return NULL;
+ }
+ /* \w match '_' also. */
+ bitset_set (sbcset, '_');
+
+ /* If it is non-matching list. */
+#ifdef RE_ENABLE_I18N
+ if (mbcset->non_match)
+#else /* not RE_ENABLE_I18N */
+ if (non_match)
+#endif /* not RE_ENABLE_I18N */
+ bitset_not (sbcset);
+
+ /* Build a tree for simple bracket. */
+ br_token.type = SIMPLE_BRACKET;
+ br_token.opr.sbcset = sbcset;
+ new_idx = re_dfa_add_node (dfa, br_token, 0);
+ tree = create_tree (NULL, NULL, 0, new_idx);
+ if (BE (new_idx == -1 || tree == NULL, 0))
+ goto build_word_op_espace;
+
+#ifdef RE_ENABLE_I18N
+ if (MB_CUR_MAX > 1)
+ {
+ re_token_t alt_token;
+ bin_tree_t *mbc_tree;
+ /* Build a tree for complex bracket. */
+ br_token.type = COMPLEX_BRACKET;
+ br_token.opr.mbcset = mbcset;
+ dfa->has_mb_node = 1;
+ new_idx = re_dfa_add_node (dfa, br_token, 0);
+ mbc_tree = create_tree (NULL, NULL, 0, new_idx);
+ if (BE (new_idx == -1 || mbc_tree == NULL, 0))
+ goto build_word_op_espace;
+ /* Then join them by ALT node. */
+ alt_token.type = OP_ALT;
+ new_idx = re_dfa_add_node (dfa, alt_token, 0);
+ tree = create_tree (tree, mbc_tree, 0, new_idx);
+ if (BE (new_idx != -1 && mbc_tree != NULL, 1))
+ return tree;
+ }
+ else
+ {
+ free_charset (mbcset);
+ return tree;
+ }
+#else /* not RE_ENABLE_I18N */
+ return tree;
+#endif /* not RE_ENABLE_I18N */
+
+ build_word_op_espace:
+ re_free (sbcset);
+#ifdef RE_ENABLE_I18N
+ free_charset (mbcset);
+#endif /* RE_ENABLE_I18N */
+ *err = REG_ESPACE;
+ return NULL;
+}
+
+/* This is intended for the expressions like "a{1,3}".
+ Fetch a number from `input', and return the number.
+ Return -1, if the number field is empty like "{,1}".
+ Return -2, If an error is occured. */
+
+static int
+fetch_number (input, token, syntax)
+ re_string_t *input;
+ re_token_t *token;
+ reg_syntax_t syntax;
+{
+ int num = -1;
+ unsigned char c;
+ while (1)
+ {
+ *token = fetch_token (input, syntax);
+ c = token->opr.c;
+ if (BE (token->type == END_OF_RE, 0))
+ return -2;
+ if (token->type == OP_CLOSE_DUP_NUM || c == ',')
+ break;
+ num = ((token->type != CHARACTER || c < '0' || '9' < c || num == -2)
+ ? -2 : ((num == -1) ? c - '0' : num * 10 + c - '0'));
+ num = (num > RE_DUP_MAX) ? -2 : num;
+ }
+ return num;
+}
+
+#ifdef RE_ENABLE_I18N
+static void
+free_charset (re_charset_t *cset)
+{
+ re_free (cset->mbchars);
+# ifdef _LIBC
+ re_free (cset->coll_syms);
+ re_free (cset->equiv_classes);
+ re_free (cset->range_starts);
+ re_free (cset->range_ends);
+# endif
+ re_free (cset->char_classes);
+ re_free (cset);
+}
+#endif /* RE_ENABLE_I18N */
+
+/* Functions for binary tree operation. */
+
+/* Create a node of tree.
+ Note: This function automatically free left and right if malloc fails. */
+
+static bin_tree_t *
+create_tree (left, right, type, index)
+ bin_tree_t *left;
+ bin_tree_t *right;
+ re_token_type_t type;
+ int index;
+{
+ bin_tree_t *tree;
+ tree = re_malloc (bin_tree_t, 1);
+ if (BE (tree == NULL, 0))
+ {
+ free_bin_tree (left);
+ free_bin_tree (right);
+ return NULL;
+ }
+ tree->parent = NULL;
+ tree->left = left;
+ tree->right = right;
+ tree->type = type;
+ tree->node_idx = index;
+ tree->first = -1;
+ tree->next = -1;
+ re_node_set_init_empty (&tree->eclosure);
+
+ if (left != NULL)
+ left->parent = tree;
+ if (right != NULL)
+ right->parent = tree;
+ return tree;
+}
+
+/* Free the sub tree pointed by TREE. */
+
+static void
+free_bin_tree (tree)
+ bin_tree_t *tree;
+{
+ if (tree == NULL)
+ return;
+ /*re_node_set_free (&tree->eclosure);*/
+ free_bin_tree (tree->left);
+ free_bin_tree (tree->right);
+ re_free (tree);
+}
+
+/* Duplicate the node SRC, and return new node. */
+
+static bin_tree_t *
+duplicate_tree (src, dfa)
+ const bin_tree_t *src;
+ re_dfa_t *dfa;
+{
+ bin_tree_t *left = NULL, *right = NULL, *new_tree;
+ int new_node_idx;
+ /* Since node indies must be according to Post-order of the tree,
+ we must duplicate the left at first. */
+ if (src->left != NULL)
+ {
+ left = duplicate_tree (src->left, dfa);
+ if (left == NULL)
+ return NULL;
+ }
+
+ /* Secondaly, duplicate the right. */
+ if (src->right != NULL)
+ {
+ right = duplicate_tree (src->right, dfa);
+ if (right == NULL)
+ {
+ free_bin_tree (left);
+ return NULL;
+ }
+ }
+
+ /* At last, duplicate itself. */
+ if (src->type == NON_TYPE)
+ {
+ new_node_idx = re_dfa_add_node (dfa, dfa->nodes[src->node_idx], 0);
+ dfa->nodes[new_node_idx].duplicated = 1;
+ if (BE (new_node_idx == -1, 0))
+ {
+ free_bin_tree (left);
+ free_bin_tree (right);
+ return NULL;
+ }
+ }
+ else
+ new_node_idx = src->type;
+
+ new_tree = create_tree (left, right, src->type, new_node_idx);
+ if (BE (new_tree == NULL, 0))
+ {
+ free_bin_tree (left);
+ free_bin_tree (right);
+ }
+ return new_tree;
+}
diff --git a/util/regex.c b/util/regex.c
new file mode 100644
index 0000000..34d4120
--- /dev/null
+++ b/util/regex.c
@@ -0,0 +1,62 @@
+/* Extended regular expression matching and search library.
+ Copyright (C) 2002 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02110-1301 USA. */
+
+#ifdef _LIBC
+/* We have to keep the namespace clean. */
+# define regfree(preg) __regfree (preg)
+# define regexec(pr, st, nm, pm, ef) __regexec (pr, st, nm, pm, ef)
+# define regcomp(preg, pattern, cflags) __regcomp (preg, pattern, cflags)
+# define regerror(errcode, preg, errbuf, errbuf_size) \
+ __regerror(errcode, preg, errbuf, errbuf_size)
+# define re_set_registers(bu, re, nu, st, en) \
+ __re_set_registers (bu, re, nu, st, en)
+# define re_match_2(bufp, string1, size1, string2, size2, pos, regs, stop) \
+ __re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop)
+# define re_match(bufp, string, size, pos, regs) \
+ __re_match (bufp, string, size, pos, regs)
+# define re_search(bufp, string, size, startpos, range, regs) \
+ __re_search (bufp, string, size, startpos, range, regs)
+# define re_compile_pattern(pattern, length, bufp) \
+ __re_compile_pattern (pattern, length, bufp)
+# define re_set_syntax(syntax) __re_set_syntax (syntax)
+# define re_search_2(bufp, st1, s1, st2, s2, startpos, range, regs, stop) \
+ __re_search_2 (bufp, st1, s1, st2, s2, startpos, range, regs, stop)
+# define re_compile_fastmap(bufp) __re_compile_fastmap (bufp)
+#endif
+
+#if _LIBC || __GNUC__ >= 3
+# define BE(expr, val) __builtin_expect (expr, val)
+#else
+# define BE(expr, val) (expr)
+# define inline
+#endif
+
+#include "regcomp.c"
+#include "regexec.c"
+#include "regex_internal.c"
+
+/* Binary backward compatibility. */
+#if _LIBC
+# include <shlib-compat.h>
+# if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_3)
+link_warning (re_max_failures, "the 're_max_failures' variable is obsolete and will go away.")
+int re_max_failures = 2000;
+# endif
+#endif
diff --git a/util/regex_internal.c b/util/regex_internal.c
new file mode 100644
index 0000000..0f8b897
--- /dev/null
+++ b/util/regex_internal.c
@@ -0,0 +1,1229 @@
+/* Extended regular expression matching and search library.
+ Copyright (C) 2002 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02110-1301 USA. */
+
+#include <assert.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if defined HAVE_WCHAR_H || defined _LIBC
+# include <wchar.h>
+#endif /* HAVE_WCHAR_H || _LIBC */
+#if defined HAVE_WCTYPE_H || defined _LIBC
+# include <wctype.h>
+#endif /* HAVE_WCTYPE_H || _LIBC */
+
+#ifdef _LIBC
+# ifndef _RE_DEFINE_LOCALE_FUNCTIONS
+# define _RE_DEFINE_LOCALE_FUNCTIONS 1
+# include <locale/localeinfo.h>
+# include <locale/elem-hash.h>
+# include <locale/coll-lookup.h>
+# endif
+#endif
+
+/* This is for other GNU distributions with internationalized messages. */
+#if HAVE_LIBINTL_H || defined _LIBC
+# include <libintl.h>
+# ifdef _LIBC
+# undef gettext
+# define gettext(msgid) \
+ INTUSE(__dcgettext) (_libc_intl_domainname_internal, msgid, LC_MESSAGES)
+# endif
+#else
+# define gettext(msgid) (msgid)
+#endif
+
+#ifndef gettext_noop
+/* This define is so xgettext can find the internationalizable
+ strings. */
+# define gettext_noop(String) String
+#endif
+
+#include "_regex.h" /* gnupg */
+#include "regex_internal.h"
+
+static void re_string_construct_common (const char *str, int len,
+ re_string_t *pstr,
+ RE_TRANSLATE_TYPE trans, int icase);
+#ifdef RE_ENABLE_I18N
+static int re_string_skip_chars (re_string_t *pstr, int new_raw_idx);
+#endif /* RE_ENABLE_I18N */
+static re_dfastate_t *create_newstate_common (re_dfa_t *dfa,
+ const re_node_set *nodes,
+ unsigned int hash);
+static reg_errcode_t register_state (re_dfa_t *dfa, re_dfastate_t *newstate,
+ unsigned int hash);
+static re_dfastate_t *create_ci_newstate (re_dfa_t *dfa,
+ const re_node_set *nodes,
+ unsigned int hash);
+static re_dfastate_t *create_cd_newstate (re_dfa_t *dfa,
+ const re_node_set *nodes,
+ unsigned int context,
+ unsigned int hash);
+static unsigned int inline calc_state_hash (const re_node_set *nodes,
+ unsigned int context);
+
+/* Functions for string operation. */
+
+/* This function allocate the buffers. It is necessary to call
+ re_string_reconstruct before using the object. */
+
+static reg_errcode_t
+re_string_allocate (pstr, str, len, init_len, trans, icase)
+ re_string_t *pstr;
+ const char *str;
+ int len, init_len, icase;
+ RE_TRANSLATE_TYPE trans;
+{
+ reg_errcode_t ret;
+ int init_buf_len = (len + 1 < init_len) ? len + 1: init_len;
+ re_string_construct_common (str, len, pstr, trans, icase);
+ pstr->stop = pstr->len;
+
+ ret = re_string_realloc_buffers (pstr, init_buf_len);
+ if (BE (ret != REG_NOERROR, 0))
+ return ret;
+
+ pstr->mbs_case = (MBS_CASE_ALLOCATED (pstr) ? pstr->mbs_case
+ : (unsigned char *) str);
+ pstr->mbs = MBS_ALLOCATED (pstr) ? pstr->mbs : pstr->mbs_case;
+ pstr->valid_len = (MBS_CASE_ALLOCATED (pstr) || MBS_ALLOCATED (pstr)
+ || MB_CUR_MAX > 1) ? pstr->valid_len : len;
+ return REG_NOERROR;
+}
+
+/* This function allocate the buffers, and initialize them. */
+
+static reg_errcode_t
+re_string_construct (pstr, str, len, trans, icase)
+ re_string_t *pstr;
+ const char *str;
+ int len, icase;
+ RE_TRANSLATE_TYPE trans;
+{
+ reg_errcode_t ret;
+ re_string_construct_common (str, len, pstr, trans, icase);
+ pstr->stop = pstr->len;
+ /* Set 0 so that this function can initialize whole buffers. */
+ pstr->valid_len = 0;
+
+ if (len > 0)
+ {
+ ret = re_string_realloc_buffers (pstr, len + 1);
+ if (BE (ret != REG_NOERROR, 0))
+ return ret;
+ }
+ pstr->mbs_case = (MBS_CASE_ALLOCATED (pstr) ? pstr->mbs_case
+ : (unsigned char *) str);
+ pstr->mbs = MBS_ALLOCATED (pstr) ? pstr->mbs : pstr->mbs_case;
+
+ if (icase)
+ {
+#ifdef RE_ENABLE_I18N
+ if (MB_CUR_MAX > 1)
+ build_wcs_upper_buffer (pstr);
+ else
+#endif /* RE_ENABLE_I18N */
+ build_upper_buffer (pstr);
+ }
+ else
+ {
+#ifdef RE_ENABLE_I18N
+ if (MB_CUR_MAX > 1)
+ build_wcs_buffer (pstr);
+ else
+#endif /* RE_ENABLE_I18N */
+ {
+ if (trans != NULL)
+ re_string_translate_buffer (pstr);
+ else
+ pstr->valid_len = len;
+ }
+ }
+
+ /* Initialized whole buffers, then valid_len == bufs_len. */
+ pstr->valid_len = pstr->bufs_len;
+ return REG_NOERROR;
+}
+
+/* Helper functions for re_string_allocate, and re_string_construct. */
+
+static reg_errcode_t
+re_string_realloc_buffers (pstr, new_buf_len)
+ re_string_t *pstr;
+ int new_buf_len;
+{
+#ifdef RE_ENABLE_I18N
+ if (MB_CUR_MAX > 1)
+ {
+ pstr->wcs = re_realloc (pstr->wcs, wint_t, new_buf_len);
+ if (BE (pstr->wcs == NULL, 0))
+ return REG_ESPACE;
+ }
+#endif /* RE_ENABLE_I18N */
+ if (MBS_ALLOCATED (pstr))
+ {
+ pstr->mbs = re_realloc (pstr->mbs, unsigned char, new_buf_len);
+ if (BE (pstr->mbs == NULL, 0))
+ return REG_ESPACE;
+ }
+ if (MBS_CASE_ALLOCATED (pstr))
+ {
+ pstr->mbs_case = re_realloc (pstr->mbs_case, unsigned char, new_buf_len);
+ if (BE (pstr->mbs_case == NULL, 0))
+ return REG_ESPACE;
+ if (!MBS_ALLOCATED (pstr))
+ pstr->mbs = pstr->mbs_case;
+ }
+ pstr->bufs_len = new_buf_len;
+ return REG_NOERROR;
+}
+
+
+static void
+re_string_construct_common (str, len, pstr, trans, icase)
+ const char *str;
+ int len;
+ re_string_t *pstr;
+ RE_TRANSLATE_TYPE trans;
+ int icase;
+{
+ memset (pstr, '\0', sizeof (re_string_t));
+ pstr->raw_mbs = (const unsigned char *) str;
+ pstr->len = len;
+ pstr->trans = trans;
+ pstr->icase = icase ? 1 : 0;
+}
+
+#ifdef RE_ENABLE_I18N
+
+/* Build wide character buffer PSTR->WCS.
+ If the byte sequence of the string are:
+ <mb1>(0), <mb1>(1), <mb2>(0), <mb2>(1), <sb3>
+ Then wide character buffer will be:
+ <wc1> , WEOF , <wc2> , WEOF , <wc3>
+ We use WEOF for padding, they indicate that the position isn't
+ a first byte of a multibyte character.
+
+ Note that this function assumes PSTR->VALID_LEN elements are already
+ built and starts from PSTR->VALID_LEN. */
+
+static void
+build_wcs_buffer (pstr)
+ re_string_t *pstr;
+{
+ mbstate_t prev_st;
+ int byte_idx, end_idx, mbclen, remain_len;
+ /* Build the buffers from pstr->valid_len to either pstr->len or
+ pstr->bufs_len. */
+ end_idx = (pstr->bufs_len > pstr->len)? pstr->len : pstr->bufs_len;
+ for (byte_idx = pstr->valid_len; byte_idx < end_idx;)
+ {
+ wchar_t wc;
+ remain_len = end_idx - byte_idx;
+ prev_st = pstr->cur_state;
+ mbclen = mbrtowc (&wc, ((const char *) pstr->raw_mbs + pstr->raw_mbs_idx
+ + byte_idx), remain_len, &pstr->cur_state);
+ if (BE (mbclen == (size_t) -2, 0))
+ {
+ /* The buffer doesn't have enough space, finish to build. */
+ pstr->cur_state = prev_st;
+ break;
+ }
+ else if (BE (mbclen == (size_t) -1 || mbclen == 0, 0))
+ {
+ /* We treat these cases as a singlebyte character. */
+ mbclen = 1;
+ wc = (wchar_t) pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx];
+ pstr->cur_state = prev_st;
+ }
+
+ /* Apply the translateion if we need. */
+ if (pstr->trans != NULL && mbclen == 1)
+ {
+ int ch = pstr->trans[pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx]];
+ pstr->mbs_case[byte_idx] = ch;
+ }
+ /* Write wide character and padding. */
+ pstr->wcs[byte_idx++] = wc;
+ /* Write paddings. */
+ for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;)
+ pstr->wcs[byte_idx++] = WEOF;
+ }
+ pstr->valid_len = byte_idx;
+}
+
+/* Build wide character buffer PSTR->WCS like build_wcs_buffer,
+ but for REG_ICASE. */
+
+static void
+build_wcs_upper_buffer (pstr)
+ re_string_t *pstr;
+{
+ mbstate_t prev_st;
+ int byte_idx, end_idx, mbclen, remain_len;
+ /* Build the buffers from pstr->valid_len to either pstr->len or
+ pstr->bufs_len. */
+ end_idx = (pstr->bufs_len > pstr->len)? pstr->len : pstr->bufs_len;
+ for (byte_idx = pstr->valid_len; byte_idx < end_idx;)
+ {
+ wchar_t wc;
+ remain_len = end_idx - byte_idx;
+ prev_st = pstr->cur_state;
+ mbclen = mbrtowc (&wc, ((const char *) pstr->raw_mbs + pstr->raw_mbs_idx
+ + byte_idx), remain_len, &pstr->cur_state);
+ if (BE (mbclen == (size_t) -2, 0))
+ {
+ /* The buffer doesn't have enough space, finish to build. */
+ pstr->cur_state = prev_st;
+ break;
+ }
+ else if (mbclen == 1 || mbclen == (size_t) -1 || mbclen == 0)
+ {
+ /* In case of a singlebyte character. */
+ int ch = pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx];
+ /* Apply the translateion if we need. */
+ if (pstr->trans != NULL && mbclen == 1)
+ {
+ ch = pstr->trans[ch];
+ pstr->mbs_case[byte_idx] = ch;
+ }
+ pstr->wcs[byte_idx] = iswlower (wc) ? toupper (wc) : wc;
+ pstr->mbs[byte_idx++] = islower (ch) ? toupper (ch) : ch;
+ if (BE (mbclen == (size_t) -1, 0))
+ pstr->cur_state = prev_st;
+ }
+ else /* mbclen > 1 */
+ {
+ if (iswlower (wc))
+ wcrtomb ((char *) pstr->mbs + byte_idx, towupper (wc), &prev_st);
+ else
+ memcpy (pstr->mbs + byte_idx,
+ pstr->raw_mbs + pstr->raw_mbs_idx + byte_idx, mbclen);
+ pstr->wcs[byte_idx++] = iswlower (wc) ? toupper (wc) : wc;
+ /* Write paddings. */
+ for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;)
+ pstr->wcs[byte_idx++] = WEOF;
+ }
+ }
+ pstr->valid_len = byte_idx;
+}
+
+/* Skip characters until the index becomes greater than NEW_RAW_IDX.
+ Return the index. */
+
+static int
+re_string_skip_chars (pstr, new_raw_idx)
+ re_string_t *pstr;
+ int new_raw_idx;
+{
+ mbstate_t prev_st;
+ int rawbuf_idx, mbclen;
+
+ /* Skip the characters which are not necessary to check. */
+ for (rawbuf_idx = pstr->raw_mbs_idx + pstr->valid_len;
+ rawbuf_idx < new_raw_idx;)
+ {
+ int remain_len = pstr->len - rawbuf_idx;
+ prev_st = pstr->cur_state;
+ mbclen = mbrlen ((const char *) pstr->raw_mbs + rawbuf_idx, remain_len,
+ &pstr->cur_state);
+ if (BE (mbclen == (size_t) -2 || mbclen == (size_t) -1 || mbclen == 0, 0))
+ {
+ /* We treat these cases as a singlebyte character. */
+ mbclen = 1;
+ pstr->cur_state = prev_st;
+ }
+ /* Then proceed the next character. */
+ rawbuf_idx += mbclen;
+ }
+ return rawbuf_idx;
+}
+#endif /* RE_ENABLE_I18N */
+
+/* Build the buffer PSTR->MBS, and apply the translation if we need.
+ This function is used in case of REG_ICASE. */
+
+static void
+build_upper_buffer (pstr)
+ re_string_t *pstr;
+{
+ int char_idx, end_idx;
+ end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len;
+
+ for (char_idx = pstr->valid_len; char_idx < end_idx; ++char_idx)
+ {
+ int ch = pstr->raw_mbs[pstr->raw_mbs_idx + char_idx];
+ if (pstr->trans != NULL)
+ {
+ ch = pstr->trans[ch];
+ pstr->mbs_case[char_idx] = ch;
+ }
+ if (islower (ch))
+ pstr->mbs[char_idx] = toupper (ch);
+ else
+ pstr->mbs[char_idx] = ch;
+ }
+ pstr->valid_len = char_idx;
+}
+
+/* Apply TRANS to the buffer in PSTR. */
+
+static void
+re_string_translate_buffer (pstr)
+ re_string_t *pstr;
+{
+ int buf_idx, end_idx;
+ end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len;
+
+ for (buf_idx = pstr->valid_len; buf_idx < end_idx; ++buf_idx)
+ {
+ int ch = pstr->raw_mbs[pstr->raw_mbs_idx + buf_idx];
+ pstr->mbs_case[buf_idx] = pstr->trans[ch];
+ }
+
+ pstr->valid_len = buf_idx;
+}
+
+/* This function re-construct the buffers.
+ Concretely, convert to wide character in case of MB_CUR_MAX > 1,
+ convert to upper case in case of REG_ICASE, apply translation. */
+
+static reg_errcode_t
+re_string_reconstruct (pstr, idx, eflags, newline)
+ re_string_t *pstr;
+ int idx, eflags, newline;
+{
+ int offset = idx - pstr->raw_mbs_idx;
+ if (offset < 0)
+ {
+ /* Reset buffer. */
+#ifdef RE_ENABLE_I18N
+ if (MB_CUR_MAX > 1)
+ memset (&pstr->cur_state, '\0', sizeof (mbstate_t));
+#endif /* RE_ENABLE_I18N */
+ pstr->len += pstr->raw_mbs_idx;
+ pstr->stop += pstr->raw_mbs_idx;
+ pstr->valid_len = pstr->raw_mbs_idx = 0;
+ pstr->tip_context = ((eflags & REG_NOTBOL) ? CONTEXT_BEGBUF
+ : CONTEXT_NEWLINE | CONTEXT_BEGBUF);
+ if (!MBS_CASE_ALLOCATED (pstr))
+ pstr->mbs_case = (unsigned char *) pstr->raw_mbs;
+ if (!MBS_ALLOCATED (pstr) && !MBS_CASE_ALLOCATED (pstr))
+ pstr->mbs = (unsigned char *) pstr->raw_mbs;
+ offset = idx;
+ }
+
+ if (offset != 0)
+ {
+ pstr->tip_context = re_string_context_at (pstr, offset - 1, eflags,
+ newline);
+ /* Are the characters which are already checked remain? */
+ if (offset < pstr->valid_len)
+ {
+ /* Yes, move them to the front of the buffer. */
+#ifdef RE_ENABLE_I18N
+ if (MB_CUR_MAX > 1)
+ memmove (pstr->wcs, pstr->wcs + offset,
+ (pstr->valid_len - offset) * sizeof (wint_t));
+#endif /* RE_ENABLE_I18N */
+ if (MBS_ALLOCATED (pstr))
+ memmove (pstr->mbs, pstr->mbs + offset,
+ pstr->valid_len - offset);
+ if (MBS_CASE_ALLOCATED (pstr))
+ memmove (pstr->mbs_case, pstr->mbs_case + offset,
+ pstr->valid_len - offset);
+ pstr->valid_len -= offset;
+#if DEBUG
+ assert (pstr->valid_len > 0);
+#endif
+ }
+ else
+ {
+ /* No, skip all characters until IDX. */
+ pstr->valid_len = 0;
+#ifdef RE_ENABLE_I18N
+ if (MB_CUR_MAX > 1)
+ {
+ int wcs_idx;
+ pstr->valid_len = re_string_skip_chars (pstr, idx) - idx;
+ for (wcs_idx = 0; wcs_idx < pstr->valid_len; ++wcs_idx)
+ pstr->wcs[wcs_idx] = WEOF;
+ }
+#endif /* RE_ENABLE_I18N */
+ }
+ if (!MBS_CASE_ALLOCATED (pstr))
+ {
+ pstr->mbs_case += offset;
+ /* In case of !MBS_ALLOCATED && !MBS_CASE_ALLOCATED. */
+ if (!MBS_ALLOCATED (pstr))
+ pstr->mbs += offset;
+ }
+ }
+ pstr->raw_mbs_idx = idx;
+ pstr->len -= offset;
+ pstr->stop -= offset;
+
+ /* Then build the buffers. */
+#ifdef RE_ENABLE_I18N
+ if (MB_CUR_MAX > 1)
+ {
+ if (pstr->icase)
+ build_wcs_upper_buffer (pstr);
+ else
+ build_wcs_buffer (pstr);
+ }
+ else
+#endif /* RE_ENABLE_I18N */
+ {
+ if (pstr->icase)
+ build_upper_buffer (pstr);
+ else if (pstr->trans != NULL)
+ re_string_translate_buffer (pstr);
+ }
+ pstr->cur_idx = 0;
+
+ return REG_NOERROR;
+}
+
+static void
+re_string_destruct (pstr)
+ re_string_t *pstr;
+{
+#ifdef RE_ENABLE_I18N
+ re_free (pstr->wcs);
+#endif /* RE_ENABLE_I18N */
+ if (MBS_ALLOCATED (pstr))
+ re_free (pstr->mbs);
+ if (MBS_CASE_ALLOCATED (pstr))
+ re_free (pstr->mbs_case);
+}
+
+/* Return the context at IDX in INPUT. */
+
+static unsigned int
+re_string_context_at (input, idx, eflags, newline_anchor)
+ const re_string_t *input;
+ int idx, eflags, newline_anchor;
+{
+ int c;
+ if (idx < 0 || idx == input->len)
+ {
+ if (idx < 0)
+ /* In this case, we use the value stored in input->tip_context,
+ since we can't know the character in input->mbs[-1] here. */
+ return input->tip_context;
+ else /* (idx == input->len) */
+ return ((eflags & REG_NOTEOL) ? CONTEXT_ENDBUF
+ : CONTEXT_NEWLINE | CONTEXT_ENDBUF);
+ }
+ c = re_string_byte_at (input, idx);
+ if (IS_WORD_CHAR (c))
+ return CONTEXT_WORD;
+ return (newline_anchor && IS_NEWLINE (c)) ? CONTEXT_NEWLINE : 0;
+}
+
+/* Functions for set operation. */
+
+static reg_errcode_t
+re_node_set_alloc (set, size)
+ re_node_set *set;
+ int size;
+{
+ set->alloc = size;
+ set->nelem = 0;
+ set->elems = re_malloc (int, size);
+ if (BE (set->elems == NULL, 0))
+ return REG_ESPACE;
+ return REG_NOERROR;
+}
+
+static reg_errcode_t
+re_node_set_init_1 (set, elem)
+ re_node_set *set;
+ int elem;
+{
+ set->alloc = 1;
+ set->nelem = 1;
+ set->elems = re_malloc (int, 1);
+ if (BE (set->elems == NULL, 0))
+ return REG_ESPACE;
+ set->elems[0] = elem;
+ return REG_NOERROR;
+}
+
+static reg_errcode_t
+re_node_set_init_2 (set, elem1, elem2)
+ re_node_set *set;
+ int elem1, elem2;
+{
+ set->alloc = 2;
+ set->elems = re_malloc (int, 2);
+ if (BE (set->elems == NULL, 0))
+ return REG_ESPACE;
+ if (elem1 == elem2)
+ {
+ set->nelem = 1;
+ set->elems[0] = elem1;
+ }
+ else
+ {
+ set->nelem = 2;
+ if (elem1 < elem2)
+ {
+ set->elems[0] = elem1;
+ set->elems[1] = elem2;
+ }
+ else
+ {
+ set->elems[0] = elem2;
+ set->elems[1] = elem1;
+ }
+ }
+ return REG_NOERROR;
+}
+
+static reg_errcode_t
+re_node_set_init_copy (dest, src)
+ re_node_set *dest;
+ const re_node_set *src;
+{
+ dest->nelem = src->nelem;
+ if (src->nelem > 0)
+ {
+ dest->alloc = dest->nelem;
+ dest->elems = re_malloc (int, dest->alloc);
+ if (BE (dest->elems == NULL, 0))
+ return REG_ESPACE;
+ memcpy (dest->elems, src->elems, src->nelem * sizeof (int));
+ }
+ else
+ re_node_set_init_empty (dest);
+ return REG_NOERROR;
+}
+
+/* Calculate the intersection of the sets SRC1 and SRC2. And merge it to
+ DEST. Return value indicate the error code or REG_NOERROR if succeeded.
+ Note: We assume dest->elems is NULL, when dest->alloc is 0. */
+
+static reg_errcode_t
+re_node_set_add_intersect (dest, src1, src2)
+ re_node_set *dest;
+ const re_node_set *src1, *src2;
+{
+ int i1, i2, id;
+ if (src1->nelem > 0 && src2->nelem > 0)
+ {
+ if (src1->nelem + src2->nelem + dest->nelem > dest->alloc)
+ {
+ dest->alloc = src1->nelem + src2->nelem + dest->nelem;
+ dest->elems = re_realloc (dest->elems, int, dest->alloc);
+ if (BE (dest->elems == NULL, 0))
+ return REG_ESPACE;
+ }
+ }
+ else
+ return REG_NOERROR;
+
+ for (i1 = i2 = id = 0 ; i1 < src1->nelem && i2 < src2->nelem ;)
+ {
+ if (src1->elems[i1] > src2->elems[i2])
+ {
+ ++i2;
+ continue;
+ }
+ if (src1->elems[i1] == src2->elems[i2])
+ {
+ while (id < dest->nelem && dest->elems[id] < src2->elems[i2])
+ ++id;
+ if (id < dest->nelem && dest->elems[id] == src2->elems[i2])
+ ++id;
+ else
+ {
+ memmove (dest->elems + id + 1, dest->elems + id,
+ sizeof (int) * (dest->nelem - id));
+ dest->elems[id++] = src2->elems[i2++];
+ ++dest->nelem;
+ }
+ }
+ ++i1;
+ }
+ return REG_NOERROR;
+}
+
+/* Calculate the union set of the sets SRC1 and SRC2. And store it to
+ DEST. Return value indicate the error code or REG_NOERROR if succeeded. */
+
+static reg_errcode_t
+re_node_set_init_union (dest, src1, src2)
+ re_node_set *dest;
+ const re_node_set *src1, *src2;
+{
+ int i1, i2, id;
+ if (src1 != NULL && src1->nelem > 0 && src2 != NULL && src2->nelem > 0)
+ {
+ dest->alloc = src1->nelem + src2->nelem;
+ dest->elems = re_malloc (int, dest->alloc);
+ if (BE (dest->elems == NULL, 0))
+ return REG_ESPACE;
+ }
+ else
+ {
+ if (src1 != NULL && src1->nelem > 0)
+ return re_node_set_init_copy (dest, src1);
+ else if (src2 != NULL && src2->nelem > 0)
+ return re_node_set_init_copy (dest, src2);
+ else
+ re_node_set_init_empty (dest);
+ return REG_NOERROR;
+ }
+ for (i1 = i2 = id = 0 ; i1 < src1->nelem && i2 < src2->nelem ;)
+ {
+ if (src1->elems[i1] > src2->elems[i2])
+ {
+ dest->elems[id++] = src2->elems[i2++];
+ continue;
+ }
+ if (src1->elems[i1] == src2->elems[i2])
+ ++i2;
+ dest->elems[id++] = src1->elems[i1++];
+ }
+ if (i1 < src1->nelem)
+ {
+ memcpy (dest->elems + id, src1->elems + i1,
+ (src1->nelem - i1) * sizeof (int));
+ id += src1->nelem - i1;
+ }
+ else if (i2 < src2->nelem)
+ {
+ memcpy (dest->elems + id, src2->elems + i2,
+ (src2->nelem - i2) * sizeof (int));
+ id += src2->nelem - i2;
+ }
+ dest->nelem = id;
+ return REG_NOERROR;
+}
+
+/* Calculate the union set of the sets DEST and SRC. And store it to
+ DEST. Return value indicate the error code or REG_NOERROR if succeeded. */
+
+static reg_errcode_t
+re_node_set_merge (dest, src)
+ re_node_set *dest;
+ const re_node_set *src;
+{
+ int si, di;
+ if (src == NULL || src->nelem == 0)
+ return REG_NOERROR;
+ if (dest->alloc < src->nelem + dest->nelem)
+ {
+ dest->alloc = 2 * (src->nelem + dest->alloc);
+ dest->elems = re_realloc (dest->elems, int, dest->alloc);
+ if (BE (dest->elems == NULL, 0))
+ return REG_ESPACE;
+ }
+
+ for (si = 0, di = 0 ; si < src->nelem && di < dest->nelem ;)
+ {
+ int cp_from, ncp, mid, right, src_elem = src->elems[si];
+ /* Binary search the spot we will add the new element. */
+ right = dest->nelem;
+ while (di < right)
+ {
+ mid = (di + right) / 2;
+ if (dest->elems[mid] < src_elem)
+ di = mid + 1;
+ else
+ right = mid;
+ }
+ if (di >= dest->nelem)
+ break;
+
+ if (dest->elems[di] == src_elem)
+ {
+ /* Skip since, DEST already has the element. */
+ ++di;
+ ++si;
+ continue;
+ }
+
+ /* Skip the src elements which are less than dest->elems[di]. */
+ cp_from = si;
+ while (si < src->nelem && src->elems[si] < dest->elems[di])
+ ++si;
+ /* Copy these src elements. */
+ ncp = si - cp_from;
+ memmove (dest->elems + di + ncp, dest->elems + di,
+ sizeof (int) * (dest->nelem - di));
+ memcpy (dest->elems + di, src->elems + cp_from,
+ sizeof (int) * ncp);
+ /* Update counters. */
+ di += ncp;
+ dest->nelem += ncp;
+ }
+
+ /* Copy remaining src elements. */
+ if (si < src->nelem)
+ {
+ memcpy (dest->elems + di, src->elems + si,
+ sizeof (int) * (src->nelem - si));
+ dest->nelem += src->nelem - si;
+ }
+ return REG_NOERROR;
+}
+
+/* Insert the new element ELEM to the re_node_set* SET.
+ return 0 if SET already has ELEM,
+ return -1 if an error is occured, return 1 otherwise. */
+
+static int
+re_node_set_insert (set, elem)
+ re_node_set *set;
+ int elem;
+{
+ int idx, right, mid;
+ /* In case of the set is empty. */
+ if (set->elems == NULL || set->alloc == 0)
+ {
+ if (BE (re_node_set_init_1 (set, elem) == REG_NOERROR, 1))
+ return 1;
+ else
+ return -1;
+ }
+
+ /* Binary search the spot we will add the new element. */
+ idx = 0;
+ right = set->nelem;
+ while (idx < right)
+ {
+ mid = (idx + right) / 2;
+ if (set->elems[mid] < elem)
+ idx = mid + 1;
+ else
+ right = mid;
+ }
+
+ /* Realloc if we need. */
+ if (set->alloc < set->nelem + 1)
+ {
+ int *new_array;
+ set->alloc = set->alloc * 2;
+ new_array = re_malloc (int, set->alloc);
+ if (BE (new_array == NULL, 0))
+ return -1;
+ /* Copy the elements they are followed by the new element. */
+ if (idx > 0)
+ memcpy (new_array, set->elems, sizeof (int) * (idx));
+ /* Copy the elements which follows the new element. */
+ if (set->nelem - idx > 0)
+ memcpy (new_array + idx + 1, set->elems + idx,
+ sizeof (int) * (set->nelem - idx));
+ re_free (set->elems);
+ set->elems = new_array;
+ }
+ else
+ {
+ /* Move the elements which follows the new element. */
+ if (set->nelem - idx > 0)
+ memmove (set->elems + idx + 1, set->elems + idx,
+ sizeof (int) * (set->nelem - idx));
+ }
+ /* Insert the new element. */
+ set->elems[idx] = elem;
+ ++set->nelem;
+ return 1;
+}
+
+/* Compare two node sets SET1 and SET2.
+ return 1 if SET1 and SET2 are equivalent, retrun 0 otherwise. */
+
+static int
+re_node_set_compare (set1, set2)
+ const re_node_set *set1, *set2;
+{
+ int i;
+ if (set1 == NULL || set2 == NULL || set1->nelem != set2->nelem)
+ return 0;
+ for (i = 0 ; i < set1->nelem ; i++)
+ if (set1->elems[i] != set2->elems[i])
+ return 0;
+ return 1;
+}
+
+/* Return (idx + 1) if SET contains the element ELEM, return 0 otherwise. */
+
+static int
+re_node_set_contains (set, elem)
+ const re_node_set *set;
+ int elem;
+{
+ int idx, right, mid;
+ if (set->nelem <= 0)
+ return 0;
+
+ /* Binary search the element. */
+ idx = 0;
+ right = set->nelem - 1;
+ while (idx < right)
+ {
+ mid = (idx + right) / 2;
+ if (set->elems[mid] < elem)
+ idx = mid + 1;
+ else
+ right = mid;
+ }
+ return set->elems[idx] == elem ? idx + 1 : 0;
+}
+
+static void
+re_node_set_remove_at (set, idx)
+ re_node_set *set;
+ int idx;
+{
+ if (idx < 0 || idx >= set->nelem)
+ return;
+ if (idx < set->nelem - 1)
+ memmove (set->elems + idx, set->elems + idx + 1,
+ sizeof (int) * (set->nelem - idx - 1));
+ --set->nelem;
+}
+
+
+/* Add the token TOKEN to dfa->nodes, and return the index of the token.
+ Or return -1, if an error will be occured. */
+
+static int
+re_dfa_add_node (dfa, token, mode)
+ re_dfa_t *dfa;
+ re_token_t token;
+ int mode;
+{
+ if (dfa->nodes_len >= dfa->nodes_alloc)
+ {
+ re_token_t *new_array;
+ dfa->nodes_alloc *= 2;
+ new_array = re_realloc (dfa->nodes, re_token_t, dfa->nodes_alloc);
+ if (BE (new_array == NULL, 0))
+ return -1;
+ else
+ dfa->nodes = new_array;
+ if (mode)
+ {
+ int *new_firsts, *new_nexts;
+ re_node_set *new_edests, *new_eclosures, *new_inveclosures;
+
+ new_firsts = re_realloc (dfa->firsts, int, dfa->nodes_alloc);
+ new_nexts = re_realloc (dfa->nexts, int, dfa->nodes_alloc);
+ new_edests = re_realloc (dfa->edests, re_node_set, dfa->nodes_alloc);
+ new_eclosures = re_realloc (dfa->eclosures, re_node_set,
+ dfa->nodes_alloc);
+ new_inveclosures = re_realloc (dfa->inveclosures, re_node_set,
+ dfa->nodes_alloc);
+ if (BE (new_firsts == NULL || new_nexts == NULL || new_edests == NULL
+ || new_eclosures == NULL || new_inveclosures == NULL, 0))
+ return -1;
+ dfa->firsts = new_firsts;
+ dfa->nexts = new_nexts;
+ dfa->edests = new_edests;
+ dfa->eclosures = new_eclosures;
+ dfa->inveclosures = new_inveclosures;
+ }
+ }
+ dfa->nodes[dfa->nodes_len] = token;
+ dfa->nodes[dfa->nodes_len].duplicated = 0;
+ return dfa->nodes_len++;
+}
+
+static unsigned int inline
+calc_state_hash (nodes, context)
+ const re_node_set *nodes;
+ unsigned int context;
+{
+ unsigned int hash = nodes->nelem + context;
+ int i;
+ for (i = 0 ; i < nodes->nelem ; i++)
+ hash += nodes->elems[i];
+ return hash;
+}
+
+/* Search for the state whose node_set is equivalent to NODES.
+ Return the pointer to the state, if we found it in the DFA.
+ Otherwise create the new one and return it. In case of an error
+ return NULL and set the error code in ERR.
+ Note: - We assume NULL as the invalid state, then it is possible that
+ return value is NULL and ERR is REG_NOERROR.
+ - We never return non-NULL value in case of any errors, it is for
+ optimization. */
+
+static re_dfastate_t*
+re_acquire_state (err, dfa, nodes)
+ reg_errcode_t *err;
+ re_dfa_t *dfa;
+ const re_node_set *nodes;
+{
+ unsigned int hash;
+ re_dfastate_t *new_state;
+ struct re_state_table_entry *spot;
+ int i;
+ if (BE (nodes->nelem == 0, 0))
+ {
+ *err = REG_NOERROR;
+ return NULL;
+ }
+ hash = calc_state_hash (nodes, 0);
+ spot = dfa->state_table + (hash & dfa->state_hash_mask);
+
+ for (i = 0 ; i < spot->num ; i++)
+ {
+ re_dfastate_t *state = spot->array[i];
+ if (hash != state->hash)
+ continue;
+ if (re_node_set_compare (&state->nodes, nodes))
+ return state;
+ }
+
+ /* There are no appropriate state in the dfa, create the new one. */
+ new_state = create_ci_newstate (dfa, nodes, hash);
+ if (BE (new_state != NULL, 1))
+ return new_state;
+ else
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+}
+
+/* Search for the state whose node_set is equivalent to NODES and
+ whose context is equivalent to CONTEXT.
+ Return the pointer to the state, if we found it in the DFA.
+ Otherwise create the new one and return it. In case of an error
+ return NULL and set the error code in ERR.
+ Note: - We assume NULL as the invalid state, then it is possible that
+ return value is NULL and ERR is REG_NOERROR.
+ - We never return non-NULL value in case of any errors, it is for
+ optimization. */
+
+static re_dfastate_t*
+re_acquire_state_context (err, dfa, nodes, context)
+ reg_errcode_t *err;
+ re_dfa_t *dfa;
+ const re_node_set *nodes;
+ unsigned int context;
+{
+ unsigned int hash;
+ re_dfastate_t *new_state;
+ struct re_state_table_entry *spot;
+ int i;
+ if (nodes->nelem == 0)
+ {
+ *err = REG_NOERROR;
+ return NULL;
+ }
+ hash = calc_state_hash (nodes, context);
+ spot = dfa->state_table + (hash & dfa->state_hash_mask);
+
+ for (i = 0 ; i < spot->num ; i++)
+ {
+ re_dfastate_t *state = spot->array[i];
+ if (hash != state->hash)
+ continue;
+ if (re_node_set_compare (state->entrance_nodes, nodes)
+ && state->context == context)
+ return state;
+ }
+ /* There are no appropriate state in `dfa', create the new one. */
+ new_state = create_cd_newstate (dfa, nodes, context, hash);
+ if (BE (new_state != NULL, 1))
+ return new_state;
+ else
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+}
+
+/* Allocate memory for DFA state and initialize common properties.
+ Return the new state if succeeded, otherwise return NULL. */
+
+static re_dfastate_t *
+create_newstate_common (dfa, nodes, hash)
+ re_dfa_t *dfa;
+ const re_node_set *nodes;
+ unsigned int hash;
+{
+ re_dfastate_t *newstate;
+ newstate = (re_dfastate_t *) calloc (sizeof (re_dfastate_t), 1);
+ if (BE (newstate == NULL, 0))
+ return NULL;
+ re_node_set_init_copy (&newstate->nodes, nodes);
+ newstate->trtable = NULL;
+ newstate->trtable_search = NULL;
+ newstate->hash = hash;
+ return newstate;
+}
+
+/* Store the new state NEWSTATE whose hash value is HASH in appropriate
+ position. Return value indicate the error code if failed. */
+
+static reg_errcode_t
+register_state (dfa, newstate, hash)
+ re_dfa_t *dfa;
+ re_dfastate_t *newstate;
+ unsigned int hash;
+{
+ struct re_state_table_entry *spot;
+ spot = dfa->state_table + (hash & dfa->state_hash_mask);
+
+ if (spot->alloc <= spot->num)
+ {
+ spot->alloc = 2 * spot->num + 2;
+ spot->array = re_realloc (spot->array, re_dfastate_t *, spot->alloc);
+ if (BE (spot->array == NULL, 0))
+ return REG_ESPACE;
+ }
+ spot->array[spot->num++] = newstate;
+ return REG_NOERROR;
+}
+
+/* Create the new state which is independ of contexts.
+ Return the new state if succeeded, otherwise return NULL. */
+
+static re_dfastate_t *
+create_ci_newstate (dfa, nodes, hash)
+ re_dfa_t *dfa;
+ const re_node_set *nodes;
+ unsigned int hash;
+{
+ int i;
+ reg_errcode_t err;
+ re_dfastate_t *newstate;
+ newstate = create_newstate_common (dfa, nodes, hash);
+ if (BE (newstate == NULL, 0))
+ return NULL;
+ newstate->entrance_nodes = &newstate->nodes;
+
+ for (i = 0 ; i < nodes->nelem ; i++)
+ {
+ re_token_t *node = dfa->nodes + nodes->elems[i];
+ re_token_type_t type = node->type;
+ if (type == CHARACTER)
+ continue;
+
+ /* If the state has the halt node, the state is a halt state. */
+ else if (type == END_OF_RE)
+ newstate->halt = 1;
+#ifdef RE_ENABLE_I18N
+ else if (type == COMPLEX_BRACKET
+ || (type == OP_PERIOD && MB_CUR_MAX > 1))
+ newstate->accept_mb = 1;
+#endif /* RE_ENABLE_I18N */
+ else if (type == OP_BACK_REF)
+ newstate->has_backref = 1;
+ else if (type == ANCHOR || OP_CONTEXT_NODE)
+ {
+ newstate->has_constraint = 1;
+ if (type == OP_CONTEXT_NODE
+ && dfa->nodes[node->opr.ctx_info->entity].type == END_OF_RE)
+ newstate->halt = 1;
+ }
+ }
+ err = register_state (dfa, newstate, hash);
+ return (err != REG_NOERROR) ? NULL : newstate;
+}
+
+/* Create the new state which is depend on the context CONTEXT.
+ Return the new state if succeeded, otherwise return NULL. */
+
+static re_dfastate_t *
+create_cd_newstate (dfa, nodes, context, hash)
+ re_dfa_t *dfa;
+ const re_node_set *nodes;
+ unsigned int context, hash;
+{
+ int i, nctx_nodes = 0;
+ reg_errcode_t err;
+ re_dfastate_t *newstate;
+
+ newstate = create_newstate_common (dfa, nodes, hash);
+ if (BE (newstate == NULL, 0))
+ return NULL;
+ newstate->context = context;
+ newstate->entrance_nodes = &newstate->nodes;
+
+ for (i = 0 ; i < nodes->nelem ; i++)
+ {
+ unsigned int constraint = 0;
+ re_token_t *node = dfa->nodes + nodes->elems[i];
+ re_token_type_t type = node->type;
+ if (type == CHARACTER)
+ continue;
+
+ /* If the state has the halt node, the state is a halt state. */
+ else if (type == END_OF_RE)
+ newstate->halt = 1;
+#ifdef RE_ENABLE_I18N
+ else if (type == COMPLEX_BRACKET
+ || (type == OP_PERIOD && MB_CUR_MAX > 1))
+ newstate->accept_mb = 1;
+#endif /* RE_ENABLE_I18N */
+ else if (type == OP_BACK_REF)
+ newstate->has_backref = 1;
+ else if (type == ANCHOR)
+ constraint = node->opr.ctx_type;
+ else if (type == OP_CONTEXT_NODE)
+ {
+ re_token_type_t ctype = dfa->nodes[node->opr.ctx_info->entity].type;
+ constraint = node->constraint;
+ if (ctype == END_OF_RE)
+ newstate->halt = 1;
+ else if (ctype == OP_BACK_REF)
+ newstate->has_backref = 1;
+#ifdef RE_ENABLE_I18N
+ else if (ctype == COMPLEX_BRACKET
+ || (type == OP_PERIOD && MB_CUR_MAX > 1))
+ newstate->accept_mb = 1;
+#endif /* RE_ENABLE_I18N */
+ }
+
+ if (constraint)
+ {
+ if (newstate->entrance_nodes == &newstate->nodes)
+ {
+ newstate->entrance_nodes = re_malloc (re_node_set, 1);
+ if (BE (newstate->entrance_nodes == NULL, 0))
+ return NULL;
+ re_node_set_init_copy (newstate->entrance_nodes, nodes);
+ nctx_nodes = 0;
+ newstate->has_constraint = 1;
+ }
+
+ if (NOT_SATISFY_PREV_CONSTRAINT (constraint,context))
+ {
+ re_node_set_remove_at (&newstate->nodes, i - nctx_nodes);
+ ++nctx_nodes;
+ }
+ }
+ }
+ err = register_state (dfa, newstate, hash);
+ return (err != REG_NOERROR) ? NULL : newstate;
+}
diff --git a/util/regex_internal.h b/util/regex_internal.h
new file mode 100644
index 0000000..679bea1
--- /dev/null
+++ b/util/regex_internal.h
@@ -0,0 +1,643 @@
+/* Extended regular expression matching and search library.
+ Copyright (C) 2002 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02110-1301 USA. */
+
+#ifndef _REGEX_INTERNAL_H
+#define _REGEX_INTERNAL_H 1
+
+/* Number of bits in a byte. */
+#define BYTE_BITS 8
+/* Number of single byte character. */
+#define SBC_MAX 256
+
+#define COLL_ELEM_LEN_MAX 8
+
+/* The character which represents newline. */
+#define NEWLINE_CHAR '\n'
+
+/* Rename to standard API for using out of glibc. */
+#ifndef _LIBC
+# define __wctype wctype
+# define __iswctype iswctype
+# define __btowc btowc
+# define __mempcpy memcpy
+# define attribute_hidden
+#endif /* not _LIBC */
+
+extern const char __re_error_msgid[] attribute_hidden;
+extern const size_t __re_error_msgid_idx[] attribute_hidden;
+
+/* Number of bits in an unsinged int. */
+#define UINT_BITS (sizeof (unsigned int) * BYTE_BITS)
+/* Number of unsigned int in an bit_set. */
+#define BITSET_UINTS ((SBC_MAX + UINT_BITS - 1) / UINT_BITS)
+typedef unsigned int bitset[BITSET_UINTS];
+typedef unsigned int *re_bitset_ptr_t;
+
+#define bitset_set(set,i) (set[i / UINT_BITS] |= 1 << i % UINT_BITS)
+#define bitset_clear(set,i) (set[i / UINT_BITS] &= ~(1 << i % UINT_BITS))
+#define bitset_contain(set,i) (set[i / UINT_BITS] & (1 << i % UINT_BITS))
+#define bitset_empty(set) memset (set, 0, sizeof (unsigned int) * BITSET_UINTS)
+#define bitset_set_all(set) \
+ memset (set, 255, sizeof (unsigned int) * BITSET_UINTS)
+#define bitset_copy(dest,src) \
+ memcpy (dest, src, sizeof (unsigned int) * BITSET_UINTS)
+static inline void bitset_not (bitset set);
+static inline void bitset_merge (bitset dest, const bitset src);
+#if 0 /* gnupg */
+static inline void bitset_not_merge (bitset dest, const bitset src);
+#endif
+
+#define PREV_WORD_CONSTRAINT 0x0001
+#define PREV_NOTWORD_CONSTRAINT 0x0002
+#define NEXT_WORD_CONSTRAINT 0x0004
+#define NEXT_NOTWORD_CONSTRAINT 0x0008
+#define PREV_NEWLINE_CONSTRAINT 0x0010
+#define NEXT_NEWLINE_CONSTRAINT 0x0020
+#define PREV_BEGBUF_CONSTRAINT 0x0040
+#define NEXT_ENDBUF_CONSTRAINT 0x0080
+#define DUMMY_CONSTRAINT 0x0100
+
+typedef enum
+{
+ INSIDE_WORD = PREV_WORD_CONSTRAINT | NEXT_WORD_CONSTRAINT,
+ WORD_FIRST = PREV_NOTWORD_CONSTRAINT | NEXT_WORD_CONSTRAINT,
+ WORD_LAST = PREV_WORD_CONSTRAINT | NEXT_NOTWORD_CONSTRAINT,
+ LINE_FIRST = PREV_NEWLINE_CONSTRAINT,
+ LINE_LAST = NEXT_NEWLINE_CONSTRAINT,
+ BUF_FIRST = PREV_BEGBUF_CONSTRAINT,
+ BUF_LAST = NEXT_ENDBUF_CONSTRAINT,
+ WORD_DELIM = DUMMY_CONSTRAINT
+} re_context_type;
+
+typedef struct
+{
+ int alloc;
+ int nelem;
+ int *elems;
+} re_node_set;
+
+typedef enum
+{
+ NON_TYPE = 0,
+
+ /* Token type, these are used only by token. */
+ OP_OPEN_BRACKET,
+ OP_CLOSE_BRACKET,
+ OP_CHARSET_RANGE,
+ OP_OPEN_DUP_NUM,
+ OP_CLOSE_DUP_NUM,
+ OP_NON_MATCH_LIST,
+ OP_OPEN_COLL_ELEM,
+ OP_CLOSE_COLL_ELEM,
+ OP_OPEN_EQUIV_CLASS,
+ OP_CLOSE_EQUIV_CLASS,
+ OP_OPEN_CHAR_CLASS,
+ OP_CLOSE_CHAR_CLASS,
+ OP_WORD,
+ OP_NOTWORD,
+ BACK_SLASH,
+
+ /* Tree type, these are used only by tree. */
+ CONCAT,
+ ALT,
+ SUBEXP,
+ SIMPLE_BRACKET,
+#ifdef RE_ENABLE_I18N
+ COMPLEX_BRACKET,
+#endif /* RE_ENABLE_I18N */
+
+ /* Node type, These are used by token, node, tree. */
+ OP_OPEN_SUBEXP,
+ OP_CLOSE_SUBEXP,
+ OP_PERIOD,
+ CHARACTER,
+ END_OF_RE,
+ OP_ALT,
+ OP_DUP_ASTERISK,
+ OP_DUP_PLUS,
+ OP_DUP_QUESTION,
+ OP_BACK_REF,
+ ANCHOR,
+ OP_CONTEXT_NODE,
+
+ /* Dummy marker. */
+ END_OF_RE_TOKEN_T
+} re_token_type_t;
+
+#ifdef RE_ENABLE_I18N
+typedef struct
+{
+ /* Multibyte characters. */
+ wchar_t *mbchars;
+
+ /* Collating symbols. */
+# ifdef _LIBC
+ int32_t *coll_syms;
+# endif
+
+ /* Equivalence classes. */
+# ifdef _LIBC
+ int32_t *equiv_classes;
+# endif
+
+ /* Range expressions. */
+# ifdef _LIBC
+ uint32_t *range_starts;
+ uint32_t *range_ends;
+# else /* not _LIBC */
+ wchar_t *range_starts;
+ wchar_t *range_ends;
+# endif /* not _LIBC */
+
+ /* Character classes. */
+ wctype_t *char_classes;
+
+ /* If this character set is the non-matching list. */
+ unsigned int non_match : 1;
+
+ /* # of multibyte characters. */
+ int nmbchars;
+
+ /* # of collating symbols. */
+ int ncoll_syms;
+
+ /* # of equivalence classes. */
+ int nequiv_classes;
+
+ /* # of range expressions. */
+ int nranges;
+
+ /* # of character classes. */
+ int nchar_classes;
+} re_charset_t;
+#endif /* RE_ENABLE_I18N */
+
+typedef struct
+{
+ union
+ {
+ unsigned char c; /* for CHARACTER */
+ re_bitset_ptr_t sbcset; /* for SIMPLE_BRACKET */
+#ifdef RE_ENABLE_I18N
+ re_charset_t *mbcset; /* for COMPLEX_BRACKET */
+#endif /* RE_ENABLE_I18N */
+ int idx; /* for BACK_REF */
+ re_context_type ctx_type; /* for ANCHOR */
+ struct
+ {
+ int entity; /* for OP_CONTEXT_NODE, index of the entity */
+ re_node_set *bkref_eclosure;
+ } *ctx_info;
+ } opr;
+#if __GNUC__ >= 2
+ re_token_type_t type : 8;
+#else
+ re_token_type_t type;
+#endif
+ unsigned int constraint : 10; /* context constraint */
+ unsigned int duplicated : 1;
+#ifdef RE_ENABLE_I18N
+ unsigned int mb_partial : 1;
+#endif
+} re_token_t;
+
+#define IS_EPSILON_NODE(type) \
+ ((type) == OP_ALT || (type) == OP_DUP_ASTERISK || (type) == OP_DUP_PLUS \
+ || (type) == OP_DUP_QUESTION || (type) == ANCHOR \
+ || (type) == OP_OPEN_SUBEXP || (type) == OP_CLOSE_SUBEXP)
+
+#define ACCEPT_MB_NODE(type) \
+ ((type) == COMPLEX_BRACKET || (type) == OP_PERIOD)
+
+struct re_string_t
+{
+ /* Indicate the raw buffer which is the original string passed as an
+ argument of regexec(), re_search(), etc.. */
+ const unsigned char *raw_mbs;
+ /* Store the multibyte string. In case of "case insensitive mode" like
+ REG_ICASE, upper cases of the string are stored, otherwise MBS points
+ the same address that RAW_MBS points. */
+ unsigned char *mbs;
+ /* Store the case sensitive multibyte string. In case of
+ "case insensitive mode", the original string are stored,
+ otherwise MBS_CASE points the same address that MBS points. */
+ unsigned char *mbs_case;
+#ifdef RE_ENABLE_I18N
+ /* Store the wide character string which is corresponding to MBS. */
+ wint_t *wcs;
+ mbstate_t cur_state;
+#endif
+ /* Index in RAW_MBS. Each character mbs[i] corresponds to
+ raw_mbs[raw_mbs_idx + i]. */
+ int raw_mbs_idx;
+ /* The length of the valid characters in the buffers. */
+ int valid_len;
+ /* The length of the buffers MBS, MBS_CASE, and WCS. */
+ int bufs_len;
+ /* The index in MBS, which is updated by re_string_fetch_byte. */
+ int cur_idx;
+ /* This is length_of_RAW_MBS - RAW_MBS_IDX. */
+ int len;
+ /* End of the buffer may be shorter than its length in the cases such
+ as re_match_2, re_search_2. Then, we use STOP for end of the buffer
+ instead of LEN. */
+ int stop;
+
+ /* The context of mbs[0]. We store the context independently, since
+ the context of mbs[0] may be different from raw_mbs[0], which is
+ the beginning of the input string. */
+ unsigned int tip_context;
+ /* The translation passed as a part of an argument of re_compile_pattern. */
+ RE_TRANSLATE_TYPE trans;
+ /* 1 if REG_ICASE. */
+ unsigned int icase : 1;
+};
+typedef struct re_string_t re_string_t;
+/* In case of REG_ICASE, we allocate the buffer dynamically for mbs. */
+#define MBS_ALLOCATED(pstr) (pstr->icase)
+/* In case that we need translation, we allocate the buffer dynamically
+ for mbs_case. Note that mbs == mbs_case if not REG_ICASE. */
+#define MBS_CASE_ALLOCATED(pstr) (pstr->trans != NULL)
+
+
+static reg_errcode_t re_string_allocate (re_string_t *pstr, const char *str,
+ int len, int init_len,
+ RE_TRANSLATE_TYPE trans, int icase);
+static reg_errcode_t re_string_construct (re_string_t *pstr, const char *str,
+ int len, RE_TRANSLATE_TYPE trans,
+ int icase);
+static reg_errcode_t re_string_reconstruct (re_string_t *pstr, int idx,
+ int eflags, int newline);
+static reg_errcode_t re_string_realloc_buffers (re_string_t *pstr,
+ int new_buf_len);
+#ifdef RE_ENABLE_I18N
+static void build_wcs_buffer (re_string_t *pstr);
+static void build_wcs_upper_buffer (re_string_t *pstr);
+#endif /* RE_ENABLE_I18N */
+static void build_upper_buffer (re_string_t *pstr);
+static void re_string_translate_buffer (re_string_t *pstr);
+static void re_string_destruct (re_string_t *pstr);
+#ifdef RE_ENABLE_I18N
+static int re_string_elem_size_at (const re_string_t *pstr, int idx);
+static inline int re_string_char_size_at (const re_string_t *pstr, int idx);
+static inline wint_t re_string_wchar_at (const re_string_t *pstr, int idx);
+#endif /* RE_ENABLE_I18N */
+static unsigned int re_string_context_at (const re_string_t *input, int idx,
+ int eflags, int newline_anchor);
+#define re_string_peek_byte(pstr, offset) \
+ ((pstr)->mbs[(pstr)->cur_idx + offset])
+#define re_string_peek_byte_case(pstr, offset) \
+ ((pstr)->mbs_case[(pstr)->cur_idx + offset])
+#define re_string_fetch_byte(pstr) \
+ ((pstr)->mbs[(pstr)->cur_idx++])
+#define re_string_fetch_byte_case(pstr) \
+ ((pstr)->mbs_case[(pstr)->cur_idx++])
+#define re_string_first_byte(pstr, idx) \
+ ((idx) == (pstr)->len || (pstr)->wcs[idx] != WEOF)
+#define re_string_is_single_byte_char(pstr, idx) \
+ ((pstr)->wcs[idx] != WEOF && ((pstr)->len == (idx) \
+ || (pstr)->wcs[(idx) + 1] != WEOF))
+#define re_string_eoi(pstr) ((pstr)->stop <= (pstr)->cur_idx)
+#define re_string_cur_idx(pstr) ((pstr)->cur_idx)
+#define re_string_get_buffer(pstr) ((pstr)->mbs)
+#define re_string_length(pstr) ((pstr)->len)
+#define re_string_byte_at(pstr,idx) ((pstr)->mbs[idx])
+#define re_string_skip_bytes(pstr,idx) ((pstr)->cur_idx += (idx))
+#define re_string_set_index(pstr,idx) ((pstr)->cur_idx = (idx))
+
+#define re_malloc(t,n) ((t *) malloc ((n) * sizeof (t)))
+#define re_realloc(p,t,n) ((t *) realloc (p, (n) * sizeof (t)))
+#define re_free(p) free (p)
+
+struct bin_tree_t
+{
+ struct bin_tree_t *parent;
+ struct bin_tree_t *left;
+ struct bin_tree_t *right;
+
+ /* `node_idx' is the index in dfa->nodes, if `type' == 0.
+ Otherwise `type' indicate the type of this node. */
+ re_token_type_t type;
+ int node_idx;
+
+ int first;
+ int next;
+ re_node_set eclosure;
+};
+typedef struct bin_tree_t bin_tree_t;
+
+
+#define CONTEXT_WORD 1
+#define CONTEXT_NEWLINE (CONTEXT_WORD << 1)
+#define CONTEXT_BEGBUF (CONTEXT_NEWLINE << 1)
+#define CONTEXT_ENDBUF (CONTEXT_BEGBUF << 1)
+
+#define IS_WORD_CONTEXT(c) ((c) & CONTEXT_WORD)
+#define IS_NEWLINE_CONTEXT(c) ((c) & CONTEXT_NEWLINE)
+#define IS_BEGBUF_CONTEXT(c) ((c) & CONTEXT_BEGBUF)
+#define IS_ENDBUF_CONTEXT(c) ((c) & CONTEXT_ENDBUF)
+#define IS_ORDINARY_CONTEXT(c) ((c) == 0)
+
+#define IS_WORD_CHAR(ch) (isalnum (ch) || (ch) == '_')
+#define IS_NEWLINE(ch) ((ch) == NEWLINE_CHAR)
+
+#define NOT_SATISFY_PREV_CONSTRAINT(constraint,context) \
+ ((((constraint) & PREV_WORD_CONSTRAINT) && !IS_WORD_CONTEXT (context)) \
+ || ((constraint & PREV_NOTWORD_CONSTRAINT) && IS_WORD_CONTEXT (context)) \
+ || ((constraint & PREV_NEWLINE_CONSTRAINT) && !IS_NEWLINE_CONTEXT (context))\
+ || ((constraint & PREV_BEGBUF_CONSTRAINT) && !IS_BEGBUF_CONTEXT (context)))
+
+#define NOT_SATISFY_NEXT_CONSTRAINT(constraint,context) \
+ ((((constraint) & NEXT_WORD_CONSTRAINT) && !IS_WORD_CONTEXT (context)) \
+ || (((constraint) & NEXT_NOTWORD_CONSTRAINT) && IS_WORD_CONTEXT (context)) \
+ || (((constraint) & NEXT_NEWLINE_CONSTRAINT) && !IS_NEWLINE_CONTEXT (context)) \
+ || (((constraint) & NEXT_ENDBUF_CONSTRAINT) && !IS_ENDBUF_CONTEXT (context)))
+
+struct re_dfastate_t
+{
+ unsigned int hash;
+ re_node_set nodes;
+ re_node_set *entrance_nodes;
+ struct re_dfastate_t **trtable;
+ struct re_dfastate_t **trtable_search;
+ /* If this state is a special state.
+ A state is a special state if the state is the halt state, or
+ a anchor. */
+ unsigned int context : 2;
+ unsigned int halt : 1;
+ /* If this state can accept `multi byte'.
+ Note that we refer to multibyte characters, and multi character
+ collating elements as `multi byte'. */
+ unsigned int accept_mb : 1;
+ /* If this state has backreference node(s). */
+ unsigned int has_backref : 1;
+ unsigned int has_constraint : 1;
+};
+typedef struct re_dfastate_t re_dfastate_t;
+
+typedef struct
+{
+ /* start <= node < end */
+ int start;
+ int end;
+} re_subexp_t;
+
+struct re_state_table_entry
+{
+ int num;
+ int alloc;
+ re_dfastate_t **array;
+};
+
+struct re_backref_cache_entry
+{
+ int node;
+ int str_idx;
+ int subexp_from;
+ int subexp_to;
+ int flag;
+};
+
+typedef struct
+{
+ /* EFLAGS of the argument of regexec. */
+ int eflags;
+ /* Where the matching ends. */
+ int match_last;
+ int last_node;
+ /* The string object corresponding to the input string. */
+ re_string_t *input;
+ /* The state log used by the matcher. */
+ re_dfastate_t **state_log;
+ int state_log_top;
+ /* Back reference cache. */
+ int nbkref_ents;
+ int abkref_ents;
+ struct re_backref_cache_entry *bkref_ents;
+ int max_mb_elem_len;
+} re_match_context_t;
+
+typedef struct
+{
+ int cur_bkref;
+ int cls_subexp_idx;
+
+ re_dfastate_t **sifted_states;
+ re_dfastate_t **limited_states;
+
+ re_node_set limits;
+
+ int last_node;
+ int last_str_idx;
+ int check_subexp;
+} re_sift_context_t;
+
+struct re_fail_stack_ent_t
+{
+ int idx;
+ int node;
+ regmatch_t *regs;
+ re_node_set eps_via_nodes;
+};
+
+struct re_fail_stack_t
+{
+ int num;
+ int alloc;
+ struct re_fail_stack_ent_t *stack;
+};
+
+struct re_dfa_t
+{
+ re_bitset_ptr_t word_char;
+
+ /* number of subexpressions `re_nsub' is in regex_t. */
+ int subexps_alloc;
+ re_subexp_t *subexps;
+
+ re_token_t *nodes;
+ int nodes_alloc;
+ int nodes_len;
+ bin_tree_t *str_tree;
+ int *firsts;
+ int *nexts;
+ re_node_set *edests;
+ re_node_set *eclosures;
+ re_node_set *inveclosures;
+ struct re_state_table_entry *state_table;
+ unsigned int state_hash_mask;
+ re_dfastate_t *init_state;
+ re_dfastate_t *init_state_word;
+ re_dfastate_t *init_state_nl;
+ re_dfastate_t *init_state_begbuf;
+ int states_alloc;
+ int init_node;
+ int nbackref; /* The number of backreference in this dfa. */
+ /* If this dfa has "multibyte node", which is a backreference or
+ a node which can accept multibyte character or multi character
+ collating element. */
+#ifdef DEBUG
+ char* re_str;
+#endif
+ unsigned int has_plural_match : 1;
+ unsigned int has_mb_node : 1;
+};
+typedef struct re_dfa_t re_dfa_t;
+
+static reg_errcode_t re_node_set_alloc (re_node_set *set, int size);
+static reg_errcode_t re_node_set_init_1 (re_node_set *set, int elem);
+static reg_errcode_t re_node_set_init_2 (re_node_set *set, int elem1,
+ int elem2);
+#define re_node_set_init_empty(set) memset (set, '\0', sizeof (re_node_set))
+static reg_errcode_t re_node_set_init_copy (re_node_set *dest,
+ const re_node_set *src);
+static reg_errcode_t re_node_set_add_intersect (re_node_set *dest,
+ const re_node_set *src1,
+ const re_node_set *src2);
+static reg_errcode_t re_node_set_init_union (re_node_set *dest,
+ const re_node_set *src1,
+ const re_node_set *src2);
+static reg_errcode_t re_node_set_merge (re_node_set *dest,
+ const re_node_set *src);
+static int re_node_set_insert (re_node_set *set, int elem);
+static int re_node_set_compare (const re_node_set *set1,
+ const re_node_set *set2);
+static int re_node_set_contains (const re_node_set *set, int elem);
+static void re_node_set_remove_at (re_node_set *set, int idx);
+#define re_node_set_empty(p) ((p)->nelem = 0)
+#define re_node_set_free(set) re_free ((set)->elems)
+static int re_dfa_add_node (re_dfa_t *dfa, re_token_t token, int mode);
+static re_dfastate_t *re_acquire_state (reg_errcode_t *err, re_dfa_t *dfa,
+ const re_node_set *nodes);
+static re_dfastate_t *re_acquire_state_context (reg_errcode_t *err,
+ re_dfa_t *dfa,
+ const re_node_set *nodes,
+ unsigned int context);
+
+
+typedef enum
+{
+ SB_CHAR,
+ MB_CHAR,
+ EQUIV_CLASS,
+ COLL_SYM,
+ CHAR_CLASS
+} bracket_elem_type;
+
+typedef struct
+{
+ bracket_elem_type type;
+ union
+ {
+ unsigned char ch;
+ unsigned char *name;
+ wchar_t wch;
+ } opr;
+} bracket_elem_t;
+
+
+/* Inline functions for bitset operation. */
+static inline void
+bitset_not (set)
+ bitset set;
+{
+ int bitset_i;
+ for (bitset_i = 0; bitset_i < BITSET_UINTS; ++bitset_i)
+ set[bitset_i] = ~set[bitset_i];
+}
+
+static inline void
+bitset_merge (dest, src)
+ bitset dest;
+ const bitset src;
+{
+ int bitset_i;
+ for (bitset_i = 0; bitset_i < BITSET_UINTS; ++bitset_i)
+ dest[bitset_i] |= src[bitset_i];
+}
+
+#if 0 /* gnupg */
+static inline void
+bitset_not_merge (dest, src)
+ bitset dest;
+ const bitset src;
+{
+ int i;
+ for (i = 0; i < BITSET_UINTS; ++i)
+ dest[i] |= ~src[i];
+}
+#endif
+
+#ifdef RE_ENABLE_I18N
+/* Inline functions for re_string. */
+static inline int
+re_string_char_size_at (pstr, idx)
+ const re_string_t *pstr;
+ int idx;
+{
+ int byte_idx;
+ if (MB_CUR_MAX == 1)
+ return 1;
+ for (byte_idx = 1; idx + byte_idx < pstr->len; ++byte_idx)
+ if (pstr->wcs[idx + byte_idx] != WEOF)
+ break;
+ return byte_idx;
+}
+
+static inline wint_t
+re_string_wchar_at (pstr, idx)
+ const re_string_t *pstr;
+ int idx;
+{
+ if (MB_CUR_MAX == 1)
+ return (wint_t) pstr->mbs[idx];
+ return (wint_t) pstr->wcs[idx];
+}
+
+static int
+re_string_elem_size_at (pstr, idx)
+ const re_string_t *pstr;
+ int idx;
+{
+#ifdef _LIBC
+ const unsigned char *p, *extra;
+ const int32_t *table, *indirect;
+ int32_t tmp;
+# include <locale/weight.h>
+ uint_fast32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
+
+ if (nrules != 0)
+ {
+ table = (const int32_t *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB);
+ extra = (const unsigned char *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAMB);
+ indirect = (const int32_t *) _NL_CURRENT (LC_COLLATE,
+ _NL_COLLATE_INDIRECTMB);
+ p = pstr->mbs + idx;
+ tmp = findidx (&p);
+ return p - pstr->mbs - idx;
+ }
+ else
+#endif /* _LIBC */
+ return 1;
+}
+#endif /* RE_ENABLE_I18N */
+
+#endif /* _REGEX_INTERNAL_H */
diff --git a/util/regexec.c b/util/regexec.c
new file mode 100644
index 0000000..40d3058
--- /dev/null
+++ b/util/regexec.c
@@ -0,0 +1,3225 @@
+/* Extended regular expression matching and search library.
+ Copyright (C) 2002 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02110-1301 USA. */
+
+#include <assert.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if defined HAVE_WCHAR_H || defined _LIBC
+# include <wchar.h>
+#endif /* HAVE_WCHAR_H || _LIBC */
+#if defined HAVE_WCTYPE_H || defined _LIBC
+# include <wctype.h>
+#endif /* HAVE_WCTYPE_H || _LIBC */
+
+#ifdef _LIBC
+# ifndef _RE_DEFINE_LOCALE_FUNCTIONS
+# define _RE_DEFINE_LOCALE_FUNCTIONS 1
+# include <locale/localeinfo.h>
+# include <locale/elem-hash.h>
+# include <locale/coll-lookup.h>
+# endif
+#endif
+
+#include "_regex.h" /* gnupg */
+#include "regex_internal.h"
+
+static reg_errcode_t match_ctx_init (re_match_context_t *cache, int eflags,
+ re_string_t *input, int n);
+static void match_ctx_free (re_match_context_t *cache);
+static reg_errcode_t match_ctx_add_entry (re_match_context_t *cache, int node,
+ int str_idx, int from, int to);
+static void match_ctx_clear_flag (re_match_context_t *mctx);
+static void sift_ctx_init (re_sift_context_t *sctx, re_dfastate_t **sifted_sts,
+ re_dfastate_t **limited_sts, int last_node,
+ int last_str_idx, int check_subexp);
+static reg_errcode_t re_search_internal (const regex_t *preg,
+ const char *string, int length,
+ int start, int range, int stop,
+ size_t nmatch, regmatch_t pmatch[],
+ int eflags);
+static int re_search_2_stub (struct re_pattern_buffer *bufp,
+ const char *string1, int length1,
+ const char *string2, int length2,
+ int start, int range, struct re_registers *regs,
+ int stop, int ret_len);
+static int re_search_stub (struct re_pattern_buffer *bufp,
+ const char *string, int length, int start,
+ int range, int stop, struct re_registers *regs,
+ int ret_len);
+static unsigned re_copy_regs (struct re_registers *regs, regmatch_t *pmatch,
+ int nregs, int regs_allocated);
+static inline re_dfastate_t *acquire_init_state_context (reg_errcode_t *err,
+ const regex_t *preg,
+ const re_match_context_t *mctx,
+ int idx);
+static int check_matching (const regex_t *preg, re_match_context_t *mctx,
+ int fl_search, int fl_longest_match);
+static int check_halt_node_context (const re_dfa_t *dfa, int node,
+ unsigned int context);
+static int check_halt_state_context (const regex_t *preg,
+ const re_dfastate_t *state,
+ const re_match_context_t *mctx, int idx);
+static void update_regs (re_dfa_t *dfa, regmatch_t *pmatch, int cur_node,
+ int cur_idx, int nmatch);
+static int proceed_next_node (const regex_t *preg, int nregs, regmatch_t *regs,
+ const re_match_context_t *mctx,
+ int *pidx, int node, re_node_set *eps_via_nodes,
+ struct re_fail_stack_t *fs);
+static reg_errcode_t push_fail_stack (struct re_fail_stack_t *fs,
+ int str_idx, int *dests, int nregs,
+ regmatch_t *regs,
+ re_node_set *eps_via_nodes);
+static int pop_fail_stack (struct re_fail_stack_t *fs, int *pidx, int nregs,
+ regmatch_t *regs, re_node_set *eps_via_nodes);
+static reg_errcode_t set_regs (const regex_t *preg,
+ const re_match_context_t *mctx,
+ size_t nmatch, regmatch_t *pmatch,
+ int fl_backtrack);
+#ifdef RE_ENABLE_I18N
+static int sift_states_iter_mb (const regex_t *preg,
+ const re_match_context_t *mctx,
+ re_sift_context_t *sctx,
+ int node_idx, int str_idx, int max_str_idx);
+#endif /* RE_ENABLE_I18N */
+static reg_errcode_t sift_states_backward (const regex_t *preg,
+ re_match_context_t *mctx,
+ re_sift_context_t *sctx);
+static reg_errcode_t update_cur_sifted_state (const regex_t *preg,
+ re_match_context_t *mctx,
+ re_sift_context_t *sctx,
+ int str_idx,
+ re_node_set *dest_nodes);
+static reg_errcode_t add_epsilon_src_nodes (re_dfa_t *dfa,
+ re_node_set *dest_nodes,
+ const re_node_set *candidates);
+static reg_errcode_t sub_epsilon_src_nodes (re_dfa_t *dfa, int node,
+ re_node_set *dest_nodes,
+ const re_node_set *and_nodes);
+static int check_dst_limits (re_dfa_t *dfa, re_node_set *limits,
+ re_match_context_t *mctx, int dst_node,
+ int dst_idx, int src_node, int src_idx);
+static int check_dst_limits_calc_pos (re_dfa_t *dfa, re_match_context_t *mctx,
+ int limit, re_node_set *eclosures,
+ int subexp_idx, int node, int str_idx);
+static reg_errcode_t check_subexp_limits (re_dfa_t *dfa,
+ re_node_set *dest_nodes,
+ const re_node_set *candidates,
+ re_node_set *limits,
+ struct re_backref_cache_entry *bkref_ents,
+ int str_idx);
+static reg_errcode_t search_subexp (const regex_t *preg,
+ re_match_context_t *mctx,
+ re_sift_context_t *sctx, int str_idx,
+ re_node_set *dest_nodes);
+static reg_errcode_t sift_states_bkref (const regex_t *preg,
+ re_match_context_t *mctx,
+ re_sift_context_t *sctx,
+ int str_idx, re_node_set *dest_nodes);
+static reg_errcode_t clean_state_log_if_need (re_match_context_t *mctx,
+ int next_state_log_idx);
+static reg_errcode_t merge_state_array (re_dfa_t *dfa, re_dfastate_t **dst,
+ re_dfastate_t **src, int num);
+static re_dfastate_t *transit_state (reg_errcode_t *err, const regex_t *preg,
+ re_match_context_t *mctx,
+ re_dfastate_t *state, int fl_search);
+static re_dfastate_t *transit_state_sb (reg_errcode_t *err, const regex_t *preg,
+ re_dfastate_t *pstate,
+ int fl_search,
+ re_match_context_t *mctx);
+#ifdef RE_ENABLE_I18N
+static reg_errcode_t transit_state_mb (const regex_t *preg,
+ re_dfastate_t *pstate,
+ re_match_context_t *mctx);
+#endif /* RE_ENABLE_I18N */
+static reg_errcode_t transit_state_bkref (const regex_t *preg,
+ re_dfastate_t *pstate,
+ re_match_context_t *mctx);
+static reg_errcode_t transit_state_bkref_loop (const regex_t *preg,
+ re_node_set *nodes,
+ re_dfastate_t **work_state_log,
+ re_match_context_t *mctx);
+static re_dfastate_t **build_trtable (const regex_t *dfa,
+ const re_dfastate_t *state,
+ int fl_search);
+#ifdef RE_ENABLE_I18N
+static int check_node_accept_bytes (const regex_t *preg, int node_idx,
+ const re_string_t *input, int idx);
+# ifdef _LIBC
+static unsigned int find_collation_sequence_value (const unsigned char *mbs,
+ size_t name_len);
+# endif /* _LIBC */
+#endif /* RE_ENABLE_I18N */
+static int group_nodes_into_DFAstates (const regex_t *dfa,
+ const re_dfastate_t *state,
+ re_node_set *states_node,
+ bitset *states_ch);
+static int check_node_accept (const regex_t *preg, const re_token_t *node,
+ const re_match_context_t *mctx, int idx);
+static reg_errcode_t extend_buffers (re_match_context_t *mctx);
+
+/* Entry point for POSIX code. */
+
+/* regexec searches for a given pattern, specified by PREG, in the
+ string STRING.
+
+ If NMATCH is zero or REG_NOSUB was set in the cflags argument to
+ `regcomp', we ignore PMATCH. Otherwise, we assume PMATCH has at
+ least NMATCH elements, and we set them to the offsets of the
+ corresponding matched substrings.
+
+ EFLAGS specifies `execution flags' which affect matching: if
+ REG_NOTBOL is set, then ^ does not match at the beginning of the
+ string; if REG_NOTEOL is set, then $ does not match at the end.
+
+ We return 0 if we find a match and REG_NOMATCH if not. */
+
+int
+regexec (preg, string, nmatch, pmatch, eflags)
+ const regex_t *__restrict preg;
+ const char *__restrict string;
+ size_t nmatch;
+ regmatch_t pmatch[];
+ int eflags;
+{
+ reg_errcode_t err;
+ int length = strlen (string);
+ if (preg->no_sub)
+ err = re_search_internal (preg, string, length, 0, length, length, 0,
+ NULL, eflags);
+ else
+ err = re_search_internal (preg, string, length, 0, length, length, nmatch,
+ pmatch, eflags);
+ return err != REG_NOERROR;
+}
+#ifdef _LIBC
+weak_alias (__regexec, regexec)
+#endif
+
+/* Entry points for GNU code. */
+
+/* re_match, re_search, re_match_2, re_search_2
+
+ The former two functions operate on STRING with length LENGTH,
+ while the later two operate on concatenation of STRING1 and STRING2
+ with lengths LENGTH1 and LENGTH2, respectively.
+
+ re_match() matches the compiled pattern in BUFP against the string,
+ starting at index START.
+
+ re_search() first tries matching at index START, then it tries to match
+ starting from index START + 1, and so on. The last start position tried
+ is START + RANGE. (Thus RANGE = 0 forces re_search to operate the same
+ way as re_match().)
+
+ The parameter STOP of re_{match,search}_2 specifies that no match exceeding
+ the first STOP characters of the concatenation of the strings should be
+ concerned.
+
+ If REGS is not NULL, and BUFP->no_sub is not set, the offsets of the match
+ and all groups is stroed in REGS. (For the "_2" variants, the offsets are
+ computed relative to the concatenation, not relative to the individual
+ strings.)
+
+ On success, re_match* functions return the length of the match, re_search*
+ return the position of the start of the match. Return value -1 means no
+ match was found and -2 indicates an internal error. */
+
+int
+re_match (bufp, string, length, start, regs)
+ struct re_pattern_buffer *bufp;
+ const char *string;
+ int length, start;
+ struct re_registers *regs;
+{
+ return re_search_stub (bufp, string, length, start, 0, length, regs, 1);
+}
+#ifdef _LIBC
+weak_alias (__re_match, re_match)
+#endif
+
+int
+re_search (bufp, string, length, start, range, regs)
+ struct re_pattern_buffer *bufp;
+ const char *string;
+ int length, start, range;
+ struct re_registers *regs;
+{
+ return re_search_stub (bufp, string, length, start, range, length, regs, 0);
+}
+#ifdef _LIBC
+weak_alias (__re_search, re_search)
+#endif
+
+int
+re_match_2 (bufp, string1, length1, string2, length2, start, regs, stop)
+ struct re_pattern_buffer *bufp;
+ const char *string1, *string2;
+ int length1, length2, start, stop;
+ struct re_registers *regs;
+{
+ return re_search_2_stub (bufp, string1, length1, string2, length2,
+ start, 0, regs, stop, 1);
+}
+#ifdef _LIBC
+weak_alias (__re_match_2, re_match_2)
+#endif
+
+int
+re_search_2 (bufp, string1, length1, string2, length2, start, range, regs, stop)
+ struct re_pattern_buffer *bufp;
+ const char *string1, *string2;
+ int length1, length2, start, range, stop;
+ struct re_registers *regs;
+{
+ return re_search_2_stub (bufp, string1, length1, string2, length2,
+ start, range, regs, stop, 0);
+}
+#ifdef _LIBC
+weak_alias (__re_search_2, re_search_2)
+#endif
+
+static int
+re_search_2_stub (bufp, string1, length1, string2, length2, start, range, regs,
+ stop, ret_len)
+ struct re_pattern_buffer *bufp;
+ const char *string1, *string2;
+ int length1, length2, start, range, stop, ret_len;
+ struct re_registers *regs;
+{
+ const char *str;
+ int rval;
+ int len = length1 + length2;
+ int free_str = 0;
+
+ if (BE (length1 < 0 || length2 < 0 || stop < 0, 0))
+ return -2;
+
+ /* Concatenate the strings. */
+ if (length2 > 0)
+ if (length1 > 0)
+ {
+ char *s = re_malloc (char, len);
+
+ if (BE (s == NULL, 0))
+ return -2;
+ memcpy (s, string1, length1);
+ memcpy (s + length1, string2, length2);
+ str = s;
+ free_str = 1;
+ }
+ else
+ str = string2;
+ else
+ str = string1;
+
+ rval = re_search_stub (bufp, str, len, start, range, stop, regs,
+ ret_len);
+ if (free_str)
+ re_free ((char *) str);
+ return rval;
+}
+
+/* The parameters have the same meaning as those of re_search.
+ Additional parameters:
+ If RET_LEN is nonzero the length of the match is returned (re_match style);
+ otherwise the position of the match is returned. */
+
+static int
+re_search_stub (bufp, string, length, start, range, stop, regs, ret_len)
+ struct re_pattern_buffer *bufp;
+ const char *string;
+ int length, start, range, stop, ret_len;
+ struct re_registers *regs;
+{
+ reg_errcode_t result;
+ regmatch_t *pmatch;
+ int nregs, rval;
+ int eflags = 0;
+
+ /* Check for out-of-range. */
+ if (BE (start < 0 || start > length, 0))
+ return -1;
+ if (BE (start + range > length, 0))
+ range = length - start;
+ else if (BE (start + range < 0, 0))
+ range = -start;
+
+ eflags |= (bufp->not_bol) ? REG_NOTBOL : 0;
+ eflags |= (bufp->not_eol) ? REG_NOTEOL : 0;
+
+ /* Compile fastmap if we haven't yet. */
+ if (range > 0 && bufp->fastmap != NULL && !bufp->fastmap_accurate)
+ re_compile_fastmap (bufp);
+
+ if (BE (bufp->no_sub, 0))
+ regs = NULL;
+
+ /* We need at least 1 register. */
+ if (regs == NULL)
+ nregs = 1;
+ else if (BE (bufp->regs_allocated == REGS_FIXED &&
+ regs->num_regs < bufp->re_nsub + 1, 0))
+ {
+ nregs = regs->num_regs;
+ if (BE (nregs < 1, 0))
+ {
+ /* Nothing can be copied to regs. */
+ regs = NULL;
+ nregs = 1;
+ }
+ }
+ else
+ nregs = bufp->re_nsub + 1;
+ pmatch = re_malloc (regmatch_t, nregs);
+ if (BE (pmatch == NULL, 0))
+ return -2;
+
+ result = re_search_internal (bufp, string, length, start, range, stop,
+ nregs, pmatch, eflags);
+
+ rval = 0;
+
+ /* I hope we needn't fill ther regs with -1's when no match was found. */
+ if (result != REG_NOERROR)
+ rval = -1;
+ else if (regs != NULL)
+ {
+ /* If caller wants register contents data back, copy them. */
+ bufp->regs_allocated = re_copy_regs (regs, pmatch, nregs,
+ bufp->regs_allocated);
+ if (BE (bufp->regs_allocated == REGS_UNALLOCATED, 0))
+ rval = -2;
+ }
+
+ if (BE (rval == 0, 1))
+ {
+ if (ret_len)
+ {
+ assert (pmatch[0].rm_so == start);
+ rval = pmatch[0].rm_eo - start;
+ }
+ else
+ rval = pmatch[0].rm_so;
+ }
+ re_free (pmatch);
+ return rval;
+}
+
+static unsigned
+re_copy_regs (regs, pmatch, nregs, regs_allocated)
+ struct re_registers *regs;
+ regmatch_t *pmatch;
+ int nregs, regs_allocated;
+{
+ int rval = REGS_REALLOCATE;
+ int i;
+ int need_regs = nregs + 1;
+ /* We need one extra element beyond `num_regs' for the `-1' marker GNU code
+ uses. */
+
+ /* Have the register data arrays been allocated? */
+ if (regs_allocated == REGS_UNALLOCATED)
+ { /* No. So allocate them with malloc. */
+ regs->start = re_malloc (regoff_t, need_regs);
+ if (BE (regs->start == NULL, 0))
+ return REGS_UNALLOCATED;
+ regs->end = re_malloc (regoff_t, need_regs);
+ if (BE (regs->end == NULL, 0))
+ {
+ re_free (regs->start);
+ return REGS_UNALLOCATED;
+ }
+ regs->num_regs = need_regs;
+ }
+ else if (regs_allocated == REGS_REALLOCATE)
+ { /* Yes. If we need more elements than were already
+ allocated, reallocate them. If we need fewer, just
+ leave it alone. */
+ if (need_regs > regs->num_regs)
+ {
+ regs->start = re_realloc (regs->start, regoff_t, need_regs);
+ if (BE (regs->start == NULL, 0))
+ {
+ if (regs->end != NULL)
+ re_free (regs->end);
+ return REGS_UNALLOCATED;
+ }
+ regs->end = re_realloc (regs->end, regoff_t, need_regs);
+ if (BE (regs->end == NULL, 0))
+ {
+ re_free (regs->start);
+ return REGS_UNALLOCATED;
+ }
+ regs->num_regs = need_regs;
+ }
+ }
+ else
+ {
+ assert (regs_allocated == REGS_FIXED);
+ /* This function may not be called with REGS_FIXED and nregs too big. */
+ assert (regs->num_regs >= nregs);
+ rval = REGS_FIXED;
+ }
+
+ /* Copy the regs. */
+ for (i = 0; i < nregs; ++i)
+ {
+ regs->start[i] = pmatch[i].rm_so;
+ regs->end[i] = pmatch[i].rm_eo;
+ }
+ for ( ; i < regs->num_regs; ++i)
+ regs->start[i] = regs->end[i] = -1;
+
+ return rval;
+}
+
+/* Set REGS to hold NUM_REGS registers, storing them in STARTS and
+ ENDS. Subsequent matches using PATTERN_BUFFER and REGS will use
+ this memory for recording register information. STARTS and ENDS
+ must be allocated using the malloc library routine, and must each
+ be at least NUM_REGS * sizeof (regoff_t) bytes long.
+
+ If NUM_REGS == 0, then subsequent matches should allocate their own
+ register data.
+
+ Unless this function is called, the first search or match using
+ PATTERN_BUFFER will allocate its own register data, without
+ freeing the old data. */
+
+void
+re_set_registers (bufp, regs, num_regs, starts, ends)
+ struct re_pattern_buffer *bufp;
+ struct re_registers *regs;
+ unsigned num_regs;
+ regoff_t *starts, *ends;
+{
+ if (num_regs)
+ {
+ bufp->regs_allocated = REGS_REALLOCATE;
+ regs->num_regs = num_regs;
+ regs->start = starts;
+ regs->end = ends;
+ }
+ else
+ {
+ bufp->regs_allocated = REGS_UNALLOCATED;
+ regs->num_regs = 0;
+ regs->start = regs->end = (regoff_t *) 0;
+ }
+}
+#ifdef _LIBC
+weak_alias (__re_set_registers, re_set_registers)
+#endif
+
+/* Entry points compatible with 4.2 BSD regex library. We don't define
+ them unless specifically requested. */
+
+#if defined _REGEX_RE_COMP || defined _LIBC
+int
+# ifdef _LIBC
+weak_function
+# endif
+re_exec (s)
+ const char *s;
+{
+ return 0 == regexec (&re_comp_buf, s, 0, NULL, 0);
+}
+#endif /* _REGEX_RE_COMP */
+
+static re_node_set empty_set;
+
+/* Internal entry point. */
+
+/* Searches for a compiled pattern PREG in the string STRING, whose
+ length is LENGTH. NMATCH, PMATCH, and EFLAGS have the same
+ mingings with regexec. START, and RANGE have the same meanings
+ with re_search.
+ Return REG_NOERROR if we find a match, and REG_NOMATCH if not,
+ otherwise return the error code.
+ Note: We assume front end functions already check ranges.
+ (START + RANGE >= 0 && START + RANGE <= LENGTH) */
+
+static reg_errcode_t
+re_search_internal (preg, string, length, start, range, stop, nmatch, pmatch,
+ eflags)
+ const regex_t *preg;
+ const char *string;
+ int length, start, range, stop, eflags;
+ size_t nmatch;
+ regmatch_t pmatch[];
+{
+ reg_errcode_t err;
+ re_dfa_t *dfa = (re_dfa_t *)preg->buffer;
+ re_string_t input;
+ int left_lim, right_lim, incr;
+ int fl_longest_match, match_first, match_last = -1;
+ re_match_context_t mctx;
+ char *fastmap = ((preg->fastmap != NULL && preg->fastmap_accurate)
+ ? preg->fastmap : NULL);
+
+ /* Check if the DFA haven't been compiled. */
+ if (BE (preg->used == 0 || dfa->init_state == NULL
+ || dfa->init_state_word == NULL || dfa->init_state_nl == NULL
+ || dfa->init_state_begbuf == NULL, 0))
+ return REG_NOMATCH;
+
+ re_node_set_init_empty (&empty_set);
+
+ /* We must check the longest matching, if nmatch > 0. */
+ fl_longest_match = (nmatch != 0);
+
+ err = re_string_allocate (&input, string, length, dfa->nodes_len + 1,
+ preg->translate, preg->syntax & RE_ICASE);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ input.stop = stop;
+
+ err = match_ctx_init (&mctx, eflags, &input, dfa->nbackref * 2);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+
+ /* We will log all the DFA states through which the dfa pass,
+ if nmatch > 1, or this dfa has "multibyte node", which is a
+ back-reference or a node which can accept multibyte character or
+ multi character collating element. */
+ if (nmatch > 1 || dfa->has_mb_node)
+ {
+ mctx.state_log = re_malloc (re_dfastate_t *, dfa->nodes_len + 1);
+ if (BE (mctx.state_log == NULL, 0))
+ return REG_ESPACE;
+ }
+ else
+ mctx.state_log = NULL;
+
+#ifdef DEBUG
+ /* We assume front-end functions already check them. */
+ assert (start + range >= 0 && start + range <= length);
+#endif
+
+ match_first = start;
+ input.tip_context = ((eflags & REG_NOTBOL) ? CONTEXT_BEGBUF
+ : CONTEXT_NEWLINE | CONTEXT_BEGBUF);
+
+ /* Check incrementally whether of not the input string match. */
+ incr = (range < 0) ? -1 : 1;
+ left_lim = (range < 0) ? start + range : start;
+ right_lim = (range < 0) ? start : start + range;
+
+ for (;;)
+ {
+ /* At first get the current byte from input string. */
+ int ch;
+ if (MB_CUR_MAX > 1 && (preg->syntax & RE_ICASE || preg->translate))
+ {
+ /* In this case, we can't determin easily the current byte,
+ since it might be a component byte of a multibyte character.
+ Then we use the constructed buffer instead. */
+ /* If MATCH_FIRST is out of the valid range, reconstruct the
+ buffers. */
+ if (input.raw_mbs_idx + input.valid_len <= match_first)
+ re_string_reconstruct (&input, match_first, eflags,
+ preg->newline_anchor);
+ /* If MATCH_FIRST is out of the buffer, leave it as '\0'.
+ Note that MATCH_FIRST must not be smaller than 0. */
+ ch = ((match_first >= length) ? 0
+ : re_string_byte_at (&input, match_first - input.raw_mbs_idx));
+ }
+ else
+ {
+ /* We apply translate/conversion manually, since it is trivial
+ in this case. */
+ /* If MATCH_FIRST is out of the buffer, leave it as '\0'.
+ Note that MATCH_FIRST must not be smaller than 0. */
+ ch = (match_first < length) ? (unsigned char)string[match_first] : 0;
+ /* Apply translation if we need. */
+ ch = preg->translate ? preg->translate[ch] : ch;
+ /* In case of case insensitive mode, convert to upper case. */
+ ch = ((preg->syntax & RE_ICASE) && islower (ch)) ? toupper (ch) : ch;
+ }
+
+ /* Eliminate inappropriate one by fastmap. */
+ if (preg->can_be_null || fastmap == NULL || fastmap[ch])
+ {
+ /* Reconstruct the buffers so that the matcher can assume that
+ the matching starts from the begining of the buffer. */
+ re_string_reconstruct (&input, match_first, eflags,
+ preg->newline_anchor);
+#ifdef RE_ENABLE_I18N
+ /* Eliminate it when it is a component of a multibyte character
+ and isn't the head of a multibyte character. */
+ if (MB_CUR_MAX == 1 || re_string_first_byte (&input, 0))
+#endif
+ {
+ /* It seems to be appropriate one, then use the matcher. */
+ /* We assume that the matching starts from 0. */
+ mctx.state_log_top = mctx.nbkref_ents = mctx.max_mb_elem_len = 0;
+ match_last = check_matching (preg, &mctx, 0, fl_longest_match);
+ if (match_last != -1)
+ {
+ if (BE (match_last == -2, 0))
+ return REG_ESPACE;
+ else
+ break; /* We found a matching. */
+ }
+ }
+ }
+ /* Update counter. */
+ match_first += incr;
+ if (match_first < left_lim || right_lim < match_first)
+ break;
+ }
+
+ /* Set pmatch[] if we need. */
+ if (match_last != -1 && nmatch > 0)
+ {
+ int reg_idx;
+
+ /* Initialize registers. */
+ for (reg_idx = 0; reg_idx < nmatch; ++reg_idx)
+ pmatch[reg_idx].rm_so = pmatch[reg_idx].rm_eo = -1;
+
+ /* Set the points where matching start/end. */
+ pmatch[0].rm_so = 0;
+ mctx.match_last = pmatch[0].rm_eo = match_last;
+
+ if (!preg->no_sub && nmatch > 1)
+ {
+ /* We need the ranges of all the subexpressions. */
+ int halt_node;
+ re_dfastate_t **sifted_states;
+ re_dfastate_t **lim_states = NULL;
+ re_dfastate_t *pstate = mctx.state_log[match_last];
+ re_sift_context_t sctx;
+#ifdef DEBUG
+ assert (mctx.state_log != NULL);
+#endif
+ halt_node = check_halt_state_context (preg, pstate, &mctx,
+ match_last);
+ if (dfa->has_plural_match)
+ {
+ match_ctx_clear_flag (&mctx);
+ sifted_states = re_malloc (re_dfastate_t *, match_last + 1);
+ if (BE (sifted_states == NULL, 0))
+ return REG_ESPACE;
+ if (dfa->nbackref)
+ {
+ lim_states = calloc (sizeof (re_dfastate_t *),
+ match_last + 1);
+ if (BE (lim_states == NULL, 0))
+ return REG_ESPACE;
+ }
+ sift_ctx_init (&sctx, sifted_states, lim_states, halt_node,
+ mctx.match_last, 0);
+ err = sift_states_backward (preg, &mctx, &sctx);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ if (lim_states != NULL)
+ {
+ err = merge_state_array (dfa, sifted_states, lim_states,
+ match_last + 1);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ re_free (lim_states);
+ }
+ re_node_set_free (&sctx.limits);
+ re_free (mctx.state_log);
+ mctx.state_log = sifted_states;
+ }
+ mctx.last_node = halt_node;
+ err = set_regs (preg, &mctx, nmatch, pmatch,
+ dfa->has_plural_match && dfa->nbackref > 0);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+
+ /* At last, add the offset to the each registers, since we slided
+ the buffers so that We can assume that the matching starts from 0. */
+ for (reg_idx = 0; reg_idx < nmatch; ++reg_idx)
+ if (pmatch[reg_idx].rm_so != -1)
+ {
+ pmatch[reg_idx].rm_so += match_first;
+ pmatch[reg_idx].rm_eo += match_first;
+ }
+ }
+
+ re_free (mctx.state_log);
+ if (dfa->nbackref)
+ match_ctx_free (&mctx);
+ re_string_destruct (&input);
+
+ return (match_last == -1) ? REG_NOMATCH : REG_NOERROR;
+}
+
+/* Acquire an initial state and return it.
+ We must select appropriate initial state depending on the context,
+ since initial states may have constraints like "\<", "^", etc.. */
+
+static inline re_dfastate_t *
+acquire_init_state_context (err, preg, mctx, idx)
+ reg_errcode_t *err;
+ const regex_t *preg;
+ const re_match_context_t *mctx;
+ int idx;
+{
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+
+ *err = REG_NOERROR;
+ if (dfa->init_state->has_constraint)
+ {
+ unsigned int context;
+ context = re_string_context_at (mctx->input, idx - 1, mctx->eflags,
+ preg->newline_anchor);
+ if (IS_WORD_CONTEXT (context))
+ return dfa->init_state_word;
+ else if (IS_ORDINARY_CONTEXT (context))
+ return dfa->init_state;
+ else if (IS_BEGBUF_CONTEXT (context) && IS_NEWLINE_CONTEXT (context))
+ return dfa->init_state_begbuf;
+ else if (IS_NEWLINE_CONTEXT (context))
+ return dfa->init_state_nl;
+ else if (IS_BEGBUF_CONTEXT (context))
+ {
+ /* It is relatively rare case, then calculate on demand. */
+ return re_acquire_state_context (err, dfa,
+ dfa->init_state->entrance_nodes,
+ context);
+ }
+ else
+ /* Must not happen? */
+ return dfa->init_state;
+ }
+ else
+ return dfa->init_state;
+}
+
+/* Check whether the regular expression match input string INPUT or not,
+ and return the index where the matching end, return -1 if not match,
+ or return -2 in case of an error.
+ FL_SEARCH means we must search where the matching starts,
+ FL_LONGEST_MATCH means we want the POSIX longest matching.
+ Note that the matcher assume that the maching starts from the current
+ index of the buffer. */
+
+static int
+check_matching (preg, mctx, fl_search, fl_longest_match)
+ const regex_t *preg;
+ re_match_context_t *mctx;
+ int fl_search, fl_longest_match;
+{
+ reg_errcode_t err;
+ int match = 0;
+ int match_last = -1;
+ int cur_str_idx = re_string_cur_idx (mctx->input);
+ re_dfastate_t *cur_state;
+
+ cur_state = acquire_init_state_context (&err, preg, mctx, cur_str_idx);
+ /* An initial state must not be NULL(invalid state). */
+ if (BE (cur_state == NULL, 0))
+ return -2;
+ if (mctx->state_log != NULL)
+ mctx->state_log[cur_str_idx] = cur_state;
+
+ if (cur_state->has_backref)
+ {
+ int i;
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ for (i = 0; i < cur_state->nodes.nelem; ++i)
+ {
+ re_token_type_t type;
+ int node = cur_state->nodes.elems[i];
+ int entity = (dfa->nodes[node].type != OP_CONTEXT_NODE ? node
+ : dfa->nodes[node].opr.ctx_info->entity);
+ type = dfa->nodes[entity].type;
+ if (type == OP_BACK_REF)
+ {
+ int clexp_idx;
+ for (clexp_idx = 0; clexp_idx < cur_state->nodes.nelem;
+ ++clexp_idx)
+ {
+ re_token_t *clexp_node;
+ clexp_node = dfa->nodes + cur_state->nodes.elems[clexp_idx];
+ if (clexp_node->type == OP_CLOSE_SUBEXP
+ && clexp_node->opr.idx + 1== dfa->nodes[entity].opr.idx)
+ {
+ err = match_ctx_add_entry (mctx, node, 0, 0, 0);
+ if (BE (err != REG_NOERROR, 0))
+ return -2;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /* If the RE accepts NULL string. */
+ if (cur_state->halt)
+ {
+ if (!cur_state->has_constraint
+ || check_halt_state_context (preg, cur_state, mctx, cur_str_idx))
+ {
+ if (!fl_longest_match)
+ return cur_str_idx;
+ else
+ {
+ match_last = cur_str_idx;
+ match = 1;
+ }
+ }
+ }
+
+ while (!re_string_eoi (mctx->input))
+ {
+ cur_state = transit_state (&err, preg, mctx, cur_state,
+ fl_search && !match);
+ if (cur_state == NULL) /* Reached at the invalid state or an error. */
+ {
+ cur_str_idx = re_string_cur_idx (mctx->input);
+ if (BE (err != REG_NOERROR, 0))
+ return -2;
+ if (fl_search && !match)
+ {
+ /* Restart from initial state, since we are searching
+ the point from where matching start. */
+#ifdef RE_ENABLE_I18N
+ if (MB_CUR_MAX == 1
+ || re_string_first_byte (mctx->input, cur_str_idx))
+#endif /* RE_ENABLE_I18N */
+ cur_state = acquire_init_state_context (&err, preg, mctx,
+ cur_str_idx);
+ if (BE (cur_state == NULL && err != REG_NOERROR, 0))
+ return -2;
+ if (mctx->state_log != NULL)
+ mctx->state_log[cur_str_idx] = cur_state;
+ }
+ else if (!fl_longest_match && match)
+ break;
+ else /* (fl_longest_match && match) || (!fl_search && !match) */
+ {
+ if (mctx->state_log == NULL)
+ break;
+ else
+ {
+ int max = mctx->state_log_top;
+ for (; cur_str_idx <= max; ++cur_str_idx)
+ if (mctx->state_log[cur_str_idx] != NULL)
+ break;
+ if (cur_str_idx > max)
+ break;
+ }
+ }
+ }
+
+ if (cur_state != NULL && cur_state->halt)
+ {
+ /* Reached at a halt state.
+ Check the halt state can satisfy the current context. */
+ if (!cur_state->has_constraint
+ || check_halt_state_context (preg, cur_state, mctx,
+ re_string_cur_idx (mctx->input)))
+ {
+ /* We found an appropriate halt state. */
+ match_last = re_string_cur_idx (mctx->input);
+ match = 1;
+ if (!fl_longest_match)
+ break;
+ }
+ }
+ }
+ return match_last;
+}
+
+/* Check NODE match the current context. */
+
+static int check_halt_node_context (dfa, node, context)
+ const re_dfa_t *dfa;
+ int node;
+ unsigned int context;
+{
+ int entity;
+ re_token_type_t type = dfa->nodes[node].type;
+ if (type == END_OF_RE)
+ return 1;
+ if (type != OP_CONTEXT_NODE)
+ return 0;
+ entity = dfa->nodes[node].opr.ctx_info->entity;
+ if (dfa->nodes[entity].type != END_OF_RE
+ || NOT_SATISFY_NEXT_CONSTRAINT (dfa->nodes[node].constraint, context))
+ return 0;
+ return 1;
+}
+
+/* Check the halt state STATE match the current context.
+ Return 0 if not match, if the node, STATE has, is a halt node and
+ match the context, return the node. */
+
+static int
+check_halt_state_context (preg, state, mctx, idx)
+ const regex_t *preg;
+ const re_dfastate_t *state;
+ const re_match_context_t *mctx;
+ int idx;
+{
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ int i;
+ unsigned int context;
+#ifdef DEBUG
+ assert (state->halt);
+#endif
+ context = re_string_context_at (mctx->input, idx, mctx->eflags,
+ preg->newline_anchor);
+ for (i = 0; i < state->nodes.nelem; ++i)
+ if (check_halt_node_context (dfa, state->nodes.elems[i], context))
+ return state->nodes.elems[i];
+ return 0;
+}
+
+/* Compute the next node to which "NFA" transit from NODE("NFA" is a NFA
+ corresponding to the DFA).
+ Return the destination node, and update EPS_VIA_NODES, return -1 in case
+ of errors. */
+
+static int
+proceed_next_node (preg, nregs, regs, mctx, pidx, node, eps_via_nodes, fs)
+ const regex_t *preg;
+ regmatch_t *regs;
+ const re_match_context_t *mctx;
+ int nregs, *pidx, node;
+ re_node_set *eps_via_nodes;
+ struct re_fail_stack_t *fs;
+{
+ re_dfa_t *dfa = (re_dfa_t *)preg->buffer;
+ int i, err, dest_node, cur_entity;
+ dest_node = -1;
+ cur_entity = ((dfa->nodes[node].type == OP_CONTEXT_NODE)
+ ? dfa->nodes[node].opr.ctx_info->entity : node);
+ if (IS_EPSILON_NODE (dfa->nodes[node].type))
+ {
+ int ndest, dest_nodes[2], dest_entities[2];
+ err = re_node_set_insert (eps_via_nodes, node);
+ if (BE (err < 0, 0))
+ return -1;
+ /* Pick up valid destinations. */
+ for (ndest = 0, i = 0; i < mctx->state_log[*pidx]->nodes.nelem; ++i)
+ {
+ int candidate = mctx->state_log[*pidx]->nodes.elems[i];
+ int entity;
+ entity = ((dfa->nodes[candidate].type == OP_CONTEXT_NODE)
+ ? dfa->nodes[candidate].opr.ctx_info->entity : candidate);
+ if (!re_node_set_contains (dfa->edests + node, entity))
+ continue;
+ dest_nodes[0] = (ndest == 0) ? candidate : dest_nodes[0];
+ dest_entities[0] = (ndest == 0) ? entity : dest_entities[0];
+ dest_nodes[1] = (ndest == 1) ? candidate : dest_nodes[1];
+ dest_entities[1] = (ndest == 1) ? entity : dest_entities[1];
+ ++ndest;
+ }
+ if (ndest <= 1)
+ return ndest == 0 ? -1 : (ndest == 1 ? dest_nodes[0] : 0);
+ if (dest_entities[0] > dest_entities[1])
+ {
+ int swap_work = dest_nodes[0];
+ dest_nodes[0] = dest_nodes[1];
+ dest_nodes[1] = swap_work;
+ }
+ /* In order to avoid infinite loop like "(a*)*". */
+ if (re_node_set_contains (eps_via_nodes, dest_nodes[0]))
+ return dest_nodes[1];
+ if (fs != NULL)
+ push_fail_stack (fs, *pidx, dest_nodes, nregs, regs, eps_via_nodes);
+ return dest_nodes[0];
+ }
+ else
+ {
+ int naccepted = 0, entity = node;
+ re_token_type_t type = dfa->nodes[node].type;
+ if (type == OP_CONTEXT_NODE)
+ {
+ entity = dfa->nodes[node].opr.ctx_info->entity;
+ type = dfa->nodes[entity].type;
+ }
+
+#ifdef RE_ENABLE_I18N
+ if (ACCEPT_MB_NODE (type))
+ naccepted = check_node_accept_bytes (preg, entity, mctx->input, *pidx);
+ else
+#endif /* RE_ENABLE_I18N */
+ if (type == OP_BACK_REF)
+ {
+ int subexp_idx = dfa->nodes[entity].opr.idx;
+ naccepted = regs[subexp_idx].rm_eo - regs[subexp_idx].rm_so;
+ if (fs != NULL)
+ {
+ if (regs[subexp_idx].rm_so == -1 || regs[subexp_idx].rm_eo == -1)
+ return -1;
+ else if (naccepted)
+ {
+ char *buf = re_string_get_buffer (mctx->input);
+ if (strncmp (buf + regs[subexp_idx].rm_so, buf + *pidx,
+ naccepted) != 0)
+ return -1;
+ }
+ }
+
+ if (naccepted == 0)
+ {
+ err = re_node_set_insert (eps_via_nodes, node);
+ if (BE (err < 0, 0))
+ return -2;
+ dest_node = dfa->nexts[node];
+ if (re_node_set_contains (&mctx->state_log[*pidx]->nodes,
+ dest_node))
+ return dest_node;
+ for (i = 0; i < mctx->state_log[*pidx]->nodes.nelem; ++i)
+ {
+ dest_node = mctx->state_log[*pidx]->nodes.elems[i];
+ if ((dfa->nodes[dest_node].type == OP_CONTEXT_NODE
+ && (dfa->nexts[node]
+ == dfa->nodes[dest_node].opr.ctx_info->entity)))
+ return dest_node;
+ }
+ }
+ }
+
+ if (naccepted != 0
+ || check_node_accept (preg, dfa->nodes + node, mctx, *pidx))
+ {
+ dest_node = dfa->nexts[node];
+ *pidx = (naccepted == 0) ? *pidx + 1 : *pidx + naccepted;
+ if (fs && (*pidx > mctx->match_last || mctx->state_log[*pidx] == NULL
+ || !re_node_set_contains (&mctx->state_log[*pidx]->nodes,
+ dest_node)))
+ return -1;
+ re_node_set_empty (eps_via_nodes);
+ return dest_node;
+ }
+ }
+ return -1;
+}
+
+static reg_errcode_t
+push_fail_stack (fs, str_idx, dests, nregs, regs, eps_via_nodes)
+ struct re_fail_stack_t *fs;
+ int str_idx, *dests, nregs;
+ regmatch_t *regs;
+ re_node_set *eps_via_nodes;
+{
+ reg_errcode_t err;
+ int num = fs->num++;
+ if (fs->num == fs->alloc)
+ {
+ fs->alloc *= 2;
+ fs->stack = realloc (fs->stack, (sizeof (struct re_fail_stack_ent_t)
+ * fs->alloc));
+ if (fs->stack == NULL)
+ return REG_ESPACE;
+ }
+ fs->stack[num].idx = str_idx;
+ fs->stack[num].node = dests[1];
+ fs->stack[num].regs = re_malloc (regmatch_t, nregs);
+ memcpy (fs->stack[num].regs, regs, sizeof (regmatch_t) * nregs);
+ err = re_node_set_init_copy (&fs->stack[num].eps_via_nodes, eps_via_nodes);
+ return err;
+}
+
+static int
+pop_fail_stack (fs, pidx, nregs, regs, eps_via_nodes)
+ struct re_fail_stack_t *fs;
+ int *pidx, nregs;
+ regmatch_t *regs;
+ re_node_set *eps_via_nodes;
+{
+ int num = --fs->num;
+ assert (num >= 0);
+ *pidx = fs->stack[num].idx;
+ memcpy (regs, fs->stack[num].regs, sizeof (regmatch_t) * nregs);
+ re_node_set_free (eps_via_nodes);
+ *eps_via_nodes = fs->stack[num].eps_via_nodes;
+ return fs->stack[num].node;
+}
+
+/* Set the positions where the subexpressions are starts/ends to registers
+ PMATCH.
+ Note: We assume that pmatch[0] is already set, and
+ pmatch[i].rm_so == pmatch[i].rm_eo == -1 (i > 1). */
+
+static reg_errcode_t
+set_regs (preg, mctx, nmatch, pmatch, fl_backtrack)
+ const regex_t *preg;
+ const re_match_context_t *mctx;
+ size_t nmatch;
+ regmatch_t *pmatch;
+ int fl_backtrack;
+{
+ re_dfa_t *dfa = (re_dfa_t *)preg->buffer;
+ int idx, cur_node, real_nmatch;
+ re_node_set eps_via_nodes;
+ struct re_fail_stack_t *fs;
+ struct re_fail_stack_t fs_body = {0, 2, NULL};
+#ifdef DEBUG
+ assert (nmatch > 1);
+ assert (mctx->state_log != NULL);
+#endif
+ if (fl_backtrack)
+ {
+ fs = &fs_body;
+ fs->stack = re_malloc (struct re_fail_stack_ent_t, fs->alloc);
+ }
+ else
+ fs = NULL;
+ cur_node = dfa->init_node;
+ real_nmatch = (nmatch <= preg->re_nsub) ? nmatch : preg->re_nsub + 1;
+ re_node_set_init_empty (&eps_via_nodes);
+ for (idx = pmatch[0].rm_so; idx <= pmatch[0].rm_eo ;)
+ {
+ update_regs (dfa, pmatch, cur_node, idx, real_nmatch);
+ if (idx == pmatch[0].rm_eo && cur_node == mctx->last_node)
+ {
+ int reg_idx;
+ if (fs)
+ {
+ for (reg_idx = 0; reg_idx < nmatch; ++reg_idx)
+ if (pmatch[reg_idx].rm_so > -1 && pmatch[reg_idx].rm_eo == -1)
+ break;
+ if (reg_idx == nmatch)
+ return REG_NOERROR;
+ cur_node = pop_fail_stack (fs, &idx, nmatch, pmatch,
+ &eps_via_nodes);
+ }
+ else
+ return REG_NOERROR;
+ }
+
+ /* Proceed to next node. */
+ cur_node = proceed_next_node (preg, nmatch, pmatch, mctx, &idx, cur_node,
+ &eps_via_nodes, fs);
+
+ if (BE (cur_node < 0, 0))
+ {
+ if (cur_node == -2)
+ return REG_ESPACE;
+ if (fs)
+ cur_node = pop_fail_stack (fs, &idx, nmatch, pmatch,
+ &eps_via_nodes);
+ else
+ return REG_NOMATCH;
+ }
+ }
+ re_node_set_free (&eps_via_nodes);
+ return REG_NOERROR;
+}
+
+static void
+update_regs (dfa, pmatch, cur_node, cur_idx, nmatch)
+ re_dfa_t *dfa;
+ regmatch_t *pmatch;
+ int cur_node, cur_idx, nmatch;
+{
+ int type = dfa->nodes[cur_node].type;
+ int reg_num;
+ if (type != OP_OPEN_SUBEXP && type != OP_CLOSE_SUBEXP)
+ return;
+ reg_num = dfa->nodes[cur_node].opr.idx + 1;
+ if (reg_num >= nmatch)
+ return;
+ if (type == OP_OPEN_SUBEXP)
+ {
+ /* We are at the first node of this sub expression. */
+ pmatch[reg_num].rm_so = cur_idx;
+ pmatch[reg_num].rm_eo = -1;
+ }
+ else if (type == OP_CLOSE_SUBEXP)
+ /* We are at the first node of this sub expression. */
+ pmatch[reg_num].rm_eo = cur_idx;
+}
+
+#define NUMBER_OF_STATE 1
+
+/* This function checks the STATE_LOG from the SCTX->last_str_idx to 0
+ and sift the nodes in each states according to the following rules.
+ Updated state_log will be wrote to STATE_LOG.
+
+ Rules: We throw away the Node `a' in the STATE_LOG[STR_IDX] if...
+ 1. When STR_IDX == MATCH_LAST(the last index in the state_log):
+ If `a' isn't the LAST_NODE and `a' can't epsilon transit to
+ the LAST_NODE, we throw away the node `a'.
+ 2. When 0 <= STR_IDX < MATCH_LAST and `a' accepts
+ string `s' and transit to `b':
+ i. If 'b' isn't in the STATE_LOG[STR_IDX+strlen('s')], we throw
+ away the node `a'.
+ ii. If 'b' is in the STATE_LOG[STR_IDX+strlen('s')] but 'b' is
+ throwed away, we throw away the node `a'.
+ 3. When 0 <= STR_IDX < n and 'a' epsilon transit to 'b':
+ i. If 'b' isn't in the STATE_LOG[STR_IDX], we throw away the
+ node `a'.
+ ii. If 'b' is in the STATE_LOG[STR_IDX] but 'b' is throwed away,
+ we throw away the node `a'. */
+
+#define STATE_NODE_CONTAINS(state,node) \
+ ((state) != NULL && re_node_set_contains (&(state)->nodes, node))
+
+static reg_errcode_t
+sift_states_backward (preg, mctx, sctx)
+ const regex_t *preg;
+ re_match_context_t *mctx;
+ re_sift_context_t *sctx;
+{
+ reg_errcode_t err;
+ re_dfa_t *dfa = (re_dfa_t *)preg->buffer;
+ int null_cnt = 0;
+ int str_idx = sctx->last_str_idx;
+ re_node_set cur_dest;
+ re_node_set *cur_src; /* Points the state_log[str_idx]->nodes */
+
+#ifdef DEBUG
+ assert (mctx->state_log != NULL && mctx->state_log[str_idx] != NULL);
+#endif
+ cur_src = &mctx->state_log[str_idx]->nodes;
+
+ /* Build sifted state_log[str_idx]. It has the nodes which can epsilon
+ transit to the last_node and the last_node itself. */
+ err = re_node_set_init_1 (&cur_dest, sctx->last_node);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ err = update_cur_sifted_state (preg, mctx, sctx, str_idx, &cur_dest);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+
+ /* Then check each states in the state_log. */
+ while (str_idx > 0)
+ {
+ int i, ret;
+ /* Update counters. */
+ null_cnt = (sctx->sifted_states[str_idx] == NULL) ? null_cnt + 1 : 0;
+ if (null_cnt > mctx->max_mb_elem_len)
+ {
+ memset (sctx->sifted_states, '\0',
+ sizeof (re_dfastate_t *) * str_idx);
+ return REG_NOERROR;
+ }
+ re_node_set_empty (&cur_dest);
+ --str_idx;
+ cur_src = ((mctx->state_log[str_idx] == NULL) ? &empty_set
+ : &mctx->state_log[str_idx]->nodes);
+
+ /* Then build the next sifted state.
+ We build the next sifted state on `cur_dest', and update
+ `sifted_states[str_idx]' with `cur_dest'.
+ Note:
+ `cur_dest' is the sifted state from `state_log[str_idx + 1]'.
+ `cur_src' points the node_set of the old `state_log[str_idx]'. */
+ for (i = 0; i < cur_src->nelem; i++)
+ {
+ int prev_node = cur_src->elems[i];
+ int entity = prev_node;
+ int naccepted = 0;
+ re_token_type_t type = dfa->nodes[prev_node].type;
+
+ if (IS_EPSILON_NODE(type))
+ continue;
+ if (type == OP_CONTEXT_NODE)
+ {
+ entity = dfa->nodes[prev_node].opr.ctx_info->entity;
+ type = dfa->nodes[entity].type;
+ }
+#ifdef RE_ENABLE_I18N
+ /* If the node may accept `multi byte'. */
+ if (ACCEPT_MB_NODE (type))
+ naccepted = sift_states_iter_mb (preg, mctx, sctx, entity, str_idx,
+ sctx->last_str_idx);
+
+#endif /* RE_ENABLE_I18N */
+ /* We don't check backreferences here.
+ See update_cur_sifted_state(). */
+
+ if (!naccepted
+ && check_node_accept (preg, dfa->nodes + prev_node, mctx,
+ str_idx)
+ && STATE_NODE_CONTAINS (sctx->sifted_states[str_idx + 1],
+ dfa->nexts[prev_node]))
+ naccepted = 1;
+
+ if (naccepted == 0)
+ continue;
+
+ if (sctx->limits.nelem)
+ {
+ int to_idx = str_idx + naccepted;
+ if (check_dst_limits (dfa, &sctx->limits, mctx,
+ dfa->nexts[prev_node], to_idx,
+ prev_node, str_idx))
+ continue;
+ }
+ ret = re_node_set_insert (&cur_dest, prev_node);
+ if (BE (ret == -1, 0))
+ return err;
+ }
+
+ /* Add all the nodes which satisfy the following conditions:
+ - It can epsilon transit to a node in CUR_DEST.
+ - It is in CUR_SRC.
+ And update state_log. */
+ err = update_cur_sifted_state (preg, mctx, sctx, str_idx, &cur_dest);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+
+ re_node_set_free (&cur_dest);
+ return REG_NOERROR;
+}
+
+/* Helper functions. */
+
+static inline reg_errcode_t
+clean_state_log_if_need (mctx, next_state_log_idx)
+ re_match_context_t *mctx;
+ int next_state_log_idx;
+{
+ int top = mctx->state_log_top;
+
+ if (next_state_log_idx >= mctx->input->bufs_len
+ || (next_state_log_idx >= mctx->input->valid_len
+ && mctx->input->valid_len < mctx->input->len))
+ {
+ reg_errcode_t err;
+ err = extend_buffers (mctx);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+
+ if (top < next_state_log_idx)
+ {
+ memset (mctx->state_log + top + 1, '\0',
+ sizeof (re_dfastate_t *) * (next_state_log_idx - top));
+ mctx->state_log_top = next_state_log_idx;
+ }
+ return REG_NOERROR;
+}
+
+static reg_errcode_t merge_state_array (dfa, dst, src, num)
+ re_dfa_t *dfa;
+ re_dfastate_t **dst;
+ re_dfastate_t **src;
+ int num;
+{
+ int st_idx;
+ reg_errcode_t err;
+ for (st_idx = 0; st_idx < num; ++st_idx)
+ {
+ if (dst[st_idx] == NULL)
+ dst[st_idx] = src[st_idx];
+ else if (src[st_idx] != NULL)
+ {
+ re_node_set merged_set;
+ err = re_node_set_init_union (&merged_set, &dst[st_idx]->nodes,
+ &src[st_idx]->nodes);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ dst[st_idx] = re_acquire_state (&err, dfa, &merged_set);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ re_node_set_free (&merged_set);
+ }
+ }
+ return REG_NOERROR;
+}
+
+static reg_errcode_t
+update_cur_sifted_state (preg, mctx, sctx, str_idx, dest_nodes)
+ const regex_t *preg;
+ re_match_context_t *mctx;
+ re_sift_context_t *sctx;
+ int str_idx;
+ re_node_set *dest_nodes;
+{
+ reg_errcode_t err;
+ re_dfa_t *dfa = (re_dfa_t *)preg->buffer;
+ const re_node_set *candidates;
+ candidates = ((mctx->state_log[str_idx] == NULL) ? &empty_set
+ : &mctx->state_log[str_idx]->nodes);
+
+ /* At first, add the nodes which can epsilon transit to a node in
+ DEST_NODE. */
+ err = add_epsilon_src_nodes (dfa, dest_nodes, candidates);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+
+ /* Then, check the limitations in the current sift_context. */
+ if (sctx->limits.nelem)
+ {
+ err = check_subexp_limits (dfa, dest_nodes, candidates, &sctx->limits,
+ mctx->bkref_ents, str_idx);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+
+ /* Update state_log. */
+ sctx->sifted_states[str_idx] = re_acquire_state (&err, dfa, dest_nodes);
+ if (BE (sctx->sifted_states[str_idx] == NULL && err != REG_NOERROR, 0))
+ return err;
+
+ /* If we are searching for the subexpression candidates.
+ Note that we were from transit_state_bkref_loop() in this case. */
+ if (sctx->check_subexp)
+ {
+ err = search_subexp (preg, mctx, sctx, str_idx, dest_nodes);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+
+ if ((mctx->state_log[str_idx] != NULL
+ && mctx->state_log[str_idx]->has_backref))
+ {
+ err = sift_states_bkref (preg, mctx, sctx, str_idx, dest_nodes);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ return REG_NOERROR;
+}
+
+static reg_errcode_t
+add_epsilon_src_nodes (dfa, dest_nodes, candidates)
+ re_dfa_t *dfa;
+ re_node_set *dest_nodes;
+ const re_node_set *candidates;
+{
+ reg_errcode_t err;
+ int src_idx;
+ re_node_set src_copy;
+
+ err = re_node_set_init_copy (&src_copy, dest_nodes);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ for (src_idx = 0; src_idx < src_copy.nelem; ++src_idx)
+ {
+ err = re_node_set_add_intersect (dest_nodes, candidates,
+ dfa->inveclosures
+ + src_copy.elems[src_idx]);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ re_node_set_free (&src_copy);
+ return REG_NOERROR;
+}
+
+static reg_errcode_t
+sub_epsilon_src_nodes (dfa, node, dest_nodes, candidates)
+ re_dfa_t *dfa;
+ int node;
+ re_node_set *dest_nodes;
+ const re_node_set *candidates;
+{
+ int ecl_idx;
+ reg_errcode_t err;
+ re_node_set *inv_eclosure = dfa->inveclosures + node;
+ re_node_set except_nodes;
+ re_node_set_init_empty (&except_nodes);
+ for (ecl_idx = 0; ecl_idx < inv_eclosure->nelem; ++ecl_idx)
+ {
+ int cur_node = inv_eclosure->elems[ecl_idx];
+ if (cur_node == node)
+ continue;
+ if (dfa->edests[cur_node].nelem)
+ {
+ int edst1 = dfa->edests[cur_node].elems[0];
+ int edst2 = ((dfa->edests[cur_node].nelem > 1)
+ ? dfa->edests[cur_node].elems[1] : -1);
+ if ((!re_node_set_contains (inv_eclosure, edst1)
+ && re_node_set_contains (dest_nodes, edst1))
+ || (edst2 > 0
+ && !re_node_set_contains (inv_eclosure, edst2)
+ && re_node_set_contains (dest_nodes, edst2)))
+ {
+ err = re_node_set_add_intersect (&except_nodes, candidates,
+ dfa->inveclosures + cur_node);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ }
+ }
+ for (ecl_idx = 0; ecl_idx < inv_eclosure->nelem; ++ecl_idx)
+ {
+ int cur_node = inv_eclosure->elems[ecl_idx];
+ if (!re_node_set_contains (&except_nodes, cur_node))
+ {
+ int idx = re_node_set_contains (dest_nodes, cur_node) - 1;
+ re_node_set_remove_at (dest_nodes, idx);
+ }
+ }
+ re_node_set_free (&except_nodes);
+ return REG_NOERROR;
+}
+
+static int
+check_dst_limits (dfa, limits, mctx, dst_node, dst_idx, src_node, src_idx)
+ re_dfa_t *dfa;
+ re_node_set *limits;
+ re_match_context_t *mctx;
+ int dst_node, dst_idx, src_node, src_idx;
+{
+ int lim_idx, src_pos, dst_pos;
+
+ for (lim_idx = 0; lim_idx < limits->nelem; ++lim_idx)
+ {
+ int bkref, subexp_idx/*, node_idx, cls_node*/;
+ struct re_backref_cache_entry *ent;
+ ent = mctx->bkref_ents + limits->elems[lim_idx];
+ bkref = (dfa->nodes[ent->node].type == OP_CONTEXT_NODE
+ ? dfa->nodes[ent->node].opr.ctx_info->entity : ent->node);
+ subexp_idx = dfa->nodes[bkref].opr.idx - 1;
+
+ dst_pos = check_dst_limits_calc_pos (dfa, mctx, limits->elems[lim_idx],
+ dfa->eclosures + dst_node,
+ subexp_idx, dst_node, dst_idx);
+ src_pos = check_dst_limits_calc_pos (dfa, mctx, limits->elems[lim_idx],
+ dfa->eclosures + src_node,
+ subexp_idx, src_node, src_idx);
+
+ /* In case of:
+ <src> <dst> ( <subexp> )
+ ( <subexp> ) <src> <dst>
+ ( <subexp1> <src> <subexp2> <dst> <subexp3> ) */
+ if (src_pos == dst_pos)
+ continue; /* This is unrelated limitation. */
+ else
+ return 1;
+ }
+ return 0;
+}
+
+static int
+check_dst_limits_calc_pos (dfa, mctx, limit, eclosures, subexp_idx, node,
+ str_idx)
+ re_dfa_t *dfa;
+ re_match_context_t *mctx;
+ re_node_set *eclosures;
+ int limit, subexp_idx, node, str_idx;
+{
+ struct re_backref_cache_entry *lim = mctx->bkref_ents + limit;
+ int pos = (str_idx < lim->subexp_from ? -1
+ : (lim->subexp_to < str_idx ? 1 : 0));
+ if (pos == 0
+ && (str_idx == lim->subexp_from || str_idx == lim->subexp_to))
+ {
+ int node_idx;
+ for (node_idx = 0; node_idx < eclosures->nelem; ++node_idx)
+ {
+ int node = eclosures->elems[node_idx];
+ int entity = node;
+ re_token_type_t type= dfa->nodes[node].type;
+ if (type == OP_CONTEXT_NODE)
+ {
+ entity = dfa->nodes[node].opr.ctx_info->entity;
+ type = dfa->nodes[entity].type;
+ }
+ if (type == OP_BACK_REF)
+ {
+ int bi;
+ for (bi = 0; bi < mctx->nbkref_ents; ++bi)
+ {
+ struct re_backref_cache_entry *ent = mctx->bkref_ents + bi;
+ if (ent->node == node && ent->subexp_from == ent->subexp_to
+ && ent->str_idx == str_idx)
+ {
+ int cpos, dst;
+ dst = dfa->nexts[node];
+ cpos = check_dst_limits_calc_pos (dfa, mctx, limit,
+ dfa->eclosures + dst,
+ subexp_idx, dst,
+ str_idx);
+ if ((str_idx == lim->subexp_from && cpos == -1)
+ || (str_idx == lim->subexp_to && cpos == 0))
+ return cpos;
+ }
+ }
+ }
+ if (type == OP_OPEN_SUBEXP && subexp_idx == dfa->nodes[node].opr.idx
+ && str_idx == lim->subexp_from)
+ {
+ pos = -1;
+ break;
+ }
+ if (type == OP_CLOSE_SUBEXP && subexp_idx == dfa->nodes[node].opr.idx
+ && str_idx == lim->subexp_to)
+ break;
+ }
+ if (node_idx == eclosures->nelem && str_idx == lim->subexp_to)
+ pos = 1;
+ }
+ return pos;
+}
+
+/* Check the limitations of sub expressions LIMITS, and remove the nodes
+ which are against limitations from DEST_NODES. */
+
+static reg_errcode_t
+check_subexp_limits (dfa, dest_nodes, candidates, limits, bkref_ents, str_idx)
+ re_dfa_t *dfa;
+ re_node_set *dest_nodes;
+ const re_node_set *candidates;
+ re_node_set *limits;
+ struct re_backref_cache_entry *bkref_ents;
+ int str_idx;
+{
+ reg_errcode_t err;
+ int node_idx, lim_idx;
+
+ for (lim_idx = 0; lim_idx < limits->nelem; ++lim_idx)
+ {
+ int bkref, subexp_idx;
+ struct re_backref_cache_entry *ent;
+ ent = bkref_ents + limits->elems[lim_idx];
+
+ if (str_idx <= ent->subexp_from || ent->str_idx < str_idx)
+ continue; /* This is unrelated limitation. */
+
+ bkref = (dfa->nodes[ent->node].type == OP_CONTEXT_NODE
+ ? dfa->nodes[ent->node].opr.ctx_info->entity : ent->node);
+ subexp_idx = dfa->nodes[bkref].opr.idx - 1;
+
+ if (ent->subexp_to == str_idx)
+ {
+ int ops_node = -1;
+ int cls_node = -1;
+ for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx)
+ {
+ int node = dest_nodes->elems[node_idx];
+ re_token_type_t type= dfa->nodes[node].type;
+ if (type == OP_OPEN_SUBEXP
+ && subexp_idx == dfa->nodes[node].opr.idx)
+ ops_node = node;
+ else if (type == OP_CLOSE_SUBEXP
+ && subexp_idx == dfa->nodes[node].opr.idx)
+ cls_node = node;
+ }
+
+ /* Check the limitation of the open subexpression. */
+ /* Note that (ent->subexp_to = str_idx != ent->subexp_from). */
+ if (ops_node >= 0)
+ {
+ err = sub_epsilon_src_nodes(dfa, ops_node, dest_nodes,
+ candidates);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ /* Check the limitation of the close subexpression. */
+ for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx)
+ {
+ int node = dest_nodes->elems[node_idx];
+ if (!re_node_set_contains (dfa->inveclosures + node, cls_node)
+ && !re_node_set_contains (dfa->eclosures + node, cls_node))
+ {
+ /* It is against this limitation.
+ Remove it form the current sifted state. */
+ err = sub_epsilon_src_nodes(dfa, node, dest_nodes,
+ candidates);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ --node_idx;
+ }
+ }
+ }
+ else /* (ent->subexp_to != str_idx) */
+ {
+ for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx)
+ {
+ int node = dest_nodes->elems[node_idx];
+ re_token_type_t type= dfa->nodes[node].type;
+ if (type == OP_CLOSE_SUBEXP || type == OP_OPEN_SUBEXP)
+ {
+ if (subexp_idx != dfa->nodes[node].opr.idx)
+ continue;
+ if ((type == OP_CLOSE_SUBEXP && ent->subexp_to != str_idx)
+ || (type == OP_OPEN_SUBEXP))
+ {
+ /* It is against this limitation.
+ Remove it form the current sifted state. */
+ err = sub_epsilon_src_nodes(dfa, node, dest_nodes,
+ candidates);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ }
+ }
+ }
+ }
+ return REG_NOERROR;
+}
+
+/* Search for the top (in case of sctx->check_subexp < 0) or the
+ bottom (in case of sctx->check_subexp > 0) of the subexpressions
+ which the backreference sctx->cur_bkref can match. */
+
+static reg_errcode_t
+search_subexp (preg, mctx, sctx, str_idx, dest_nodes)
+ const regex_t *preg;
+ re_match_context_t *mctx;
+ re_sift_context_t *sctx;
+ int str_idx;
+ re_node_set *dest_nodes;
+{
+ reg_errcode_t err;
+ re_dfa_t *dfa = (re_dfa_t *)preg->buffer;
+ re_sift_context_t local_sctx;
+ int node_idx, node=0; /* gnupg */
+ const re_node_set *candidates;
+ re_dfastate_t **lim_states = NULL;
+ candidates = ((mctx->state_log[str_idx] == NULL) ? &empty_set
+ : &mctx->state_log[str_idx]->nodes);
+ local_sctx.sifted_states = NULL; /* Mark that it hasn't been initialized. */
+
+ for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx)
+ {
+ re_token_type_t type;
+ int entity;
+ node = dest_nodes->elems[node_idx];
+ type = dfa->nodes[node].type;
+ entity = (type != OP_CONTEXT_NODE ? node
+ : dfa->nodes[node].opr.ctx_info->entity);
+ type = (type != OP_CONTEXT_NODE ? type : dfa->nodes[entity].type);
+
+ if (type == OP_CLOSE_SUBEXP
+ && sctx->check_subexp == dfa->nodes[node].opr.idx + 1)
+ {
+ re_dfastate_t *cur_state;
+ /* Found the bottom of the subexpression, then search for the
+ top of it. */
+ if (local_sctx.sifted_states == NULL)
+ {
+ /* It hasn't been initialized yet, initialize it now. */
+ local_sctx = *sctx;
+ err = re_node_set_init_copy (&local_sctx.limits, &sctx->limits);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ local_sctx.check_subexp = -sctx->check_subexp;
+ local_sctx.limited_states = sctx->limited_states;
+ local_sctx.last_node = node;
+ local_sctx.last_str_idx = local_sctx.cls_subexp_idx = str_idx;
+ cur_state = local_sctx.sifted_states[str_idx];
+ err = sift_states_backward (preg, mctx, &local_sctx);
+ local_sctx.sifted_states[str_idx] = cur_state;
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ /* There must not 2 same node in a node set. */
+ break;
+ }
+ else if (type == OP_OPEN_SUBEXP
+ && -sctx->check_subexp == dfa->nodes[node].opr.idx + 1)
+ {
+ /* Found the top of the subexpression, check that the
+ backreference can match the input string. */
+ char *buf;
+ int dest_str_idx;
+ int bkref_str_idx = re_string_cur_idx (mctx->input);
+ int subexp_len = sctx->cls_subexp_idx - str_idx;
+ if (subexp_len < 0 || bkref_str_idx + subexp_len > mctx->input->len)
+ break;
+
+ if (bkref_str_idx + subexp_len > mctx->input->valid_len
+ && mctx->input->valid_len < mctx->input->len)
+ {
+ reg_errcode_t err;
+ err = extend_buffers (mctx);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ buf = (char *) re_string_get_buffer (mctx->input);
+ if (strncmp (buf + str_idx, buf + bkref_str_idx, subexp_len) != 0)
+ break;
+
+ if (sctx->limits.nelem && str_idx > 0)
+ {
+ re_dfastate_t *cur_state = sctx->sifted_states[str_idx];
+ if (lim_states == NULL)
+ {
+ lim_states = re_malloc (re_dfastate_t *, str_idx + 1);
+ }
+ if (local_sctx.sifted_states == NULL)
+ {
+ /* It hasn't been initialized yet, initialize it now. */
+ local_sctx = *sctx;
+ if (BE (lim_states == NULL, 0))
+ return REG_ESPACE;
+ err = re_node_set_init_copy (&local_sctx.limits,
+ &sctx->limits);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ local_sctx.check_subexp = 0;
+ local_sctx.last_node = node;
+ local_sctx.last_str_idx = str_idx;
+ local_sctx.limited_states = lim_states;
+ memset (lim_states, '\0',
+ sizeof (re_dfastate_t*) * (str_idx + 1));
+ err = sift_states_backward (preg, mctx, &local_sctx);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ if (local_sctx.sifted_states[0] == NULL
+ && local_sctx.limited_states[0] == NULL)
+ {
+ sctx->sifted_states[str_idx] = cur_state;
+ break;
+ }
+ sctx->sifted_states[str_idx] = cur_state;
+ }
+ /* Successfully matched, add a new cache entry. */
+ dest_str_idx = bkref_str_idx + subexp_len;
+ err = match_ctx_add_entry (mctx, sctx->cur_bkref, bkref_str_idx,
+ str_idx, sctx->cls_subexp_idx);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ err = clean_state_log_if_need (mctx, dest_str_idx);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ break;
+ }
+ }
+
+ /* Remove the top/bottom of the sub expression we processed. */
+ if (node_idx < dest_nodes->nelem)
+ {
+ err = sub_epsilon_src_nodes(dfa, node, dest_nodes, candidates);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ /* Update state_log. */
+ sctx->sifted_states[str_idx] = re_acquire_state (&err, dfa, dest_nodes);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+
+ if (local_sctx.sifted_states != NULL)
+ re_node_set_free (&local_sctx.limits);
+ if (lim_states != NULL)
+ re_free (lim_states);
+ return REG_NOERROR;
+}
+
+static reg_errcode_t
+sift_states_bkref (preg, mctx, sctx, str_idx, dest_nodes)
+ const regex_t *preg;
+ re_match_context_t *mctx;
+ re_sift_context_t *sctx;
+ int str_idx;
+ re_node_set *dest_nodes;
+{
+ reg_errcode_t err;
+ re_dfa_t *dfa = (re_dfa_t *)preg->buffer;
+ int node_idx, node;
+ re_sift_context_t local_sctx;
+ const re_node_set *candidates;
+ candidates = ((mctx->state_log[str_idx] == NULL) ? &empty_set
+ : &mctx->state_log[str_idx]->nodes);
+ local_sctx.sifted_states = NULL; /* Mark that it hasn't been initialized. */
+
+ for (node_idx = 0; node_idx < candidates->nelem; ++node_idx)
+ {
+ int entity;
+ int cur_bkref_idx = re_string_cur_idx (mctx->input);
+ re_token_type_t type;
+ node = candidates->elems[node_idx];
+ type = dfa->nodes[node].type;
+ entity = (type != OP_CONTEXT_NODE ? node
+ : dfa->nodes[node].opr.ctx_info->entity);
+ type = (type != OP_CONTEXT_NODE ? type : dfa->nodes[entity].type);
+ if (node == sctx->cur_bkref && str_idx == cur_bkref_idx)
+ continue;
+ /* Avoid infinite loop for the REs like "()\1+". */
+ if (node == sctx->last_node && str_idx == sctx->last_str_idx)
+ continue;
+ if (type == OP_BACK_REF)
+ {
+ int enabled_idx;
+ for (enabled_idx = 0; enabled_idx < mctx->nbkref_ents; ++enabled_idx)
+ {
+ int disabled_idx, subexp_len, to_idx;
+ struct re_backref_cache_entry *entry;
+ entry = mctx->bkref_ents + enabled_idx;
+ subexp_len = entry->subexp_to - entry->subexp_from;
+ to_idx = str_idx + subexp_len;
+
+ if (entry->node != node || entry->str_idx != str_idx
+ || to_idx > sctx->last_str_idx
+ || sctx->sifted_states[to_idx] == NULL)
+ continue;
+ if (!STATE_NODE_CONTAINS (sctx->sifted_states[to_idx],
+ dfa->nexts[node]))
+ {
+ int dst_idx;
+ re_node_set *dsts = &sctx->sifted_states[to_idx]->nodes;
+ for (dst_idx = 0; dst_idx < dsts->nelem; ++dst_idx)
+ {
+ int dst_node = dsts->elems[dst_idx];
+ if (dfa->nodes[dst_node].type == OP_CONTEXT_NODE
+ && (dfa->nodes[dst_node].opr.ctx_info->entity
+ == dfa->nexts[node]))
+ break;
+ }
+ if (dst_idx == dsts->nelem)
+ continue;
+ }
+
+ if (check_dst_limits (dfa, &sctx->limits, mctx, node,
+ str_idx, dfa->nexts[node], to_idx))
+ continue;
+ if (sctx->check_subexp == dfa->nodes[entity].opr.idx)
+ {
+ char *buf;
+ buf = (char *) re_string_get_buffer (mctx->input);
+ if (strncmp (buf + entry->subexp_from,
+ buf + cur_bkref_idx, subexp_len) != 0)
+ continue;
+ err = match_ctx_add_entry (mctx, sctx->cur_bkref,
+ cur_bkref_idx, entry->subexp_from,
+ entry->subexp_to);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ err = clean_state_log_if_need (mctx, cur_bkref_idx
+ + subexp_len);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ else
+ {
+ re_dfastate_t *cur_state;
+ entry->flag = 0;
+ for (disabled_idx = enabled_idx + 1;
+ disabled_idx < mctx->nbkref_ents; ++disabled_idx)
+ {
+ struct re_backref_cache_entry *entry2;
+ entry2 = mctx->bkref_ents + disabled_idx;
+ if (entry2->node != node || entry2->str_idx != str_idx)
+ continue;
+ entry2->flag = 1;
+ }
+
+ if (local_sctx.sifted_states == NULL)
+ {
+ local_sctx = *sctx;
+ err = re_node_set_init_copy (&local_sctx.limits,
+ &sctx->limits);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ local_sctx.last_node = node;
+ local_sctx.last_str_idx = str_idx;
+ err = re_node_set_insert (&local_sctx.limits, enabled_idx);
+ if (BE (err < 0, 0))
+ return REG_ESPACE;
+ cur_state = local_sctx.sifted_states[str_idx];
+ err = sift_states_backward (preg, mctx, &local_sctx);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ if (sctx->limited_states != NULL)
+ {
+ err = merge_state_array (dfa, sctx->limited_states,
+ local_sctx.sifted_states,
+ str_idx + 1);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ local_sctx.sifted_states[str_idx] = cur_state;
+ re_node_set_remove_at (&local_sctx.limits,
+ local_sctx.limits.nelem - 1);
+ entry->flag = 1;
+ }
+ }
+ for (enabled_idx = 0; enabled_idx < mctx->nbkref_ents; ++enabled_idx)
+ {
+ struct re_backref_cache_entry *entry;
+ entry = mctx->bkref_ents + enabled_idx;
+ if (entry->node == node && entry->str_idx == str_idx)
+ entry->flag = 0;
+ }
+ }
+ }
+ if (local_sctx.sifted_states != NULL)
+ {
+ re_node_set_free (&local_sctx.limits);
+ }
+
+ return REG_NOERROR;
+}
+
+
+#ifdef RE_ENABLE_I18N
+static int
+sift_states_iter_mb (preg, mctx, sctx, node_idx, str_idx, max_str_idx)
+ const regex_t *preg;
+ const re_match_context_t *mctx;
+ re_sift_context_t *sctx;
+ int node_idx, str_idx, max_str_idx;
+{
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ int naccepted;
+ /* Check the node can accept `multi byte'. */
+ naccepted = check_node_accept_bytes (preg, node_idx, mctx->input, str_idx);
+ if (naccepted > 0 && str_idx + naccepted <= max_str_idx &&
+ !STATE_NODE_CONTAINS (sctx->sifted_states[str_idx + naccepted],
+ dfa->nexts[node_idx]))
+ /* The node can't accept the `multi byte', or the
+ destination was already throwed away, then the node
+ could't accept the current input `multi byte'. */
+ naccepted = 0;
+ /* Otherwise, it is sure that the node could accept
+ `naccepted' bytes input. */
+ return naccepted;
+}
+#endif /* RE_ENABLE_I18N */
+
+
+/* Functions for state transition. */
+
+/* Return the next state to which the current state STATE will transit by
+ accepting the current input byte, and update STATE_LOG if necessary.
+ If STATE can accept a multibyte char/collating element/back reference
+ update the destination of STATE_LOG. */
+
+static re_dfastate_t *
+transit_state (err, preg, mctx, state, fl_search)
+ reg_errcode_t *err;
+ const regex_t *preg;
+ re_match_context_t *mctx;
+ re_dfastate_t *state;
+ int fl_search;
+{
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ re_dfastate_t **trtable, *next_state;
+ unsigned char ch;
+
+ if (re_string_cur_idx (mctx->input) + 1 >= mctx->input->bufs_len
+ || (re_string_cur_idx (mctx->input) + 1 >= mctx->input->valid_len
+ && mctx->input->valid_len < mctx->input->len))
+ {
+ *err = extend_buffers (mctx);
+ if (BE (*err != REG_NOERROR, 0))
+ return NULL;
+ }
+
+ *err = REG_NOERROR;
+ if (state == NULL)
+ {
+ next_state = state;
+ re_string_skip_bytes (mctx->input, 1);
+ }
+ else
+ {
+#ifdef RE_ENABLE_I18N
+ /* If the current state can accept multibyte. */
+ if (state->accept_mb)
+ {
+ *err = transit_state_mb (preg, state, mctx);
+ if (BE (*err != REG_NOERROR, 0))
+ return NULL;
+ }
+#endif /* RE_ENABLE_I18N */
+
+ /* Then decide the next state with the single byte. */
+ if (1)
+ {
+ /* Use transition table */
+ ch = re_string_fetch_byte (mctx->input);
+ trtable = fl_search ? state->trtable_search : state->trtable;
+ if (trtable == NULL)
+ {
+ trtable = build_trtable (preg, state, fl_search);
+ if (fl_search)
+ state->trtable_search = trtable;
+ else
+ state->trtable = trtable;
+ }
+ next_state = trtable[ch];
+ }
+ else
+ {
+ /* don't use transition table */
+ next_state = transit_state_sb (err, preg, state, fl_search, mctx);
+ if (BE (next_state == NULL && err != REG_NOERROR, 0))
+ return NULL;
+ }
+ }
+
+ /* Update the state_log if we need. */
+ if (mctx->state_log != NULL)
+ {
+ int cur_idx = re_string_cur_idx (mctx->input);
+ if (cur_idx > mctx->state_log_top)
+ {
+ mctx->state_log[cur_idx] = next_state;
+ mctx->state_log_top = cur_idx;
+ }
+ else if (mctx->state_log[cur_idx] == 0)
+ {
+ mctx->state_log[cur_idx] = next_state;
+ }
+ else
+ {
+ re_dfastate_t *pstate;
+ unsigned int context;
+ re_node_set next_nodes, *log_nodes, *table_nodes = NULL;
+ /* If (state_log[cur_idx] != 0), it implies that cur_idx is
+ the destination of a multibyte char/collating element/
+ back reference. Then the next state is the union set of
+ these destinations and the results of the transition table. */
+ pstate = mctx->state_log[cur_idx];
+ log_nodes = pstate->entrance_nodes;
+ if (next_state != NULL)
+ {
+ table_nodes = next_state->entrance_nodes;
+ *err = re_node_set_init_union (&next_nodes, table_nodes,
+ log_nodes);
+ if (BE (*err != REG_NOERROR, 0))
+ return NULL;
+ }
+ else
+ next_nodes = *log_nodes;
+ /* Note: We already add the nodes of the initial state,
+ then we don't need to add them here. */
+
+ context = re_string_context_at (mctx->input,
+ re_string_cur_idx (mctx->input) - 1,
+ mctx->eflags, preg->newline_anchor);
+ next_state = mctx->state_log[cur_idx]
+ = re_acquire_state_context (err, dfa, &next_nodes, context);
+ /* We don't need to check errors here, since the return value of
+ this function is next_state and ERR is already set. */
+
+ if (table_nodes != NULL)
+ re_node_set_free (&next_nodes);
+ }
+ /* If the next state has back references. */
+ if (next_state != NULL && next_state->has_backref)
+ {
+ *err = transit_state_bkref (preg, next_state, mctx);
+ if (BE (*err != REG_NOERROR, 0))
+ return NULL;
+ next_state = mctx->state_log[cur_idx];
+ }
+ }
+ return next_state;
+}
+
+/* Helper functions for transit_state. */
+
+/* Return the next state to which the current state STATE will transit by
+ accepting the current input byte. */
+
+static re_dfastate_t *
+transit_state_sb (err, preg, state, fl_search, mctx)
+ reg_errcode_t *err;
+ const regex_t *preg;
+ re_dfastate_t *state;
+ int fl_search;
+ re_match_context_t *mctx;
+{
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ re_node_set next_nodes;
+ re_dfastate_t *next_state;
+ int node_cnt, cur_str_idx = re_string_cur_idx (mctx->input);
+ unsigned int context;
+
+ *err = re_node_set_alloc (&next_nodes, state->nodes.nelem + 1);
+ if (BE (*err != REG_NOERROR, 0))
+ return NULL;
+ for (node_cnt = 0; node_cnt < state->nodes.nelem; ++node_cnt)
+ {
+ int cur_node = state->nodes.elems[node_cnt];
+ if (check_node_accept (preg, dfa->nodes + cur_node, mctx, cur_str_idx))
+ {
+ *err = re_node_set_merge (&next_nodes,
+ dfa->eclosures + dfa->nexts[cur_node]);
+ if (BE (*err != REG_NOERROR, 0))
+ return NULL;
+ }
+ }
+ if (fl_search)
+ {
+#ifdef RE_ENABLE_I18N
+ int not_initial = 0;
+ if (MB_CUR_MAX > 1)
+ for (node_cnt = 0; node_cnt < next_nodes.nelem; ++node_cnt)
+ if (dfa->nodes[next_nodes.elems[node_cnt]].type == CHARACTER)
+ {
+ not_initial = dfa->nodes[next_nodes.elems[node_cnt]].mb_partial;
+ break;
+ }
+ if (!not_initial)
+#endif
+ {
+ *err = re_node_set_merge (&next_nodes,
+ dfa->init_state->entrance_nodes);
+ if (BE (*err != REG_NOERROR, 0))
+ return NULL;
+ }
+ }
+ context = re_string_context_at (mctx->input, cur_str_idx, mctx->eflags,
+ preg->newline_anchor);
+ next_state = re_acquire_state_context (err, dfa, &next_nodes, context);
+ /* We don't need to check errors here, since the return value of
+ this function is next_state and ERR is already set. */
+
+ re_node_set_free (&next_nodes);
+ re_string_skip_bytes (mctx->input, 1);
+ return next_state;
+}
+
+#ifdef RE_ENABLE_I18N
+static reg_errcode_t
+transit_state_mb (preg, pstate, mctx)
+ const regex_t *preg;
+ re_dfastate_t *pstate;
+ re_match_context_t *mctx;
+{
+ reg_errcode_t err;
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ int i;
+
+ for (i = 0; i < pstate->nodes.nelem; ++i)
+ {
+ re_node_set dest_nodes, *new_nodes;
+ int cur_node_idx = pstate->nodes.elems[i];
+ int naccepted = 0, dest_idx;
+ unsigned int context;
+ re_dfastate_t *dest_state;
+
+ if (dfa->nodes[cur_node_idx].type == OP_CONTEXT_NODE)
+ {
+ context = re_string_context_at (mctx->input,
+ re_string_cur_idx (mctx->input),
+ mctx->eflags, preg->newline_anchor);
+ if (NOT_SATISFY_NEXT_CONSTRAINT (dfa->nodes[cur_node_idx].constraint,
+ context))
+ continue;
+ cur_node_idx = dfa->nodes[cur_node_idx].opr.ctx_info->entity;
+ }
+
+ /* How many bytes the node can accepts? */
+ if (ACCEPT_MB_NODE (dfa->nodes[cur_node_idx].type))
+ naccepted = check_node_accept_bytes (preg, cur_node_idx, mctx->input,
+ re_string_cur_idx (mctx->input));
+ if (naccepted == 0)
+ continue;
+
+ /* The node can accepts `naccepted' bytes. */
+ dest_idx = re_string_cur_idx (mctx->input) + naccepted;
+ mctx->max_mb_elem_len = ((mctx->max_mb_elem_len < naccepted) ? naccepted
+ : mctx->max_mb_elem_len);
+ err = clean_state_log_if_need (mctx, dest_idx);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+#ifdef DEBUG
+ assert (dfa->nexts[cur_node_idx] != -1);
+#endif
+ /* `cur_node_idx' may point the entity of the OP_CONTEXT_NODE,
+ then we use pstate->nodes.elems[i] instead. */
+ new_nodes = dfa->eclosures + dfa->nexts[pstate->nodes.elems[i]];
+
+ dest_state = mctx->state_log[dest_idx];
+ if (dest_state == NULL)
+ dest_nodes = *new_nodes;
+ else
+ {
+ err = re_node_set_init_union (&dest_nodes,
+ dest_state->entrance_nodes, new_nodes);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ context = re_string_context_at (mctx->input, dest_idx - 1, mctx->eflags,
+ preg->newline_anchor);
+ mctx->state_log[dest_idx]
+ = re_acquire_state_context (&err, dfa, &dest_nodes, context);
+ if (BE (mctx->state_log[dest_idx] == NULL && err != REG_NOERROR, 0))
+ return err;
+ if (dest_state != NULL)
+ re_node_set_free (&dest_nodes);
+ }
+ return REG_NOERROR;
+}
+#endif /* RE_ENABLE_I18N */
+
+static reg_errcode_t
+transit_state_bkref (preg, pstate, mctx)
+ const regex_t *preg;
+ re_dfastate_t *pstate;
+ re_match_context_t *mctx;
+{
+ reg_errcode_t err;
+ re_dfastate_t **work_state_log;
+
+ work_state_log = re_malloc (re_dfastate_t *,
+ re_string_cur_idx (mctx->input) + 1);
+ if (BE (work_state_log == NULL, 0))
+ return REG_ESPACE;
+
+ err = transit_state_bkref_loop (preg, &pstate->nodes, work_state_log, mctx);
+ re_free (work_state_log);
+ return err;
+}
+
+/* Caller must allocate `work_state_log'. */
+
+static reg_errcode_t
+transit_state_bkref_loop (preg, nodes, work_state_log, mctx)
+ const regex_t *preg;
+ re_node_set *nodes;
+ re_dfastate_t **work_state_log;
+ re_match_context_t *mctx;
+{
+ reg_errcode_t err;
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ int i;
+ regmatch_t *cur_regs = re_malloc (regmatch_t, preg->re_nsub + 1);
+ int cur_str_idx = re_string_cur_idx (mctx->input);
+ if (BE (cur_regs == NULL, 0))
+ return REG_ESPACE;
+
+ for (i = 0; i < nodes->nelem; ++i)
+ {
+ int dest_str_idx, subexp_idx, prev_nelem, bkc_idx;
+ int node_idx = nodes->elems[i];
+ unsigned int context;
+ re_token_t *node = dfa->nodes + node_idx;
+ re_node_set *new_dest_nodes;
+ re_sift_context_t sctx;
+
+ /* Check whether `node' is a backreference or not. */
+ if (node->type == OP_BACK_REF)
+ subexp_idx = node->opr.idx;
+ else if (node->type == OP_CONTEXT_NODE &&
+ dfa->nodes[node->opr.ctx_info->entity].type == OP_BACK_REF)
+ {
+ context = re_string_context_at (mctx->input, cur_str_idx,
+ mctx->eflags, preg->newline_anchor);
+ if (NOT_SATISFY_NEXT_CONSTRAINT (node->constraint, context))
+ continue;
+ subexp_idx = dfa->nodes[node->opr.ctx_info->entity].opr.idx;
+ }
+ else
+ continue;
+
+ /* `node' is a backreference.
+ Check the substring which the substring matched. */
+ sift_ctx_init (&sctx, work_state_log, NULL, node_idx, cur_str_idx,
+ subexp_idx);
+ sctx.cur_bkref = node_idx;
+ match_ctx_clear_flag (mctx);
+ err = sift_states_backward (preg, mctx, &sctx);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+
+ /* And add the epsilon closures (which is `new_dest_nodes') of
+ the backreference to appropriate state_log. */
+#ifdef DEBUG
+ assert (dfa->nexts[node_idx] != -1);
+#endif
+ for (bkc_idx = 0; bkc_idx < mctx->nbkref_ents; ++bkc_idx)
+ {
+ int subexp_len;
+ re_dfastate_t *dest_state;
+ struct re_backref_cache_entry *bkref_ent;
+ bkref_ent = mctx->bkref_ents + bkc_idx;
+ if (bkref_ent->node != node_idx || bkref_ent->str_idx != cur_str_idx)
+ continue;
+ subexp_len = bkref_ent->subexp_to - bkref_ent->subexp_from;
+ new_dest_nodes = ((node->type == OP_CONTEXT_NODE && subexp_len == 0)
+ ? dfa->nodes[node_idx].opr.ctx_info->bkref_eclosure
+ : dfa->eclosures + dfa->nexts[node_idx]);
+ dest_str_idx = (cur_str_idx + bkref_ent->subexp_to
+ - bkref_ent->subexp_from);
+ context = (IS_WORD_CHAR (re_string_byte_at (mctx->input,
+ dest_str_idx - 1))
+ ? CONTEXT_WORD : 0);
+ dest_state = mctx->state_log[dest_str_idx];
+ prev_nelem = ((mctx->state_log[cur_str_idx] == NULL) ? 0
+ : mctx->state_log[cur_str_idx]->nodes.nelem);
+ /* Add `new_dest_node' to state_log. */
+ if (dest_state == NULL)
+ {
+ mctx->state_log[dest_str_idx]
+ = re_acquire_state_context (&err, dfa, new_dest_nodes,
+ context);
+ if (BE (mctx->state_log[dest_str_idx] == NULL
+ && err != REG_NOERROR, 0))
+ return err;
+ }
+ else
+ {
+ re_node_set dest_nodes;
+ err = re_node_set_init_union (&dest_nodes,
+ dest_state->entrance_nodes,
+ new_dest_nodes);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ mctx->state_log[dest_str_idx]
+ = re_acquire_state_context (&err, dfa, &dest_nodes, context);
+ if (BE (mctx->state_log[dest_str_idx] == NULL
+ && err != REG_NOERROR, 0))
+ return err;
+ re_node_set_free (&dest_nodes);
+ }
+ /* We need to check recursively if the backreference can epsilon
+ transit. */
+ if (subexp_len == 0
+ && mctx->state_log[cur_str_idx]->nodes.nelem > prev_nelem)
+ {
+ err = transit_state_bkref_loop (preg, new_dest_nodes,
+ work_state_log, mctx);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ }
+ }
+ re_free (cur_regs);
+ return REG_NOERROR;
+}
+
+/* Build transition table for the state.
+ Return the new table if succeeded, otherwise return NULL. */
+
+static re_dfastate_t **
+build_trtable (preg, state, fl_search)
+ const regex_t *preg;
+ const re_dfastate_t *state;
+ int fl_search;
+{
+ reg_errcode_t err;
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ int i, j, k, ch;
+ int ndests; /* Number of the destination states from `state'. */
+ re_dfastate_t **trtable, **dest_states, **dest_states_word, **dest_states_nl;
+ re_node_set follows, *dests_node;
+ bitset *dests_ch;
+ bitset acceptable;
+
+ /* We build DFA states which corresponds to the destination nodes
+ from `state'. `dests_node[i]' represents the nodes which i-th
+ destination state contains, and `dests_ch[i]' represents the
+ characters which i-th destination state accepts. */
+ dests_node = re_malloc (re_node_set, SBC_MAX);
+ dests_ch = re_malloc (bitset, SBC_MAX);
+
+ /* Initialize transiton table. */
+ trtable = (re_dfastate_t **) calloc (sizeof (re_dfastate_t *), SBC_MAX);
+ if (BE (dests_node == NULL || dests_ch == NULL || trtable == NULL, 0))
+ return NULL;
+
+ /* At first, group all nodes belonging to `state' into several
+ destinations. */
+ ndests = group_nodes_into_DFAstates (preg, state, dests_node, dests_ch);
+ if (BE (ndests <= 0, 0))
+ {
+ re_free (dests_node);
+ re_free (dests_ch);
+ /* Return NULL in case of an error, trtable otherwise. */
+ return (ndests < 0) ? NULL : trtable;
+ }
+
+ dest_states = re_malloc (re_dfastate_t *, ndests);
+ dest_states_word = re_malloc (re_dfastate_t *, ndests);
+ dest_states_nl = re_malloc (re_dfastate_t *, ndests);
+ bitset_empty (acceptable);
+
+ err = re_node_set_alloc (&follows, ndests + 1);
+ if (BE (dest_states == NULL || dest_states_word == NULL
+ || dest_states_nl == NULL || err != REG_NOERROR, 0))
+ return NULL;
+
+ /* Then build the states for all destinations. */
+ for (i = 0; i < ndests; ++i)
+ {
+ int next_node;
+ re_node_set_empty (&follows);
+ /* Merge the follows of this destination states. */
+ for (j = 0; j < dests_node[i].nelem; ++j)
+ {
+ next_node = dfa->nexts[dests_node[i].elems[j]];
+ if (next_node != -1)
+ {
+ err = re_node_set_merge (&follows, dfa->eclosures + next_node);
+ if (BE (err != REG_NOERROR, 0))
+ return NULL;
+ }
+ }
+ /* If search flag is set, merge the initial state. */
+ if (fl_search)
+ {
+#ifdef RE_ENABLE_I18N
+ int not_initial = 0;
+ for (j = 0; j < follows.nelem; ++j)
+ if (dfa->nodes[follows.elems[j]].type == CHARACTER)
+ {
+ not_initial = dfa->nodes[follows.elems[j]].mb_partial;
+ break;
+ }
+ if (!not_initial)
+#endif
+ {
+ err = re_node_set_merge (&follows,
+ dfa->init_state->entrance_nodes);
+ if (BE (err != REG_NOERROR, 0))
+ return NULL;
+ }
+ }
+ dest_states[i] = re_acquire_state_context (&err, dfa, &follows, 0);
+ if (BE (dest_states[i] == NULL && err != REG_NOERROR, 0))
+ return NULL;
+ /* If the new state has context constraint,
+ build appropriate states for these contexts. */
+ if (dest_states[i]->has_constraint)
+ {
+ dest_states_word[i] = re_acquire_state_context (&err, dfa, &follows,
+ CONTEXT_WORD);
+ if (BE (dest_states_word[i] == NULL && err != REG_NOERROR, 0))
+ return NULL;
+ dest_states_nl[i] = re_acquire_state_context (&err, dfa, &follows,
+ CONTEXT_NEWLINE);
+ if (BE (dest_states_nl[i] == NULL && err != REG_NOERROR, 0))
+ return NULL;
+ }
+ else
+ {
+ dest_states_word[i] = dest_states[i];
+ dest_states_nl[i] = dest_states[i];
+ }
+ bitset_merge (acceptable, dests_ch[i]);
+ }
+
+ /* Update the transition table. */
+ /* For all characters ch...: */
+ for (i = 0, ch = 0; i < BITSET_UINTS; ++i)
+ for (j = 0; j < UINT_BITS; ++j, ++ch)
+ if ((acceptable[i] >> j) & 1)
+ {
+ /* The current state accepts the character ch. */
+ if (IS_WORD_CHAR (ch))
+ {
+ for (k = 0; k < ndests; ++k)
+ if ((dests_ch[k][i] >> j) & 1)
+ {
+ /* k-th destination accepts the word character ch. */
+ trtable[ch] = dest_states_word[k];
+ /* There must be only one destination which accepts
+ character ch. See group_nodes_into_DFAstates. */
+ break;
+ }
+ }
+ else /* not WORD_CHAR */
+ {
+ for (k = 0; k < ndests; ++k)
+ if ((dests_ch[k][i] >> j) & 1)
+ {
+ /* k-th destination accepts the non-word character ch. */
+ trtable[ch] = dest_states[k];
+ /* There must be only one destination which accepts
+ character ch. See group_nodes_into_DFAstates. */
+ break;
+ }
+ }
+ }
+ /* new line */
+ if (bitset_contain (acceptable, NEWLINE_CHAR))
+ {
+ /* The current state accepts newline character. */
+ for (k = 0; k < ndests; ++k)
+ if (bitset_contain (dests_ch[k], NEWLINE_CHAR))
+ {
+ /* k-th destination accepts newline character. */
+ trtable[NEWLINE_CHAR] = dest_states_nl[k];
+ /* There must be only one destination which accepts
+ newline. See group_nodes_into_DFAstates. */
+ break;
+ }
+ }
+
+ re_free (dest_states_nl);
+ re_free (dest_states_word);
+ re_free (dest_states);
+
+ re_node_set_free (&follows);
+ for (i = 0; i < ndests; ++i)
+ re_node_set_free (dests_node + i);
+
+ re_free (dests_ch);
+ re_free (dests_node);
+
+ return trtable;
+}
+
+/* Group all nodes belonging to STATE into several destinations.
+ Then for all destinations, set the nodes belonging to the destination
+ to DESTS_NODE[i] and set the characters accepted by the destination
+ to DEST_CH[i]. This function return the number of destinations. */
+
+static int
+group_nodes_into_DFAstates (preg, state, dests_node, dests_ch)
+ const regex_t *preg;
+ const re_dfastate_t *state;
+ re_node_set *dests_node;
+ bitset *dests_ch;
+{
+ reg_errcode_t err;
+ const re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ int i, j, k;
+ int ndests; /* Number of the destinations from `state'. */
+ bitset accepts; /* Characters a node can accept. */
+ const re_node_set *cur_nodes = &state->nodes;
+ bitset_empty (accepts);
+ ndests = 0;
+
+ /* For all the nodes belonging to `state', */
+ for (i = 0; i < cur_nodes->nelem; ++i)
+ {
+ unsigned int constraint = 0;
+ re_token_t *node = &dfa->nodes[cur_nodes->elems[i]];
+ re_token_type_t type = node->type;
+
+ if (type == OP_CONTEXT_NODE)
+ {
+ constraint = node->constraint;
+ node = dfa->nodes + node->opr.ctx_info->entity;
+ type = node->type;
+ }
+
+ /* Enumerate all single byte character this node can accept. */
+ if (type == CHARACTER)
+ bitset_set (accepts, node->opr.c);
+ else if (type == SIMPLE_BRACKET)
+ {
+ bitset_merge (accepts, node->opr.sbcset);
+ }
+ else if (type == OP_PERIOD)
+ {
+ bitset_set_all (accepts);
+ if (!(preg->syntax & RE_DOT_NEWLINE))
+ bitset_clear (accepts, '\n');
+ if (preg->syntax & RE_DOT_NOT_NULL)
+ bitset_clear (accepts, '\0');
+ }
+ else
+ continue;
+
+ /* Check the `accepts' and sift the characters which are not
+ match it the context. */
+ if (constraint)
+ {
+ if (constraint & NEXT_WORD_CONSTRAINT)
+ for (j = 0; j < BITSET_UINTS; ++j)
+ accepts[j] &= dfa->word_char[j];
+ else if (constraint & NEXT_NOTWORD_CONSTRAINT)
+ for (j = 0; j < BITSET_UINTS; ++j)
+ accepts[j] &= ~dfa->word_char[j];
+ else if (constraint & NEXT_NEWLINE_CONSTRAINT)
+ {
+ int accepts_newline = bitset_contain (accepts, NEWLINE_CHAR);
+ bitset_empty (accepts);
+ if (accepts_newline)
+ bitset_set (accepts, NEWLINE_CHAR);
+ else
+ continue;
+ }
+ }
+
+ /* Then divide `accepts' into DFA states, or create a new
+ state. */
+ for (j = 0; j < ndests; ++j)
+ {
+ bitset intersec; /* Intersection sets, see below. */
+ bitset remains;
+ /* Flags, see below. */
+ int has_intersec, not_subset, not_consumed;
+
+ /* Optimization, skip if this state doesn't accept the character. */
+ if (type == CHARACTER && !bitset_contain (dests_ch[j], node->opr.c))
+ continue;
+
+ /* Enumerate the intersection set of this state and `accepts'. */
+ has_intersec = 0;
+ for (k = 0; k < BITSET_UINTS; ++k)
+ has_intersec |= intersec[k] = accepts[k] & dests_ch[j][k];
+ /* And skip if the intersection set is empty. */
+ if (!has_intersec)
+ continue;
+
+ /* Then check if this state is a subset of `accepts'. */
+ not_subset = not_consumed = 0;
+ for (k = 0; k < BITSET_UINTS; ++k)
+ {
+ not_subset |= remains[k] = ~accepts[k] & dests_ch[j][k];
+ not_consumed |= accepts[k] = accepts[k] & ~dests_ch[j][k];
+ }
+
+ /* If this state isn't a subset of `accepts', create a
+ new group state, which has the `remains'. */
+ if (not_subset)
+ {
+ bitset_copy (dests_ch[ndests], remains);
+ bitset_copy (dests_ch[j], intersec);
+ err = re_node_set_init_copy (dests_node + ndests, &dests_node[j]);
+ if (BE (err != REG_NOERROR, 0))
+ return -1;
+ ++ndests;
+ }
+
+ /* Put the position in the current group. */
+ err = re_node_set_insert (&dests_node[j], cur_nodes->elems[i]);
+ if (BE (err < 0, 0))
+ return -1;
+
+ /* If all characters are consumed, go to next node. */
+ if (!not_consumed)
+ break;
+ }
+ /* Some characters remain, create a new group. */
+ if (j == ndests)
+ {
+ bitset_copy (dests_ch[ndests], accepts);
+ err = re_node_set_init_1 (dests_node + ndests, cur_nodes->elems[i]);
+ if (BE (err != REG_NOERROR, 0))
+ return -1;
+ ++ndests;
+ bitset_empty (accepts);
+ }
+ }
+ return ndests;
+}
+
+#ifdef RE_ENABLE_I18N
+/* Check how many bytes the node `dfa->nodes[node_idx]' accepts.
+ Return the number of the bytes the node accepts.
+ STR_IDX is the current index of the input string.
+
+ This function handles the nodes which can accept one character, or
+ one collating element like '.', '[a-z]', opposite to the other nodes
+ can only accept one byte. */
+
+static int
+check_node_accept_bytes (preg, node_idx, input, str_idx)
+ const regex_t *preg;
+ int node_idx, str_idx;
+ const re_string_t *input;
+{
+ const re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ const re_token_t *node = dfa->nodes + node_idx;
+ int elem_len = re_string_elem_size_at (input, str_idx);
+ int char_len = re_string_char_size_at (input, str_idx);
+ int i;
+# ifdef _LIBC
+ int j;
+ uint32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
+# endif /* _LIBC */
+ if (elem_len <= 1 && char_len <= 1)
+ return 0;
+ if (node->type == OP_PERIOD)
+ {
+ /* '.' accepts any one character except the following two cases. */
+ if ((!(preg->syntax & RE_DOT_NEWLINE) &&
+ re_string_byte_at (input, str_idx) == '\n') ||
+ ((preg->syntax & RE_DOT_NOT_NULL) &&
+ re_string_byte_at (input, str_idx) == '\0'))
+ return 0;
+ return char_len;
+ }
+ else if (node->type == COMPLEX_BRACKET)
+ {
+ const re_charset_t *cset = node->opr.mbcset;
+# ifdef _LIBC
+ const unsigned char *pin = re_string_get_buffer (input) + str_idx;
+# endif /* _LIBC */
+ int match_len = 0;
+ wchar_t wc = ((cset->nranges || cset->nchar_classes || cset->nmbchars)
+ ? re_string_wchar_at (input, str_idx) : 0);
+
+ /* match with multibyte character? */
+ for (i = 0; i < cset->nmbchars; ++i)
+ if (wc == cset->mbchars[i])
+ {
+ match_len = char_len;
+ goto check_node_accept_bytes_match;
+ }
+ /* match with character_class? */
+ for (i = 0; i < cset->nchar_classes; ++i)
+ {
+ wctype_t wt = cset->char_classes[i];
+ if (__iswctype (wc, wt))
+ {
+ match_len = char_len;
+ goto check_node_accept_bytes_match;
+ }
+ }
+
+# ifdef _LIBC
+ if (nrules != 0)
+ {
+ unsigned int in_collseq = 0;
+ const int32_t *table, *indirect;
+ const unsigned char *weights, *extra;
+ const char *collseqwc;
+ int32_t idx;
+ /* This #include defines a local function! */
+# include <locale/weight.h>
+
+ /* match with collating_symbol? */
+ if (cset->ncoll_syms)
+ extra = (const unsigned char *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB);
+ for (i = 0; i < cset->ncoll_syms; ++i)
+ {
+ const unsigned char *coll_sym = extra + cset->coll_syms[i];
+ /* Compare the length of input collating element and
+ the length of current collating element. */
+ if (*coll_sym != elem_len)
+ continue;
+ /* Compare each bytes. */
+ for (j = 0; j < *coll_sym; j++)
+ if (pin[j] != coll_sym[1 + j])
+ break;
+ if (j == *coll_sym)
+ {
+ /* Match if every bytes is equal. */
+ match_len = j;
+ goto check_node_accept_bytes_match;
+ }
+ }
+
+ if (cset->nranges)
+ {
+ if (elem_len <= char_len)
+ {
+ collseqwc = _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQWC);
+ in_collseq = collseq_table_lookup (collseqwc, wc);
+ }
+ else
+ in_collseq = find_collation_sequence_value (pin, elem_len);
+ }
+ /* match with range expression? */
+ for (i = 0; i < cset->nranges; ++i)
+ if (cset->range_starts[i] <= in_collseq
+ && in_collseq <= cset->range_ends[i])
+ {
+ match_len = elem_len;
+ goto check_node_accept_bytes_match;
+ }
+
+ /* match with equivalence_class? */
+ if (cset->nequiv_classes)
+ {
+ const unsigned char *cp = pin;
+ table = (const int32_t *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB);
+ weights = (const unsigned char *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_WEIGHTMB);
+ extra = (const unsigned char *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAMB);
+ indirect = (const int32_t *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_INDIRECTMB);
+ idx = findidx (&cp);
+ if (idx > 0)
+ for (i = 0; i < cset->nequiv_classes; ++i)
+ {
+ int32_t equiv_class_idx = cset->equiv_classes[i];
+ size_t weight_len = weights[idx];
+ if (weight_len == weights[equiv_class_idx])
+ {
+ int cnt = 0;
+ while (cnt <= weight_len
+ && (weights[equiv_class_idx + 1 + cnt]
+ == weights[idx + 1 + cnt]))
+ ++cnt;
+ if (cnt > weight_len)
+ {
+ match_len = elem_len;
+ goto check_node_accept_bytes_match;
+ }
+ }
+ }
+ }
+ }
+ else
+# endif /* _LIBC */
+ {
+ /* match with range expression? */
+#if __GNUC__ >= 2
+ wchar_t cmp_buf[] = {L'\0', L'\0', wc, L'\0', L'\0', L'\0'};
+#else
+ wchar_t cmp_buf[] = {L'\0', L'\0', L'\0', L'\0', L'\0', L'\0'};
+ cmp_buf[2] = wc;
+#endif
+ for (i = 0; i < cset->nranges; ++i)
+ {
+ cmp_buf[0] = cset->range_starts[i];
+ cmp_buf[4] = cset->range_ends[i];
+ if (wcscoll (cmp_buf, cmp_buf + 2) <= 0
+ && wcscoll (cmp_buf + 2, cmp_buf + 4) <= 0)
+ {
+ match_len = char_len;
+ goto check_node_accept_bytes_match;
+ }
+ }
+ }
+ check_node_accept_bytes_match:
+ if (!cset->non_match)
+ return match_len;
+ else
+ {
+ if (match_len > 0)
+ return 0;
+ else
+ return (elem_len > char_len) ? elem_len : char_len;
+ }
+ }
+ return 0;
+}
+
+# ifdef _LIBC
+static unsigned int
+find_collation_sequence_value (mbs, mbs_len)
+ const unsigned char *mbs;
+ size_t mbs_len;
+{
+ uint32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
+ if (nrules == 0)
+ {
+ if (mbs_len == 1)
+ {
+ /* No valid character. Match it as a single byte character. */
+ const unsigned char *collseq = (const unsigned char *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQMB);
+ return collseq[mbs[0]];
+ }
+ return UINT_MAX;
+ }
+ else
+ {
+ int32_t idx;
+ const unsigned char *extra = (const unsigned char *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB);
+
+ for (idx = 0; ;)
+ {
+ int mbs_cnt, found = 0;
+ int32_t elem_mbs_len;
+ /* Skip the name of collating element name. */
+ idx = idx + extra[idx] + 1;
+ elem_mbs_len = extra[idx++];
+ if (mbs_len == elem_mbs_len)
+ {
+ for (mbs_cnt = 0; mbs_cnt < elem_mbs_len; ++mbs_cnt)
+ if (extra[idx + mbs_cnt] != mbs[mbs_cnt])
+ break;
+ if (mbs_cnt == elem_mbs_len)
+ /* Found the entry. */
+ found = 1;
+ }
+ /* Skip the byte sequence of the collating element. */
+ idx += elem_mbs_len;
+ /* Adjust for the alignment. */
+ idx = (idx + 3) & ~3;
+ /* Skip the collation sequence value. */
+ idx += sizeof (uint32_t);
+ /* Skip the wide char sequence of the collating element. */
+ idx = idx + sizeof (uint32_t) * (extra[idx] + 1);
+ /* If we found the entry, return the sequence value. */
+ if (found)
+ return *(uint32_t *) (extra + idx);
+ /* Skip the collation sequence value. */
+ idx += sizeof (uint32_t);
+ }
+ }
+}
+# endif /* _LIBC */
+#endif /* RE_ENABLE_I18N */
+
+/* Check whether the node accepts the byte which is IDX-th
+ byte of the INPUT. */
+
+static int
+check_node_accept (preg, node, mctx, idx)
+ const regex_t *preg;
+ const re_token_t *node;
+ const re_match_context_t *mctx;
+ int idx;
+{
+ const re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ const re_token_t *cur_node;
+ unsigned char ch;
+ if (node->type == OP_CONTEXT_NODE)
+ {
+ /* The node has constraints. Check whether the current context
+ satisfies the constraints. */
+ unsigned int context = re_string_context_at (mctx->input, idx,
+ mctx->eflags,
+ preg->newline_anchor);
+ if (NOT_SATISFY_NEXT_CONSTRAINT (node->constraint, context))
+ return 0;
+ cur_node = dfa->nodes + node->opr.ctx_info->entity;
+ }
+ else
+ cur_node = node;
+
+ ch = re_string_byte_at (mctx->input, idx);
+ if (cur_node->type == CHARACTER)
+ return cur_node->opr.c == ch;
+ else if (cur_node->type == SIMPLE_BRACKET)
+ return bitset_contain (cur_node->opr.sbcset, ch);
+ else if (cur_node->type == OP_PERIOD)
+ return !((ch == '\n' && !(preg->syntax & RE_DOT_NEWLINE))
+ || (ch == '\0' && (preg->syntax & RE_DOT_NOT_NULL)));
+ else
+ return 0;
+}
+
+/* Extend the buffers, if the buffers have run out. */
+
+static reg_errcode_t
+extend_buffers (mctx)
+ re_match_context_t *mctx;
+{
+ reg_errcode_t ret;
+ re_string_t *pstr = mctx->input;
+
+ /* Double the lengthes of the buffers. */
+ ret = re_string_realloc_buffers (pstr, pstr->bufs_len * 2);
+ if (BE (ret != REG_NOERROR, 0))
+ return ret;
+
+ if (mctx->state_log != NULL)
+ {
+ /* And double the length of state_log. */
+ mctx->state_log = re_realloc (mctx->state_log, re_dfastate_t *,
+ pstr->bufs_len * 2);
+ if (BE (mctx->state_log == NULL, 0))
+ return REG_ESPACE;
+ }
+
+ /* Then reconstruct the buffers. */
+ if (pstr->icase)
+ {
+#ifdef RE_ENABLE_I18N
+ if (MB_CUR_MAX > 1)
+ build_wcs_upper_buffer (pstr);
+ else
+#endif /* RE_ENABLE_I18N */
+ build_upper_buffer (pstr);
+ }
+ else
+ {
+#ifdef RE_ENABLE_I18N
+ if (MB_CUR_MAX > 1)
+ build_wcs_buffer (pstr);
+ else
+#endif /* RE_ENABLE_I18N */
+ {
+ if (pstr->trans != NULL)
+ re_string_translate_buffer (pstr);
+ else
+ pstr->valid_len = pstr->bufs_len;
+ }
+ }
+ return REG_NOERROR;
+}
+
+
+/* Functions for matching context. */
+
+static reg_errcode_t
+match_ctx_init (mctx, eflags, input, n)
+ re_match_context_t *mctx;
+ int eflags, n;
+ re_string_t *input;
+{
+ mctx->eflags = eflags;
+ mctx->input = input;
+ mctx->match_last = -1;
+ if (n > 0)
+ {
+ mctx->bkref_ents = re_malloc (struct re_backref_cache_entry, n);
+ if (BE (mctx->bkref_ents == NULL, 0))
+ return REG_ESPACE;
+ }
+ else
+ mctx->bkref_ents = NULL;
+ mctx->nbkref_ents = 0;
+ mctx->abkref_ents = n;
+ mctx->max_mb_elem_len = 0;
+ return REG_NOERROR;
+}
+
+static void
+match_ctx_free (mctx)
+ re_match_context_t *mctx;
+{
+ re_free (mctx->bkref_ents);
+}
+
+/* Add a new backreference entry to the cache. */
+
+static reg_errcode_t
+match_ctx_add_entry (mctx, node, str_idx, from, to)
+ re_match_context_t *mctx;
+ int node, str_idx, from, to;
+{
+ if (mctx->nbkref_ents >= mctx->abkref_ents)
+ {
+ mctx->bkref_ents = re_realloc (mctx->bkref_ents,
+ struct re_backref_cache_entry,
+ mctx->abkref_ents * 2);
+ if (BE (mctx->bkref_ents == NULL, 0))
+ return REG_ESPACE;
+ memset (mctx->bkref_ents + mctx->nbkref_ents, '\0',
+ sizeof (struct re_backref_cache_entry) * mctx->abkref_ents);
+ mctx->abkref_ents *= 2;
+ }
+ mctx->bkref_ents[mctx->nbkref_ents].node = node;
+ mctx->bkref_ents[mctx->nbkref_ents].str_idx = str_idx;
+ mctx->bkref_ents[mctx->nbkref_ents].subexp_from = from;
+ mctx->bkref_ents[mctx->nbkref_ents].subexp_to = to;
+ mctx->bkref_ents[mctx->nbkref_ents++].flag = 0;
+ if (mctx->max_mb_elem_len < to - from)
+ mctx->max_mb_elem_len = to - from;
+ return REG_NOERROR;
+}
+
+static void
+match_ctx_clear_flag (mctx)
+ re_match_context_t *mctx;
+{
+ int i;
+ for (i = 0; i < mctx->nbkref_ents; ++i)
+ {
+ mctx->bkref_ents[i].flag = 0;
+ }
+}
+
+static void
+sift_ctx_init (sctx, sifted_sts, limited_sts, last_node, last_str_idx,
+ check_subexp)
+ re_sift_context_t *sctx;
+ re_dfastate_t **sifted_sts, **limited_sts;
+ int last_node, last_str_idx, check_subexp;
+{
+ sctx->sifted_states = sifted_sts;
+ sctx->limited_states = limited_sts;
+ sctx->last_node = last_node;
+ sctx->last_str_idx = last_str_idx;
+ sctx->check_subexp = check_subexp;
+ re_node_set_init_empty (&sctx->limits);
+}
diff --git a/util/secmem.c b/util/secmem.c
new file mode 100644
index 0000000..5ffbfc6
--- /dev/null
+++ b/util/secmem.c
@@ -0,0 +1,499 @@
+/* secmem.c - memory allocation from a secure heap
+ * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <unistd.h>
+#if defined(HAVE_MLOCK) || defined(HAVE_MMAP)
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#ifdef USE_CAPABILITIES
+#include <sys/capability.h>
+#endif
+#ifdef HAVE_PLOCK
+#include <sys/lock.h>
+#endif
+#endif
+
+#include "types.h"
+#include "memory.h"
+#include "util.h"
+#include "i18n.h"
+
+/* MinGW doesn't seem to prototype getpagesize, though it does have
+ it. */
+#if !HAVE_DECL_GETPAGESIZE
+int getpagesize(void);
+#endif
+
+#if defined(MAP_ANON) && !defined(MAP_ANONYMOUS)
+#define MAP_ANONYMOUS MAP_ANON
+#endif
+/* It seems that Slackware 7.1 does not know about EPERM */
+#if !defined(EPERM) && defined(ENOMEM)
+#define EPERM ENOMEM
+#endif
+
+
+#define DEFAULT_POOLSIZE 16384
+
+typedef struct memblock_struct MEMBLOCK;
+struct memblock_struct {
+ unsigned size;
+ union {
+ MEMBLOCK *next;
+ PROPERLY_ALIGNED_TYPE aligned;
+ } u;
+};
+
+
+
+static void *pool;
+static volatile int pool_okay; /* may be checked in an atexit function */
+#ifdef HAVE_MMAP
+static volatile int pool_is_mmapped;
+#endif
+static size_t poolsize; /* allocated length */
+static size_t poollen; /* used length */
+static MEMBLOCK *unused_blocks;
+static unsigned max_alloced;
+static unsigned cur_alloced;
+static unsigned max_blocks;
+static unsigned cur_blocks;
+static int disable_secmem;
+static int show_warning;
+static int no_warning;
+static int suspend_warning;
+
+
+static void
+print_warn(void)
+{
+ if (!no_warning)
+ {
+ log_info(_("WARNING: using insecure memory!\n"));
+ log_info(_("please see http://www.gnupg.org/faq.html"
+ " for more information\n"));
+ }
+}
+
+
+static void
+lock_pool( void *p, size_t n )
+{
+#if defined(USE_CAPABILITIES) && defined(HAVE_MLOCK)
+ int err;
+
+ cap_set_proc( cap_from_text("cap_ipc_lock+ep") );
+ err = mlock( p, n );
+ if( err && errno )
+ err = errno;
+ cap_set_proc( cap_from_text("cap_ipc_lock+p") );
+
+ if( err ) {
+ if( errno != EPERM
+#ifdef EAGAIN /* OpenBSD returns this */
+ && errno != EAGAIN
+#endif
+#ifdef ENOSYS /* Some SCOs return this (function not implemented) */
+ && errno != ENOSYS
+#endif
+#ifdef ENOMEM /* Linux can return this */
+ && errno != ENOMEM
+#endif
+ )
+ log_error("can't lock memory: %s\n", strerror(err));
+ show_warning = 1;
+ }
+
+#elif defined(HAVE_MLOCK)
+ uid_t uid;
+ int err;
+
+ uid = getuid();
+
+#ifdef HAVE_BROKEN_MLOCK
+ /* ick. but at least we get secured memory. about to lock
+ entire data segment. */
+#ifdef HAVE_PLOCK
+# ifdef _AIX
+ /* The configure for AIX returns broken mlock but the plock has
+ the strange requirement to somehow set the stack limit first.
+ The problem might turn out in indeterministic program behaviour
+ and hanging processes which can somehow be solved when enough
+ processes are clogging up the memory. To get this problem out
+ of the way we simply don't try to lock the memory at all.
+ */
+ errno = EPERM;
+ err = errno;
+# else /* !_AIX */
+ err = plock( DATLOCK );
+ if( err && errno )
+ err = errno;
+# endif /*_AIX*/
+#else /*!HAVE_PLOCK*/
+ if( uid ) {
+ errno = EPERM;
+ err = errno;
+ }
+ else {
+ err = mlock( p, n );
+ if( err && errno )
+ err = errno;
+ }
+#endif /*!HAVE_PLOCK*/
+#else
+ err = mlock( p, n );
+ if( err && errno )
+ err = errno;
+#endif
+
+ if( uid && !geteuid() ) {
+ /* check that we really dropped the privs.
+ * Note: setuid(0) should always fail */
+ if( setuid( uid ) || getuid() != geteuid() || !setuid(0) )
+ log_fatal("failed to reset uid: %s\n", strerror(errno));
+ }
+
+ if( err ) {
+ if( errno != EPERM
+#ifdef EAGAIN /* OpenBSD returns this */
+ && errno != EAGAIN
+#endif
+#ifdef ENOSYS /* Some SCOs return this (function not implemented) */
+ && errno != ENOSYS
+#endif
+#ifdef ENOMEM /* Linux can return this */
+ && errno != ENOMEM
+#endif
+ )
+ log_error("can't lock memory: %s\n", strerror(err));
+ show_warning = 1;
+ }
+
+#elif defined ( __QNX__ )
+ /* QNX does not page at all, so the whole secure memory stuff does
+ * not make much sense. However it is still of use because it
+ * wipes out the memory on a free().
+ * Therefore it is sufficient to suppress the warning
+ */
+#elif defined (HAVE_DOSISH_SYSTEM) || defined (__CYGWIN__)
+ /* It does not make sense to print such a warning, given the fact that
+ * this whole Windows !@#$% and their user base are inherently insecure
+ */
+#elif defined (__riscos__)
+ /* no virtual memory on RISC OS, so no pages are swapped to disc,
+ * besides we don't have mmap, so we don't use it! ;-)
+ * But don't complain, as explained above.
+ */
+#else
+ log_info("Please note that you don't have secure memory on this system\n");
+#endif
+}
+
+
+static void
+init_pool( size_t n)
+{
+ size_t pgsize;
+
+ poolsize = n;
+
+ if( disable_secmem )
+ log_bug("secure memory is disabled");
+
+#ifdef HAVE_GETPAGESIZE
+ pgsize = getpagesize();
+#else
+ pgsize = 4096;
+#endif
+
+#ifdef HAVE_MMAP
+ poolsize = (poolsize + pgsize -1 ) & ~(pgsize-1);
+#ifdef MAP_ANONYMOUS
+ pool = mmap( 0, poolsize, PROT_READ|PROT_WRITE,
+ MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
+#else /* map /dev/zero instead */
+ { int fd;
+
+ fd = open("/dev/zero", O_RDWR);
+ if( fd == -1 ) {
+ log_error("can't open /dev/zero: %s\n", strerror(errno) );
+ pool = (void*)-1;
+ }
+ else {
+ pool = mmap( 0, poolsize, PROT_READ|PROT_WRITE,
+ MAP_PRIVATE, fd, 0);
+ close (fd);
+ }
+ }
+#endif
+ if( pool == (void*)-1 )
+ log_info("can't mmap pool of %u bytes: %s - using malloc\n",
+ (unsigned)poolsize, strerror(errno));
+ else {
+ pool_is_mmapped = 1;
+ pool_okay = 1;
+ }
+
+#endif
+ if( !pool_okay ) {
+ pool = malloc( poolsize );
+ if( !pool )
+ log_fatal("can't allocate memory pool of %u bytes\n",
+ (unsigned)poolsize);
+ else
+ pool_okay = 1;
+ }
+ lock_pool( pool, poolsize );
+ poollen = 0;
+}
+
+
+/* concatenate unused blocks */
+static void
+compress_pool(void)
+{
+ /* fixme: we really should do this */
+}
+
+void
+secmem_set_flags( unsigned flags )
+{
+ int was_susp = suspend_warning;
+
+ no_warning = flags & 1;
+ suspend_warning = flags & 2;
+
+ /* and now issue the warning if it is not longer suspended */
+ if( was_susp && !suspend_warning && show_warning ) {
+ show_warning = 0;
+ print_warn();
+ }
+}
+
+unsigned
+secmem_get_flags(void)
+{
+ unsigned flags;
+
+ flags = no_warning ? 1:0;
+ flags |= suspend_warning ? 2:0;
+ return flags;
+}
+
+/* Returns 1 if memory was locked, 0 if not. */
+int
+secmem_init( size_t n )
+{
+ if( !n ) {
+#ifndef __riscos__
+#ifdef USE_CAPABILITIES
+ /* drop all capabilities */
+ cap_set_proc( cap_from_text("all-eip") );
+
+#elif !defined(HAVE_DOSISH_SYSTEM)
+ uid_t uid;
+
+ disable_secmem=1;
+ uid = getuid();
+ if( uid != geteuid() ) {
+ if( setuid( uid ) || getuid() != geteuid() || !setuid(0) )
+ log_fatal("failed to drop setuid\n" );
+ }
+#endif
+#endif /* !__riscos__ */
+ }
+ else {
+ if( n < DEFAULT_POOLSIZE )
+ n = DEFAULT_POOLSIZE;
+ if( !pool_okay )
+ init_pool(n);
+ else
+ log_error("Oops, secure memory pool already initialized\n");
+ }
+
+ return !show_warning;
+}
+
+
+void *
+secmem_malloc( size_t size )
+{
+ MEMBLOCK *mb, *mb2;
+ int compressed=0;
+
+ if( !pool_okay ) {
+ log_info(
+ _("operation is not possible without initialized secure memory\n"));
+ log_info(_("(you may have used the wrong program for this task)\n"));
+ exit(2);
+ }
+ if( show_warning && !suspend_warning ) {
+ show_warning = 0;
+ print_warn();
+ }
+
+ /* Blocks are always a multiple of 32. Note that we allocate an
+ extra of the size of an entire MEMBLOCK. This is required
+ becuase we do not only need the SIZE info but also extra space
+ to chain up unused memory blocks. */
+ size += sizeof(MEMBLOCK);
+ size = ((size + 31) / 32) * 32;
+
+ retry:
+ /* try to get it from the used blocks */
+ for(mb = unused_blocks,mb2=NULL; mb; mb2=mb, mb = mb->u.next )
+ if( mb->size >= size ) {
+ if( mb2 )
+ mb2->u.next = mb->u.next;
+ else
+ unused_blocks = mb->u.next;
+ goto leave;
+ }
+ /* allocate a new block */
+ if( (poollen + size <= poolsize) ) {
+ mb = (void*)((char*)pool + poollen);
+ poollen += size;
+ mb->size = size;
+ }
+ else if( !compressed ) {
+ compressed=1;
+ compress_pool();
+ goto retry;
+ }
+ else
+ return NULL;
+
+ leave:
+ cur_alloced += mb->size;
+ cur_blocks++;
+ if( cur_alloced > max_alloced )
+ max_alloced = cur_alloced;
+ if( cur_blocks > max_blocks )
+ max_blocks = cur_blocks;
+
+ return &mb->u.aligned.c;
+}
+
+
+void *
+secmexrealloc( void *p, size_t newsize )
+{
+ MEMBLOCK *mb;
+ size_t size;
+ void *a;
+
+ mb = (MEMBLOCK*)((char*)p - ((size_t) &((MEMBLOCK*)0)->u.aligned.c));
+ size = mb->size;
+ if (size < sizeof(MEMBLOCK))
+ log_bug ("secure memory corrupted at block %p\n", (void *)mb);
+ size -= ((size_t) &((MEMBLOCK*)0)->u.aligned.c);
+
+ if( newsize <= size )
+ return p; /* It is easier not to shrink the memory. */
+ a = secmem_malloc( newsize );
+ if ( a ) {
+ memcpy(a, p, size);
+ memset((char*)a+size, 0, newsize-size);
+ secmem_free(p);
+ }
+ return a;
+}
+
+
+void
+secmem_free( void *a )
+{
+ MEMBLOCK *mb;
+ size_t size;
+
+ if( !a )
+ return;
+
+ mb = (MEMBLOCK*)((char*)a - ((size_t) &((MEMBLOCK*)0)->u.aligned.c));
+ size = mb->size;
+ /* This does not make much sense: probably this memory is held in the
+ * cache. We do it anyway: */
+ wipememory2(mb, 0xff, size );
+ wipememory2(mb, 0xaa, size );
+ wipememory2(mb, 0x55, size );
+ wipememory2(mb, 0x00, size );
+ mb->size = size;
+ mb->u.next = unused_blocks;
+ unused_blocks = mb;
+ cur_blocks--;
+ cur_alloced -= size;
+}
+
+int
+m_is_secure( const void *p )
+{
+ return p >= pool && p < (void*)((char*)pool+poolsize);
+}
+
+
+
+/****************
+ * Warning: This code might be called by an interrupt handler
+ * and frankly, there should really be such a handler,
+ * to make sure that the memory is wiped out.
+ * We hope that the OS wipes out mlocked memory after
+ * receiving a SIGKILL - it really should do so, otherwise
+ * there is no chance to get the secure memory cleaned.
+ */
+void
+secmem_term()
+{
+ if( !pool_okay )
+ return;
+
+ wipememory2( pool, 0xff, poolsize);
+ wipememory2( pool, 0xaa, poolsize);
+ wipememory2( pool, 0x55, poolsize);
+ wipememory2( pool, 0x00, poolsize);
+#ifdef HAVE_MMAP
+ if( pool_is_mmapped )
+ munmap( pool, poolsize );
+#endif
+ pool = NULL;
+ pool_okay = 0;
+ poolsize=0;
+ poollen=0;
+ unused_blocks=NULL;
+}
+
+
+void
+secmem_dump_stats()
+{
+ if( disable_secmem )
+ return;
+ fprintf(stderr,
+ "secmem usage: %u/%u bytes in %u/%u blocks of pool %lu/%lu\n",
+ cur_alloced, max_alloced, cur_blocks, max_blocks,
+ (ulong)poollen, (ulong)poolsize );
+}
diff --git a/util/simple-gettext.c b/util/simple-gettext.c
new file mode 100644
index 0000000..3249775
--- /dev/null
+++ b/util/simple-gettext.c
@@ -0,0 +1,467 @@
+/* simple-gettext.c - a simplified version of gettext.
+ * Copyright (C) 1995, 1996, 1997, 1999,
+ * 2005 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+/* This is a simplified version of gettext written by Ulrich Drepper.
+ * It is used for the Win32 version of GnuPG beucase all the overhead
+ * of gettext is not needed and we have to do some special Win32 stuff.
+ * I decided that this is far easier than to tweak gettext for the special
+ * cases (I tried it but it is a lot of code). wk 15.09.99
+ */
+
+#include <config.h>
+#ifdef USE_SIMPLE_GETTEXT
+#if !defined (_WIN32) && !defined (__CYGWIN32__)
+#error This file can only be used under Windows or Cygwin32
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "types.h"
+#include "util.h"
+
+
+/* The magic number of the GNU message catalog format. */
+#define MAGIC 0x950412de
+#define MAGIC_SWAPPED 0xde120495
+
+/* Revision number of the currently used .mo (binary) file format. */
+#define MO_REVISION_NUMBER 0
+
+
+/* Header for binary .mo file format. */
+struct mo_file_header
+{
+ /* The magic number. */
+ u32 magic;
+ /* The revision number of the file format. */
+ u32 revision;
+ /* The number of strings pairs. */
+ u32 nstrings;
+ /* Offset of table with start offsets of original strings. */
+ u32 orig_tab_offset;
+ /* Offset of table with start offsets of translation strings. */
+ u32 trans_tab_offset;
+ /* Size of hashing table. */
+ u32 hash_tab_size;
+ /* Offset of first hashing entry. */
+ u32 hash_tab_offset;
+};
+
+struct string_desc
+{
+ /* Length of addressed string. */
+ u32 length;
+ /* Offset of string in file. */
+ u32 offset;
+};
+
+
+struct overflow_space_s
+{
+ struct overflow_space_s *next;
+ u32 idx;
+ char d[1];
+};
+
+struct loaded_domain
+{
+ char *data;
+ int must_swap;
+ u32 nstrings;
+ char *mapped; /* 0 = not yet mapped, 1 = mapped,
+ 2 = mapped to
+ overflow space */
+ struct overflow_space_s *overflow_space;
+ struct string_desc *orig_tab;
+ struct string_desc *trans_tab;
+ u32 hash_size;
+ u32 *hash_tab;
+};
+
+
+static struct loaded_domain *the_domain;
+
+static __inline__ u32
+do_swap_u32( u32 i )
+{
+ return (i << 24) | ((i & 0xff00) << 8) | ((i >> 8) & 0xff00) | (i >> 24);
+}
+
+#define SWAPIT(flag, data) ((flag) ? do_swap_u32(data) : (data) )
+
+
+/* We assume to have `unsigned long int' value with at least 32 bits. */
+#define HASHWORDBITS 32
+
+/* The so called `hashpjw' function by P.J. Weinberger
+ [see Aho/Sethi/Ullman, COMPILERS: Principles, Techniques and Tools,
+ 1986, 1987 Bell Telephone Laboratories, Inc.] */
+
+static __inline__ ulong
+hash_string( const char *str_param )
+{
+ unsigned long int hval, g;
+ const char *str = str_param;
+
+ hval = 0;
+ while (*str != '\0')
+ {
+ hval <<= 4;
+ hval += (unsigned long int) *str++;
+ g = hval & ((unsigned long int) 0xf << (HASHWORDBITS - 4));
+ if (g != 0)
+ {
+ hval ^= g >> (HASHWORDBITS - 8);
+ hval ^= g;
+ }
+ }
+ return hval;
+}
+
+
+static struct loaded_domain *
+load_domain( const char *filename )
+{
+ FILE *fp;
+ size_t size;
+ struct stat st;
+ struct mo_file_header *data = NULL;
+ struct loaded_domain *domain = NULL;
+ size_t to_read;
+ char *read_ptr;
+
+ fp = fopen( filename, "rb" );
+ if( !fp )
+ return NULL; /* can't open the file */
+ /* we must know about the size of the file */
+ if( fstat( fileno(fp ), &st )
+ || (size = (size_t)st.st_size) != st.st_size
+ || size < sizeof (struct mo_file_header) ) {
+ fclose( fp );
+ return NULL;
+ }
+
+ data = malloc( size );
+ if( !data ) {
+ fclose( fp );
+ return NULL; /* out of memory */
+ }
+
+ to_read = size;
+ read_ptr = (char *) data;
+ do {
+ long int nb = fread( read_ptr, 1, to_read, fp );
+ if( nb < to_read ) {
+ fclose (fp);
+ free(data);
+ return NULL; /* read error */
+ }
+ read_ptr += nb;
+ to_read -= nb;
+ } while( to_read > 0 );
+ fclose (fp);
+
+ /* Using the magic number we can test whether it really is a message
+ * catalog file. */
+ if( data->magic != MAGIC && data->magic != MAGIC_SWAPPED ) {
+ /* The magic number is wrong: not a message catalog file. */
+ free( data );
+ return NULL;
+ }
+
+ domain = calloc( 1, sizeof *domain );
+ if( !domain ) {
+ free( data );
+ return NULL;
+ }
+ domain->data = (char *) data;
+ domain->must_swap = data->magic != MAGIC;
+
+ /* Fill in the information about the available tables. */
+ switch( SWAPIT(domain->must_swap, data->revision) ) {
+ case 0:
+ domain->nstrings = SWAPIT(domain->must_swap, data->nstrings);
+ domain->orig_tab = (struct string_desc *)
+ ((char *) data + SWAPIT(domain->must_swap, data->orig_tab_offset));
+ domain->trans_tab = (struct string_desc *)
+ ((char *) data + SWAPIT(domain->must_swap, data->trans_tab_offset));
+ domain->hash_size = SWAPIT(domain->must_swap, data->hash_tab_size);
+ domain->hash_tab = (u32 *)
+ ((char *) data + SWAPIT(domain->must_swap, data->hash_tab_offset));
+ break;
+
+ default: /* This is an invalid revision. */
+ free( data );
+ free( domain );
+ return NULL;
+ }
+
+ /* Allocate an array to keep track of code page mappings. */
+ domain->mapped = calloc( 1, domain->nstrings );
+ if( !domain->mapped ) {
+ free( data );
+ free( domain );
+ return NULL;
+ }
+
+ return domain;
+}
+
+
+/****************
+ * Set the file used for translations. Pass a NULL to disable
+ * translation. A new filename may be set at anytime. If REGKEY is
+ * not NULL, the function tries to selected the language the registry
+ * key "Lang" below that key. WARNING: After changing the filename you
+ * should not access any data retrieved by gettext().
+ */
+int
+set_gettext_file ( const char *filename, const char *regkey )
+{
+ struct loaded_domain *domain = NULL;
+
+ if( filename && *filename ) {
+ if( filename[0] == '/'
+#ifdef HAVE_DRIVE_LETTERS
+ || ( isalpha(filename[0])
+ && filename[1] == ':'
+ && (filename[2] == '/' || filename[2] == '\\') )
+#endif
+ ) {
+ /* absolute path - use it as is */
+ domain = load_domain( filename );
+ }
+ else if (regkey) { /* Standard. */
+ char *instdir, *langid, *fname;
+ char *p;
+
+ instdir = read_w32_registry_string ("HKEY_LOCAL_MACHINE",
+ regkey,
+ "Install Directory");
+ if (!instdir)
+ return -1;
+ langid = read_w32_registry_string (NULL, /* HKCU then HKLM */
+ regkey,
+ "Lang");
+ if (!langid) {
+ free (instdir);
+ return -1;
+ }
+ /* Strip stuff after a dot in case the user tried to enter
+ * the entire locale synatcs as usual for POSIX. */
+ p = strchr (langid, '.');
+ if (p)
+ *p = 0;
+
+ /* Build the key: "<instdir>/<domain>.nls/<langid>.mo" We
+ use a directory below the installation directory with
+ the domain included in case the software has been
+ insalled with other software altogether at the same
+ place. */
+ fname = malloc (strlen (instdir) + 1 + strlen (filename) + 5
+ + strlen (langid) + 3 + 1);
+ if (!fname) {
+ free (instdir);
+ free (langid);
+ return -1;
+ }
+ strcpy (stpcpy (stpcpy (stpcpy (stpcpy ( stpcpy (fname,
+ instdir),"\\"), filename), ".nls\\"), langid), ".mo");
+ free (instdir);
+ free (langid);
+
+ /* Better make sure that we don't mix forward and
+ backward slashes. It seems that some Windoze
+ versions don't accept this. */
+ for (p=fname; *p; p++) {
+ if (*p == '/')
+ *p = '\\';
+ }
+ domain = load_domain (fname);
+ free(fname);
+ }
+
+ if (!domain)
+ return -1;
+ }
+
+ if( the_domain ) {
+ struct overflow_space_s *os, *os2;
+ free( the_domain->data );
+ free( the_domain->mapped );
+ for (os=the_domain->overflow_space; os; os = os2) {
+ os2 = os->next;
+ free (os);
+ }
+ free( the_domain );
+ the_domain = NULL;
+ }
+ the_domain = domain;
+ return 0;
+}
+
+
+static const char*
+get_string( struct loaded_domain *domain, u32 idx )
+{
+ struct overflow_space_s *os;
+ char *p;
+
+ p = domain->data + SWAPIT(domain->must_swap, domain->trans_tab[idx].offset);
+ if (!domain->mapped[idx])
+ {
+ size_t plen, buflen;
+ char *buf;
+
+ domain->mapped[idx] = 1;
+
+ plen = strlen (p);
+ buf = utf8_to_native (p, plen, -1);
+ buflen = strlen (buf);
+ if (buflen <= plen)
+ strcpy (p, buf);
+ else
+ {
+ /* There is not enough space for the translation - store it
+ in the overflow_space else and mark that in the mapped
+ array. Because we expect that this won't happen too
+ often, we use a simple linked list. */
+ os = malloc (sizeof *os + buflen);
+ if (os)
+ {
+ os->idx = idx;
+ strcpy (os->d, buf);
+ os->next = domain->overflow_space;
+ domain->overflow_space = os;
+ p = os->d;
+ }
+ else
+ p = "ERROR in GETTEXT MALLOC";
+ }
+ xfree (buf);
+ }
+ else if (domain->mapped[idx] == 2)
+ { /* We need to get the string from the overflow_space. */
+ for (os=domain->overflow_space; os; os = os->next)
+ if (os->idx == idx)
+ return (const char*)os->d;
+ p = "ERROR in GETTEXT\n";
+ }
+ return (const char*)p;
+}
+
+
+
+const char *
+gettext( const char *msgid )
+{
+ struct loaded_domain *domain;
+ size_t act = 0;
+ size_t top, bottom;
+
+ if( !(domain = the_domain) )
+ goto not_found;
+
+ /* Locate the MSGID and its translation. */
+ if( domain->hash_size > 2 && domain->hash_tab ) {
+ /* Use the hashing table. */
+ u32 len = strlen (msgid);
+ u32 hash_val = hash_string (msgid);
+ u32 idx = hash_val % domain->hash_size;
+ u32 incr = 1 + (hash_val % (domain->hash_size - 2));
+ u32 nstr = SWAPIT (domain->must_swap, domain->hash_tab[idx]);
+
+ if ( !nstr ) /* Hash table entry is empty. */
+ goto not_found;
+
+ if( SWAPIT(domain->must_swap,
+ domain->orig_tab[nstr - 1].length) == len
+ && !strcmp( msgid,
+ domain->data + SWAPIT(domain->must_swap,
+ domain->orig_tab[nstr - 1].offset)) )
+ return get_string( domain, nstr - 1 );
+
+ for(;;) {
+ if (idx >= domain->hash_size - incr)
+ idx -= domain->hash_size - incr;
+ else
+ idx += incr;
+
+ nstr = SWAPIT(domain->must_swap, domain->hash_tab[idx]);
+ if( !nstr )
+ goto not_found; /* Hash table entry is empty. */
+
+ if ( SWAPIT(domain->must_swap,
+ domain->orig_tab[nstr - 1].length) == len
+ && !strcmp (msgid,
+ domain->data + SWAPIT(domain->must_swap,
+ domain->orig_tab[nstr - 1].offset)))
+ return get_string( domain, nstr-1 );
+ }
+ /* NOTREACHED */
+ }
+
+ /* Now we try the default method: binary search in the sorted
+ array of messages. */
+ bottom = 0;
+ top = domain->nstrings;
+ while( bottom < top ) {
+ int cmp_val;
+
+ act = (bottom + top) / 2;
+ cmp_val = strcmp(msgid, domain->data
+ + SWAPIT(domain->must_swap,
+ domain->orig_tab[act].offset));
+ if (cmp_val < 0)
+ top = act;
+ else if (cmp_val > 0)
+ bottom = act + 1;
+ else
+ return get_string( domain, act );
+ }
+
+ not_found:
+ return msgid;
+}
+
+#if 0
+ unsigned int cp1, cp2;
+
+ cp1 = GetConsoleCP();
+ cp2 = GetConsoleOutputCP();
+
+ log_info("InputCP=%u OutputCP=%u\n", cp1, cp2 );
+
+ if( !SetConsoleOutputCP( 1252 ) )
+ log_info("SetConsoleOutputCP failed: %s\n", w32_strerror (0));
+
+ cp1 = GetConsoleCP();
+ cp2 = GetConsoleOutputCP();
+ log_info("InputCP=%u OutputCP=%u after switch1\n", cp1, cp2 );
+#endif
+
+#endif /* USE_SIMPLE_GETTEXT */
diff --git a/util/srv.c b/util/srv.c
new file mode 100644
index 0000000..95187db
--- /dev/null
+++ b/util/srv.c
@@ -0,0 +1,257 @@
+/* srv.c - DNS SRV code
+ * Copyright (C) 2003 Free Software Foundation, Inc.
+ *
+ * This file is part of GNUPG.
+ *
+ * GNUPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GNUPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <sys/types.h>
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+#endif
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "memory.h"
+#include "types.h"
+#include "srv.h"
+
+/* Not every installation has gotten around to supporting SRVs
+ yet... */
+#ifndef T_SRV
+#define T_SRV 33
+#endif
+
+static int
+priosort(const void *a,const void *b)
+{
+ const struct srventry *sa=a,*sb=b;
+ if(sa->priority>sb->priority)
+ return 1;
+ else if(sa->priority<sb->priority)
+ return -1;
+ else
+ return 0;
+}
+
+int
+getsrv(const char *name,struct srventry **list)
+{
+ unsigned char answer[PACKETSZ];
+ int r,srvcount=0;
+ unsigned char *pt,*emsg;
+ u16 count,dlen;
+
+ *list=NULL;
+
+ r=res_query(name,C_IN,T_SRV,answer,PACKETSZ);
+ if(r<sizeof(HEADER) || r>PACKETSZ)
+ return -1;
+
+ if((((HEADER *)answer)->rcode)==NOERROR &&
+ (count=ntohs(((HEADER *)answer)->ancount)))
+ {
+ int i,rc;
+
+ emsg=&answer[r];
+ pt=&answer[sizeof(HEADER)];
+
+ /* Skip over the query */
+
+ rc=dn_skipname(pt,emsg);
+ if(rc==-1)
+ goto fail;
+
+ pt+=rc+QFIXEDSZ;
+
+ while(count-->0 && pt<emsg)
+ {
+ struct srventry *srv=NULL;
+ u16 type,class;
+
+ *list=xrealloc(*list,(srvcount+1)*sizeof(struct srventry));
+ memset(&(*list)[srvcount],0,sizeof(struct srventry));
+ srv=&(*list)[srvcount];
+ srvcount++;
+
+ rc=dn_skipname(pt,emsg); /* the name we just queried for */
+ if(rc==-1)
+ goto fail;
+ pt+=rc;
+
+ /* Truncated message? */
+ if((emsg-pt)<16)
+ goto fail;
+
+ type=*pt++ << 8;
+ type|=*pt++;
+ /* We asked for SRV and got something else !? */
+ if(type!=T_SRV)
+ goto fail;
+
+ class=*pt++ << 8;
+ class|=*pt++;
+ /* We asked for IN and got something else !? */
+ if(class!=C_IN)
+ goto fail;
+
+ pt+=4; /* ttl */
+ dlen=*pt++ << 8;
+ dlen|=*pt++;
+ srv->priority=*pt++ << 8;
+ srv->priority|=*pt++;
+ srv->weight=*pt++ << 8;
+ srv->weight|=*pt++;
+ srv->port=*pt++ << 8;
+ srv->port|=*pt++;
+
+ /* Get the name. 2782 doesn't allow name compression, but
+ dn_expand still works to pull the name out of the
+ packet. */
+ rc=dn_expand(answer,emsg,pt,srv->target,MAXDNAME);
+ if(rc==1 && srv->target[0]==0) /* "." */
+ goto noanswer;
+ if(rc==-1)
+ goto fail;
+ pt+=rc;
+ /* Corrupt packet? */
+ if(dlen!=rc+6)
+ goto fail;
+
+#if 0
+ printf("count=%d\n",srvcount);
+ printf("priority=%d\n",srv->priority);
+ printf("weight=%d\n",srv->weight);
+ printf("port=%d\n",srv->port);
+ printf("target=%s\n",srv->target);
+#endif
+ }
+
+ /* Now we have an array of all the srv records. */
+
+ /* Order by priority */
+ qsort(*list,srvcount,sizeof(struct srventry),priosort);
+
+ /* For each priority, move the zero-weighted items first. */
+ for(i=0;i<srvcount;i++)
+ {
+ int j;
+
+ for(j=i;j<srvcount && (*list)[i].priority==(*list)[j].priority;j++)
+ {
+ if((*list)[j].weight==0)
+ {
+ /* Swap j with i */
+ if(j!=i)
+ {
+ struct srventry temp;
+
+ memcpy(&temp,&(*list)[j],sizeof(struct srventry));
+ memcpy(&(*list)[j],&(*list)[i],sizeof(struct srventry));
+ memcpy(&(*list)[i],&temp,sizeof(struct srventry));
+ }
+
+ break;
+ }
+ }
+ }
+
+ /* Run the RFC-2782 weighting algorithm. We don't need very
+ high quality randomness for this, so regular libc srand/rand
+ is sufficient. */
+ srand(time(NULL)*getpid());
+
+ for(i=0;i<srvcount;i++)
+ {
+ int j;
+ float prio_count=0,chose;
+
+ for(j=i;j<srvcount && (*list)[i].priority==(*list)[j].priority;j++)
+ {
+ prio_count+=(*list)[j].weight;
+ (*list)[j].run_count=prio_count;
+ }
+
+ chose=prio_count*rand()/RAND_MAX;
+
+ for(j=i;j<srvcount && (*list)[i].priority==(*list)[j].priority;j++)
+ {
+ if(chose<=(*list)[j].run_count)
+ {
+ /* Swap j with i */
+ if(j!=i)
+ {
+ struct srventry temp;
+
+ memcpy(&temp,&(*list)[j],sizeof(struct srventry));
+ memcpy(&(*list)[j],&(*list)[i],sizeof(struct srventry));
+ memcpy(&(*list)[i],&temp,sizeof(struct srventry));
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ return srvcount;
+
+ noanswer:
+ xfree(*list);
+ *list=NULL;
+ return 0;
+
+ fail:
+ xfree(*list);
+ *list=NULL;
+ return -1;
+}
+
+#ifdef TEST
+int
+main(int argc,char *argv[])
+{
+ struct srventry *srv;
+ int rc,i;
+
+ rc=getsrv("_hkp._tcp.wwwkeys.pgp.net",&srv);
+ printf("Count=%d\n\n",rc);
+ for(i=0;i<rc;i++)
+ {
+ printf("priority=%d\n",srv[i].priority);
+ printf("weight=%d\n",srv[i].weight);
+ printf("port=%d\n",srv[i].port);
+ printf("target=%s\n",srv[i].target);
+ printf("\n");
+ }
+
+ xfree(srv);
+
+ return 0;
+}
+#endif /* TEST */
+
+/*
+Local Variables:
+compile-command: "cc -DTEST -I.. -I../include -Wall -g -o srv srv.c -lresolv libutil.a"
+End:
+*/
diff --git a/util/srv.h b/util/srv.h
new file mode 100644
index 0000000..a81ebd5
--- /dev/null
+++ b/util/srv.h
@@ -0,0 +1,51 @@
+/* srv.h
+ * Copyright (C) 2003, 2004 Free Software Foundation, Inc.
+ *
+ * This file is part of GNUPG.
+ *
+ * GNUPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GNUPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#ifndef _SRV_H_
+#define _SRV_H_
+
+#ifdef USE_DNS_SRV
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+#endif /* !_WIN32 */
+#endif /* USE_DNS_SRV */
+#include "types.h"
+
+#ifndef MAXDNAME
+#define MAXDNAME 1025
+#endif
+
+struct srventry
+{
+ u16 priority;
+ u16 weight;
+ u16 port;
+ int run_count;
+ char target[MAXDNAME];
+};
+
+int getsrv(const char *name,struct srventry **list);
+
+#endif /* !_SRV_H_ */
diff --git a/util/strgutil.c b/util/strgutil.c
new file mode 100644
index 0000000..95cb0f2
--- /dev/null
+++ b/util/strgutil.c
@@ -0,0 +1,1323 @@
+/* strgutil.c - string utilities
+ * Copyright (C) 1994, 1998, 1999, 2000, 2001,
+ * 2003, 2004, 2005 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#ifdef HAVE_LANGINFO_CODESET
+#include <langinfo.h>
+#endif
+
+/* For W32 we use dynamic loading of the iconv dll and don't need any
+ * iconv headers at all. */
+#ifndef _WIN32
+# ifndef HAVE_ICONV
+# undef USE_GNUPG_ICONV
+# endif
+#endif
+
+#ifdef USE_GNUPG_ICONV
+# include <limits.h>
+# ifndef _WIN32
+# include <iconv.h>
+# endif
+#endif
+
+#include "types.h"
+#include "util.h"
+#include "memory.h"
+#include "i18n.h"
+#include "dynload.h"
+
+
+#ifndef USE_GNUPG_ICONV
+static ushort koi8_unicode[128] = {
+ 0x2500,0x2502,0x250c,0x2510,0x2514,0x2518,0x251c,0x2524,
+ 0x252c,0x2534,0x253c,0x2580,0x2584,0x2588,0x258c,0x2590,
+ 0x2591,0x2592,0x2593,0x2320,0x25a0,0x2219,0x221a,0x2248,
+ 0x2264,0x2265,0x00a0,0x2321,0x00b0,0x00b2,0x00b7,0x00f7,
+ 0x2550,0x2551,0x2552,0x0451,0x2553,0x2554,0x2555,0x2556,
+ 0x2557,0x2558,0x2559,0x255a,0x255b,0x255c,0x255d,0x255e,
+ 0x255f,0x2560,0x2561,0x0401,0x2562,0x2563,0x2564,0x2565,
+ 0x2566,0x2567,0x2568,0x2569,0x256a,0x256b,0x256c,0x00a9,
+ 0x044e,0x0430,0x0431,0x0446,0x0434,0x0435,0x0444,0x0433,
+ 0x0445,0x0438,0x0439,0x043a,0x043b,0x043c,0x043d,0x043e,
+ 0x043f,0x044f,0x0440,0x0441,0x0442,0x0443,0x0436,0x0432,
+ 0x044c,0x044b,0x0437,0x0448,0x044d,0x0449,0x0447,0x044a,
+ 0x042e,0x0410,0x0411,0x0426,0x0414,0x0415,0x0424,0x0413,
+ 0x0425,0x0418,0x0419,0x041a,0x041b,0x041c,0x041d,0x041e,
+ 0x041f,0x042f,0x0420,0x0421,0x0422,0x0423,0x0416,0x0412,
+ 0x042c,0x042b,0x0417,0x0428,0x042d,0x0429,0x0427,0x042a
+};
+
+static ushort latin2_unicode[128] = {
+ 0x0080,0x0081,0x0082,0x0083,0x0084,0x0085,0x0086,0x0087,
+ 0x0088,0x0089,0x008A,0x008B,0x008C,0x008D,0x008E,0x008F,
+ 0x0090,0x0091,0x0092,0x0093,0x0094,0x0095,0x0096,0x0097,
+ 0x0098,0x0099,0x009A,0x009B,0x009C,0x009D,0x009E,0x009F,
+ 0x00A0,0x0104,0x02D8,0x0141,0x00A4,0x013D,0x015A,0x00A7,
+ 0x00A8,0x0160,0x015E,0x0164,0x0179,0x00AD,0x017D,0x017B,
+ 0x00B0,0x0105,0x02DB,0x0142,0x00B4,0x013E,0x015B,0x02C7,
+ 0x00B8,0x0161,0x015F,0x0165,0x017A,0x02DD,0x017E,0x017C,
+ 0x0154,0x00C1,0x00C2,0x0102,0x00C4,0x0139,0x0106,0x00C7,
+ 0x010C,0x00C9,0x0118,0x00CB,0x011A,0x00CD,0x00CE,0x010E,
+ 0x0110,0x0143,0x0147,0x00D3,0x00D4,0x0150,0x00D6,0x00D7,
+ 0x0158,0x016E,0x00DA,0x0170,0x00DC,0x00DD,0x0162,0x00DF,
+ 0x0155,0x00E1,0x00E2,0x0103,0x00E4,0x013A,0x0107,0x00E7,
+ 0x010D,0x00E9,0x0119,0x00EB,0x011B,0x00ED,0x00EE,0x010F,
+ 0x0111,0x0144,0x0148,0x00F3,0x00F4,0x0151,0x00F6,0x00F7,
+ 0x0159,0x016F,0x00FA,0x0171,0x00FC,0x00FD,0x0163,0x02D9
+};
+#endif /*!USE_GNUPG_ICONV*/
+
+
+#ifndef MB_LEN_MAX
+#define MB_LEN_MAX 16
+#endif
+
+
+static const char *active_charset_name = "iso-8859-1";
+static ushort *active_charset = NULL;
+static int no_translation = 0;
+static int use_iconv = 0;
+
+
+#ifdef _WIN32
+typedef void* iconv_t;
+#ifndef ICONV_CONST
+#define ICONV_CONST const
+#endif
+
+iconv_t (* __stdcall iconv_open) (const char *tocode, const char *fromcode);
+size_t (* __stdcall iconv) (iconv_t cd,
+ const char **inbuf, size_t *inbytesleft,
+ char **outbuf, size_t *outbytesleft);
+int (* __stdcall iconv_close) (iconv_t cd);
+
+#endif /*_WIN32*/
+
+
+
+#ifdef _WIN32
+static int
+load_libiconv (void)
+{
+ static int done;
+
+ if (!done)
+ {
+ void *handle;
+
+ done = 1; /* Do it right now because we might get called recursivly
+ through gettext. */
+
+ handle = dlopen ("iconv.dll", RTLD_LAZY);
+ if (handle)
+ {
+ iconv_open = dlsym (handle, "libiconv_open");
+ if (iconv_open)
+ iconv = dlsym (handle, "libiconv");
+ if (iconv)
+ iconv_close = dlsym (handle, "libiconv_close");
+ }
+ if (!handle || !iconv_close)
+ {
+ log_info (_("error loading `%s': %s\n"),
+ "iconv.dll", dlerror ());
+ log_info(_("please see http://www.gnupg.org/download/iconv.html "
+ "for more information\n"));
+ iconv_open = NULL;
+ iconv = NULL;
+ iconv_close = NULL;
+ if (handle)
+ dlclose (handle);
+ }
+ }
+ return iconv_open? 0: -1;
+}
+#endif /* _WIN32 */
+
+
+
+
+void
+free_strlist( STRLIST sl )
+{
+ STRLIST sl2;
+
+ for(; sl; sl = sl2 ) {
+ sl2 = sl->next;
+ xfree(sl);
+ }
+}
+
+
+STRLIST
+add_to_strlist( STRLIST *list, const char *string )
+{
+ STRLIST sl;
+
+ sl = xmalloc( sizeof *sl + strlen(string));
+ sl->flags = 0;
+ strcpy(sl->d, string);
+ sl->next = *list;
+ *list = sl;
+ return sl;
+}
+
+/****************
+ * Same as add_to_strlist() but if is_utf8 is *not* set a conversion
+ * to UTF8 is done
+ */
+STRLIST
+add_to_strlist2( STRLIST *list, const char *string, int is_utf8 )
+{
+ STRLIST sl;
+
+ if( is_utf8 )
+ sl = add_to_strlist( list, string );
+ else {
+ char *p = native_to_utf8( string );
+ sl = add_to_strlist( list, p );
+ xfree( p );
+ }
+ return sl;
+}
+
+STRLIST
+append_to_strlist( STRLIST *list, const char *string )
+{
+ STRLIST r, sl;
+
+ sl = xmalloc( sizeof *sl + strlen(string));
+ sl->flags = 0;
+ strcpy(sl->d, string);
+ sl->next = NULL;
+ if( !*list )
+ *list = sl;
+ else {
+ for( r = *list; r->next; r = r->next )
+ ;
+ r->next = sl;
+ }
+ return sl;
+}
+
+STRLIST
+append_to_strlist2( STRLIST *list, const char *string, int is_utf8 )
+{
+ STRLIST sl;
+
+ if( is_utf8 )
+ sl = append_to_strlist( list, string );
+ else {
+ char *p = native_to_utf8( string );
+ sl = append_to_strlist( list, p );
+ xfree( p );
+ }
+ return sl;
+}
+
+
+STRLIST
+strlist_prev( STRLIST head, STRLIST node )
+{
+ STRLIST n;
+
+ for(n=NULL; head && head != node; head = head->next )
+ n = head;
+ return n;
+}
+
+STRLIST
+strlist_last( STRLIST node )
+{
+ if( node )
+ for( ; node->next ; node = node->next )
+ ;
+ return node;
+}
+
+char *
+pop_strlist( STRLIST *list )
+{
+ char *str=NULL;
+ STRLIST sl=*list;
+
+ if(sl)
+ {
+ str=xmalloc(strlen(sl->d)+1);
+ strcpy(str,sl->d);
+
+ *list=sl->next;
+ xfree(sl);
+ }
+
+ return str;
+}
+
+/****************
+ * Look for the substring SUB in buffer and return a pointer to that
+ * substring in BUF or NULL if not found.
+ * Comparison is case-insensitive.
+ */
+const char *
+memistr( const char *buf, size_t buflen, const char *sub )
+{
+ const byte *t, *s ;
+ size_t n;
+
+ for( t=buf, n=buflen, s=sub ; n ; t++, n-- )
+ if( toupper(*t) == toupper(*s) ) {
+ for( buf=t++, buflen = n--, s++;
+ n && toupper(*t) == toupper(*s); t++, s++, n-- )
+ ;
+ if( !*s )
+ return buf;
+ t = buf; n = buflen; s = sub ;
+ }
+
+ return NULL ;
+}
+
+const char *
+ascii_memistr( const char *buf, size_t buflen, const char *sub )
+{
+ const byte *t, *s ;
+ size_t n;
+
+ for( t=buf, n=buflen, s=sub ; n ; t++, n-- )
+ if( ascii_toupper(*t) == ascii_toupper(*s) ) {
+ for( buf=t++, buflen = n--, s++;
+ n && ascii_toupper(*t) == ascii_toupper(*s); t++, s++, n-- )
+ ;
+ if( !*s )
+ return buf;
+ t = buf; n = buflen; s = sub ;
+ }
+
+ return NULL ;
+}
+
+
+/* Like strncpy() but copy at max N-1 bytes and append a '\0'. With
+ * N given as 0 nothing is copied at all. With DEST given as NULL
+ * sufficient memory is allocated using xmalloc (note that xmalloc is
+ * guaranteed to succeed or to abort the process). */
+char *
+mem2str( char *dest , const void *src , size_t n )
+{
+ char *d;
+ const char *s;
+
+ if( n ) {
+ if( !dest )
+ dest = xmalloc( n ) ;
+ d = dest;
+ s = src ;
+ for(n--; n && *s; n-- )
+ *d++ = *s++;
+ *d = '\0' ;
+ }
+
+ return dest ;
+}
+
+
+/*
+ * Remove leading and trailing white spaces
+ */
+char *
+trim_spaces( char *str )
+{
+ char *string, *p, *mark;
+
+ string = str;
+ /* Find first non space character. */
+ for( p=string; *p && isspace( *(byte*)p ) ; p++ )
+ ;
+ /* Move characters. */
+ for( (mark = NULL); (*string = *p); string++, p++ )
+ if( isspace( *(byte*)p ) ) {
+ if( !mark )
+ mark = string ;
+ }
+ else
+ mark = NULL ;
+ if( mark )
+ *mark = '\0' ; /* Remove trailing spaces. */
+
+ return str ;
+}
+
+
+
+unsigned int
+trim_trailing_chars( byte *line, unsigned len, const char *trimchars )
+{
+ byte *p, *mark;
+ unsigned n;
+
+ for(mark=NULL, p=line, n=0; n < len; n++, p++ ) {
+ if( strchr(trimchars, *p ) ) {
+ if( !mark )
+ mark = p;
+ }
+ else
+ mark = NULL;
+ }
+
+ if( mark ) {
+ *mark = 0;
+ return mark - line;
+ }
+ return len;
+}
+
+/****************
+ * Remove trailing white spaces and return the length of the buffer
+ */
+unsigned
+trim_trailing_ws( byte *line, unsigned len )
+{
+ return trim_trailing_chars( line, len, " \t\r\n" );
+}
+
+
+unsigned int
+check_trailing_chars( const byte *line, unsigned int len,
+ const char *trimchars )
+{
+ const byte *p, *mark;
+ unsigned int n;
+
+ for(mark=NULL, p=line, n=0; n < len; n++, p++ ) {
+ if( strchr(trimchars, *p ) ) {
+ if( !mark )
+ mark = p;
+ }
+ else
+ mark = NULL;
+ }
+
+ if( mark ) {
+ return mark - line;
+ }
+ return len;
+}
+
+
+/****************
+ * Remove trailing white spaces and return the length of the buffer
+ */
+unsigned int
+check_trailing_ws( const byte *line, unsigned int len )
+{
+ return check_trailing_chars( line, len, " \t\r\n" );
+}
+
+
+
+int
+string_count_chr( const char *string, int c )
+{
+ int count;
+ for(count=0; *string; string++ )
+ if( *string == c )
+ count++;
+ return count;
+}
+
+#ifdef USE_GNUPG_ICONV
+static void
+handle_iconv_error (const char *to, const char *from, int use_fallback)
+{
+ if (errno == EINVAL)
+ {
+ static int shown1, shown2;
+ int x;
+
+ if (to && !strcmp (to, "utf-8"))
+ {
+ x = shown1;
+ shown1 = 1;
+ }
+ else
+ {
+ x = shown2;
+ shown2 = 1;
+ }
+
+ if (!x)
+ log_info (_("conversion from `%s' to `%s' not available\n"),
+ from, to);
+ }
+ else
+ {
+ static int shown;
+
+ if (!shown)
+ log_info (_("iconv_open failed: %s\n"), strerror (errno));
+ shown = 1;
+ }
+
+ if (use_fallback)
+ {
+ /* To avoid further error messages we fallback to Latin-1 for the
+ native encoding. This is justified as one can expect that on a
+ utf-8 enabled system nl_langinfo() will work and thus we won't
+ never get to here. Thus Latin-1 seems to be a reasonable
+ default. */
+ active_charset_name = "iso-8859-1";
+ no_translation = 0;
+ active_charset = NULL;
+ use_iconv = 0;
+ }
+}
+#endif /*USE_GNUPG_ICONV*/
+
+int
+set_native_charset( const char *newset )
+{
+ const char *full_newset;
+
+ if (!newset) {
+#ifdef _WIN32
+ static char codepage[30];
+ unsigned int cpno;
+ const char *aliases;
+
+ /* We are a console program thus we need to use the
+ GetConsoleOutputCP function and not the the GetACP which
+ would give the codepage for a GUI program. Note this is
+ not a bulletproof detection because GetConsoleCP might
+ return a different one for console input. Not sure how to
+ cope with that. If the console Code page is not known we
+ fall back to the system code page. */
+ cpno = GetConsoleOutputCP ();
+ if (!cpno)
+ cpno = GetACP ();
+ sprintf (codepage, "CP%u", cpno );
+ /* Resolve alias. We use a long string string and not the
+ usual array to optimize if the code is taken to a DSO.
+ Taken from libiconv 1.9.2. */
+ newset = codepage;
+ for (aliases = ("CP936" "\0" "GBK" "\0"
+ "CP1361" "\0" "JOHAB" "\0"
+ "CP20127" "\0" "ASCII" "\0"
+ "CP20866" "\0" "KOI8-R" "\0"
+ "CP21866" "\0" "KOI8-RU" "\0"
+ "CP28591" "\0" "ISO-8859-1" "\0"
+ "CP28592" "\0" "ISO-8859-2" "\0"
+ "CP28593" "\0" "ISO-8859-3" "\0"
+ "CP28594" "\0" "ISO-8859-4" "\0"
+ "CP28595" "\0" "ISO-8859-5" "\0"
+ "CP28596" "\0" "ISO-8859-6" "\0"
+ "CP28597" "\0" "ISO-8859-7" "\0"
+ "CP28598" "\0" "ISO-8859-8" "\0"
+ "CP28599" "\0" "ISO-8859-9" "\0"
+ "CP28605" "\0" "ISO-8859-15" "\0"
+ "CP65001" "\0" "UTF-8" "\0");
+ *aliases;
+ aliases += strlen (aliases) + 1, aliases += strlen (aliases) + 1)
+ {
+ if (!strcmp (codepage, aliases) ||(*aliases == '*' && !aliases[1]))
+ {
+ newset = aliases + strlen (aliases) + 1;
+ break;
+ }
+ }
+
+#else
+#ifdef HAVE_LANGINFO_CODESET
+ newset = nl_langinfo (CODESET);
+#else /* !HAVE_LANGINFO_CODESET */
+ /* Try to get the used charset from environment variables. */
+ static char codepage[30];
+ const char *lc, *dot, *mod;
+
+ strcpy (codepage, "iso-8859-1");
+ lc = getenv ("LC_ALL");
+ if (!lc || !*lc) {
+ lc = getenv ("LC_CTYPE");
+ if (!lc || !*lc)
+ lc = getenv ("LANG");
+ }
+ if (lc && *lc) {
+ dot = strchr (lc, '.');
+ if (dot) {
+ mod = strchr (++dot, '@');
+ if (!mod)
+ mod = dot + strlen (dot);
+ if (mod - dot < sizeof codepage && dot != mod) {
+ memcpy (codepage, dot, mod - dot);
+ codepage [mod - dot] = 0;
+ }
+ }
+ }
+ newset = codepage;
+#endif /* !HAVE_LANGINFO_CODESET */
+#endif
+ }
+
+ full_newset = newset;
+ if (strlen (newset) > 3 && !ascii_memcasecmp (newset, "iso", 3)) {
+ newset += 3;
+ if (*newset == '-' || *newset == '_')
+ newset++;
+ }
+
+ /* Note that we silently assume that plain ASCII is actually meant
+ as Latin-1. This makes sense because many Unix system don't
+ have their locale set up properly and thus would get annoying
+ error messages and we have to handle all the "bug"
+ reports. Latin-1 has always been the character set used for 8
+ bit characters on Unix systems. */
+ if( !*newset
+ || !ascii_strcasecmp (newset, "8859-1" )
+ || !ascii_strcasecmp (newset, "646" )
+ || !ascii_strcasecmp (newset, "ASCII" )
+ || !ascii_strcasecmp (newset, "ANSI_X3.4-1968" )
+ ) {
+ active_charset_name = "iso-8859-1";
+ no_translation = 0;
+ active_charset = NULL;
+ use_iconv = 0;
+ }
+ else if( !ascii_strcasecmp (newset, "utf8" )
+ || !ascii_strcasecmp(newset, "utf-8") ) {
+ active_charset_name = "utf-8";
+ no_translation = 1;
+ active_charset = NULL;
+ use_iconv = 0;
+ }
+#ifdef USE_GNUPG_ICONV
+ else {
+ iconv_t cd;
+
+#ifdef _WIN32
+ if (load_libiconv ())
+ return G10ERR_GENERAL;
+#endif /*_WIN32*/
+
+ cd = iconv_open (full_newset, "utf-8");
+ if (cd == (iconv_t)-1) {
+ handle_iconv_error (full_newset, "utf-8", 0);
+ return G10ERR_GENERAL;
+ }
+ iconv_close (cd);
+ cd = iconv_open ("utf-8", full_newset);
+ if (cd == (iconv_t)-1) {
+ handle_iconv_error ("utf-8", full_newset, 0);
+ return G10ERR_GENERAL;
+ }
+ iconv_close (cd);
+ active_charset_name = full_newset;
+ no_translation = 0;
+ active_charset = NULL;
+ use_iconv = 1;
+ }
+#else /*!USE_GNUPG_ICONV*/
+ else if( !ascii_strcasecmp( newset, "8859-2" ) ) {
+ active_charset_name = "iso-8859-2";
+ no_translation = 0;
+ active_charset = latin2_unicode;
+ use_iconv = 0;
+ }
+ else if( !ascii_strcasecmp( newset, "koi8-r" ) ) {
+ active_charset_name = "koi8-r";
+ no_translation = 0;
+ active_charset = koi8_unicode;
+ use_iconv = 0;
+ }
+ else
+ return G10ERR_GENERAL;
+#endif /*!USE_GNUPG_ICONV*/
+ return 0;
+}
+
+const char*
+get_native_charset()
+{
+ return active_charset_name;
+}
+
+/****************
+ * Convert string, which is in native encoding to UTF8 and return the
+ * new allocated UTF8 string.
+ */
+char *
+native_to_utf8( const char *string )
+{
+ const byte *s;
+ char *buffer;
+ byte *p;
+ size_t length=0;
+
+ if (no_translation)
+ { /* Already utf-8 encoded. */
+ buffer = xstrdup (string);
+ }
+ else if( !active_charset && !use_iconv) /* Shortcut implementation
+ for Latin-1. */
+ {
+ for(s=string; *s; s++ )
+ {
+ length++;
+ if( *s & 0x80 )
+ length++;
+ }
+ buffer = xmalloc( length + 1 );
+ for(p=buffer, s=string; *s; s++ )
+ {
+ if( *s & 0x80 )
+ {
+ *p++ = 0xc0 | ((*s >> 6) & 3);
+ *p++ = 0x80 | ( *s & 0x3f );
+ }
+ else
+ *p++ = *s;
+ }
+ *p = 0;
+ }
+ else /* Need to use a translation table. */
+ {
+#ifdef USE_GNUPG_ICONV
+ iconv_t cd;
+ const char *inptr;
+ char *outptr;
+ size_t inbytes, outbytes;
+
+ cd = iconv_open ("utf-8", active_charset_name);
+ if (cd == (iconv_t)-1)
+ {
+ handle_iconv_error ("utf-8", active_charset_name, 1);
+ return native_to_utf8 (string);
+ }
+
+ for (s=string; *s; s++ )
+ {
+ length++;
+ if ((*s & 0x80))
+ length += 5; /* We may need up to 6 bytes for the utf8 output. */
+ }
+ buffer = xmalloc (length + 1);
+
+ inptr = string;
+ inbytes = strlen (string);
+ outptr = buffer;
+ outbytes = length;
+ if ( iconv (cd, (ICONV_CONST char **)&inptr, &inbytes,
+ &outptr, &outbytes) == (size_t)-1)
+ {
+ static int shown;
+
+ if (!shown)
+ log_info (_("conversion from `%s' to `%s' failed: %s\n"),
+ active_charset_name, "utf-8", strerror (errno));
+ shown = 1;
+ /* We don't do any conversion at all but use the strings as is. */
+ strcpy (buffer, string);
+ }
+ else /* Success. */
+ {
+ *outptr = 0;
+ /* We could realloc the buffer now but I doubt that it makes
+ much sense given that it will get freed anyway soon
+ after. */
+ }
+ iconv_close (cd);
+
+#else /*!USE_GNUPG_ICONV*/
+ for(s=string; *s; s++ )
+ {
+ length++;
+ if( *s & 0x80 )
+ length += 2; /* We may need up to 3 bytes. */
+ }
+ buffer = xmalloc( length + 1 );
+ for(p=buffer, s=string; *s; s++ ) {
+ if( *s & 0x80 ) {
+ ushort val = active_charset[ *s & 0x7f ];
+ if( val < 0x0800 ) {
+ *p++ = 0xc0 | ( (val >> 6) & 0x1f );
+ *p++ = 0x80 | ( val & 0x3f );
+ }
+ else {
+ *p++ = 0xe0 | ( (val >> 12) & 0x0f );
+ *p++ = 0x80 | ( (val >> 6) & 0x3f );
+ *p++ = 0x80 | ( val & 0x3f );
+ }
+ }
+ else
+ *p++ = *s;
+ }
+ *p = 0;
+#endif /*!USE_GNUPG_ICONV*/
+
+ }
+ return buffer;
+}
+
+
+/****************
+ * Convert string, which is in UTF8 to native encoding. illegal
+ * encodings by some "\xnn" and quote all control characters. A
+ * character with value DELIM will always be quoted, it must be a
+ * vanilla ASCII character. A DELIM value of -1 is special: it disables
+ * all quoting of control characters.
+ */
+char *
+utf8_to_native( const char *string, size_t length, int delim )
+{
+ int nleft;
+ int i;
+ byte encbuf[8];
+ int encidx;
+ const byte *s;
+ size_t n;
+ byte *buffer = NULL, *p = NULL;
+ unsigned long val = 0;
+ size_t slen;
+ int resync = 0;
+
+ /* 1. pass (p==NULL): count the extended utf-8 characters */
+ /* 2. pass (p!=NULL): create string */
+ for( ;; ) {
+ for( slen=length, nleft=encidx=0, n=0, s=string; slen; s++, slen-- ) {
+ if( resync ) {
+ if( !(*s < 128 || (*s >= 0xc0 && *s <= 0xfd)) ) {
+ /* still invalid */
+ if( p ) {
+ sprintf(p, "\\x%02x", *s );
+ p += 4;
+ }
+ n += 4;
+ continue;
+ }
+ resync = 0;
+ }
+ if( !nleft ) {
+ if( !(*s & 0x80) ) { /* plain ascii */
+ if( delim != -1
+ && (*s < 0x20 || *s == 0x7f || *s == delim
+ || (delim && *s=='\\'))) {
+ n++;
+ if( p )
+ *p++ = '\\';
+ switch( *s ) {
+ case '\n': n++; if( p ) *p++ = 'n'; break;
+ case '\r': n++; if( p ) *p++ = 'r'; break;
+ case '\f': n++; if( p ) *p++ = 'f'; break;
+ case '\v': n++; if( p ) *p++ = 'v'; break;
+ case '\b': n++; if( p ) *p++ = 'b'; break;
+ case 0 : n++; if( p ) *p++ = '0'; break;
+ default:
+ n += 3;
+ if ( p ) {
+ sprintf( p, "x%02x", *s );
+ p += 3;
+ }
+ break;
+ }
+ }
+ else {
+ if( p ) *p++ = *s;
+ n++;
+ }
+ }
+ else if( (*s & 0xe0) == 0xc0 ) { /* 110x xxxx */
+ val = *s & 0x1f;
+ nleft = 1;
+ encidx = 0;
+ encbuf[encidx++] = *s;
+ }
+ else if( (*s & 0xf0) == 0xe0 ) { /* 1110 xxxx */
+ val = *s & 0x0f;
+ nleft = 2;
+ encidx = 0;
+ encbuf[encidx++] = *s;
+ }
+ else if( (*s & 0xf8) == 0xf0 ) { /* 1111 0xxx */
+ val = *s & 0x07;
+ nleft = 3;
+ encidx = 0;
+ encbuf[encidx++] = *s;
+ }
+ else if( (*s & 0xfc) == 0xf8 ) { /* 1111 10xx */
+ val = *s & 0x03;
+ nleft = 4;
+ encidx = 0;
+ encbuf[encidx++] = *s;
+ }
+ else if( (*s & 0xfe) == 0xfc ) { /* 1111 110x */
+ val = *s & 0x01;
+ nleft = 5;
+ encidx = 0;
+ encbuf[encidx++] = *s;
+ }
+ else { /* invalid encoding: print as \xnn */
+ if( p ) {
+ sprintf(p, "\\x%02x", *s );
+ p += 4;
+ }
+ n += 4;
+ resync = 1;
+ }
+ }
+ else if( *s < 0x80 || *s >= 0xc0 ) { /* invalid */
+ if( p ) {
+ for(i=0; i < encidx; i++ ) {
+ sprintf(p, "\\x%02x", encbuf[i] );
+ p += 4;
+ }
+ sprintf(p, "\\x%02x", *s );
+ p += 4;
+ }
+ n += 4 + 4*encidx;
+ nleft = 0;
+ encidx = 0;
+ resync = 1;
+ }
+ else {
+ encbuf[encidx++] = *s;
+ val <<= 6;
+ val |= *s & 0x3f;
+ if( !--nleft ) { /* ready */
+ if (no_translation) {
+ if( p ) {
+ for(i=0; i < encidx; i++ )
+ *p++ = encbuf[i];
+ }
+ n += encidx;
+ encidx = 0;
+ }
+#ifdef USE_GNUPG_ICONV
+ else if(use_iconv) {
+ /* Our strategy for using iconv is a bit
+ * strange but it better keeps compatibility
+ * with previous versions in regard to how
+ * invalid encodings are displayed. What we
+ * do is to keep the utf-8 as is and have the
+ * real translation step then at the end.
+ * Yes, I know that this is ugly. However we
+ * are short of the 1.4 release and for this
+ * branch we should not mee too much around
+ * with iconv things. One reason for this is
+ * that we don't know enough about non-GNU
+ * iconv implementation and want to minimize
+ * the risk of breaking the code on too many
+ * platforms. */
+ if( p ) {
+ for(i=0; i < encidx; i++ )
+ *p++ = encbuf[i];
+ }
+ n += encidx;
+ encidx = 0;
+ }
+#endif /*USE_GNUPG_ICONV*/
+ else if( active_charset ) { /* table lookup */
+ for(i=0; i < 128; i++ ) {
+ if( active_charset[i] == val )
+ break;
+ }
+ if( i < 128 ) { /* we can print this one */
+ if( p ) *p++ = i+128;
+ n++;
+ }
+ else { /* we do not have a translation: print utf8 */
+ if( p ) {
+ for(i=0; i < encidx; i++ ) {
+ sprintf(p, "\\x%02x", encbuf[i] );
+ p += 4;
+ }
+ }
+ n += encidx*4;
+ encidx = 0;
+ }
+ }
+ else { /* native set */
+ if( val >= 0x80 && val < 256 ) {
+ n++; /* we can simply print this character */
+ if( p ) *p++ = val;
+ }
+ else { /* we do not have a translation: print utf8 */
+ if( p ) {
+ for(i=0; i < encidx; i++ ) {
+ sprintf(p, "\\x%02x", encbuf[i] );
+ p += 4;
+ }
+ }
+ n += encidx*4;
+ encidx = 0;
+ }
+ }
+ }
+
+ }
+ }
+ if( !buffer ) { /* allocate the buffer after the first pass */
+ buffer = p = xmalloc( n + 1 );
+ }
+#ifdef USE_GNUPG_ICONV
+ else if(use_iconv) {
+ /* Note: See above for comments. */
+ iconv_t cd;
+ const char *inptr;
+ char *outbuf, *outptr;
+ size_t inbytes, outbytes;
+
+ *p = 0; /* Terminate the buffer. */
+
+ cd = iconv_open (active_charset_name, "utf-8");
+ if (cd == (iconv_t)-1)
+ {
+ handle_iconv_error (active_charset_name, "utf-8", 1);
+ xfree (buffer);
+ return utf8_to_native (string, length, delim);
+ }
+
+ /* Allocate a new buffer large enough to hold all possible
+ * encodings. */
+ n = p - buffer + 1;
+ inbytes = n - 1;;
+ inptr = buffer;
+ outbytes = n * MB_LEN_MAX;
+ if (outbytes / MB_LEN_MAX != n)
+ BUG (); /* Actually an overflow. */
+ outbuf = outptr = xmalloc (outbytes);
+ if ( iconv (cd, (ICONV_CONST char **)&inptr, &inbytes,
+ &outptr, &outbytes) == (size_t)-1) {
+ static int shown;
+
+ if (!shown)
+ log_info (_("conversion from `%s' to `%s' failed: %s\n"),
+ "utf-8", active_charset_name, strerror (errno));
+ shown = 1;
+ /* Didn't worked out. Temporary disable the use of
+ * iconv and fall back to our old code. */
+ xfree (buffer);
+ buffer = NULL;
+ xfree (outbuf);
+ use_iconv = 0;
+ outbuf = utf8_to_native (string, length, delim);
+ use_iconv = 1;
+ }
+ else { /* Success. */
+ *outptr = 0;
+ /* We could realloc the buffer now but I doubt that it makes
+ much sense given that it will get freed anyway soon
+ after. */
+ xfree (buffer);
+ }
+ iconv_close (cd);
+ return outbuf;
+ }
+#endif /*USE_GNUPG_ICONV*/
+ else {
+ *p = 0; /* make a string */
+ return buffer;
+ }
+ }
+}
+
+/****************************************************
+ ******** locale insensitive ctype functions ********
+ ****************************************************/
+/* FIXME: replace them by a table lookup and macros */
+int
+ascii_isupper (int c)
+{
+ return c >= 'A' && c <= 'Z';
+}
+
+int
+ascii_islower (int c)
+{
+ return c >= 'a' && c <= 'z';
+}
+
+int
+ascii_toupper (int c)
+{
+ if (c >= 'a' && c <= 'z')
+ c &= ~0x20;
+ return c;
+}
+
+int
+ascii_tolower (int c)
+{
+ if (c >= 'A' && c <= 'Z')
+ c |= 0x20;
+ return c;
+}
+
+
+int
+ascii_strcasecmp (const char *a, const char *b)
+{
+ const unsigned char *p1 = (const unsigned char *)a;
+ const unsigned char *p2 = (const unsigned char *)b;
+ unsigned char c1, c2;
+
+ if (p1 == p2)
+ return 0;
+
+ do
+ {
+ c1 = ascii_tolower (*p1);
+ c2 = ascii_tolower (*p2);
+
+ if (c1 == '\0')
+ break;
+
+ ++p1;
+ ++p2;
+ }
+ while (c1 == c2);
+
+ return c1 - c2;
+}
+
+int
+ascii_strncasecmp (const char *a, const char *b, size_t n)
+{
+ const unsigned char *p1 = (const unsigned char *)a;
+ const unsigned char *p2 = (const unsigned char *)b;
+ unsigned char c1, c2;
+
+ if (p1 == p2 || !n )
+ return 0;
+
+ do
+ {
+ c1 = ascii_tolower (*p1);
+ c2 = ascii_tolower (*p2);
+
+ if ( !--n || c1 == '\0')
+ break;
+
+ ++p1;
+ ++p2;
+ }
+ while (c1 == c2);
+
+ return c1 - c2;
+}
+
+
+int
+ascii_memcasecmp( const char *a, const char *b, size_t n )
+{
+ if (a == b)
+ return 0;
+ for ( ; n; n--, a++, b++ ) {
+ if( *a != *b && ascii_toupper (*a) != ascii_toupper (*b) )
+ return *a == *b? 0 : (ascii_toupper (*a) - ascii_toupper (*b));
+ }
+ return 0;
+}
+
+
+
+/*********************************************
+ ********** missing string functions *********
+ *********************************************/
+
+#ifndef HAVE_STPCPY
+char *
+stpcpy(char *a,const char *b)
+{
+ while( *b )
+ *a++ = *b++;
+ *a = 0;
+
+ return (char*)a;
+}
+#endif
+
+#ifndef HAVE_STRLWR
+char *
+strlwr(char *s)
+{
+ char *p;
+ for(p=s; *p; p++ )
+ *p = tolower(*(unsigned char *)p);
+ return s;
+}
+#endif
+
+#ifndef HAVE_STRCASECMP
+int
+strcasecmp( const char *a, const char *b )
+{
+ for( ; *a && *b; a++, b++ ) {
+ if( *a != *b
+ && toupper(*(const byte *)a) != toupper(*(const byte *)b) )
+ break;
+ }
+ return *(const byte*)a - *(const byte*)b;
+}
+#endif
+
+#ifndef HAVE_STRNCASECMP
+int
+strncasecmp( const char *a, const char *b, size_t n )
+{
+ for( ; n && *a && *b; a++, b++, n--) {
+ if( *a != *b
+ && toupper(*(const byte *)a) != toupper(*(const byte *)b) )
+ break;
+ }
+ if (!n)
+ return 0;
+ return *(const byte*)a - *(const byte*)b;
+}
+#endif
+
+
+#ifdef _WIN32
+/*
+ * Like vsprintf but provides a pointer to malloc'd storage, which
+ * must be freed by the caller (xfree). Taken from libiberty as
+ * found in gcc-2.95.2 and a little bit modernized.
+ * FIXME: Write a new CRT for W32.
+ */
+int
+vasprintf (char **result, const char *format, va_list args)
+{
+ const char *p = format;
+ /* Add one to make sure that it is never zero, which might cause malloc
+ to return NULL. */
+ int total_width = strlen (format) + 1;
+ va_list ap;
+
+ /* this is not really portable but works under Windows */
+ memcpy ( &ap, &args, sizeof (va_list));
+
+ while (*p != '\0')
+ {
+ if (*p++ == '%')
+ {
+ while (strchr ("-+ #0", *p))
+ ++p;
+ if (*p == '*')
+ {
+ ++p;
+ total_width += abs (va_arg (ap, int));
+ }
+ else
+ {
+ char *endp;
+ total_width += strtoul (p, &endp, 10);
+ p = endp;
+ }
+ if (*p == '.')
+ {
+ ++p;
+ if (*p == '*')
+ {
+ ++p;
+ total_width += abs (va_arg (ap, int));
+ }
+ else
+ {
+ char *endp;
+ total_width += strtoul (p, &endp, 10);
+ p = endp;
+ }
+ }
+ while (strchr ("hlL", *p))
+ ++p;
+ /* Should be big enough for any format specifier except %s
+ and floats. */
+ total_width += 30;
+ switch (*p)
+ {
+ case 'd':
+ case 'i':
+ case 'o':
+ case 'u':
+ case 'x':
+ case 'X':
+ case 'c':
+ (void) va_arg (ap, int);
+ break;
+ case 'f':
+ case 'e':
+ case 'E':
+ case 'g':
+ case 'G':
+ (void) va_arg (ap, double);
+ /* Since an ieee double can have an exponent of 307, we'll
+ make the buffer wide enough to cover the gross case. */
+ total_width += 307;
+
+ case 's':
+ total_width += strlen (va_arg (ap, char *));
+ break;
+ case 'p':
+ case 'n':
+ (void) va_arg (ap, char *);
+ break;
+ }
+ }
+ }
+ *result = xmalloc (total_width);
+ if (*result != NULL)
+ return vsprintf (*result, format, args);
+ else
+ return 0;
+}
+
+int
+asprintf (char **buf, const char *fmt, ...)
+{
+ int status;
+ va_list ap;
+
+ va_start (ap, fmt);
+ status = vasprintf (buf, fmt, ap);
+ va_end (ap);
+ return status;
+}
+
+const char *
+w32_strerror (int w32_errno)
+{
+ static char strerr[256];
+ int ec = (int)GetLastError ();
+
+ if (w32_errno == 0)
+ w32_errno = ec;
+ FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, w32_errno,
+ MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
+ strerr, DIM (strerr)-1, NULL);
+ return strerr;
+}
+#endif /*_WIN32*/
+
+
+
diff --git a/util/strsep.c b/util/strsep.c
new file mode 100644
index 0000000..dae7902
--- /dev/null
+++ b/util/strsep.c
@@ -0,0 +1,48 @@
+#include <config.h>
+#include <string.h>
+
+/* code taken from glibc-2.2.1/sysdeps/generic/strsep.c */
+char *
+strsep (char **stringp, const char *delim)
+{
+ char *begin, *end;
+
+ begin = *stringp;
+ if (begin == NULL)
+ return NULL;
+
+ /* A frequent case is when the delimiter string contains only one
+ character. Here we don't need to call the expensive `strpbrk'
+ function and instead work using `strchr'. */
+ if (delim[0] == '\0' || delim[1] == '\0')
+ {
+ char ch = delim[0];
+
+ if (ch == '\0')
+ end = NULL;
+ else
+ {
+ if (*begin == ch)
+ end = begin;
+ else if (*begin == '\0')
+ end = NULL;
+ else
+ end = strchr (begin + 1, ch);
+ }
+ }
+ else
+ /* Find the end of the token. */
+ end = strpbrk (begin, delim);
+
+ if (end)
+ {
+ /* Terminate the token and set *STRINGP past NUL character. */
+ *end++ = '\0';
+ *stringp = end;
+ }
+ else
+ /* No more delimiters; this is the last token. */
+ *stringp = NULL;
+
+ return begin;
+}
diff --git a/util/timegm.c b/util/timegm.c
new file mode 100644
index 0000000..3b7931e
--- /dev/null
+++ b/util/timegm.c
@@ -0,0 +1,68 @@
+/* timegm.c - libc replacement function
+ * Copyright (C) 2004 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+/*
+ timegm() is a GNU function that might not be available everywhere.
+ It's basically the inverse of gmtime() - you give it a struct tm,
+ and get back a time_t. It differs from mktime() in that it handles
+ the case where the struct tm is UTC and the local environment isn't.
+
+ Some BSDs don't handle the putenv("foo") case properly, so we use
+ unsetenv if the platform has it to remove environment variables.
+*/
+
+#include <config.h>
+#include <time.h>
+#include <stdlib.h>
+#include <string.h>
+
+time_t
+timegm(struct tm *tm)
+{
+ time_t answer;
+ char *zone;
+
+ zone=getenv("TZ");
+ putenv("TZ=UTC");
+ tzset();
+ answer=mktime(tm);
+ if(zone)
+ {
+ char *old_zone;
+
+ old_zone=malloc(3+strlen(zone)+1);
+ if(old_zone)
+ {
+ strcpy(old_zone,"TZ=");
+ strcat(old_zone,zone);
+ putenv(old_zone);
+ }
+ }
+ else
+#ifdef HAVE_UNSETENV
+ unsetenv("TZ");
+#else
+ putenv("TZ");
+#endif
+
+ tzset();
+ return answer;
+}
diff --git a/util/ttyio.c b/util/ttyio.c
new file mode 100644
index 0000000..97b160e
--- /dev/null
+++ b/util/ttyio.c
@@ -0,0 +1,629 @@
+/* ttyio.c - tty i/O functions
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002,
+ * 2004 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <unistd.h>
+#ifdef HAVE_TCGETATTR
+#include <termios.h>
+#else
+#ifdef HAVE_TERMIO_H
+/* simulate termios with termio */
+#include <termio.h>
+#define termios termio
+#define tcsetattr ioctl
+#define TCSAFLUSH TCSETAF
+#define tcgetattr(A,B) ioctl(A,TCGETA,B)
+#define HAVE_TCGETATTR
+#endif
+#endif
+#ifdef _WIN32 /* use the odd Win32 functions */
+#include <windows.h>
+#ifdef HAVE_TCGETATTR
+#error windows and termios
+#endif
+#endif
+#include <errno.h>
+#include <ctype.h>
+#ifdef HAVE_LIBREADLINE
+#include <readline/readline.h>
+#include <readline/history.h>
+#endif
+
+#include "util.h"
+#include "memory.h"
+#include "ttyio.h"
+
+#define CONTROL_D ('D' - 'A' + 1)
+
+#ifdef _WIN32 /* use the odd Win32 functions */
+static struct {
+ HANDLE in, out;
+} con;
+#define DEF_INPMODE (ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT \
+ |ENABLE_PROCESSED_INPUT )
+#define HID_INPMODE (ENABLE_LINE_INPUT|ENABLE_PROCESSED_INPUT )
+#define DEF_OUTMODE (ENABLE_WRAP_AT_EOL_OUTPUT|ENABLE_PROCESSED_OUTPUT)
+
+#else /* yeah, we have a real OS */
+static FILE *ttyfp = NULL;
+#endif
+
+static int initialized;
+static int last_prompt_len;
+static int batchmode;
+static int no_terminal;
+
+#ifdef HAVE_TCGETATTR
+ static struct termios termsave;
+ static int restore_termios;
+#endif
+
+
+
+/* This is a wrapper around ttyname so that we can use it even when
+ the standard streams are redirected. It figures the name out the
+ first time and returns it in a statically allocated buffer. */
+const char *
+tty_get_ttyname (void)
+{
+ static char *name;
+
+ /* On a GNU system ctermid() always return /dev/tty, so this does
+ not make much sense - however if it is ever changed we do the
+ Right Thing now. */
+#ifdef HAVE_CTERMID
+ static int got_name;
+
+ if (!got_name)
+ {
+ const char *s;
+ s = ctermid (NULL);
+ if (s)
+ name = strdup (s);
+ got_name = 1;
+ }
+#endif
+ /* Assume the standard tty on memory error or when there is no
+ ctermid. */
+ return name? name : "/dev/tty";
+}
+
+
+#ifdef HAVE_TCGETATTR
+static void
+cleanup(void)
+{
+ if( restore_termios ) {
+ restore_termios = 0; /* do it prios in case it is interrupted again */
+ if( tcsetattr(fileno(ttyfp), TCSAFLUSH, &termsave) )
+ log_error("tcsetattr() failed: %s\n", strerror(errno) );
+ }
+}
+#endif
+
+static void
+init_ttyfp(void)
+{
+ if( initialized )
+ return;
+
+#if defined(_WIN32)
+ {
+ SECURITY_ATTRIBUTES sa;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.nLength = sizeof(sa);
+ sa.bInheritHandle = TRUE;
+ con.out = CreateFileA( "CONOUT$", GENERIC_READ|GENERIC_WRITE,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ &sa, OPEN_EXISTING, 0, 0 );
+ if( con.out == INVALID_HANDLE_VALUE )
+ log_fatal ("open(CONOUT$) failed: %s", w32_strerror (0));
+ memset(&sa, 0, sizeof(sa));
+ sa.nLength = sizeof(sa);
+ sa.bInheritHandle = TRUE;
+ con.in = CreateFileA( "CONIN$", GENERIC_READ|GENERIC_WRITE,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ &sa, OPEN_EXISTING, 0, 0 );
+ if (con.in == INVALID_HANDLE_VALUE)
+ log_fatal ("open(CONIN$) failed: %s", w32_strerror (0));
+ }
+ SetConsoleMode(con.in, DEF_INPMODE );
+ SetConsoleMode(con.out, DEF_OUTMODE );
+
+#elif defined(__EMX__)
+ ttyfp = stdout; /* Fixme: replace by the real functions: see wklib */
+#else
+ ttyfp = batchmode? stderr : fopen( tty_get_ttyname (), "r+");
+ if( !ttyfp ) {
+ log_error("cannot open `%s': %s\n",
+ tty_get_ttyname (), strerror(errno) );
+ exit(2);
+ }
+#ifdef HAVE_LIBREADLINE
+ rl_catch_signals = 0;
+ rl_instream = rl_outstream = ttyfp;
+ rl_inhibit_completion = 1;
+#endif
+#endif
+#ifdef HAVE_TCGETATTR
+ atexit( cleanup );
+#endif
+ initialized = 1;
+}
+
+#ifdef HAVE_LIBREADLINE
+void
+tty_enable_completion(rl_completion_func_t *completer)
+{
+ if( no_terminal )
+ return;
+
+ if( !initialized )
+ init_ttyfp();
+
+ rl_attempted_completion_function=completer;
+ rl_inhibit_completion=0;
+}
+
+void
+tty_disable_completion(void)
+{
+ if( no_terminal )
+ return;
+
+ if( !initialized )
+ init_ttyfp();
+
+ rl_inhibit_completion=1;
+}
+#endif /*HAVE_LIBREADLINE*/
+
+int
+tty_batchmode( int onoff )
+{
+ int old = batchmode;
+ if( onoff != -1 )
+ batchmode = onoff;
+ return old;
+}
+
+int
+tty_no_terminal(int onoff)
+{
+ int old = no_terminal;
+ no_terminal = onoff ? 1 : 0;
+ return old;
+}
+
+void
+tty_printf( const char *fmt, ... )
+{
+ va_list arg_ptr;
+
+ if (no_terminal)
+ return;
+
+ if( !initialized )
+ init_ttyfp();
+
+ va_start( arg_ptr, fmt ) ;
+#ifdef _WIN32
+ {
+ char *buf = NULL;
+ int n;
+ DWORD nwritten;
+
+ n = vasprintf(&buf, fmt, arg_ptr);
+ if( !buf )
+ log_bug("vasprintf() failed\n");
+
+ if (!WriteConsoleA (con.out, buf, n, &nwritten, NULL))
+ log_fatal ("WriteConsole failed: %s", w32_strerror (0));
+ if( n != nwritten )
+ log_fatal ("WriteConsole failed: %d != %d\n", n, (int)nwritten );
+ last_prompt_len += n;
+ xfree (buf);
+ }
+#else
+ last_prompt_len += vfprintf(ttyfp,fmt,arg_ptr) ;
+ fflush(ttyfp);
+#endif
+ va_end(arg_ptr);
+}
+
+
+/* Same as tty_printf but if FP is not NULL, behave like the standard
+ fprintf. */
+void
+tty_fprintf (FILE *fp, const char *fmt, ... )
+{
+ va_list arg_ptr;
+
+ if (fp)
+ {
+ va_start (arg_ptr, fmt) ;
+ vfprintf (fp, fmt, arg_ptr );
+ va_end (arg_ptr);
+ return;
+ }
+
+ if (no_terminal)
+ return;
+
+ if( !initialized )
+ init_ttyfp();
+
+ va_start( arg_ptr, fmt ) ;
+#ifdef _WIN32
+ {
+ char *buf = NULL;
+ int n;
+ DWORD nwritten;
+
+ n = vasprintf(&buf, fmt, arg_ptr);
+ if( !buf )
+ log_bug("vasprintf() failed\n");
+
+ if (!WriteConsoleA (con.out, buf, n, &nwritten, NULL))
+ log_fatal ("WriteConsole failed: %s", w32_strerror (0));
+ if (n != nwritten)
+ log_fatal ("WriteConsole failed: %d != %d\n", n, (int)nwritten);
+ last_prompt_len += n;
+ xfree (buf);
+ }
+#else
+ last_prompt_len += vfprintf(ttyfp,fmt,arg_ptr) ;
+ fflush(ttyfp);
+#endif
+ va_end(arg_ptr);
+}
+
+
+/****************
+ * Print a string, but filter all control characters out.
+ */
+void
+tty_print_string( const byte *p, size_t n )
+{
+ if (no_terminal)
+ return;
+
+ if( !initialized )
+ init_ttyfp();
+
+#ifdef _WIN32
+ /* not so effective, change it if you want */
+ for( ; n; n--, p++ )
+ if( iscntrl( *p ) ) {
+ if( *p == '\n' )
+ tty_printf("\\n");
+ else if( !*p )
+ tty_printf("\\0");
+ else
+ tty_printf("\\x%02x", *p);
+ }
+ else
+ tty_printf("%c", *p);
+#else
+ for( ; n; n--, p++ )
+ if( iscntrl( *p ) ) {
+ putc('\\', ttyfp);
+ if( *p == '\n' )
+ putc('n', ttyfp);
+ else if( !*p )
+ putc('0', ttyfp);
+ else
+ fprintf(ttyfp, "x%02x", *p );
+ }
+ else
+ putc(*p, ttyfp);
+#endif
+}
+
+void
+tty_print_utf8_string2 (const byte *p, size_t n, size_t max_n )
+{
+ size_t i;
+ char *buf;
+
+ if (no_terminal)
+ return;
+
+ /* we can handle plain ascii simpler, so check for it first */
+ for(i=0; i < n; i++ ) {
+ if( p[i] & 0x80 )
+ break;
+ }
+ if( i < n ) {
+ buf = utf8_to_native( p, n, 0 );
+ if( max_n && (strlen( buf ) > max_n )) {
+ buf[max_n] = 0;
+ }
+ /*(utf8 conversion already does the control character quoting)*/
+ tty_printf("%s", buf );
+ xfree( buf );
+ }
+ else {
+ if( max_n && (n > max_n) ) {
+ n = max_n;
+ }
+ tty_print_string( p, n );
+ }
+}
+
+void
+tty_print_utf8_string( const byte *p, size_t n )
+{
+ tty_print_utf8_string2( p, n, 0 );
+}
+
+
+static char *
+do_get( const char *prompt, int hidden )
+{
+ char *buf;
+#ifndef __riscos__
+ byte cbuf[1];
+#endif
+ int c, n, i;
+
+ if( batchmode ) {
+ log_error("Sorry, we are in batchmode - can't get input\n");
+ exit(2);
+ }
+
+ if (no_terminal) {
+ log_error("Sorry, no terminal at all requested - can't get input\n");
+ exit(2);
+ }
+
+ if( !initialized )
+ init_ttyfp();
+
+ last_prompt_len = 0;
+ buf = xmalloc(n=50);
+ i = 0;
+
+#ifdef _WIN32 /* windoze version */
+ if( hidden )
+ SetConsoleMode(con.in, HID_INPMODE );
+
+ tty_printf( "%s", prompt );
+
+ for(;;) {
+ DWORD nread;
+
+ if (!ReadConsoleA (con.in, cbuf, 1, &nread, NULL))
+ log_fatal ("ReadConsole failed: %s", w32_strerror (0));
+ if( !nread )
+ continue;
+ if( *cbuf == '\n' )
+ break;
+
+ if( !hidden )
+ last_prompt_len++;
+ c = *cbuf;
+ if( c == '\t' )
+ c = ' ';
+ else if( c > 0xa0 )
+ ; /* we don't allow 0xa0, as this is a protected blank which may
+ * confuse the user */
+ else if( iscntrl(c) )
+ continue;
+ if( !(i < n-1) ) {
+ n += 50;
+ buf = xrealloc( buf, n );
+ }
+ buf[i++] = c;
+ }
+
+ if( hidden )
+ SetConsoleMode(con.in, DEF_INPMODE );
+
+#elif defined(__riscos__)
+ tty_printf( "%s", prompt );
+ do {
+ c = riscos_getchar();
+ if (c == 0xa || c == 0xd) { /* Return || Enter */
+ c = (int) '\n';
+ } else if (c == 0x8 || c == 0x7f) { /* Backspace || Delete */
+ if (i>0) {
+ i--;
+ if (!hidden) {
+ last_prompt_len--;
+ fputc(8, ttyfp);
+ fputc(32, ttyfp);
+ fputc(8, ttyfp);
+ fflush(ttyfp);
+ }
+ } else {
+ fputc(7, ttyfp);
+ fflush(ttyfp);
+ }
+ continue;
+ } else if (c == (int) '\t') { /* Tab */
+ c = ' ';
+ } else if (c > 0xa0) {
+ ; /* we don't allow 0xa0, as this is a protected blank which may
+ * confuse the user */
+ } else if (iscntrl(c)) {
+ continue;
+ }
+ if(!(i < n-1)) {
+ n += 50;
+ buf = xrealloc(buf, n);
+ }
+ buf[i++] = c;
+ if (!hidden) {
+ last_prompt_len++;
+ fputc(c, ttyfp);
+ fflush(ttyfp);
+ }
+ } while (c != '\n');
+ i = (i>0) ? i-1 : 0;
+#else /* unix version */
+ if( hidden ) {
+#ifdef HAVE_TCGETATTR
+ struct termios term;
+
+ if( tcgetattr(fileno(ttyfp), &termsave) )
+ log_fatal("tcgetattr() failed: %s\n", strerror(errno) );
+ restore_termios = 1;
+ term = termsave;
+ term.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
+ if( tcsetattr( fileno(ttyfp), TCSAFLUSH, &term ) )
+ log_fatal("tcsetattr() failed: %s\n", strerror(errno) );
+#endif
+ }
+
+ tty_printf( "%s", prompt );
+
+ /* fixme: How can we avoid that the \n is echoed w/o disabling
+ * canonical mode - w/o this kill_prompt can't work */
+ while( read(fileno(ttyfp), cbuf, 1) == 1 && *cbuf != '\n' ) {
+ if( !hidden )
+ last_prompt_len++;
+ c = *cbuf;
+ if( c == CONTROL_D )
+ log_info("control d found\n");
+ if( c == '\t' )
+ c = ' ';
+ else if( c > 0xa0 )
+ ; /* we don't allow 0xa0, as this is a protected blank which may
+ * confuse the user */
+ else if( iscntrl(c) )
+ continue;
+ if( !(i < n-1) ) {
+ n += 50;
+ buf = xrealloc( buf, n );
+ }
+ buf[i++] = c;
+ }
+ if( *cbuf != '\n' ) {
+ buf[0] = CONTROL_D;
+ i = 1;
+ }
+
+
+ if( hidden ) {
+#ifdef HAVE_TCGETATTR
+ if( tcsetattr(fileno(ttyfp), TCSAFLUSH, &termsave) )
+ log_error("tcsetattr() failed: %s\n", strerror(errno) );
+ restore_termios = 0;
+#endif
+ }
+#endif /* end unix version */
+ buf[i] = 0;
+ return buf;
+}
+
+
+char *
+tty_get( const char *prompt )
+{
+#ifdef HAVE_LIBREADLINE
+ if (!batchmode && !no_terminal) {
+ char *line;
+ char *buf;
+
+ if( !initialized )
+ init_ttyfp();
+
+ last_prompt_len = 0;
+
+ line = readline (prompt?prompt:"");
+
+ /* We need to copy it to memory controlled by our malloc
+ implementations; further we need to convert an EOF to our
+ convention. */
+ buf = xmalloc(line? strlen(line)+1:2);
+ if (line)
+ {
+ strcpy (buf, line);
+ trim_spaces (buf);
+ if (strlen (buf) > 2 )
+ add_history (line); /* Note that we test BUF but add LINE. */
+ free (line);
+ }
+ else
+ {
+ buf[0] = CONTROL_D;
+ buf[1] = 0;
+ }
+ return buf;
+ }
+ else
+#endif /* HAVE_LIBREADLINE */
+ return do_get( prompt, 0 );
+}
+
+char *
+tty_get_hidden( const char *prompt )
+{
+ return do_get( prompt, 1 );
+}
+
+
+void
+tty_kill_prompt()
+{
+ if ( no_terminal )
+ return;
+
+ if( !initialized )
+ init_ttyfp();
+
+ if( batchmode )
+ last_prompt_len = 0;
+ if( !last_prompt_len )
+ return;
+#ifdef _WIN32
+ tty_printf("\r%*s\r", last_prompt_len, "");
+#else
+ {
+ int i;
+ putc('\r', ttyfp);
+ for(i=0; i < last_prompt_len; i ++ )
+ putc(' ', ttyfp);
+ putc('\r', ttyfp);
+ fflush(ttyfp);
+ }
+#endif
+ last_prompt_len = 0;
+}
+
+
+int
+tty_get_answer_is_yes( const char *prompt )
+{
+ int yes;
+ char *p = tty_get( prompt );
+ tty_kill_prompt();
+ yes = answer_is_yes(p);
+ xfree(p);
+ return yes;
+}
diff --git a/util/w32reg.c b/util/w32reg.c
new file mode 100644
index 0000000..f65c954
--- /dev/null
+++ b/util/w32reg.c
@@ -0,0 +1,181 @@
+/* w32reg.c - MS-Windows Registry access
+ * Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#if defined (_WIN32) || defined (__CYGWIN32__)
+ /* This module is only used in this environment */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <windows.h>
+#include "util.h"
+#include "memory.h"
+
+static HKEY
+get_root_key(const char *root)
+{
+ HKEY root_key;
+
+ if( !root )
+ root_key = HKEY_CURRENT_USER;
+ else if( !strcmp( root, "HKEY_CLASSES_ROOT" ) )
+ root_key = HKEY_CLASSES_ROOT;
+ else if( !strcmp( root, "HKEY_CURRENT_USER" ) )
+ root_key = HKEY_CURRENT_USER;
+ else if( !strcmp( root, "HKEY_LOCAL_MACHINE" ) )
+ root_key = HKEY_LOCAL_MACHINE;
+ else if( !strcmp( root, "HKEY_USERS" ) )
+ root_key = HKEY_USERS;
+ else if( !strcmp( root, "HKEY_PERFORMANCE_DATA" ) )
+ root_key = HKEY_PERFORMANCE_DATA;
+ else if( !strcmp( root, "HKEY_CURRENT_CONFIG" ) )
+ root_key = HKEY_CURRENT_CONFIG;
+ else
+ return NULL;
+
+ return root_key;
+}
+
+
+/****************
+ * Return a string from the Win32 Registry or NULL in case of
+ * error. Caller must release the return value. A NULL for root
+ * is an alias for HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE in turn.
+ * NOTE: The value is allocated with a plain malloc() - use free() and not
+ * the usual xfree()!!!
+ */
+char *
+read_w32_registry_string( const char *root, const char *dir, const char *name )
+{
+ HKEY root_key, key_handle;
+ DWORD n1, nbytes, type;
+ char *result = NULL;
+
+ if ( !(root_key = get_root_key(root) ) )
+ return NULL;
+
+ if( RegOpenKeyEx( root_key, dir, 0, KEY_READ, &key_handle ) )
+ {
+ if (root)
+ return NULL; /* no need for a RegClose, so return direct */
+ /* It seems to be common practise to fall back to HKLM. */
+ if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &key_handle) )
+ return NULL; /* still no need for a RegClose, so return direct */
+ }
+
+ nbytes = 1;
+ if( RegQueryValueEx( key_handle, name, 0, NULL, NULL, &nbytes ) ) {
+ if (root)
+ goto leave;
+ /* Try to fallback to HKLM also vor a missing value. */
+ RegCloseKey (key_handle);
+ if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &key_handle) )
+ return NULL; /* Nope. */
+ if (RegQueryValueEx( key_handle, name, 0, NULL, NULL, &nbytes))
+ goto leave;
+ }
+ result = malloc( (n1=nbytes+1) );
+ if( !result )
+ goto leave;
+ if( RegQueryValueEx( key_handle, name, 0, &type, result, &n1 ) ) {
+ free(result); result = NULL;
+ goto leave;
+ }
+ result[nbytes] = 0; /* make sure it is really a string */
+ if (type == REG_EXPAND_SZ && strchr (result, '%')) {
+ char *tmp;
+
+ n1 += 1000;
+ tmp = malloc (n1+1);
+ if (!tmp)
+ goto leave;
+ nbytes = ExpandEnvironmentStrings (result, tmp, n1);
+ if (nbytes && nbytes > n1) {
+ free (tmp);
+ n1 = nbytes;
+ tmp = malloc (n1 + 1);
+ if (!tmp)
+ goto leave;
+ nbytes = ExpandEnvironmentStrings (result, tmp, n1);
+ if (nbytes && nbytes > n1) {
+ free (tmp); /* oops - truncated, better don't expand at all */
+ goto leave;
+ }
+ tmp[nbytes] = 0;
+ free (result);
+ result = tmp;
+ }
+ else if (nbytes) { /* okay, reduce the length */
+ tmp[nbytes] = 0;
+ free (result);
+ result = malloc (strlen (tmp)+1);
+ if (!result)
+ result = tmp;
+ else {
+ strcpy (result, tmp);
+ free (tmp);
+ }
+ }
+ else { /* error - don't expand */
+ free (tmp);
+ }
+ }
+
+ leave:
+ RegCloseKey( key_handle );
+ return result;
+}
+
+
+int
+write_w32_registry_string(const char *root, const char *dir,
+ const char *name, const char *value)
+{
+ HKEY root_key, reg_key;
+
+ if ( !(root_key = get_root_key(root) ) )
+ return -1;
+
+ if ( RegOpenKeyEx( root_key, dir, 0, KEY_WRITE, &reg_key )
+ != ERROR_SUCCESS )
+ return -1;
+
+ if ( RegSetValueEx( reg_key, name, 0, REG_SZ, (BYTE *)value,
+ strlen( value ) ) != ERROR_SUCCESS ) {
+ if ( RegCreateKey( root_key, name, &reg_key ) != ERROR_SUCCESS ) {
+ RegCloseKey(reg_key);
+ return -1;
+ }
+ if ( RegSetValueEx( reg_key, name, 0, REG_SZ, (BYTE *)value,
+ strlen( value ) ) != ERROR_SUCCESS ) {
+ RegCloseKey(reg_key);
+ return -1;
+ }
+ }
+
+ RegCloseKey( reg_key );
+
+ return 0;
+}
+
+#endif /* __MINGW32__ || __CYGWIN32__ */