diff options
Diffstat (limited to 'util')
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 (®exp, 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 (®exp, 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 (®exp); + + 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, ¤t_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, ®_key ) + != ERROR_SUCCESS ) + return -1; + + if ( RegSetValueEx( reg_key, name, 0, REG_SZ, (BYTE *)value, + strlen( value ) ) != ERROR_SUCCESS ) { + if ( RegCreateKey( root_key, name, ®_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__ */ |