diff options
Diffstat (limited to 'jnlib')
-rw-r--r-- | jnlib/ChangeLog-2011 | 709 | ||||
-rw-r--r-- | jnlib/Makefile.am | 70 | ||||
-rw-r--r-- | jnlib/Makefile.in | 727 | ||||
-rw-r--r-- | jnlib/README | 8 | ||||
-rw-r--r-- | jnlib/argparse.c | 1205 | ||||
-rw-r--r-- | jnlib/argparse.h | 183 | ||||
-rw-r--r-- | jnlib/dotlock.c | 697 | ||||
-rw-r--r-- | jnlib/dotlock.h | 33 | ||||
-rw-r--r-- | jnlib/dynload.h | 72 | ||||
-rw-r--r-- | jnlib/libjnlib-config.h | 84 | ||||
-rw-r--r-- | jnlib/logging.c | 651 | ||||
-rw-r--r-- | jnlib/logging.h | 88 | ||||
-rw-r--r-- | jnlib/mischelp.c | 133 | ||||
-rw-r--r-- | jnlib/mischelp.h | 98 | ||||
-rw-r--r-- | jnlib/stringhelp.c | 1141 | ||||
-rw-r--r-- | jnlib/stringhelp.h | 136 | ||||
-rw-r--r-- | jnlib/strlist.c | 185 | ||||
-rw-r--r-- | jnlib/strlist.h | 49 | ||||
-rw-r--r-- | jnlib/t-stringhelp.c | 414 | ||||
-rw-r--r-- | jnlib/t-support.c | 143 | ||||
-rw-r--r-- | jnlib/t-support.h | 50 | ||||
-rw-r--r-- | jnlib/types.h | 114 | ||||
-rw-r--r-- | jnlib/utf8conv.c | 738 | ||||
-rw-r--r-- | jnlib/utf8conv.h | 41 | ||||
-rw-r--r-- | jnlib/w32-afunix.c | 137 | ||||
-rw-r--r-- | jnlib/w32-afunix.h | 49 | ||||
-rw-r--r-- | jnlib/w32-gettext.c | 1703 | ||||
-rw-r--r-- | jnlib/w32-reg.c | 182 | ||||
-rw-r--r-- | jnlib/w32help.h | 41 |
29 files changed, 9881 insertions, 0 deletions
diff --git a/jnlib/ChangeLog-2011 b/jnlib/ChangeLog-2011 new file mode 100644 index 0000000..c8306fc --- /dev/null +++ b/jnlib/ChangeLog-2011 @@ -0,0 +1,709 @@ +2011-12-02 Werner Koch <wk@g10code.com> + + NB: ChangeLog files are no longer manually maintained. Starting + on December 1st, 2011 we put change information only in the GIT + commit log, and generate a top-level ChangeLog file from logs at + "make dist". See doc/HACKING for details. + +2009-08-26 Werner Koch <wk@g10code.com> + + * stringhelp.c (do_make_filename): Factor some code out to .. + (get_pwdir): .. new. + +2009-08-26 Werner Koch <wk@g10code.com> + + * stringhelp.c [HAVE_PWD_H]: Include pwd.h. + (do_make_filename): New. + (make_filename, make_filename_try): Implement using the new + function. + * t-stringhelp.c (test_make_filename_try): New. + * t-support.c (gcry_strdup): Fix. + + * stringhelp.h (make_filename, make_filename_try): Add sentinel + attribute. + +2009-08-25 Werner Koch <wk@g10code.com> + + * stringhelp.c: Include errno.h. + (do_strconcat): New. + (strconcat, xstrconcat): New. + * types.h (GNUPG_GCC_A_SENTINEL): New. + * t-stringhelp.c (test_strconcat, test_xstrconcat): New. + (main): Run them. + +2009-07-07 Werner Koch <wk@g10code.com> + + * stringhelp.c (make_filename_try): Use jnlib_malloc. + + * dotlock.c (read_lockfile): Replace jnlib_xmalloc by jnlib_malloc. + +2009-06-04 Werner Koch <wk@g10code.com> + + * mischelp.h: Include SUN_LEN etc also for W32. + +2009-05-19 Werner Koch <wk@g10code.com> + + * mischelp.h: Define PF_LOCAL, AF_LOCAL and SUN_LEN if requested. + * logging.c (fun_writer): Use SUN_LEN to fix a Mac OS X freeze. + +2009-03-25 Werner Koch <wk@g10code.com> + + * logging.c (fun_closer): Never close fd 2. + (set_file_fd): Close logstream early. + +2009-02-25 Werner Koch <wk@g10code.com> + + * logging.c (get_tid_callback): New. + (do_logv): Use it. + (log_set_get_tid_callback): New. + +2009-01-22 Werner Koch <wk@g10code.com> + + * t-support.c (gpg_err_code_from_errno) + (gpg_err_code_from_syserror): New. + +2008-11-20 Werner Koch <wk@g10code.com> + + * argparse.c (arg_parse): Fix last change. + +2008-11-11 Werner Koch <wk@g10code.com> + + * argparse.h: Add a bunch of macros and constants. + * argparse.c: Use the new macros. Re-indent the code. Change + license back to LGPL 2.1. + +2008-11-04 Werner Koch <wk@g10code.com> + + * w32-gettext.c: Merged with code from libgpg-error and rewrote + most parts. + + * Makefile.am (AM_CFLAGS): Add -DJNLIB_IN_JNLIB. + +2008-10-29 Werner Koch <wk@g10code.com> + + * stringhelp.c (make_filename): Implement using macros. Factor some + code out to .. + (change_slashes): New. + (make_filename_try): New. + + * w32-gettext.c (gettext): Return if no domain is loaded. + Reported by Tom Pegios. + +2008-10-28 Werner Koch <wk@g10code.com> + + * w32-gettext.c (gettext): Try the binary search if the string was + not found in the hash table. + +2008-10-20 Werner Koch <wk@g10code.com> + + * w32-afunix.c (_w32_sock_connect): Mark ADDRLEN as unused. + + * dotlock.c (release_dotlock): Do not mix declaration and code. + + * stringhelp.c (make_basename): Silent gcc warning about unused arg. + * argparse.c (store_alias): Ditto. + (find_long_option): + +2008-10-15 Werner Koch <wk@g10code.com> + + * logging.c (do_logv) [W32]: Flush the log stream. + +2008-09-29 Werner Koch <wk@g10code.com> + + * argparse.c (ARGERR_): Use constants for error values. + (optfile_parse): Prettify. Replace xmalloc and xrealloc by malloc + and realloc. + * libjnlib-config.h (jnlib_strdup, jnlib_realloc): New. + +2008-06-26 Werner Koch <wk@g10code.com> + + * stringhelp.c (print_sanitized_buffer2): Loose check for control + characters to better cope with utf-8. The range 0x80..0x9f is + nowadays not anymore accidently used for control charaters. + +2008-06-13 Werner Koch <wk@g10code.com> + + * dotlock.c: Reformat code and implement locking for W32. + (create_dotlock): Use snprintf. + +2008-06-11 Werner Koch <wk@g10code.com> + + * utf8conv.c: Remove useless variable ACTIVE_CHARSET. Suggested + by Petr Uzel. + +2008-05-26 Werner Koch <wk@g10code.com> + + * argparse.c (usage): Make sure to print a trailing LF for usage(1). + +2008-04-08 Werner Koch <wk@g10code.com> + + * w32-gettext.c (gettext_select_utf8): New. + (get_string): Support switching encodings. + (load_domain): Allocate space for DATA_NATIVE. + +2008-03-25 Werner Koch <wk@g10code.com> + + * w32-gettext.c (_nl_locale_name): New. Taken from + ../common/localename and GNU gettext's localename.c. + (set_gettext_file): Rewritten. + (gettext_localename): New. + +2008-03-17 Werner Koch <wk@g10code.com> + + * logging.c (my_funopen_hook_size_t): New. + (fun_writer): Use it to cope with fopencookie/funopen differences. + * dotlock.c (read_lockfile): Initialize PID. Reported by Stéphane + Corthésy. + +2008-02-22 Werner Koch <wk@g10code.com> + + * argparse.c (strusage): Set copyright year to 2008. + +2007-11-19 Werner Koch <wk@g10code.com> + + * stringhelp.c (percent_escape): Factor code out to + (do_percent_escape): .. new. + (try_percent_escape): New. + +2007-10-01 Werner Koch <wk@g10code.com> + + * w32-afunix.c: Only keep the client related code. + (read_port_and_nonce): New. Taken from Assuan. + (_w32_sock_connect): Rewritten. + +2007-08-29 Werner Koch <wk@g10code.com> + + * argparse.c (initialize): Make strings translatable and remove + extra LF. + +2007-08-24 Werner Koch <wk@g10code.com> + + * mischelp.c (same_file_p): New. + (libjnlib_dummy_mischelp_func): Remove as we now always have one + function. + +2007-08-09 Werner Koch <wk@g10code.com> + + * argparse.c (show_help): Expand the @EMAIL@ macro in the package + bug reporting address. + +2007-08-02 Werner Koch <wk@g10code.com> + + * t-stringhelp.c (test_compare_filenames): New. + + * stringhelp.c (compare_filenames) [HAVE_DRIVE_LETTERS]: Fixed + comparison to take slash and backslash in account. + (make_filename): Avoid mixing / and \. + +2007-07-04 Werner Koch <wk@g10code.com> + + * utf8conv.c (load_libiconv): Remove URL from translatble string. + + Switched JNLIB from LGPLv2.1 to LGPLv3. + +2007-07-01 Werner Koch <wk@g10code.com> + + * argparse.c (strusage): Use id 10 for the license string; + default to GPL3+. Change long note to version 3 or later. + (show_version): Print the license info. + +2007-06-19 Werner Koch <wk@g10code.com> + + * Makefile.am: Add support for regression tests. + * t-support.h, t-support.c: New. + * t-stringhelp.c: New. + + * stringhelp.c (percent_escape): Add arg EXTRA to make it a more + general function. Changed all callers. + +2007-06-18 Werner Koch <wk@g10code.com> + + * w32-afunix.c (_w32_sock_bind): Changed to properly detect an + already used socket. + +2007-06-18 Marcus Brinkmann <marcus@g10code.de> + + * stringhelp.h (percent_escape): New prototype. + * stringhelp.c (percent_escape): New function. + +2007-06-11 Werner Koch <wk@g10code.com> + + * utf8conv.c (jnlib_iconv_open, jnlib_iconv, jnlib_iconv_close): New. + +2007-06-06 Werner Koch <wk@g10code.com> + + * w32help.h: New. + * w32-gettext.c: New. Taken from gnupg 1.4, added ngettext, + changed to use jnlib malloc functions and put under the LGPL. + * w32-reg.c: New. Taken from../common/w32reg.c and changed to + LGPL. Changed API to use the jnlib malloc functions. + * Makefile.am (libjnlib_a_SOURCES) [!W32]: Do not build the w32 + specific modules. + + * dotlock.c: Include stringhelp.h for stpcpy prototype. + +2007-06-04 Werner Koch <wk@g10code.com> + + * dynload.h: New. Taken from ../common and changed to LGPL. + + * utf8conv.c (load_libiconv): New. Taken from GnuPG 1.4 + +2007-05-30 Werner Koch <wk@g10code.com> + + * w32-pth.h, w32-pth.c: Remove. + +2007-04-25 Werner Koch <wk@g10code.com> + + * argparse.c (long_opt_strlen): Fixed for utf-8. + +2007-03-07 Werner Koch <wk@g10code.com> + + * argparse.c (strusage): Set copyright year to 2007. + +2007-01-25 Werner Koch <wk@g10code.com> + + * stringhelp.c (utf8_charcount): New. + +2006-11-29 Werner Koch <wk@g10code.com> + + * utf8conv.c (set_native_charset) [HAVE_W32_SYSTEM]: Fixed typo in + macro name. + +2006-11-15 Werner Koch <wk@g10code.com> + + * logging.c (my_funopen_hook_ret_t): New. + (fun_writer): Use it. + +2006-10-19 Werner Koch <wk@g10code.com> + + * stringhelp.c (memrchr) [!HAVE_MEMRCHR]: Provide a replacement. + +2006-09-27 Werner Koch <wk@g10code.com> + + * mischelp.c: New. + (timegm): Copied from gnupg 1.4, changed from GPL to LGPL. Fixed + a memory leak. + + * stringhelp.h (isascii): New. + + * stringhelp.c (strsep): New. Copied from gnupg 1.4.5 + util/strgutil.c. + + * strlist.h (STRLIST): Removed deprecated typedef. + + * types.h: Made cpp commands work with old compilers. Also shows + up nicer with Emacs' font locking. + + * w32-afunix.c (_w32_sock_connect): Set ERRNO for an invalid port. + + Changed license from GPL to LGPL. Note that all code has either + been written by me, David, employees of g10 Code or taken from + glibc. + + * libjnlib-config.h, stringhelp.c, stringhelp.h: + * strlist.c, strlist.h, utf8conv.c, utf8conv.h: + * argparse.c, argparse.h, logging.c, logging.h: + * dotlock.c, dotlock.h, types.h, mischelp.h: + * xmalloc.c, xmalloc.h, w32-pth.c, w32-pth.h: + * w32-afunix.c, w32-afunix.h: Tagged them to be long to jnlib + which is a part of GnuPG but also used by other projetcs. + +2006-09-22 Werner Koch <wk@g10code.com> + + * utf8conv.c: Reworked to match the gnupg 1.4.5 code. This now + requires iconv support but this is reasonable for all modern + systems. + +2006-08-29 Werner Koch <wk@g10code.com> + + * logging.c (do_logv): Emit a missing LF for fatal errors. + +2006-06-28 Werner Koch <wk@g10code.com> + + * dotlock.c (make_dotlock, release_dotlock, read_lockfile) + (maybe_deadlock, destroy_dotlock, create_dotlock): Re-indented. + (create_dotlock): Repalces some log_fatal by log_error as it was + not intended that they should terminate. Write the nodename to + the locking file. Code cleanups. + (read_lockfile): Reworked to read the node name. + (make_dotlock): Test for identical node name and delete lock stale + file. + (release_dotlock): Likewise. + +2006-05-23 Werner Koch <wk@g10code.com> + + * libjnlib-config.h (JNLIB_NEED_UTF8CONV): Fixed typo in name. + + * dotlock.c (release_dotlock): Don't act if we don't have any + locks at all. + (destroy_dotlock): New. From 1.4.3. + (dotlock_remove_lockfiles): Make use of destroy function. + +2006-05-19 Werner Koch <wk@g10code.com> + + * strlist.c (append_to_strlist2): Enabled. + + * stringhelp.c (print_sanitized_buffer2): New. Changed the rules + to match the behaviour of print_string2 from gnupg 1.4.3. + (print_sanitized_buffer): Use the new function. + (print_sanitized_string2): New. + (hextobyte): New. Taken from gpg 1.4.3. + +2006-04-28 Werner Koch <wk@g10code.com> + + * stringhelp.c (print_sanitized_buffer): Fix bug where the count + got wrong for the \xNN representation. + (sanitize_buffer): Fix bug where some control characters lose part + of their \xNN representation. + +2006-04-20 Werner Koch <wk@g10code.com> + + * stringhelp.c (make_basename): New arg INPUTPATH for future + riscos compatibility. + +2006-04-18 Werner Koch <wk@g10code.com> + + * libjnlib-config.h (JNLIB_NEED_UTF8CONF): Defined. + * strlist.c (add_to_strlist2) [JNLIB_NEED_UTF8CONV]: Enabled. + +2005-06-15 Werner Koch <wk@g10code.com> + + * stringhelp.c (sanitize_buffer): Make P a void*. + (ascii_memistr, memistr): Ditto. + (ascii_memcasecmp): Ditto. + * logging.c (writen): Use void * for arg BUFFER. + * stringhelp.c (memistr): Fixed unsigned/signed pointer conflict. + (ascii_memistr): Ditto. + (ascii_memcasemem): Ditto. + * utf8conv.c (utf8_to_native): Ditto. + (utf8_to_native): Ditto. + * argparse.c (show_version): Removed non-required cast. + +2005-01-19 Werner Koch <wk@g10code.com> + + * logging.c (fun_writer): Don't fallback to stderr. Print to + stderr only if connected to a tty. + +2004-12-20 Werner Koch <wk@g10code.com> + + * w32-pth.c (do_pth_event_free): The events are hold in a ring + buffer. Adjust for that. + (do_pth_event_body): Ditto. + (pth_event_isolate): Ditto. + (do_pth_wait): Ditto. + (_pth_event_count): Renamed to .. + (event_count): .. and adjusted as above. + (pth_init): Define 3 debug levels and change all debug calls to + make use of them. This makes the moule now silent. + +2004-12-19 Werner Koch <wk@g10code.com> + + * w32-pth.c (pth_init): Enable debugging depending on env var. + (pth_self): New. + (pth_mutex_release, pth_mutex_acquire): Implemented directly using + the W32 API. + +2004-12-18 Werner Koch <wk@g10code.com> + + * w32-pth.c (pth_init): Reverse return values. Use TRUE and FALSE + constants. + (pth_kill, pth_mutex_acquire, pth_attr_set, pth_join, pth_cancel): + Ditto. + +2004-12-15 Werner Koch <wk@g10code.com> + + * logging.c [W32]: Don't include unavailable headers. + +2004-12-14 Werner Koch <wk@g10code.com> + + * w32-pth.c (_pth_strerror): Renamed to ... + (w32_strerror): .. this. And let callers provide a buffer. + (spawn_helper_thread): Removed HD arg and hardwire the stack size + to 32k. + (do_pth_wait): Removed use of ATTR; not needed for the helper + threads. + (helper_thread): Renamed to .. + (launch_thread): .. this. Release handle if not joinable. + (struct pth_priv_hd_s): Renamed to ... + (struct thread_info_s): .. this. Add member JOINABLE and TH. + +2004-12-14 Timo Schulz <twoaday@g10code.com> + + * w32-pth.c (pth_kill): Just release the crit section if + pth_init was really called. And set all handles to NULL. + (_pth_strerror): New. + (do_pth_wait): Before we enter the loop we check if there + are too much events in the ring. + +2004-12-14 Werner Koch <wk@g10code.com> + + * w32-pth.h (pth_event_occured): Removed macro. + * w32-pth.c: Fixed license statement; its under the LGPL. + (enter_pth, leave_pth): Use them to bracket almost all public + functions. + +2004-12-13 Timo Schulz <twoaday@g10code.com> + + * w32-pth.c (enter_pth, leave_pth): New. + (pth_init): Initialize global mutex section. + (pth_kill): Release global mutex section. + (helper_thread): New. + (pth_spawn): Make sure only one thread is running. + +2004-12-13 Werner Koch <wk@g10code.com> + + * stringhelp.c (w32_strerror) [W32]: New. + + * w32-pth.c, w32-pth.h: Added real code written by Timo Schulz. + Not finished, though. + +2004-12-07 Werner Koch <wk@g10code.com> + + * w32-pth.c, w32-pth.h: New. + +2004-11-26 Werner Koch <wk@g10code.com> + + * logging.c [_WIN32]: Don't include socket headers. + +2004-11-30 Timo Schulz <ts@g10code.com> + + * w32-afunix.c: New. AF_UNIX emulation for W32. + * w32-afunix.h: Likewise. + +2004-11-22 Werner Koch <wk@g10code.com> + + * logging.c (log_test_fd): Add test on LOGSTREAM. Reported by + Barry Schwartz. + +2004-11-18 Werner Koch <wk@g10code.com> + + * logging.c: Explicitly include sys/stat.h for the S_I* constants. + +2004-10-21 Werner Koch <wk@g10code.com> + + * logging.c (do_logv): Use set_log_stream to setup a default. + (log_set_file): Factored code out to .. + (set_file_fd): .. New function to allow using a file descriptor. + (log_set_fd): Make use of new fucntion. + (fun_writer): Reworked. + +2004-08-18 Werner Koch <wk@g10code.de> + + * stringhelp.c (print_sanitized_utf8_string): Actually implement + it. + +2004-06-21 Werner Koch <wk@g10code.com> + + * logging.c (log_set_file): Do not close an old logstream if it + used to be stderr or stdout. + +2004-05-05 Werner Koch <wk@gnupg.org> + + * logging.c (log_set_file): Oops, don't close if LOGSTREAM is NULL. + +2004-04-30 Werner Koch <wk@gnupg.org> + + * logging.c (log_set_file): Make sure the log stream will be + closed even if the stderr fileno will be assigned to a new socket. + +2004-04-16 Werner Koch <wk@gnupg.org> + + * logging.h (JNLIB_LOG_WITH_PREFIX): Add constants for the flag + values. + * logging.c (log_set_prefix): New flag DETACHED. + (fun_writer): Take care of this flag. + (log_test_fd): New. + +2004-02-18 Werner Koch <wk@gnupg.org> + + * stringhelp.c (print_sanitized_buffer): Don't care about + non-ASCII characaters. + (sanitize_buffer): Ditto. + +2004-02-12 Werner Koch <wk@gnupg.org> + + * Makefile.am: Replaced INCLUDES by AM_CPPFLAGS. + +2004-01-05 Werner Koch <wk@gnupg.org> + + * argparse.c (strusage): Changed default copyright year to 2004. + +2003-12-17 Werner Koch <wk@gnupg.org> + + * argparse.c (initialize): Replaced use of non-literal format + args. Suggested by Florian Weimer. + +2003-12-16 Werner Koch <wk@gnupg.org> + + * logging.c (writen, fun_writer, fun_closer): New. + (log_set_file): Add feature to log to a socket. + (log_set_file, do_logv): Force printing with prefix and pid. + +2003-11-13 Werner Koch <wk@gnupg.org> + + * strlist.c (strlist_copy): New. + + * dotlock.c: Define DIRSEP_C et al. if not defined. + +2003-11-06 Werner Koch <wk@gnupg.org> + + * strlist.h (strlist_t): New. STRLIST is now deprecated. + +2003-06-18 Werner Koch <wk@gnupg.org> + + * strlist.c (strlist_pop): New. + + * dotlock.c (dotlock_remove_lockfiles): Prefixed with dotlock_ and + made global. + +2003-06-17 Werner Koch <wk@gnupg.org> + + * stringhelp.c (length_sans_trailing_chars) + (length_sans_trailing_ws): New. + + * logging.c (log_inc_errorcount): New. + + * stringhelp.c (print_sanitized_utf8_buffer): Implement utf8 + conversion. + (sanitize_buffer): New. Based on gnupg 1.3.2 make_printable_string. + + * dotlock.c: Updated to match the version from 1.3.2 + * utf8conv.c: New. Code taken from strgutil.c of gnupg 1.3.2. + * utf8conv.h: New. + +2003-06-16 Werner Koch <wk@gnupg.org> + + * logging.c (do_logv): Hack to optionally suppress a leading space. + + * stringhelp.c (ascii_strncasecmp): New. Taken from gnupg 1.3. + (ascii_memistr): New. Taken from gnupg 1.3 + +2003-06-13 Werner Koch <wk@gnupg.org> + + * mischelp.h (wipememory2,wipememory): New. Taken from GnuPG 1.3.2. + +2002-06-04 Werner Koch <wk@gnupg.org> + + * stringhelp.c (print_sanitized_utf8_string): New. No real + implementation for now. + (print_sanitized_utf8_buffer): Ditto. + +2002-04-04 Werner Koch <wk@gnupg.org> + + * logging.c (log_get_prefix): New. + +2002-03-15 Werner Koch <wk@gnupg.org> + + * argparse.c (optfile_parse): Fixed missing argument handling. + +2002-02-25 Werner Koch <wk@gnupg.org> + + * stringhelp.c (ascii_memcasemem): New. + +2002-02-14 Werner Koch <wk@gnupg.org> + + * Makefile.am (INCLUDES): Add cflags for libgcrypt. + +2002-02-07 Werner Koch <wk@gnupg.org> + + * logging.c (log_set_fd): New. + + * stringhelp.c (print_sanitized_buffer): New. + (print_sanitized_string): New. + +2002-01-24 Werner Koch <wk@gnupg.org> + + * argparse.c (strusage): Set default copyright notice year to 2002. + + Fixed the copyright notice of this file, as it has always been + part of GnuPG and therefore belongs to the FSF. + +2001-11-01 Marcus Brinkmann <marcus@g10code.de> + + * logging.c (log_printf): Do not initialize ARG_PTR with 0, we + don't know the correct type. Instead, run va_start and va_end + unconditionally. + Reported by Jose Carlos Garcia Sogo <jsogo@debian.org>. + +2002-01-19 Werner Koch <wk@gnupg.org> + + * logging.c (log_get_stream): New. + +2001-12-05 Werner Koch <wk@gnupg.org> + + * logging.c (log_set_prefix): New. + (do_logv): Include prefix and pid only if enabled. Print time only + when explicitly enabled. + (log_logv): New. + * logging.h: Include log_logv() only when requested. + +2001-11-06 Werner Koch <wk@gnupg.org> + + * strlist.c, strlist.h: New. Taken from pgnupg/util/strgutil.c + +2001-08-30 Werner Koch <wk@gnupg.org> + + * logging.c (log_printf): Don't pass NULL instead of arg_ptr. + +2001-07-19 Werner Koch <wk@gnupg.org> + + * stringhelp.c (ascii_memistr,ascii_isupper,ascii_islower, + ascii_toupper,ascii_tolower, ascii_strcasecmp, ascii_memcasecmp): New. + +2000-07-26 10:02:51 Werner Koch (wk@habibti.openit.de) + + * stringhelp.c.: Add stdarg.h + * argparse.h: s/ulong/unsigned long/ although this should be defined + by types.h. + +2000-06-28 19:40:23 Werner Koch (wk@habibti.openit.de) + + * Makefile.am: Replaced second logging.c by .h + +2000-05-24 08:58:15 Werner Koch (wk@habibti.openit.de) + + * logging.c (log_get_errorcount): New. + +2000-05-24 08:44:47 Werner Koch (wk@habibti.openit.de) + + * stringhelp.c: Added a few filename related helper functions. + +2000-05-11 18:04:43 Werner Koch (wk@habibti.openit.de) + + * xmalloc.c (xstrcat2): Replaced stpcpy to quickly address W32 + problems. + +2000-05-02 19:43:38 Werner Koch (wk@habibti.openit.de) + + * xmalloc.c (xstrcat2): New. + +Mon Jan 24 13:04:28 CET 2000 Werner Koch <wk@gnupg.de> + + * README: New. + * Makefile.am: new. + * argparse.c, argparse.h, logging.c, logging.h: + * mischelp.h, stringhelp.c, stringhelp.h, xmalloc.c: + * xmalloc.h, dotlock.c: Moved from ../util to here. + * dotlock.h: New. + * libjnlib-config.h: New. + + * logging.c (log_set_file): New. + (log_printf): New. + (do_logv): Add kludge to insert LFs. + + + *********************************************************** + * Please note that JNLIB is maintained as part of GnuPG. * + * You may find it source-copied in other packages. * + *********************************************************** + + Copyright 2000, 2001, 2002, 2003, 2004, + 2005, 2006, 2007, 2008 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/jnlib/Makefile.am b/jnlib/Makefile.am new file mode 100644 index 0000000..b3e7d7d --- /dev/null +++ b/jnlib/Makefile.am @@ -0,0 +1,70 @@ +# Makefile for the JNLIB part of GnuPG +# Copyright (C) 1999, 2000, 2001, 2004, +# 2006 Feee Software Soundation, Inc. +# +# This file is part of JNLIB. +# +# JNLIB 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 3 of +# the License, or (at your option) any later version. +# +# JNLIB 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, see <http://www.gnu.org/licenses/>. + + +## Process this file with automake to produce Makefile.in + +EXTRA_DIST = README ChangeLog-2011 +noinst_PROGRAMS = $(module_tests) +TESTS = $(module_tests) + +AM_CPPFLAGS = -I$(top_srcdir)/intl + +# We need libgcrypt because libjnlib-config includes gcrypt.h +AM_CFLAGS = -DJNLIB_IN_JNLIB $(LIBGCRYPT_CFLAGS) + +noinst_LIBRARIES = libjnlib.a + + +#libjnlib_a_LDFLAGS = +libjnlib_a_SOURCES = \ + libjnlib-config.h \ + stringhelp.c stringhelp.h \ + strlist.c strlist.h \ + utf8conv.c utf8conv.h \ + argparse.c argparse.h \ + logging.c logging.h \ + dotlock.c dotlock.h \ + types.h mischelp.c mischelp.h dynload.h w32help.h + +if HAVE_W32_SYSTEM +libjnlib_a_SOURCES += w32-reg.c w32-afunix.c w32-afunix.h w32-gettext.c +endif + + +# For GnuPG we don't need the xmalloc stuff. +# xmalloc.c xmalloc.h + + +# +# Module tests. +# +# These tests should only be used at the canonical location of jnlib +# which is the GnuPG package. The reason for this is that t-support.c +# defines replacements for the actual used memory allocation functions +# so that there is no dependency on libgcrypt. +# +module_tests = t-stringhelp + +t_jnlib_src = t-support.c t-support.h +t_jnlib_ldadd = libjnlib.a $(LIBINTL) $(LIBICONV) + +t_stringhelp_SOURCES = t-stringhelp.c $(t_jnlib_src) +t_stringhelp_LDADD = $(t_jnlib_ldadd) + diff --git a/jnlib/Makefile.in b/jnlib/Makefile.in new file mode 100644 index 0000000..a2d9663 --- /dev/null +++ b/jnlib/Makefile.in @@ -0,0 +1,727 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 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@ + +# Makefile for the JNLIB part of GnuPG +# Copyright (C) 1999, 2000, 2001, 2004, +# 2006 Feee Software Soundation, Inc. +# +# This file is part of JNLIB. +# +# JNLIB 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 3 of +# the License, or (at your option) any later version. +# +# JNLIB 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, see <http://www.gnu.org/licenses/>. + + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +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@ +noinst_PROGRAMS = $(am__EXEEXT_1) +TESTS = $(am__EXEEXT_1) +@HAVE_W32_SYSTEM_TRUE@am__append_1 = w32-reg.c w32-afunix.c w32-afunix.h w32-gettext.c +subdir = jnlib +DIST_COMMON = README $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/gl/m4/absolute-header.m4 \ + $(top_srcdir)/gl/m4/alloca.m4 $(top_srcdir)/gl/m4/allocsa.m4 \ + $(top_srcdir)/gl/m4/eealloc.m4 \ + $(top_srcdir)/gl/m4/gnulib-comp.m4 \ + $(top_srcdir)/gl/m4/gnulib-tool.m4 \ + $(top_srcdir)/gl/m4/mkdtemp.m4 $(top_srcdir)/gl/m4/setenv.m4 \ + $(top_srcdir)/gl/m4/stdint.m4 $(top_srcdir)/gl/m4/strpbrk.m4 \ + $(top_srcdir)/gl/m4/unistd_h.m4 $(top_srcdir)/m4/autobuild.m4 \ + $(top_srcdir)/m4/codeset.m4 $(top_srcdir)/m4/estream.m4 \ + $(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/gnupg-pth.m4 \ + $(top_srcdir)/m4/gpg-error.m4 $(top_srcdir)/m4/iconv.m4 \ + $(top_srcdir)/m4/isc-posix.m4 $(top_srcdir)/m4/ksba.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/libassuan.m4 \ + $(top_srcdir)/m4/libcurl.m4 $(top_srcdir)/m4/libgcrypt.m4 \ + $(top_srcdir)/m4/longdouble.m4 $(top_srcdir)/m4/nls.m4 \ + $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \ + $(top_srcdir)/m4/readline.m4 $(top_srcdir)/m4/size_max.m4 \ + $(top_srcdir)/m4/socklen.m4 $(top_srcdir)/m4/sys_socket_h.m4 \ + $(top_srcdir)/m4/tar-ustar.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 = +CONFIG_CLEAN_VPATH_FILES = +LIBRARIES = $(noinst_LIBRARIES) +ARFLAGS = cru +libjnlib_a_AR = $(AR) $(ARFLAGS) +libjnlib_a_LIBADD = +am__libjnlib_a_SOURCES_DIST = libjnlib-config.h stringhelp.c \ + stringhelp.h strlist.c strlist.h utf8conv.c utf8conv.h \ + argparse.c argparse.h logging.c logging.h dotlock.c dotlock.h \ + types.h mischelp.c mischelp.h dynload.h w32help.h w32-reg.c \ + w32-afunix.c w32-afunix.h w32-gettext.c +@HAVE_W32_SYSTEM_TRUE@am__objects_1 = w32-reg.$(OBJEXT) \ +@HAVE_W32_SYSTEM_TRUE@ w32-afunix.$(OBJEXT) \ +@HAVE_W32_SYSTEM_TRUE@ w32-gettext.$(OBJEXT) +am_libjnlib_a_OBJECTS = stringhelp.$(OBJEXT) strlist.$(OBJEXT) \ + utf8conv.$(OBJEXT) argparse.$(OBJEXT) logging.$(OBJEXT) \ + dotlock.$(OBJEXT) mischelp.$(OBJEXT) $(am__objects_1) +libjnlib_a_OBJECTS = $(am_libjnlib_a_OBJECTS) +am__EXEEXT_1 = t-stringhelp$(EXEEXT) +PROGRAMS = $(noinst_PROGRAMS) +am__objects_2 = t-support.$(OBJEXT) +am_t_stringhelp_OBJECTS = t-stringhelp.$(OBJEXT) $(am__objects_2) +t_stringhelp_OBJECTS = $(am_t_stringhelp_OBJECTS) +am__DEPENDENCIES_1 = +am__DEPENDENCIES_2 = libjnlib.a $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) +t_stringhelp_DEPENDENCIES = $(am__DEPENDENCIES_2) +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/scripts/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +SOURCES = $(libjnlib_a_SOURCES) $(t_stringhelp_SOURCES) +DIST_SOURCES = $(am__libjnlib_a_SOURCES_DIST) $(t_stringhelp_SOURCES) +ETAGS = etags +CTAGS = ctags +am__tty_colors = \ +red=; grn=; lgn=; blu=; std= +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ABSOLUTE_STDINT_H = @ABSOLUTE_STDINT_H@ +ACLOCAL = @ACLOCAL@ +ADNSLIBS = @ADNSLIBS@ +ALLOCA = @ALLOCA@ +ALLOCA_H = @ALLOCA_H@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BITSIZEOF_PTRDIFF_T = @BITSIZEOF_PTRDIFF_T@ +BITSIZEOF_SIG_ATOMIC_T = @BITSIZEOF_SIG_ATOMIC_T@ +BITSIZEOF_SIZE_T = @BITSIZEOF_SIZE_T@ +BITSIZEOF_WCHAR_T = @BITSIZEOF_WCHAR_T@ +BITSIZEOF_WINT_T = @BITSIZEOF_WINT_T@ +BUILD_INCLUDED_LIBINTL = @BUILD_INCLUDED_LIBINTL@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CC_FOR_BUILD = @CC_FOR_BUILD@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DL_LIBS = @DL_LIBS@ +DNSLIBS = @DNSLIBS@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FAQPROG = @FAQPROG@ +GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ +GMSGFMT = @GMSGFMT@ +GMSGFMT_015 = @GMSGFMT_015@ +GNUPG_AGENT_PGM = @GNUPG_AGENT_PGM@ +GNUPG_DIRMNGR_PGM = @GNUPG_DIRMNGR_PGM@ +GNUPG_PINENTRY_PGM = @GNUPG_PINENTRY_PGM@ +GNUPG_PROTECT_TOOL_PGM = @GNUPG_PROTECT_TOOL_PGM@ +GNUPG_SCDAEMON_PGM = @GNUPG_SCDAEMON_PGM@ +GPGKEYS_CURL = @GPGKEYS_CURL@ +GPGKEYS_FINGER = @GPGKEYS_FINGER@ +GPGKEYS_HKP = @GPGKEYS_HKP@ +GPGKEYS_KDNS = @GPGKEYS_KDNS@ +GPGKEYS_LDAP = @GPGKEYS_LDAP@ +GPGKEYS_MAILTO = @GPGKEYS_MAILTO@ +GPG_ERROR_CFLAGS = @GPG_ERROR_CFLAGS@ +GPG_ERROR_CONFIG = @GPG_ERROR_CONFIG@ +GPG_ERROR_LIBS = @GPG_ERROR_LIBS@ +GREP = @GREP@ +HAVE_INTTYPES_H = @HAVE_INTTYPES_H@ +HAVE_LONG_LONG_INT = @HAVE_LONG_LONG_INT@ +HAVE_SIGNED_SIG_ATOMIC_T = @HAVE_SIGNED_SIG_ATOMIC_T@ +HAVE_SIGNED_WCHAR_T = @HAVE_SIGNED_WCHAR_T@ +HAVE_SIGNED_WINT_T = @HAVE_SIGNED_WINT_T@ +HAVE_STDINT_H = @HAVE_STDINT_H@ +HAVE_SYS_BITYPES_H = @HAVE_SYS_BITYPES_H@ +HAVE_SYS_INTTYPES_H = @HAVE_SYS_INTTYPES_H@ +HAVE_SYS_TYPES_H = @HAVE_SYS_TYPES_H@ +HAVE_UNSIGNED_LONG_LONG_INT = @HAVE_UNSIGNED_LONG_LONG_INT@ +HAVE_WCHAR_H = @HAVE_WCHAR_H@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INTLLIBS = @INTLLIBS@ +INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@ +KSBA_CFLAGS = @KSBA_CFLAGS@ +KSBA_CONFIG = @KSBA_CONFIG@ +KSBA_LIBS = @KSBA_LIBS@ +LDAPLIBS = @LDAPLIBS@ +LDAP_CPPFLAGS = @LDAP_CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBASSUAN_CFLAGS = @LIBASSUAN_CFLAGS@ +LIBASSUAN_CONFIG = @LIBASSUAN_CONFIG@ +LIBASSUAN_LIBS = @LIBASSUAN_LIBS@ +LIBCURL = @LIBCURL@ +LIBCURL_CPPFLAGS = @LIBCURL_CPPFLAGS@ +LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@ +LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@ +LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ +LIBGNU_LIBDEPS = @LIBGNU_LIBDEPS@ +LIBGNU_LTLIBDEPS = @LIBGNU_LTLIBDEPS@ +LIBICONV = @LIBICONV@ +LIBINTL = @LIBINTL@ +LIBOBJS = @LIBOBJS@ +LIBREADLINE = @LIBREADLINE@ +LIBS = @LIBS@ +LIBUSB_LIBS = @LIBUSB_LIBS@ +LIBUTIL_LIBS = @LIBUTIL_LIBS@ +LN_S = @LN_S@ +LTLIBICONV = @LTLIBICONV@ +LTLIBINTL = @LTLIBINTL@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +MSGFMT = @MSGFMT@ +MSGFMT_015 = @MSGFMT_015@ +MSGMERGE = @MSGMERGE@ +NETLIBS = @NETLIBS@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_GT = @PACKAGE_GT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +POSUB = @POSUB@ +PTH_CFLAGS = @PTH_CFLAGS@ +PTH_CONFIG = @PTH_CONFIG@ +PTH_LIBS = @PTH_LIBS@ +PTRDIFF_T_SUFFIX = @PTRDIFF_T_SUFFIX@ +RANLIB = @RANLIB@ +SENDMAIL = @SENDMAIL@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SHRED = @SHRED@ +SIG_ATOMIC_T_SUFFIX = @SIG_ATOMIC_T_SUFFIX@ +SIZE_T_SUFFIX = @SIZE_T_SUFFIX@ +STDINT_H = @STDINT_H@ +STRIP = @STRIP@ +SYS_SOCKET_H = @SYS_SOCKET_H@ +TAR = @TAR@ +UNISTD_H = @UNISTD_H@ +USE_INCLUDED_LIBINTL = @USE_INCLUDED_LIBINTL@ +USE_NLS = @USE_NLS@ +VERSION = @VERSION@ +W32SOCKLIBS = @W32SOCKLIBS@ +WCHAR_T_SUFFIX = @WCHAR_T_SUFFIX@ +WINDRES = @WINDRES@ +WINT_T_SUFFIX = @WINT_T_SUFFIX@ +XGETTEXT = @XGETTEXT@ +XGETTEXT_015 = @XGETTEXT_015@ +XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@ +ZLIBS = @ZLIBS@ +_libcurl_config = @_libcurl_config@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +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@ +builddir = @builddir@ +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@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +EXTRA_DIST = README ChangeLog-2011 +AM_CPPFLAGS = -I$(top_srcdir)/intl + +# We need libgcrypt because libjnlib-config includes gcrypt.h +AM_CFLAGS = -DJNLIB_IN_JNLIB $(LIBGCRYPT_CFLAGS) +noinst_LIBRARIES = libjnlib.a + +#libjnlib_a_LDFLAGS = +libjnlib_a_SOURCES = libjnlib-config.h stringhelp.c stringhelp.h \ + strlist.c strlist.h utf8conv.c utf8conv.h argparse.c \ + argparse.h logging.c logging.h dotlock.c dotlock.h types.h \ + mischelp.c mischelp.h dynload.h w32help.h $(am__append_1) + +# For GnuPG we don't need the xmalloc stuff. +# xmalloc.c xmalloc.h + +# +# Module tests. +# +# These tests should only be used at the canonical location of jnlib +# which is the GnuPG package. The reason for this is that t-support.c +# defines replacements for the actual used memory allocation functions +# so that there is no dependency on libgcrypt. +# +module_tests = t-stringhelp +t_jnlib_src = t-support.c t-support.h +t_jnlib_ldadd = libjnlib.a $(LIBINTL) $(LIBICONV) +t_stringhelp_SOURCES = t-stringhelp.c $(t_jnlib_src) +t_stringhelp_LDADD = $(t_jnlib_ldadd) +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 ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu jnlib/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu jnlib/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 +$(am__aclocal_m4_deps): + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libjnlib.a: $(libjnlib_a_OBJECTS) $(libjnlib_a_DEPENDENCIES) + -rm -f libjnlib.a + $(libjnlib_a_AR) libjnlib.a $(libjnlib_a_OBJECTS) $(libjnlib_a_LIBADD) + $(RANLIB) libjnlib.a + +clean-noinstPROGRAMS: + -test -z "$(noinst_PROGRAMS)" || rm -f $(noinst_PROGRAMS) +t-stringhelp$(EXEEXT): $(t_stringhelp_OBJECTS) $(t_stringhelp_DEPENDENCIES) + @rm -f t-stringhelp$(EXEEXT) + $(LINK) $(t_stringhelp_OBJECTS) $(t_stringhelp_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/argparse.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dotlock.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/logging.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mischelp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stringhelp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strlist.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-stringhelp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-support.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utf8conv.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/w32-afunix.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/w32-gettext.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/w32-reg.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@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@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@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) '$<'` + +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; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + 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; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + 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; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +check-TESTS: $(TESTS) + @failed=0; all=0; xfail=0; xpass=0; skip=0; \ + srcdir=$(srcdir); export srcdir; \ + list=' $(TESTS) '; \ + $(am__tty_colors); \ + if test -n "$$list"; then \ + for tst in $$list; do \ + if test -f ./$$tst; then dir=./; \ + elif test -f $$tst; then dir=; \ + else dir="$(srcdir)/"; fi; \ + if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$tst[\ \ ]*) \ + xpass=`expr $$xpass + 1`; \ + failed=`expr $$failed + 1`; \ + col=$$red; res=XPASS; \ + ;; \ + *) \ + col=$$grn; res=PASS; \ + ;; \ + esac; \ + elif test $$? -ne 77; then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$tst[\ \ ]*) \ + xfail=`expr $$xfail + 1`; \ + col=$$lgn; res=XFAIL; \ + ;; \ + *) \ + failed=`expr $$failed + 1`; \ + col=$$red; res=FAIL; \ + ;; \ + esac; \ + else \ + skip=`expr $$skip + 1`; \ + col=$$blu; res=SKIP; \ + fi; \ + echo "$${col}$$res$${std}: $$tst"; \ + done; \ + if test "$$all" -eq 1; then \ + tests="test"; \ + All=""; \ + else \ + tests="tests"; \ + All="All "; \ + fi; \ + if test "$$failed" -eq 0; then \ + if test "$$xfail" -eq 0; then \ + banner="$$All$$all $$tests passed"; \ + else \ + if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \ + banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \ + fi; \ + else \ + if test "$$xpass" -eq 0; then \ + banner="$$failed of $$all $$tests failed"; \ + else \ + if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \ + banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \ + fi; \ + fi; \ + dashes="$$banner"; \ + skipped=""; \ + if test "$$skip" -ne 0; then \ + if test "$$skip" -eq 1; then \ + skipped="($$skip test was not run)"; \ + else \ + skipped="($$skip tests were not run)"; \ + fi; \ + test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$skipped"; \ + fi; \ + report=""; \ + if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \ + report="Please report to $(PACKAGE_BUGREPORT)"; \ + test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$report"; \ + fi; \ + dashes=`echo "$$dashes" | sed s/./=/g`; \ + if test "$$failed" -eq 0; then \ + echo "$$grn$$dashes"; \ + else \ + echo "$$red$$dashes"; \ + fi; \ + echo "$$banner"; \ + test -z "$$skipped" || echo "$$skipped"; \ + test -z "$$report" || echo "$$report"; \ + echo "$$dashes$$std"; \ + test "$$failed" -eq 0; \ + else :; fi + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$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 + $(MAKE) $(AM_MAKEFLAGS) check-TESTS +check: check-am +all-am: Makefile $(LIBRARIES) $(PROGRAMS) +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) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_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 clean-noinstPROGRAMS \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(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: + +.MAKE: check-am install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-TESTS check-am clean \ + clean-generic clean-noinstLIBRARIES clean-noinstPROGRAMS 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-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + 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 + + +# 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/jnlib/README b/jnlib/README new file mode 100644 index 0000000..5536e1a --- /dev/null +++ b/jnlib/README @@ -0,0 +1,8 @@ +JNLIB - This is a collection of utility function which are too small +to put into a library. The code here is licensed under the LGPL. + +libjnlib-config.h should be be modified for each project to make these +functions fit into the software. Mainly these are memory functions in +case you need another allocator. + + diff --git a/jnlib/argparse.c b/jnlib/argparse.c new file mode 100644 index 0000000..c9b5384 --- /dev/null +++ b/jnlib/argparse.c @@ -0,0 +1,1205 @@ +/* [argparse.c wk 17.06.97] Argument Parser for option handling + * Copyright (C) 1998, 1999, 2000, 2001, 2006 + * 2007, 2008 Free Software Foundation, Inc. + * + * This file is part of JNLIB. + * + * JNLIB 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. + * + * JNLIB 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, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <string.h> + +#include "libjnlib-config.h" +#include "mischelp.h" +#include "stringhelp.h" +#include "logging.h" +#ifdef JNLIB_NEED_UTF8CONV +#include "utf8conv.h" +#endif +#include "argparse.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 + * 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 const char *(*strusage_handler)( int ) = NULL; + +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 as initialized. */ + if ( *arg->argc < 0 ) + jnlib_log_bug ("invalid argument for arg_parsee\n"); + } + + + if (arg->err) + { + /* Last option was erroneous. */ + const char *s; + + if (filename) + { + if ( arg->r_opt == ARGPARSE_UNEXPECTED_ARG ) + s = _("argument not expected"); + else if ( arg->r_opt == ARGPARSE_READ_ERROR ) + s = _("read error"); + else if ( arg->r_opt == ARGPARSE_KEYWORD_TOO_LONG ) + s = _("keyword too long"); + else if ( arg->r_opt == ARGPARSE_MISSING_ARG ) + s = _("missing argument"); + else if ( arg->r_opt == ARGPARSE_INVALID_COMMAND ) + s = _("invalid command"); + else if ( arg->r_opt == ARGPARSE_INVALID_ALIAS ) + s = _("invalid alias definition"); + else if ( arg->r_opt == ARGPARSE_OUT_OF_CORE ) + s = _("out of core"); + else + s = _("invalid option"); + jnlib_log_error ("%s:%u: %s\n", filename, *lineno, s); + } + else + { + s = arg->internal.last? arg->internal.last:"[??]"; + + if ( arg->r_opt == ARGPARSE_MISSING_ARG ) + jnlib_log_error (_("missing argument for option \"%.50s\"\n"), s); + else if ( arg->r_opt == ARGPARSE_UNEXPECTED_ARG ) + jnlib_log_error (_("option \"%.50s\" does not expect an " + "argument\n"), s ); + else if ( arg->r_opt == ARGPARSE_INVALID_COMMAND ) + jnlib_log_error (_("invalid command \"%.50s\"\n"), s); + else if ( arg->r_opt == ARGPARSE_AMBIGUOUS_OPTION ) + jnlib_log_error (_("option \"%.50s\" is ambiguous\n"), s); + else if ( arg->r_opt == ARGPARSE_AMBIGUOUS_OPTION ) + jnlib_log_error (_("command \"%.50s\" is ambiguous\n"),s ); + else if ( arg->r_opt == ARGPARSE_OUT_OF_CORE ) + jnlib_log_error ("%s\n", _("out of core\n")); + else + jnlib_log_error (_("invalid option \"%.50s\"\n"), s); + } + if ( arg->err != 1 ) + exit (2); + arg->err = 0; + } + + /* Zero out 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 + */ + (void)arg; + (void)name; + (void)value; +#if 0 + ALIAS_DEF a = jnlib_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 in_alias=0; + + if (!fp) /* Divert to to 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 (!opts[idx].short_opt ) + arg->r_opt = ((opts[idx].flags & ARGPARSE_OPT_COMMAND) + ? ARGPARSE_INVALID_COMMAND + : ARGPARSE_INVALID_OPTION); + else if (!(opts[idx].flags & 7)) + arg->r_type = 0; /* Does not take an arg. */ + else if ((opts[idx].flags & 8) ) + arg->r_type = 0; /* Arg is optional. */ + else + arg->r_opt = ARGPARSE_MISSING_ARG; + + break; + } + else if (state == 3) + { + /* No argument found. */ + if (in_alias) + arg->r_opt = ARGPARSE_MISSING_ARG; + else if (!(opts[idx].flags & 7)) + arg->r_type = 0; /* Does not take an arg. */ + else if ((opts[idx].flags & 8)) + arg->r_type = 0; /* No optional argument. */ + else + arg->r_opt = ARGPARSE_MISSING_ARG; + + break; + } + else if (state == 4) + { + /* Has an argument. */ + if (in_alias) + { + if (!buffer) + arg->r_opt = ARGPARSE_UNEXPECTED_ARG; + else + { + char *p; + + buffer[i] = 0; + p = strpbrk (buffer, " \t"); + if (p) + { + *p++ = 0; + trim_spaces (p); + } + if (!p || !*p) + { + jnlib_free (buffer); + arg->r_opt = ARGPARSE_INVALID_ALIAS; + } + else + { + store_alias (arg, buffer, p); + } + } + } + else if (!(opts[idx].flags & 7)) + arg->r_opt = ARGPARSE_UNEXPECTED_ARG; + else + { + char *p; + + if (!buffer) + { + keyword[i] = 0; + buffer = jnlib_strdup (keyword); + if (!buffer) + arg->r_opt = ARGPARSE_OUT_OF_CORE; + } + else + buffer[i] = 0; + + if (buffer) + { + trim_spaces (buffer); + p = buffer; + if (*p == '"') + { + /* Remove quotes. */ + p++; + if (*p && p[strlen(p)-1] == '\"' ) + p[strlen(p)-1] = 0; + } + if (!set_opt_arg (arg, opts[idx].flags, p)) + jnlib_free(buffer); + } + } + break; + } + else if (c == EOF) + { + if (ferror (fp)) + arg->r_opt = ARGPARSE_READ_ERROR; + else + arg->r_opt = 0; /* EOF. */ + break; + } + state = 0; + i = 0; + } + else if (state == -1) + ; /* Skip. */ + else if (state == 0 && isascii (c) && isspace(c)) + ; /* Skip leading white space. */ + else if (state == 0 && c == '#' ) + state = 1; /* Start of a comment. */ + else if (state == 1) + ; /* Skip comments. */ + else if (state == 2 && isascii (c) && isspace(c)) + { + /* Check keyword. */ + 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 & ARGPARSE_OPT_COMMAND) + ? ARGPARSE_INVALID_COMMAND + : ARGPARSE_INVALID_OPTION); + state = -1; /* Skip rest of line and leave. */ + } + } + else + state = 3; + } + else if (state == 3) + { + /* Skip leading spaces of the argument. */ + if (!isascii (c) || !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 + { + char *tmp; + size_t tmplen = buflen + 50; + + tmp = jnlib_realloc (buffer, tmplen); + if (tmp) + { + buflen = tmplen; + buffer = tmp; + buffer[i++] = c; + } + else + { + jnlib_free (buffer); + arg->r_opt = ARGPARSE_OUT_OF_CORE; + break; + } + } + } + else if (i < DIM(keyword)-1) + keyword[i++] = c; + else + { + size_t tmplen = DIM(keyword) + 50; + buffer = jnlib_malloc (tmplen); + if (buffer) + { + buflen = tmplen; + memcpy(buffer, keyword, i); + buffer[i++] = c; + } + else + { + arg->r_opt = ARGPARSE_OUT_OF_CORE; + break; + } + } + } + else if (i >= DIM(keyword)-1) + { + arg->r_opt = ARGPARSE_KEYWORD_TOO_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; + + (void)arg; + + /* 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 & ARGPARSE_FLAG_ARG0)) + { + /* Skip the first argument. */ + 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 & ARGPARSE_FLAG_ALL)) + { + arg->r_opt = ARGPARSE_IS_ARG; /* Not an option but an argument. */ + arg->r_type = 2; + arg->r.ret_str = s; + argc--; argv++; idx++; /* set to next one */ + } + else if( arg->internal.stopped ) + { + arg->r_opt = 0; + goto leave; /* Ready. */ + } + else if ( *s == '-' && s[1] == '-' ) + { + /* Long option. */ + char *argpos; + + arg->internal.inarg = 0; + if (!s[2] && !(arg->flags & ARGPARSE_FLAG_NOSTOP)) + { + /* 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) ) + show_help (opts, arg->flags); + else if ( i < 0 && !strcmp ( "version", s+2) ) + { + if (!(arg->flags & ARGPARSE_FLAG_NOVERSION)) + { + 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 ) + arg->r_opt = ARGPARSE_AMBIGUOUS_OPTION; + else if ( i == -1 ) + { + arg->r_opt = ARGPARSE_INVALID_OPTION; + arg->r.ret_str = s+2; + } + else + arg->r_opt = opts[i].short_opt; + if ( i < 0 ) + ; + else if ( (opts[i].flags & 0x07) ) + { + if ( argpos ) + { + s2 = argpos+1; + if ( !*s2 ) + s2 = NULL; + } + else + s2 = argv[1]; + if ( !s2 && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) ) + { + arg->r_type = ARGPARSE_TYPE_NONE; /* Argument is optional. */ + } + else if ( !s2 ) + { + arg->r_opt = ARGPARSE_MISSING_ARG; + } + else if ( !argpos && *s2 == '-' + && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) ) + { + /* 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 = ARGPARSE_TYPE_NONE; + } + 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 = ARGPARSE_UNEXPECTED_ARG; + 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 & ARGPARSE_FLAG_ONEDASH) ) + { + 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 == '?' ) ) + show_help (opts, arg->flags); + + arg->r_opt = opts[i].short_opt; + if (!opts[i].short_opt ) + { + arg->r_opt = (opts[i].flags & ARGPARSE_OPT_COMMAND)? + ARGPARSE_INVALID_COMMAND:ARGPARSE_INVALID_OPTION; + 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 & ARGPARSE_OPT_OPTIONAL) ) + { + arg->r_type = ARGPARSE_TYPE_NONE; + } + else if ( !s2 ) + { + arg->r_opt = ARGPARSE_MISSING_ARG; + } + else if ( *s2 == '-' && s2[1] + && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) ) + { + /* 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 = ARGPARSE_TYPE_NONE; + } + else + { + set_opt_arg (arg, opts[i].flags, s2); + argc--; argv++; idx++; /* Skip one. */ + } + } + s = "x"; /* This is so that !s[1] yields false. */ + } + else + { + /* Does not take an argument. */ + arg->r_type = ARGPARSE_TYPE_NONE; + 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 & ARGPARSE_FLAG_MIXED ) + { + arg->r_opt = ARGPARSE_IS_ARG; + 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 ARGPARSE_TYPE_INT: + arg->r.ret_int = (int)strtol(s,NULL,base); + return 0; + case ARGPARSE_TYPE_LONG: + arg->r.ret_long= strtol(s,NULL,base); + return 0; + case ARGPARSE_TYPE_ULONG: + arg->r.ret_ulong= strtoul(s,NULL,base); + return 0; + case ARGPARSE_TYPE_STRING: + 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; +#ifdef JNLIB_NEED_UTF8CONV + int is_utf8 = is_native_utf8 (); +#endif + + s=o->description+1; + if ( *s != '=' ) + n++; + /* For a (mostly) correct length calculation we exclude + continuation bytes (10xxxxxx) if we are on a native utf8 + terminal. */ + for (; *s && *s != '|'; s++ ) +#ifdef JNLIB_NEED_UTF8CONV + if ( is_utf8 && (*s&0xc0) != 0x80 ) +#endif + 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 int 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 & ARGPARSE_FLAG_ONEDASH) ) + puts ("\n(A single dash may be used instead of the double ones)"); + } + if ( (s=strusage(19)) ) + { + /* bug reports to ... */ + char *s2; + + putchar('\n'); + s2 = strstr (s, "@EMAIL@"); + if (s2) + { + if (s2-s) + fwrite (s, s2-s, 1, stdout); +#ifdef PACKAGE_BUGREPORT + fputs (PACKAGE_BUGREPORT, stdout); +#else + fputs ("bug@example.org", stdout); +#endif + s2 += 7; + if (*s2) + fputs (s2, stdout); + } + else + 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 ); + /* Licence string. */ + if( (s=strusage (10)) ) + 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 (s, stdout); + fflush (stdout); +} + + +void +usage (int level) +{ + const char *p; + + if (!level) + { + fprintf(stderr,"%s %s; %s\n", strusage(11), strusage(13), strusage (14)); + fflush (stderr); + } + else if (level == 1) + { + p = strusage (40); + fputs (p, stderr); + if (*p && p[strlen(p)] != '\n') + putc ('\n', stderr); + exit (2); + } + else if (level == 2) + { + puts (strusage(41)); + exit (0); + } +} + +/* Level + * 0: Print copyright string to stderr + * 1: Print a short usage hint to stderr and terminate + * 2: Print a long usage hint to stdout and terminate + * 10: Return license info string + * 11: Return the name of the program + * 12: Return 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 * +strusage( int level ) +{ + const char *p = strusage_handler? strusage_handler(level) : NULL; + + if ( p ) + return p; + + switch ( level ) + { + case 10: p = ("License GPLv3+: GNU GPL version 3 or later " + "<http://gnu.org/licenses/gpl.html>"); + break; + case 11: p = "foo"; break; + case 13: p = "0.0"; break; + case 14: p = "Copyright (C) 2012 Free Software Foundation, Inc."; break; + case 15: p = +"This is free software: you are free to change and redistribute it.\n" +"There is NO WARRANTY, to the extent permitted by law.\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 3 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 software. If not, see <http://www.gnu.org/licenses/>.\n"; + break; + case 40: /* short and long usage */ + case 41: p = ""; break; + } + + return p; +} + +void +set_strusage ( const char *(*f)( int ) ) +{ + strusage_handler = f; +} + + +#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[] = { + ARGPARSE_x('v', "verbose", NONE, 0, "Laut sein"), + ARGPARSE_s_n('e', "echo" , ("Zeile ausgeben, damit wir sehen, " + "was wir ein gegeben haben")), + ARGPARSE_s_n('d', "debug", "Debug\nfalls mal etwas\nschief geht"), + ARGPARSE_s_s('o', "output", 0 ), + ARGPARSE_o_s('c', "cross-ref", "cross-reference erzeugen\n" ), + /* Note that on a non-utf8 terminal the ß might garble the output. */ + ARGPARSE_s_n('s', "street","|Straße|set the name of the street to Straße"), + ARGPARSE_o_i('m', "my-option", 0), + ARGPARSE_s_n(500, "a-long-option", 0 ), + ARGPARSE_end + }; + ARGPARSE_ARGS pargs = { &argc, &argv, 2|4|32 }; + int i; + + while( arg_parse ( &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 = ARGPARSE_PRINT_WARNING; break; + } + } + 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/jnlib/argparse.h b/jnlib/argparse.h new file mode 100644 index 0000000..b211e5f --- /dev/null +++ b/jnlib/argparse.h @@ -0,0 +1,183 @@ +/* argparse.h - Argument parser for option handling. + * Copyright (C) 1998,1999,2000,2001,2006 Free Software Foundation, Inc. + * + * This file is part of JNLIB. + * + * JNLIB 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. + * + * JNLIB 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef LIBJNLIB_ARGPARSE_H +#define LIBJNLIB_ARGPARSE_H + +#include <stdio.h> +#include "types.h" + +typedef struct +{ + int *argc; /* Pointer to ARGC (value subject to change). */ + char ***argv; /* Pointer to ARGV (value subject to change). */ + unsigned int flags; /* Global flags. May be set prior to calling the + parser. The parser may change the value. */ + int err; /* Print error description for last option. + Either 0, ARGPARSE_PRINT_WARNING or + ARGPARSE_PRINT_ERROR. */ + + int r_opt; /* Returns option code. */ + int r_type; /* Returns type of option value. */ + union { + int ret_int; + long ret_long; + unsigned long ret_ulong; + char *ret_str; + } r; /* Return values */ + + struct { + int idx; + int inarg; + int stopped; + const char *last; + void *aliases; + const void *cur_alias; + } internal; /* Private - do not change. */ +} ARGPARSE_ARGS; + +typedef struct +{ + int short_opt; + const char *long_opt; + unsigned int flags; + const char *description; /* Optional option description. */ +} ARGPARSE_OPTS; + + +/* Global flags (ARGPARSE_ARGS). */ +#define ARGPARSE_FLAG_KEEP 1 /* Do not remove options form argv. */ +#define ARGPARSE_FLAG_ALL 2 /* Do not stop at last option but return + remaining args with R_OPT set to -1. */ +#define ARGPARSE_FLAG_MIXED 4 /* Assume options and args are mixed. */ +#define ARGPARSE_FLAG_NOSTOP 8 /* Do not stop processing at "--". */ +#define ARGPARSE_FLAG_ARG0 16 /* Do not skip the first arg. */ +#define ARGPARSE_FLAG_ONEDASH 32 /* Allow long options with one dash. */ +#define ARGPARSE_FLAG_NOVERSION 64 /* No output for "--version". */ + +/* Flags for each option (ARGPARSE_OPTS). The type code may be + ORed with the OPT flags. */ +#define ARGPARSE_TYPE_NONE 0 /* Does not take an argument. */ +#define ARGPARSE_TYPE_INT 1 /* Takes an int argument. */ +#define ARGPARSE_TYPE_STRING 2 /* Takes a string argument. */ +#define ARGPARSE_TYPE_LONG 3 /* Takes a long argument. */ +#define ARGPARSE_TYPE_ULONG 4 /* Takes an unsigned long argument. */ +#define ARGPARSE_OPT_OPTIONAL (1<<3) /* Argument is optional. */ +#define ARGPARSE_OPT_PREFIX (1<<4) /* Allow 0x etc. prefixed values. */ +#define ARGPARSE_OPT_COMMAND (1<<8) /* The argument is a command. */ + +/* A set of macros to make option definitions easier to read. */ +#define ARGPARSE_x(s,l,t,f,d) \ + { (s), (l), ARGPARSE_TYPE_ ## t | (f), (d) } + +#define ARGPARSE_s(s,l,t,d) \ + { (s), (l), ARGPARSE_TYPE_ ## t, (d) } +#define ARGPARSE_s_n(s,l,d) \ + { (s), (l), ARGPARSE_TYPE_NONE, (d) } +#define ARGPARSE_s_i(s,l,d) \ + { (s), (l), ARGPARSE_TYPE_INT, (d) } +#define ARGPARSE_s_s(s,l,d) \ + { (s), (l), ARGPARSE_TYPE_STRING, (d) } +#define ARGPARSE_s_l(s,l,d) \ + { (s), (l), ARGPARSE_TYPE_LONG, (d) } +#define ARGPARSE_s_u(s,l,d) \ + { (s), (l), ARGPARSE_TYPE_ULONG, (d) } + +#define ARGPARSE_o(s,l,t,d) \ + { (s), (l), (ARGPARSE_TYPE_ ## t | ARGPARSE_OPT_OPTIONAL), (d) } +#define ARGPARSE_o_n(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_NONE | ARGPARSE_OPT_OPTIONAL), (d) } +#define ARGPARSE_o_i(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_INT | ARGPARSE_OPT_OPTIONAL), (d) } +#define ARGPARSE_o_s(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_STRING | ARGPARSE_OPT_OPTIONAL), (d) } +#define ARGPARSE_o_l(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_LONG | ARGPARSE_OPT_OPTIONAL), (d) } +#define ARGPARSE_o_u(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_ULONG | ARGPARSE_OPT_OPTIONAL), (d) } + +#define ARGPARSE_p(s,l,t,d) \ + { (s), (l), (ARGPARSE_TYPE_ ## t | ARGPARSE_OPT_PREFIX), (d) } +#define ARGPARSE_p_n(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_NONE | ARGPARSE_OPT_PREFIX), (d) } +#define ARGPARSE_p_i(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_INT | ARGPARSE_OPT_PREFIX), (d) } +#define ARGPARSE_p_s(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_STRING | ARGPARSE_OPT_PREFIX), (d) } +#define ARGPARSE_p_l(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_LONG | ARGPARSE_OPT_PREFIX), (d) } +#define ARGPARSE_p_u(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_ULONG | ARGPARSE_OPT_PREFIX), (d) } + +#define ARGPARSE_op(s,l,t,d) \ + { (s), (l), (ARGPARSE_TYPE_ ## t \ + | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) } +#define ARGPARSE_op_n(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_NONE \ + | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) } +#define ARGPARSE_op_i(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_INT \ + | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) } +#define ARGPARSE_op_s(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_STRING \ + | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) } +#define ARGPARSE_op_l(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_LONG \ + | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) } +#define ARGPARSE_op_u(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_ULONG \ + | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) } + +#define ARGPARSE_c(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_NONE | ARGPARSE_OPT_COMMAND), (d) } + + +#define ARGPARSE_group(s,d) \ + { (s), NULL, 0, (d) } + +#define ARGPARSE_end() { 0, NULL, 0, NULL } + + +/* Other constants. */ +#define ARGPARSE_PRINT_WARNING 1 +#define ARGPARSE_PRINT_ERROR 2 + + +/* Error values. */ +#define ARGPARSE_IS_ARG (-1) +#define ARGPARSE_INVALID_OPTION (-2) +#define ARGPARSE_MISSING_ARG (-3) +#define ARGPARSE_KEYWORD_TOO_LONG (-4) +#define ARGPARSE_READ_ERROR (-5) +#define ARGPARSE_UNEXPECTED_ARG (-6) +#define ARGPARSE_INVALID_COMMAND (-7) +#define ARGPARSE_AMBIGUOUS_OPTION (-8) +#define ARGPARSE_AMBIGUOUS_COMMAND (-9) +#define ARGPARSE_INVALID_ALIAS (-10) +#define ARGPARSE_OUT_OF_CORE (-11) + + +int arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts); +int optfile_parse( FILE *fp, const char *filename, unsigned *lineno, + ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts); +void usage( int level ); +const char *strusage( int level ); +void set_strusage( const char *(*f)( int ) ); + +#endif /*LIBJNLIB_ARGPARSE_H*/ diff --git a/jnlib/dotlock.c b/jnlib/dotlock.c new file mode 100644 index 0000000..260c086 --- /dev/null +++ b/jnlib/dotlock.c @@ -0,0 +1,697 @@ +/* dotlock.c - dotfile locking + * Copyright (C) 1998, 2000, 2001, 2003, 2004, + * 2005, 2006, 2008 Free Software Foundation, Inc. + * + * This file is part of JNLIB. + * + * JNLIB 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 3 of + * the License, or (at your option) any later version. + * + * JNLIB 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, see <http://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <errno.h> +#include <unistd.h> +#ifdef HAVE_DOSISH_SYSTEM +# define WIN32_LEAN_AND_MEAN +# include <windows.h> +#else +# include <sys/utsname.h> +#endif +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <signal.h> + +#include "libjnlib-config.h" +#include "stringhelp.h" +#include "dotlock.h" + +#if !defined(DIRSEP_C) && !defined(EXTSEP_C) \ + && !defined(DIRSEP_S) && !defined(EXTSEP_S) +#ifdef HAVE_DOSISH_SYSTEM +#define DIRSEP_C '\\' +#define EXTSEP_C '.' +#define DIRSEP_S "\\" +#define EXTSEP_S "." +#else +#define DIRSEP_C '/' +#define EXTSEP_C '.' +#define DIRSEP_S "/" +#define EXTSEP_S "." +#endif +#endif + + +/* The object describing a lock. */ +struct dotlock_handle +{ + struct dotlock_handle *next; + char *lockname; /* Name of the actual lockfile. */ + int locked; /* Lock status. */ + int disable; /* If true, locking is disabled. */ + +#ifdef HAVE_DOSISH_SYSTEM + HANDLE lockhd; /* The W32 handle of the lock file. */ +#else + char *tname; /* Name of the lockfile template. */ + size_t nodename_off; /* Offset in TNAME of the nodename part. */ + size_t nodename_len; /* Length of the nodename part. */ +#endif /* HAVE_DOSISH_SYSTEM */ +}; + + +/* A list of of all lock handles. */ +static volatile DOTLOCK all_lockfiles; + +/* If this has the value true all locking is disabled. */ +static int never_lock; + + +/* Local protototypes. */ +#ifndef HAVE_DOSISH_SYSTEM +static int read_lockfile (DOTLOCK h, int *same_node); +#endif /*!HAVE_DOSISH_SYSTEM*/ + + + + +/* Entirely disable all locking. This function should be called + before any locking is done. It may be called right at startup of + the process as it only sets a global value. */ +void +disable_dotlock(void) +{ + never_lock = 1; +} + + + +/* Create a lockfile for a file name FILE_TO_LOCK and returns an + object of type DOTLOCK which may be used later to actually acquire + the lock. A cleanup routine gets installed to cleanup left over + locks or other files used internally by the lock mechanism. + + Calling this function with NULL does only install the atexit + handler and may thus be used to assure that the cleanup is called + after all other atexit handlers. + + This function creates a lock file in the same directory as + FILE_TO_LOCK using that name and a suffix of ".lock". Note that on + POSIX systems a temporary file ".#lk.<hostname>.pid[.threadid] is + used. + + The function returns an new handle which needs to be released using + destroy_dotlock but gets also released at the termination of the + process. On error NULL is returned. + */ +DOTLOCK +create_dotlock (const char *file_to_lock) +{ + static int initialized; + DOTLOCK h; +#ifndef HAVE_DOSISH_SYSTEM + int fd = -1; + char pidstr[16]; + const char *nodename; + const char *dirpart; + int dirpartlen; + struct utsname utsbuf; + size_t tnamelen; +#endif + + if ( !initialized ) + { + atexit (dotlock_remove_lockfiles); + initialized = 1; + } + + if ( !file_to_lock ) + return NULL; /* Only initialization was requested. */ + + h = jnlib_calloc (1, sizeof *h); + if (!h) + return NULL; + + if (never_lock) + { + h->disable = 1; +#ifdef _REENTRANT + /* fixme: aquire mutex on all_lockfiles */ +#endif + h->next = all_lockfiles; + all_lockfiles = h; + return h; + } + +#ifndef HAVE_DOSISH_SYSTEM + /* + This is the POSIX version which uses a temporary file and the + link system call to make locking an atomic operation. + */ + + snprintf (pidstr, sizeof pidstr, "%10d\n", (int)getpid() ); + + /* 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; + + tnamelen = dirpartlen + 6 + 30 + strlen(nodename) + 10; + h->tname = jnlib_malloc (tnamelen + 1); + if (!h->tname) + { + all_lockfiles = h->next; + jnlib_free (h); + return NULL; + } + h->nodename_len = strlen (nodename); + +#ifndef __riscos__ + snprintf (h->tname, tnamelen, "%.*s/.#lk%p.", dirpartlen, dirpart, h ); + h->nodename_off = strlen (h->tname); + snprintf (h->tname+h->nodename_off, tnamelen - h->nodename_off, + "%s.%d", nodename, (int)getpid ()); +#else /* __riscos__ */ + snprintf (h->tname, tnamelen, "%.*s.lk%p/", dirpartlen, dirpart, h ); + h->nodename_off = strlen (h->tname); + snprintf (h->tname+h->nodename_off, tnamelen - h->modename_off, + "%s/%d", 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)); + jnlib_free (h->tname); + jnlib_free (h); + return NULL; + } + if ( write (fd, pidstr, 11 ) != 11 ) + goto write_failed; + if ( write (fd, nodename, strlen (nodename) ) != strlen (nodename) ) + goto write_failed; + if ( write (fd, "\n", 1 ) != 1 ) + goto write_failed; + if ( close (fd) ) + goto write_failed; + +# ifdef _REENTRANT + /* release mutex */ +# endif + h->lockname = jnlib_malloc ( strlen (file_to_lock) + 6 ); + if (!h->lockname) + { + all_lockfiles = h->next; + unlink (h->tname); + jnlib_free (h->tname); + jnlib_free (h); + return NULL; + } + strcpy (stpcpy (h->lockname, file_to_lock), EXTSEP_S "lock"); + return h; + + write_failed: + all_lockfiles = h->next; +# ifdef _REENTRANT + /* fixme: release mutex */ +# endif + log_error ( _("error writing to `%s': %s\n"), h->tname, strerror(errno) ); + close (fd); + unlink (h->tname); + jnlib_free (h->tname); + jnlib_free (h); + return NULL; + +#else /* HAVE_DOSISH_SYSTEM */ + + /* The Windows version does not need a temporary file but uses the + plain lock file along with record locking. We create this file + here so that we later do only need to do the file locking. For + error reporting it is useful to keep the name of the file in the + handle. */ + h->next = all_lockfiles; + all_lockfiles = h; + + h->lockname = jnlib_malloc ( strlen (file_to_lock) + 6 ); + if (!h->lockname) + { + all_lockfiles = h->next; + jnlib_free (h); + return NULL; + } + strcpy (stpcpy(h->lockname, file_to_lock), EXTSEP_S "lock"); + + /* If would be nice if we would use the FILE_FLAG_DELETE_ON_CLOSE + along with FILE_SHARE_DELETE but that does not work due to a race + condition: Despite the OPEN_ALWAYS flag CreateFile may return an + error and we can't reliable create/open the lock file unless we + would wait here until it works - however there are other valid + reasons why a lock file can't be created and thus the process + would not stop as expected but spin til until Windows crashes. + Our solution is to keep the lock file open; that does not + harm. */ + h->lockhd = CreateFile (h->lockname, + GENERIC_READ|GENERIC_WRITE, + FILE_SHARE_READ|FILE_SHARE_WRITE, + NULL, OPEN_ALWAYS, 0, NULL); + if (h->lockhd == INVALID_HANDLE_VALUE) + { + log_error (_("can't create `%s': %s\n"), h->lockname, w32_strerror (-1)); + all_lockfiles = h->next; + jnlib_free (h->lockname); + jnlib_free (h); + return NULL; + } + return h; + +#endif /* HAVE_DOSISH_SYSTEM */ +} + + +/* Destroy the local handle H and release the lock. */ +void +destroy_dotlock ( DOTLOCK h ) +{ + DOTLOCK hprev, htmp; + + if ( !h ) + return; + + /* 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; + } + + /* Then destroy the lock. */ + if (!h->disable) + { +#ifdef HAVE_DOSISH_SYSTEM + if (h->locked) + { + UnlockFile (h->lockhd, 0, 0, 1, 0); + } + CloseHandle (h->lockhd); +#else /* !HAVE_DOSISH_SYSTEM */ + if (h->locked && h->lockname) + unlink (h->lockname); + if (h->tname) + unlink (h->tname); + jnlib_free (h->tname); +#endif /* HAVE_DOSISH_SYSTEM */ + jnlib_free (h->lockname); + } + jnlib_free(h); +} + + +#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; +} +#endif /*!HAVE_DOSISH_SYSTEM*/ + + + +/* Do a lock on H. A TIMEOUT of 0 returns immediately, -1 waits + forever (hopefully not), other values are reserved (should then be + timeouts in milliseconds). Returns: 0 on success */ +int +make_dotlock ( DOTLOCK h, long timeout ) +{ + int backoff = 0; +#ifndef HAVE_DOSISH_SYSTEM + int pid; + const char *maybe_dead=""; + int same_node; +#endif /*!HAVE_DOSISH_SYSTEM*/ + + if ( h->disable ) + return 0; /* Locks are completely disabled. Return success. */ + + if ( h->locked ) + { +#ifndef __riscos__ + log_debug ("Oops, `%s' is already locked\n", h->lockname); +#endif /* !__riscos__ */ + return 0; + } + + for (;;) + { +#ifndef HAVE_DOSISH_SYSTEM +# 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 ( !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, &same_node)) == -1 ) + { + if ( errno != ENOENT ) + { + log_info ("cannot read lockfile\n"); + return -1; + } + log_info( "lockfile disappeared\n"); + continue; + } + else if ( pid == getpid() && same_node ) + { + log_info( "Oops: lock already held by us\n"); + h->locked = 1; + return 0; /* okay */ + } + else if ( same_node && kill (pid, 0) && errno == ESRCH ) + { +# ifndef __riscos__ + log_info (_("removing stale lockfile (created by %d)\n"), pid ); + unlink (h->lockname); + continue; +# else /* __riscos__ */ + /* Under RISCOS we are *pretty* sure that the other task + is dead and therefore we remove the stale lock file. */ + maybe_dead = _(" - probably dead - removing lock"); + unlink(h->lockname); +# endif /* __riscos__ */ + } + + if ( timeout == -1 ) + { + /* Wait until lock has been released. */ + struct timeval tv; + + log_info (_("waiting for lock (held by %d%s) %s...\n"), + pid, maybe_dead, maybe_deadlock(h)? _("(deadlock?) "):""); + + + /* We 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; +#else /*HAVE_DOSISH_SYSTEM*/ + int w32err; + + if (LockFile (h->lockhd, 0, 0, 1, 0)) + { + h->locked = 1; + return 0; /* okay */ + } + w32err = GetLastError (); + if (w32err != ERROR_LOCK_VIOLATION) + { + log_error (_("lock `%s' not made: %s\n"), + h->lockname, w32_strerror (w32err)); + return -1; + } + + if ( timeout == -1 ) + { + /* Wait until lock has been released. */ + log_info (_("waiting for lock %s...\n"), h->lockname); + Sleep ((1 + backoff)*1000); + if ( backoff < 10 ) + backoff++ ; + } + else + return -1; +#endif /*HAVE_DOSISH_SYSTEM*/ + } + /*NOTREACHED*/ +} + + +/* Release a lock. Returns 0 on success. */ +int +release_dotlock( DOTLOCK h ) +{ +#ifndef HAVE_DOSISH_SYSTEM + int pid, same_node; +#endif + + /* 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; + } + +#ifdef HAVE_DOSISH_SYSTEM + if (!UnlockFile (h->lockhd, 0, 0, 1, 0)) + { + log_error ("release_dotlock: error removing lockfile `%s': %s\n", + h->lockname, w32_strerror (-1)); + return -1; + } +#else + + pid = read_lockfile (h, &same_node); + if ( pid == -1 ) + { + log_error( "release_dotlock: lockfile error\n"); + return -1; + } + if ( pid != getpid() || !same_node ) + { + 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'\n", + h->lockname); + return -1; + } + /* Fixme: As an extra check we could check whether the link count is + now really at 1. */ +#else /* __riscos__ */ + if ( renamefile (h->lockname, h->tname) ) + { + log_error ("release_dotlock: error renaming lockfile `%s' to `%s'\n", + h->lockname, h->tname); + return -1; + } +#endif /* __riscos__ */ + +#endif /* !HAVE_DOSISH_SYSTEM */ + h->locked = 0; + return 0; +} + + +/* Read the lock file and return the pid, returns -1 on error. True + will be stored in the integer at address SAME_NODE if the lock file + has been created on the same node. */ +#ifndef HAVE_DOSISH_SYSTEM +static int +read_lockfile (DOTLOCK h, int *same_node ) +{ + char buffer_space[10+1+70+1]; /* 70 is just an estimated value; node + name are usually shorter. */ + int fd; + int pid = -1; + char *buffer, *p; + size_t expected_len; + int res, nread; + + *same_node = 0; + expected_len = 10 + 1 + h->nodename_len + 1; + if ( expected_len >= sizeof buffer_space) + { + buffer = jnlib_malloc (expected_len); + if (!buffer) + return -1; + } + else + buffer = buffer_space; + + if ( (fd = open (h->lockname, O_RDONLY)) == -1 ) + { + int e = errno; + log_info ("error opening lockfile `%s': %s\n", + h->lockname, strerror(errno) ); + if (buffer != buffer_space) + jnlib_free (buffer); + errno = e; /* Need to return ERRNO here. */ + return -1; + } + + p = buffer; + nread = 0; + do + { + res = read (fd, p, expected_len - nread); + if (res == -1 && errno == EINTR) + continue; + if (res < 0) + { + log_info ("error reading lockfile `%s'", h->lockname ); + close (fd); + if (buffer != buffer_space) + jnlib_free (buffer); + errno = 0; /* Do not return an inappropriate ERRNO. */ + return -1; + } + p += res; + nread += res; + } + while (res && nread != expected_len); + close(fd); + + if (nread < 11) + { + log_info ("invalid size of lockfile `%s'", h->lockname ); + if (buffer != buffer_space) + jnlib_free (buffer); + errno = 0; /* Better don't return an inappropriate ERRNO. */ + return -1; + } + + if (buffer[10] != '\n' + || (buffer[10] = 0, pid = atoi (buffer)) == -1 +#ifndef __riscos__ + || !pid +#else /* __riscos__ */ + || (!pid && riscos_getpid()) +#endif /* __riscos__ */ + ) + { + log_error ("invalid pid %d in lockfile `%s'", pid, h->lockname ); + if (buffer != buffer_space) + jnlib_free (buffer); + errno = 0; + return -1; + } + + if (nread == expected_len + && !memcmp (h->tname+h->nodename_off, buffer+11, h->nodename_len) + && buffer[11+h->nodename_len] == '\n') + *same_node = 1; + + if (buffer != buffer_space) + jnlib_free (buffer); + return pid; +} +#endif /* !HAVE_DOSISH_SYSTEM */ + + +/* Remove all lockfiles. This is usually called by the atexit handler + installed by this module but may also be called by other + termination handlers. */ +void +dotlock_remove_lockfiles() +{ + DOTLOCK h, h2; + + h = all_lockfiles; + all_lockfiles = NULL; + + while ( h ) + { + h2 = h->next; + destroy_dotlock (h); + h = h2; + } +} + diff --git a/jnlib/dotlock.h b/jnlib/dotlock.h new file mode 100644 index 0000000..b2a0190 --- /dev/null +++ b/jnlib/dotlock.h @@ -0,0 +1,33 @@ +/* dotlock.h + * Copyright (C) 2000, 2001, 2006 Free Software Foundation, Inc. + * + * This file is part of JNLIB. + * + * JNLIB 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 3 of + * the License, or (at your option) any later version. + * + * JNLIB 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef LIBJNLIB_DOTLOCK_H +#define LIBJNLIB_DOTLOCK_H + +struct dotlock_handle; +typedef struct dotlock_handle *DOTLOCK; + +void disable_dotlock (void); +DOTLOCK create_dotlock(const char *file_to_lock); +void destroy_dotlock ( DOTLOCK h ); +int make_dotlock (DOTLOCK h, long timeout); +int release_dotlock (DOTLOCK h); +void dotlock_remove_lockfiles (void); + +#endif /*LIBJNLIB_DOTLOCK_H*/ diff --git a/jnlib/dynload.h b/jnlib/dynload.h new file mode 100644 index 0000000..5477465 --- /dev/null +++ b/jnlib/dynload.h @@ -0,0 +1,72 @@ +/* dynload.h - Wrapper functions for run-time dynamic loading + * Copyright (C) 2003 Free Software Foundation, Inc. + * + * This file is part of JNLIB. + * + * JNLIB 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 3 of + * the License, or (at your option) any later version. + * + * JNLIB 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef LIBJNLIB_DYNLOAD_H +#define LIBJNLIB_DYNLOAD_H + +#ifndef __MINGW32__ +# include <dlfcn.h> +#else +# include <windows.h> + +# define RTLD_LAZY 0 + +static inline void * +dlopen (const char * name, int flag) +{ + void * hd = LoadLibrary (name); + (void)flag; + return hd; +} + +static inline void * +dlsym (void *hd, const char *sym) +{ + if (hd && sym) + { + void * fnc = GetProcAddress (hd, sym); + if (!fnc) + return NULL; + return fnc; + } + return NULL; +} + + +static inline const char * +dlerror (void) +{ + static char buf[32]; + sprintf (buf, "ec=%lu", GetLastError ()); + return buf; +} + + +static inline int +dlclose (void * hd) +{ + if (hd) + { + CloseHandle (hd); + return 0; + } + return -1; +} +# endif /*__MINGW32__*/ +#endif /*LIBJNLIB_DYNLOAD_H*/ diff --git a/jnlib/libjnlib-config.h b/jnlib/libjnlib-config.h new file mode 100644 index 0000000..5c61442 --- /dev/null +++ b/jnlib/libjnlib-config.h @@ -0,0 +1,84 @@ +/* libjnlib-config.h - local configuration of the jnlib functions + * Copyright (C) 2000, 2001, 2006 Free Software Foundation, Inc. + * + * This file is part of JNLIB. + * + * JNLIB 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 3 of + * the License, or (at your option) any later version. + * + * JNLIB 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, see <http://www.gnu.org/licenses/>. + */ + +/**************** + * This header is to be included only by the files in this directory + * it should not be used by other modules. + */ + +#ifndef LIBJNLIB_CONFIG_H +#define LIBJNLIB_CONFIG_H + +#include <gcrypt.h> /* gcry_malloc & Cie. */ +#include "logging.h" + +/* We require support for utf-8 conversion. */ +#define JNLIB_NEED_UTF8CONV 1 + + + +#if !defined(JNLIB_NEED_UTF8CONV) && defined(HAVE_W32_SYSTEM) +#define JNLIB_NEED_UTF8CONV 1 +#endif + +/* Gettext stuff */ +#ifdef USE_SIMPLE_GETTEXT +# include "w32help.h" +# define _(a) gettext (a) +# define N_(a) (a) + +#else +#ifdef HAVE_LOCALE_H +# include <locale.h> +#endif + +#ifdef ENABLE_NLS +# include <libintl.h> +# define _(a) gettext (a) +# ifdef gettext_noop +# define N_(a) gettext_noop (a) +# else +# define N_(a) (a) +# endif +#else +# define _(a) (a) +# define N_(a) (a) +#endif +#endif /* !USE_SIMPLE_GETTEXT */ + +/* Malloc functions to be used by jnlib. */ +#define jnlib_malloc(a) gcry_malloc( (a) ) +#define jnlib_calloc(a,b) gcry_calloc( (a), (b) ) +#define jnlib_realloc(a,b) gcry_realloc( (a), (b) ) +#define jnlib_strdup(a) gcry_strdup( (a) ) +#define jnlib_xmalloc(a) gcry_xmalloc( (a) ) +#define jnlib_xcalloc(a,b) gcry_xcalloc( (a), (b) ) +#define jnlib_xrealloc(a,n) gcry_xrealloc( (a), (n) ) +#define jnlib_xstrdup(a) gcry_xstrdup( (a) ) +#define jnlib_free(a) gcry_free( (a) ) + +/* Logging functions to be used by jnlib. */ +#define jnlib_log_debug log_debug +#define jnlib_log_info log_info +#define jnlib_log_error log_error +#define jnlib_log_fatal log_fatal +#define jnlib_log_bug log_bug + + +#endif /*LIBJNUTIL_CONFIG_H*/ diff --git a/jnlib/logging.c b/jnlib/logging.c new file mode 100644 index 0000000..028697b --- /dev/null +++ b/jnlib/logging.c @@ -0,0 +1,651 @@ +/* logging.c - Useful logging functions + * Copyright (C) 1998, 1999, 2000, 2001, 2003, + * 2004, 2005, 2006, 2009 Free Software Foundation, Inc. + * + * This file is part of JNLIB. + * + * JNLIB 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 3 of + * the License, or (at your option) any later version. + * + * JNLIB 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, see <http://www.gnu.org/licenses/>. + */ + + +#include <config.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <stddef.h> +#include <errno.h> +#include <time.h> +#include <sys/types.h> +#include <sys/stat.h> +#ifndef HAVE_W32_SYSTEM +#include <sys/socket.h> +#include <sys/un.h> +#endif /*!HAVE_W32_SYSTEM*/ +#include <unistd.h> +#include <fcntl.h> +#include <assert.h> + + +#define JNLIB_NEED_LOG_LOGV 1 +#define JNLIB_NEED_AFLOCAL 1 +#include "libjnlib-config.h" +#include "logging.h" + +#if defined (HAVE_FOPENCOOKIE) || defined (HAVE_FUNOPEN) +#define USE_FUNWRITER 1 +#endif + +#ifdef HAVE_FOPENCOOKIE +typedef ssize_t my_funopen_hook_ret_t; +typedef size_t my_funopen_hook_size_t; +#else +typedef int my_funopen_hook_ret_t; +typedef int my_funopen_hook_size_t; +#endif + + +static FILE *logstream; +static int log_socket = -1; +static char prefix_buffer[80]; +static int with_time; +static int with_prefix; +static int with_pid; +static unsigned long (*get_tid_callback)(void); +static int running_detached; +static int force_prefixes; + +static int missing_lf; +static int errorcount; + + +int +log_get_errorcount (int clear) +{ + int n = errorcount; + if( clear ) + errorcount = 0; + return n; +} + +void +log_inc_errorcount (void) +{ + errorcount++; +} + + +/* The follwing 3 functions are used by funopen to write logs to a + socket. */ +#ifdef USE_FUNWRITER +struct fun_cookie_s { + int fd; + int quiet; + int want_socket; + int is_socket; + char name[1]; +}; + +/* Write NBYTES of BUFFER to file descriptor FD. */ +static int +writen (int fd, const void *buffer, size_t nbytes) +{ + const char *buf = buffer; + size_t nleft = nbytes; + int nwritten; + + while (nleft > 0) + { + nwritten = write (fd, buf, nleft); + if (nwritten < 0 && errno == EINTR) + continue; + if (nwritten < 0) + return -1; + nleft -= nwritten; + buf = buf + nwritten; + } + + return 0; +} + + +static my_funopen_hook_ret_t +fun_writer (void *cookie_arg, const char *buffer, my_funopen_hook_size_t size) +{ + struct fun_cookie_s *cookie = cookie_arg; + + /* Note that we always try to reconnect to the socket but print + error messages only the first time an error occured. If + RUNNING_DETACHED is set we don't fall back to stderr and even do + not print any error messages. This is needed because detached + processes often close stderr and by writing to file descriptor 2 + we might send the log message to a file not intended for logging + (e.g. a pipe or network connection). */ + if (cookie->want_socket && cookie->fd == -1) + { + /* Not yet open or meanwhile closed due to an error. */ + cookie->is_socket = 0; + cookie->fd = socket (PF_LOCAL, SOCK_STREAM, 0); + if (cookie->fd == -1) + { + if (!cookie->quiet && !running_detached + && isatty (fileno (stderr))) + fprintf (stderr, "failed to create socket for logging: %s\n", + strerror(errno)); + } + else + { + struct sockaddr_un addr; + size_t addrlen; + + memset (&addr, 0, sizeof addr); + addr.sun_family = PF_LOCAL; + strncpy (addr.sun_path, cookie->name, sizeof (addr.sun_path)-1); + addr.sun_path[sizeof (addr.sun_path)-1] = 0; + addrlen = SUN_LEN (&addr); + + if (connect (cookie->fd, (struct sockaddr *) &addr, addrlen) == -1) + { + if (!cookie->quiet && !running_detached + && isatty (fileno (stderr))) + fprintf (stderr, "can't connect to `%s': %s\n", + cookie->name, strerror(errno)); + close (cookie->fd); + cookie->fd = -1; + } + } + + if (cookie->fd == -1) + { + if (!running_detached) + { + /* Due to all the problems with apps not running + detached but being called with stderr closed or + used for a different purposes, it does not make + sense to switch to stderr. We therefore disable it. */ + if (!cookie->quiet) + { + /* fputs ("switching logging to stderr\n", stderr);*/ + cookie->quiet = 1; + } + cookie->fd = -1; /*fileno (stderr);*/ + } + } + else /* Connection has been established. */ + { + cookie->quiet = 0; + cookie->is_socket = 1; + } + } + + log_socket = cookie->fd; + if (cookie->fd != -1 && !writen (cookie->fd, buffer, size)) + return (my_funopen_hook_ret_t)size; /* Okay. */ + + if (!running_detached && cookie->fd != -1 + && isatty (fileno (stderr))) + { + if (*cookie->name) + fprintf (stderr, "error writing to `%s': %s\n", + cookie->name, strerror(errno)); + else + fprintf (stderr, "error writing to file descriptor %d: %s\n", + cookie->fd, strerror(errno)); + } + if (cookie->is_socket && cookie->fd != -1) + { + close (cookie->fd); + cookie->fd = -1; + log_socket = -1; + } + + return (my_funopen_hook_ret_t)size; +} + +static int +fun_closer (void *cookie_arg) +{ + struct fun_cookie_s *cookie = cookie_arg; + + if (cookie->fd != -1 && cookie->fd != 2) + close (cookie->fd); + jnlib_free (cookie); + log_socket = -1; + return 0; +} +#endif /*USE_FUNWRITER*/ + + + +/* Common function to either set the logging to a file or a file + descriptor. */ +static void +set_file_fd (const char *name, int fd) +{ + FILE *fp; + int want_socket; +#ifdef USE_FUNWRITER + struct fun_cookie_s *cookie; +#endif + + /* Close an open log stream. */ + if (logstream) + { + if (logstream != stderr && logstream != stdout) + fclose (logstream); + logstream = NULL; + } + + /* Figure out what kind of logging we want. */ + if (name && !strcmp (name, "-")) + { + name = NULL; + fd = fileno (stderr); + } + + if (name) + { + want_socket = (!strncmp (name, "socket://", 9) && name[9]); + if (want_socket) + name += 9; + } + else + { + want_socket = 0; + } + + /* Setup a new stream. */ +#ifdef USE_FUNWRITER + /* The xmalloc below is justified because we can expect that this + function is called only during initialization and there is no + easy way out of this error condition. */ + cookie = jnlib_xmalloc (sizeof *cookie + (name? strlen (name):0)); + strcpy (cookie->name, name? name:""); + cookie->quiet = 0; + cookie->is_socket = 0; + cookie->want_socket = want_socket; + if (!name) + cookie->fd = fd; + else if (want_socket) + cookie->fd = -1; + else + { + do + cookie->fd = open (name, O_WRONLY|O_APPEND|O_CREAT, + (S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR|S_IWGRP|S_IWOTH)); + while (cookie->fd == -1 && errno == EINTR); + } + log_socket = cookie->fd; + +#ifdef HAVE_FOPENCOOKIE + { + cookie_io_functions_t io = { NULL }; + io.write = fun_writer; + io.close = fun_closer; + + fp = fopencookie (cookie, "w", io); + } +#else /*!HAVE_FOPENCOOKIE*/ + fp = funopen (cookie, NULL, fun_writer, NULL, fun_closer); +#endif /*!HAVE_FOPENCOOKIE*/ + +#else /*!USE_FUNWRITER*/ + + /* The system does not feature custom streams. Thus fallback to + plain stdio. */ + if (want_socket) + { + fprintf (stderr, "system does not support logging to a socket - " + "using stderr\n"); + fp = stderr; + } + else if (name) + fp = fopen (name, "a"); + else if (fd == 1) + fp = stdout; + else if (fd == 2) + fp = stderr; + else + fp = fdopen (fd, "a"); + + log_socket = -1; + +#endif /*!USE_FUNWRITER*/ + + /* On error default to stderr. */ + if (!fp) + { + if (name) + fprintf (stderr, "failed to open log file `%s': %s\n", + name, strerror(errno)); + else + fprintf (stderr, "failed to fdopen file descriptor %d: %s\n", + fd, strerror(errno)); + /* We need to make sure that there is a log stream. We use stderr. */ + fp = stderr; + } + else + setvbuf (fp, NULL, _IOLBF, 0); + + logstream = fp; + + /* We always need to print the prefix and the pid for socket mode, + so that the server reading the socket can do something + meaningful. */ + force_prefixes = want_socket; + + missing_lf = 0; +} + + +/* Set the file to write log to. The special names NULL and "-" may + be used to select stderr and names formatted like + "socket:///home/foo/mylogs" may be used to write the logging to the + socket "/home/foo/mylogs". If the connection to the socket fails + or a write error is detected, the function writes to stderr and + tries the next time again to connect the socket. + */ +void +log_set_file (const char *name) +{ + set_file_fd (name? name: "-", -1); +} + +void +log_set_fd (int fd) +{ + set_file_fd (NULL, fd); +} + + +void +log_set_get_tid_callback (unsigned long (*cb)(void)) +{ + get_tid_callback = cb; +} + + +void +log_set_prefix (const char *text, unsigned int flags) +{ + if (text) + { + strncpy (prefix_buffer, text, sizeof (prefix_buffer)-1); + prefix_buffer[sizeof (prefix_buffer)-1] = 0; + } + + with_prefix = (flags & JNLIB_LOG_WITH_PREFIX); + with_time = (flags & JNLIB_LOG_WITH_TIME); + with_pid = (flags & JNLIB_LOG_WITH_PID); + running_detached = (flags & JNLIB_LOG_RUN_DETACHED); +} + + +const char * +log_get_prefix (unsigned int *flags) +{ + if (flags) + { + *flags = 0; + if (with_prefix) + *flags |= JNLIB_LOG_WITH_PREFIX; + if (with_time) + *flags |= JNLIB_LOG_WITH_TIME; + if (with_pid) + *flags |= JNLIB_LOG_WITH_PID; + if (running_detached) + *flags |= JNLIB_LOG_RUN_DETACHED; + } + return prefix_buffer; +} + +/* This function returns true if the file descriptor FD is in use for + logging. This is preferable over a test using log_get_fd in that + it allows the logging code to use more then one file descriptor. */ +int +log_test_fd (int fd) +{ + if (logstream) + { + int tmp = fileno (logstream); + if ( tmp != -1 && tmp == fd) + return 1; + } + if (log_socket != -1 && log_socket == fd) + return 1; + return 0; +} + +int +log_get_fd () +{ + return fileno(logstream?logstream:stderr); +} + +FILE * +log_get_stream () +{ + /* FIXME: We should not return stderr here but initialize the log + stream properly. This might break more things than using stderr, + though */ + return logstream?logstream:stderr; +} + +static void +do_logv (int level, const char *fmt, va_list arg_ptr) +{ + if (!logstream) + { + log_set_file (NULL); /* Make sure a log stream has been set. */ + assert (logstream); + } + + if (missing_lf && level != JNLIB_LOG_CONT) + putc('\n', logstream ); + missing_lf = 0; + + if (level != JNLIB_LOG_CONT) + { /* Note this does not work for multiple line logging as we would + * need to print to a buffer first */ + if (with_time && !force_prefixes) + { + struct tm *tp; + time_t atime = time (NULL); + + tp = localtime (&atime); + fprintf (logstream, "%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 ); + } + if (with_prefix || force_prefixes) + fputs (prefix_buffer, logstream); + if (with_pid || force_prefixes) + { + if (get_tid_callback) + fprintf (logstream, "[%u.%lx]", + (unsigned int)getpid (), get_tid_callback ()); + else + fprintf (logstream, "[%u]", (unsigned int)getpid ()); + } + if (!with_time || force_prefixes) + putc (':', logstream); + /* A leading backspace suppresses the extra space so that we can + correctly output, programname, filename and linenumber. */ + if (fmt && *fmt == '\b') + fmt++; + else + putc (' ', logstream); + } + + switch (level) + { + case JNLIB_LOG_BEGIN: break; + case JNLIB_LOG_CONT: break; + case JNLIB_LOG_INFO: break; + case JNLIB_LOG_WARN: break; + case JNLIB_LOG_ERROR: break; + case JNLIB_LOG_FATAL: fputs("Fatal: ",logstream ); break; + case JNLIB_LOG_BUG: fputs("Ohhhh jeeee: ", logstream); break; + case JNLIB_LOG_DEBUG: fputs("DBG: ", logstream ); break; + default: fprintf(logstream,"[Unknown log level %d]: ", level ); break; + } + + + if (fmt) + { + vfprintf(logstream,fmt,arg_ptr) ; + if (*fmt && fmt[strlen(fmt)-1] != '\n') + missing_lf = 1; +#ifdef HAVE_W32_SYSTEM + else + fflush (logstream); +#endif + } + + if (level == JNLIB_LOG_FATAL) + { + if (missing_lf) + putc('\n', logstream ); + exit(2); + } + if (level == JNLIB_LOG_BUG) + { + if (missing_lf) + putc('\n', logstream ); + abort(); + } +} + +static void +do_log( int level, const char *fmt, ... ) +{ + va_list arg_ptr ; + + va_start( arg_ptr, fmt ) ; + do_logv( level, fmt, arg_ptr ); + va_end(arg_ptr); +} + + +void +log_logv (int level, const char *fmt, va_list arg_ptr) +{ + do_logv (level, fmt, arg_ptr); +} + +void +log_info( const char *fmt, ... ) +{ + va_list arg_ptr ; + + va_start( arg_ptr, fmt ) ; + do_logv( JNLIB_LOG_INFO, fmt, arg_ptr ); + va_end(arg_ptr); +} + +void +log_error( const char *fmt, ... ) +{ + va_list arg_ptr ; + + va_start( arg_ptr, fmt ) ; + do_logv( JNLIB_LOG_ERROR, fmt, arg_ptr ); + va_end(arg_ptr); + /* protect against counter overflow */ + if( errorcount < 30000 ) + errorcount++; +} + + +void +log_fatal( const char *fmt, ... ) +{ + va_list arg_ptr ; + + va_start( arg_ptr, fmt ) ; + do_logv( JNLIB_LOG_FATAL, fmt, arg_ptr ); + va_end(arg_ptr); + abort(); /* never called, but it makes the compiler happy */ +} + +void +log_bug( const char *fmt, ... ) +{ + va_list arg_ptr ; + + va_start( arg_ptr, fmt ) ; + do_logv( JNLIB_LOG_BUG, fmt, arg_ptr ); + va_end(arg_ptr); + abort(); /* never called, but it makes the compiler happy */ +} + +void +log_debug( const char *fmt, ... ) +{ + va_list arg_ptr ; + + va_start( arg_ptr, fmt ) ; + do_logv( JNLIB_LOG_DEBUG, fmt, arg_ptr ); + va_end(arg_ptr); +} + + +void +log_printf (const char *fmt, ...) +{ + va_list arg_ptr; + + va_start (arg_ptr, fmt); + do_logv (fmt ? JNLIB_LOG_CONT : JNLIB_LOG_BEGIN, fmt, arg_ptr); + va_end (arg_ptr); +} + +/* Print a hexdump of BUFFER. With TEXT of NULL print just the raw + dump, with TEXT just an empty string, print a trailing linefeed, + otherwise print an entire debug line. */ +void +log_printhex (const char *text, const void *buffer, size_t length) +{ + if (text && *text) + log_debug ("%s ", text); + if (length) + { + const unsigned char *p = buffer; + log_printf ("%02X", *p); + for (length--, p++; length--; p++) + log_printf (" %02X", *p); + } + if (text) + log_printf ("\n"); +} + + +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5 ) +void +bug_at( const char *file, int line, const char *func ) +{ + do_log( JNLIB_LOG_BUG, + ("... this is a bug (%s:%d:%s)\n"), file, line, func ); + abort(); /* never called, but it makes the compiler happy */ +} +#else +void +bug_at( const char *file, int line ) +{ + do_log( JNLIB_LOG_BUG, + _("you found a bug ... (%s:%d)\n"), file, line); + abort(); /* never called, but it makes the compiler happy */ +} +#endif + diff --git a/jnlib/logging.h b/jnlib/logging.h new file mode 100644 index 0000000..0b96108 --- /dev/null +++ b/jnlib/logging.h @@ -0,0 +1,88 @@ +/* logging.h + * Copyright (C) 1999, 2000, 2001, 2004, 2006 Free Software Foundation, Inc. + * + * This file is part of JNLIB. + * + * JNLIB 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 3 of + * the License, or (at your option) any later version. + * + * JNLIB 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef LIBJNLIB_LOGGING_H +#define LIBJNLIB_LOGGING_H + +#include <stdio.h> +#include "mischelp.h" + +/* Flag values for log_set_prefix. */ +#define JNLIB_LOG_WITH_PREFIX 1 +#define JNLIB_LOG_WITH_TIME 2 +#define JNLIB_LOG_WITH_PID 4 +#define JNLIB_LOG_RUN_DETACHED 256 + +int log_get_errorcount (int clear); +void log_inc_errorcount (void); +void log_set_file( const char *name ); +void log_set_fd (int fd); +void log_set_get_tid_callback (unsigned long (*cb)(void)); +void log_set_prefix (const char *text, unsigned int flags); +const char *log_get_prefix (unsigned int *flags); +int log_test_fd (int fd); +int log_get_fd(void); +FILE *log_get_stream (void); + +#ifdef JNLIB_GCC_M_FUNCTION + void bug_at( const char *file, int line, const char *func ) JNLIB_GCC_A_NR; +# define BUG() bug_at( __FILE__ , __LINE__, __FUNCTION__ ) +#else + void bug_at( const char *file, int line ); +# define BUG() bug_at( __FILE__ , __LINE__ ) +#endif + +/* To avoid mandatory inclusion of stdarg and other stuff, do it only + if explicitly requested to do so. */ +#ifdef JNLIB_NEED_LOG_LOGV +#include <stdarg.h> +enum jnlib_log_levels { + JNLIB_LOG_BEGIN, + JNLIB_LOG_CONT, + JNLIB_LOG_INFO, + JNLIB_LOG_WARN, + JNLIB_LOG_ERROR, + JNLIB_LOG_FATAL, + JNLIB_LOG_BUG, + JNLIB_LOG_DEBUG +}; +void log_logv (int level, const char *fmt, va_list arg_ptr); +#endif /*JNLIB_NEED_LOG_LOGV*/ + + +void log_bug( const char *fmt, ... ) JNLIB_GCC_A_NR_PRINTF(1,2); +void log_fatal( const char *fmt, ... ) JNLIB_GCC_A_NR_PRINTF(1,2); +void log_error( const char *fmt, ... ) JNLIB_GCC_A_PRINTF(1,2); +void log_info( const char *fmt, ... ) JNLIB_GCC_A_PRINTF(1,2); +void log_debug( const char *fmt, ... ) JNLIB_GCC_A_PRINTF(1,2); +void log_printf( const char *fmt, ... ) JNLIB_GCC_A_PRINTF(1,2); + +/* Print a hexdump of BUFFER. With TEXT passes as NULL print just the + raw dump, with TEXT being an empty string, print a trailing + linefeed, otherwise print an entire debug line with TEXT followed + by the hexdump and a final LF. */ +void log_printhex (const char *text, const void *buffer, size_t length); + + +#endif /*LIBJNLIB_LOGGING_H*/ + + + + + diff --git a/jnlib/mischelp.c b/jnlib/mischelp.c new file mode 100644 index 0000000..f7df5c1 --- /dev/null +++ b/jnlib/mischelp.c @@ -0,0 +1,133 @@ +/* mischelp.c - Miscellaneous helper functions + * Copyright (C) 1998, 2000, 2001, 2006, 2007 Free Software Foundation, Inc. + * + * This file is part of JNLIB. + * + * JNLIB 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 3 of + * the License, or (at your option) any later version. + * + * JNLIB 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, see <http://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#ifdef HAVE_W32_SYSTEM +# define WIN32_LEAN_AND_MEAN +# include <windows.h> +#else /*!HAVE_W32_SYSTEM*/ +# include <sys/types.h> +# include <sys/stat.h> +# include <unistd.h> +#endif /*!HAVE_W32_SYSTEM*/ + +#include "libjnlib-config.h" +#include "stringhelp.h" +#include "mischelp.h" + + +/* Check whether the files NAME1 and NAME2 are identical. This is for + example achieved by comparing the inode numbers of the files. */ +int +same_file_p (const char *name1, const char *name2) +{ + int yes; + + /* First try a shortcut. */ + if (!compare_filenames (name1, name2)) + yes = 1; + else + { +#ifdef HAVE_W32_SYSTEM + HANDLE file1, file2; + BY_HANDLE_FILE_INFORMATION info1, info2; + + file1 = CreateFile (name1, 0, 0, NULL, OPEN_EXISTING, 0, NULL); + if (file1 == INVALID_HANDLE_VALUE) + yes = 0; /* If we can't open the file, it is not the same. */ + else + { + file2 = CreateFile (name2, 0, 0, NULL, OPEN_EXISTING, 0, NULL); + if (file1 == INVALID_HANDLE_VALUE) + yes = 0; /* If we can't open the file, it is not the same. */ + else + { + yes = (GetFileInformationByHandle (file1, &info1) + && GetFileInformationByHandle (file2, &info2) + && info1.dwVolumeSerialNumber==info2.dwVolumeSerialNumber + && info1.nFileIndexHigh == info2.nFileIndexHigh + && info1.nFileIndexLow == info2.nFileIndexLow); + CloseHandle (file2); + } + CloseHandle (file1); + } +#else /*!HAVE_W32_SYSTEM*/ + struct stat info1, info2; + + yes = (!stat (name1, &info1) && !stat (name2, &info2) + && info1.st_dev == info2.st_dev && info1.st_ino == info2.st_ino); +#endif /*!HAVE_W32_SYSTEM*/ + } + return yes; +} + + +/* + 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. + + Note, that this replacement implementaion is not thread-safe! + + Some BSDs don't handle the putenv("foo") case properly, so we use + unsetenv if the platform has it to remove environment variables. +*/ +#ifndef HAVE_TIMEGM +time_t +timegm (struct tm *tm) +{ + time_t answer; + char *zone; + + zone=getenv("TZ"); + putenv("TZ=UTC"); + tzset(); + answer=mktime(tm); + if(zone) + { + static char *old_zone; + + if (!old_zone) + { + old_zone = malloc(3+strlen(zone)+1); + if (old_zone) + { + strcpy(old_zone,"TZ="); + strcat(old_zone,zone); + } + } + if (old_zone) + putenv (old_zone); + } + else +#ifdef HAVE_UNSETENV + unsetenv("TZ"); +#else + putenv("TZ"); +#endif + + tzset(); + return answer; +} +#endif /*!HAVE_TIMEGM*/ + diff --git a/jnlib/mischelp.h b/jnlib/mischelp.h new file mode 100644 index 0000000..e478354 --- /dev/null +++ b/jnlib/mischelp.h @@ -0,0 +1,98 @@ +/* mischelp.h - Miscellaneous helper macros and functions + * Copyright (C) 1999, 2000, 2001, 2002, 2003, + * 2006, 2007, 2009 Free Software Foundation, Inc. + * + * This file is part of JNLIB. + * + * JNLIB 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 3 of + * the License, or (at your option) any later version. + * + * JNLIB 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef LIBJNLIB_MISCHELP_H +#define LIBJNLIB_MISCHHELP_H + + +/* Check whether the files NAME1 and NAME2 are identical. This is for + example achieved by comparing the inode numbers of the files. */ +int same_file_p (const char *name1, const char *name2); + + +#ifndef HAVE_TIMEGM +#include <time.h> +time_t timegm (struct tm *tm); +#endif /*!HAVE_TIMEGM*/ + + +#define DIM(v) (sizeof(v)/sizeof((v)[0])) +#define DIMof(type,member) DIM(((type *)0)->member) + + +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5 ) +# define JNLIB_GCC_M_FUNCTION 1 +# define JNLIB_GCC_A_NR __attribute__ ((noreturn)) +# define JNLIB_GCC_A_PRINTF( f, a ) __attribute__ ((format (printf,f,a))) +# define JNLIB_GCC_A_NR_PRINTF( f, a ) \ + __attribute__ ((noreturn, format (printf,f,a))) +#else +# define JNLIB_GCC_A_NR +# define JNLIB_GCC_A_PRINTF( f, a ) +# define JNLIB_GCC_A_NR_PRINTF( f, a ) +#endif + + +/* To avoid that a compiler optimizes certain memset calls away, these + macros may be used instead. */ +#define wipememory2(_ptr,_set,_len) do { \ + volatile char *_vptr=(volatile char *)(_ptr); \ + size_t _vlen=(_len); \ + while(_vlen) { *_vptr=(_set); _vptr++; _vlen--; } \ + } while(0) +#define wipememory(_ptr,_len) wipememory2(_ptr,0,_len) + + +/* Include hacks which are mainly required for Slowaris. */ +#ifdef JNLIB_NEED_AFLOCAL +#ifndef HAVE_W32_SYSTEM +# include <sys/socket.h> +# include <sys/un.h> +#else +# include <windows.h> +#endif + +#ifndef PF_LOCAL +# ifdef PF_UNIX +# define PF_LOCAL PF_UNIX +# else +# define PF_LOCAL AF_UNIX +# endif +#endif /*PF_LOCAL*/ +#ifndef AF_LOCAL +# define AF_LOCAL AF_UNIX +#endif /*AF_UNIX*/ + +/* We used to avoid this macro in GnuPG and inlined the AF_LOCAL name + length computation directly with the little twist of adding 1 extra + byte. It seems that this was needed once on an old HP/UX box and + there are also rumours that 4.3 Reno and DEC systems need it. This + one-off buglet did not harm any current system until it came to Mac + OS X where the kernel (as of May 2009) exhibited a strange bug: The + systems basically froze in the connect call if the passed name + contained an invalid directory part. Ignore the old Unices. */ +#ifndef SUN_LEN +# define SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path) \ + + strlen ((ptr)->sun_path)) +#endif /*SUN_LEN*/ +#endif /*JNLIB_NEED_AFLOCAL*/ + + +#endif /*LIBJNLIB_MISCHELP_H*/ diff --git a/jnlib/stringhelp.c b/jnlib/stringhelp.c new file mode 100644 index 0000000..3173ebc --- /dev/null +++ b/jnlib/stringhelp.c @@ -0,0 +1,1141 @@ +/* stringhelp.c - standard string helper functions + * Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2005, + * 2006, 2007, 2008, 2009 Free Software Foundation, Inc. + * + * This file is part of JNLIB. + * + * JNLIB 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 3 of + * the License, or (at your option) any later version. + * + * JNLIB 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, see <http://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <ctype.h> +#include <errno.h> +#ifdef HAVE_PWD_H +# include <pwd.h> +#endif +#include <unistd.h> +#include <sys/types.h> +#ifdef HAVE_W32_SYSTEM +# include <windows.h> +#endif + +#include "libjnlib-config.h" +#include "utf8conv.h" +#include "stringhelp.h" + + +#define tohex_lower(n) ((n) < 10 ? ((n) + '0') : (((n) - 10) + 'a')) + +/* Sometimes we want to avoid mixing slashes and backslashes on W32 + and prefer backslashes. There is usual no problem with mixing + them, however a very few W32 API calls can't grok plain slashes. + Printing filenames with mixed slashes also looks a bit strange. + This function has no effext on POSIX. */ +static inline char * +change_slashes (char *name) +{ +#ifdef HAVE_DRIVE_LETTERS + char *p; + + if (strchr (name, '\\')) + { + for (p=name; *p; p++) + if (*p == '/') + *p = '\\'; + } +#endif /*HAVE_DRIVE_LETTERS*/ + return name; +} + + +/* + * Look for the substring SUB in buffer and return a pointer to that + * substring in BUFFER or NULL if not found. + * Comparison is case-insensitive. + */ +const char * +memistr (const void *buffer, size_t buflen, const char *sub) +{ + const unsigned char *buf = buffer; + const unsigned char *t = (const unsigned char *)buffer; + const unsigned char *s = (const unsigned char *)sub; + size_t n = buflen; + + for ( ; 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 (const char*)buf; + t = buf; + s = (const unsigned char *)sub ; + n = buflen; + } + } + return NULL; +} + +const char * +ascii_memistr ( const void *buffer, size_t buflen, const char *sub ) +{ + const unsigned char *buf = buffer; + const unsigned char *t = (const unsigned char *)buf; + const unsigned char *s = (const unsigned char *)sub; + size_t n = buflen; + + for ( ; 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 (const char*)buf; + t = (const unsigned char *)buf; + s = (const unsigned char *)sub ; + n = buflen; + } + } + return NULL; +} + +/* This function is similar to strncpy(). However it won't copy more + than N - 1 characters and makes sure that a '\0' is appended. With + N given as 0, nothing will happen. With DEST given as NULL, memory + will be allocated using jnlib_xmalloc (i.e. if it runs out of core + the function terminates). Returns DES or a pointer to the + allocated memory. + */ +char * +mem2str( char *dest , const void *src , size_t n ) +{ + char *d; + const char *s; + + if( n ) { + if( !dest ) + dest = jnlib_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 ; +} + +/**************** + * remove trailing white spaces + */ +char * +trim_trailing_spaces( char *string ) +{ + char *p, *mark; + + for( mark = NULL, p = string; *p; p++ ) { + if( isspace( *(byte*)p ) ) { + if( !mark ) + mark = p; + } + else + mark = NULL; + } + if( mark ) + *mark = '\0' ; + + return string ; +} + + +unsigned +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" ); +} + +size_t +length_sans_trailing_chars (const unsigned char *line, size_t len, + const char *trimchars ) +{ + const unsigned char *p, *mark; + size_t 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; +} + +/* + * Return the length of line ignoring trailing white-space. + */ +size_t +length_sans_trailing_ws (const unsigned char *line, size_t len) +{ + return length_sans_trailing_chars (line, len, " \t\r\n"); +} + + + +/* + * Extract from a given path the filename component. This function + * terminates the process on memory shortage. + */ +char * +make_basename(const char *filepath, const char *inputpath) +{ +#ifdef __riscos__ + return riscos_make_basename(filepath, inputpath); +#else + char *p; + + (void)inputpath; /* Only required for riscos. */ + + if ( !(p=strrchr(filepath, '/')) ) +#ifdef HAVE_DRIVE_LETTERS + if ( !(p=strrchr(filepath, '\\')) ) + if ( !(p=strrchr(filepath, ':')) ) +#endif + { + return jnlib_xstrdup(filepath); + } + + return jnlib_xstrdup(p+1); +#endif +} + + + +/* + * Extract from a given filename the path prepended to it. If there + * isn't a path prepended to the filename, a dot is returned ('.'). + * This function terminates the process on memory shortage. + */ +char * +make_dirname(const char *filepath) +{ + char *dirname; + int dirname_length; + char *p; + + if ( !(p=strrchr(filepath, '/')) ) +#ifdef HAVE_DRIVE_LETTERS + if ( !(p=strrchr(filepath, '\\')) ) + if ( !(p=strrchr(filepath, ':')) ) +#endif + { + return jnlib_xstrdup("."); + } + + dirname_length = p-filepath; + dirname = jnlib_xmalloc(dirname_length+1); + strncpy(dirname, filepath, dirname_length); + dirname[dirname_length] = 0; + + return dirname; +} + + + +static char * +get_pwdir (int xmode, const char *name) +{ + char *result = NULL; +#ifdef HAVE_PWD_H + struct passwd *pwd = NULL; + + if (name) + { +#ifdef HAVE_GETPWNAM + /* Fixme: We should use getpwnam_r if available. */ + pwd = getpwnam (name); +#endif + } + else + { +#ifdef HAVE_GETPWUID + /* Fixme: We should use getpwuid_r if available. */ + pwd = getpwuid (getuid()); +#endif + } + if (pwd) + { + if (xmode) + result = jnlib_xstrdup (pwd->pw_dir); + else + result = jnlib_strdup (pwd->pw_dir); + } +#endif /*HAVE_PWD_H*/ + return result; +} + +static char * +do_make_filename (int xmode, const char *first_part, va_list arg_ptr) +{ + const char *argv[32]; + int argc; + size_t n; + int skip = 1; + char *home_buffer = NULL; + char *name, *home, *p; + + n = strlen (first_part) + 1; + argc = 0; + while ( (argv[argc] = va_arg (arg_ptr, const char *)) ) + { + n += strlen (argv[argc]) + 1; + if (argc >= DIM (argv)-1) + { + if (xmode) + BUG (); + errno = EINVAL; + return NULL; + } + argc++; + } + n++; + + home = NULL; + if (*first_part == '~') + { + if (first_part[1] == '/' || !first_part[1]) + { + /* This is the "~/" or "~" case. */ + home = getenv("HOME"); + if (!home) + home = home_buffer = get_pwdir (xmode, NULL); + if (home && *home) + n += strlen (home); + } + else + { + /* This is the "~username/" or "~username" case. */ + char *user; + + if (xmode) + user = jnlib_xstrdup (first_part+1); + else + { + user = jnlib_strdup (first_part+1); + if (!user) + return NULL; + } + p = strchr (user, '/'); + if (p) + *p = 0; + skip = 1 + strlen (user); + + home = home_buffer = get_pwdir (xmode, user); + jnlib_free (user); + if (home) + n += strlen (home); + else + skip = 1; + } + } + + if (xmode) + name = jnlib_xmalloc (n); + else + { + name = jnlib_malloc (n); + if (!name) + { + jnlib_free (home_buffer); + return NULL; + } + } + + if (home) + p = stpcpy (stpcpy (name, home), first_part + skip); + else + p = stpcpy (name, first_part); + + jnlib_free (home_buffer); + + for (argc=0; argv[argc]; argc++) + p = stpcpy (stpcpy (p, "/"), argv[argc]); + + return change_slashes (name); +} + +/* Construct a filename from the NULL terminated list of parts. Tilde + expansion is done for the first argument. This function terminates + the process on memory shortage. */ +char * +make_filename (const char *first_part, ... ) +{ + va_list arg_ptr; + char *result; + + va_start (arg_ptr, first_part); + result = do_make_filename (1, first_part, arg_ptr); + va_end (arg_ptr); + return result; +} + +/* Construct a filename from the NULL terminated list of parts. Tilde + expansion is done for the first argument. This function may return + NULL on error. */ +char * +make_filename_try (const char *first_part, ... ) +{ + va_list arg_ptr; + char *result; + + va_start (arg_ptr, first_part); + result = do_make_filename (0, first_part, arg_ptr); + va_end (arg_ptr); + return result; +} + + + +/* Compare whether the filenames are identical. This is a + special version of strcmp() taking the semantics of filenames in + account. Note that this function works only on the supplied names + without considereing any context like the current directory. See + also same_file_p(). */ +int +compare_filenames (const char *a, const char *b) +{ +#ifdef HAVE_DRIVE_LETTERS + for ( ; *a && *b; a++, b++ ) + { + if (*a != *b + && (toupper (*(const unsigned char*)a) + != toupper (*(const unsigned char*)b) ) + && !((*a == '/' && *b == '\\') || (*a == '\\' && *b == '/'))) + break; + } + if ((*a == '/' && *b == '\\') || (*a == '\\' && *b == '/')) + return 0; + else + return (toupper (*(const unsigned char*)a) + - toupper (*(const unsigned char*)b)); +#else + return strcmp(a,b); +#endif +} + + +/* Convert 2 hex characters at S to a byte value. Return this value + or -1 if there is an error. */ +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; +} + + +/* Print a BUFFER to stream FP while replacing all control characters + and the characters DELIM and DELIM2 with standard C escape + sequences. Returns the number of characters printed. */ +size_t +print_sanitized_buffer2 (FILE *fp, const void *buffer, size_t length, + int delim, int delim2) +{ + const unsigned char *p = buffer; + size_t count = 0; + + for (; length; length--, p++, count++) + { + if (*p < 0x20 + || *p == 0x7f + || *p == delim + || *p == delim2 + || ((delim || delim2) && *p=='\\')) + { + putc ('\\', fp); + count++; + if (*p == '\n') + { + putc ('n', fp); + count++; + } + else if (*p == '\r') + { + putc ('r', fp); + count++; + } + else if (*p == '\f') + { + putc ('f', fp); + count++; + } + else if (*p == '\v') + { + putc ('v', fp); + count++; + } + else if (*p == '\b') + { + putc ('b', fp); + count++; + } + else if (!*p) + { + putc('0', fp); + count++; + } + else + { + fprintf (fp, "x%02x", *p); + count += 3; + } + } + else + { + putc (*p, fp); + count++; + } + } + + return count; +} + +/* Same as print_sanitized_buffer2 but with just one delimiter. */ +size_t +print_sanitized_buffer (FILE *fp, const void *buffer, size_t length, + int delim) +{ + return print_sanitized_buffer2 (fp, buffer, length, delim, 0); +} + + +size_t +print_sanitized_utf8_buffer (FILE *fp, const void *buffer, + size_t length, int delim) +{ + const char *p = buffer; + size_t i; + + /* We can handle plain ascii simpler, so check for it first. */ + for (i=0; i < length; i++ ) + { + if ( (p[i] & 0x80) ) + break; + } + if (i < length) + { + char *buf = utf8_to_native (p, length, delim); + /*(utf8 conversion already does the control character quoting)*/ + i = strlen (buf); + fputs (buf, fp); + jnlib_free (buf); + return i; + } + else + return print_sanitized_buffer (fp, p, length, delim); +} + + +size_t +print_sanitized_string2 (FILE *fp, const char *string, int delim, int delim2) +{ + return string? print_sanitized_buffer2 (fp, string, strlen (string), + delim, delim2):0; +} + +size_t +print_sanitized_string (FILE *fp, const char *string, int delim) +{ + return string? print_sanitized_buffer (fp, string, strlen (string), delim):0; +} + +size_t +print_sanitized_utf8_string (FILE *fp, const char *string, int delim) +{ + return string? print_sanitized_utf8_buffer (fp, + string, strlen (string), + delim) : 0; +} + +/* Create a string from the buffer P_ARG of length N which is suitable + for printing. Caller must release the created string using xfree. + This function terminates the process on memory shortage. */ +char * +sanitize_buffer (const void *p_arg, size_t n, int delim) +{ + const unsigned char *p = p_arg; + size_t save_n, buflen; + const unsigned char *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 == delim || (delim && *p=='\\')) + { + if ( *p=='\n' || *p=='\r' || *p=='\f' + || *p=='\v' || *p=='\b' || !*p ) + buflen += 2; + else + buflen += 5; + } + else + buflen++; + } + p = save_p; + n = save_n; + /* And now make the string */ + d = buffer = jnlib_xmalloc( buflen ); + for ( ; n; n--, p++ ) + { + if (*p < 0x20 || *p == 0x7f || *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; +} + + +/* Given a string containing an UTF-8 encoded text, return the number + of characters in this string. It differs from strlen in that it + only counts complete UTF-8 characters. Note, that this function + does not take combined characters into account. */ +size_t +utf8_charcount (const char *s) +{ + size_t n; + + for (n=0; *s; s++) + if ( (*s&0xc0) != 0x80 ) /* Exclude continuation bytes: 10xxxxxx */ + n++; + + return n; +} + + +/**************************************************** + ********** W32 specific functions **************** + ****************************************************/ + +#ifdef HAVE_W32_SYSTEM +const char * +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, DIM (strerr)-1, NULL); + return strerr; +} +#endif /*HAVE_W32_SYSTEM*/ + + +/**************************************************** + ******** 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 ) +{ + if (a == b) + return 0; + + for (; *a && *b; a++, b++) { + if (*a != *b && ascii_toupper(*a) != ascii_toupper(*b)) + break; + } + return *a == *b? 0 : (ascii_toupper (*a) - ascii_toupper (*b)); +} + +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 void *a_arg, const void *b_arg, size_t n ) +{ + const char *a = a_arg; + const char *b = b_arg; + + 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; +} + +int +ascii_strcmp( const char *a, const char *b ) +{ + if (a == b) + return 0; + + for (; *a && *b; a++, b++) { + if (*a != *b ) + break; + } + return *a == *b? 0 : (*(signed char *)a - *(signed char *)b); +} + + +void * +ascii_memcasemem (const void *haystack, size_t nhaystack, + const void *needle, size_t nneedle) +{ + + if (!nneedle) + return (void*)haystack; /* finding an empty needle is really easy */ + if (nneedle <= nhaystack) + { + const char *a = haystack; + const char *b = a + nhaystack - nneedle; + + for (; a <= b; a++) + { + if ( !ascii_memcasecmp (a, needle, nneedle) ) + return (void *)a; + } + } + return NULL; +} + +/********************************************* + ********** 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_STRSEP +/* 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; +} +#endif /*HAVE_STRSEP*/ + + +#ifndef HAVE_STRLWR +char * +strlwr(char *s) +{ + char *p; + for(p=s; *p; p++ ) + *p = tolower(*p); + return s; +} +#endif + + +#ifndef HAVE_STRCASECMP +int +strcasecmp( const char *a, const char *b ) +{ + for( ; *a && *b; a++, b++ ) { + if( *a != *b && toupper(*a) != toupper(*b) ) + break; + } + return *(const byte*)a - *(const byte*)b; +} +#endif + + +/**************** + * mingw32/cpd has a memicmp() + */ +#ifndef HAVE_MEMICMP +int +memicmp( const char *a, const char *b, size_t n ) +{ + for( ; n; n--, a++, b++ ) + if( *a != *b && toupper(*(const byte*)a) != toupper(*(const byte*)b) ) + return *(const byte *)a - *(const byte*)b; + return 0; +} +#endif + + +#ifndef HAVE_MEMRCHR +void * +memrchr (const void *buffer, int c, size_t n) +{ + const unsigned char *p = buffer; + + for (p += n; n ; n--) + if (*--p == c) + return (void *)p; + return NULL; +} +#endif /*HAVE_MEMRCHR*/ + + +/* Percent-escape the string STR by replacing colons with '%3a'. If + EXTRA is not NULL all characters in EXTRA are also escaped. */ +static char * +do_percent_escape (const char *str, const char *extra, int die) +{ + int i, j; + char *ptr; + + if (!str) + return NULL; + + for (i=j=0; str[i]; i++) + if (str[i] == ':' || str[i] == '%' || (extra && strchr (extra, str[i]))) + j++; + if (die) + ptr = jnlib_xmalloc (i + 2 * j + 1); + else + { + ptr = jnlib_malloc (i + 2 * j + 1); + if (!ptr) + return NULL; + } + i = 0; + while (*str) + { + if (*str == ':') + { + ptr[i++] = '%'; + ptr[i++] = '3'; + ptr[i++] = 'a'; + } + else if (*str == '%') + { + ptr[i++] = '%'; + ptr[i++] = '2'; + ptr[i++] = '5'; + } + else if (extra && strchr (extra, *str)) + { + ptr[i++] = '%'; + ptr[i++] = tohex_lower ((*str>>4)&15); + ptr[i++] = tohex_lower (*str&15); + } + else + ptr[i++] = *str; + str++; + } + ptr[i] = '\0'; + + return ptr; +} + +/* Percent-escape the string STR by replacing colons with '%3a'. If + EXTRA is not NULL all characters in EXTRA are also escaped. This + function terminates the process on memory shortage. */ +char * +percent_escape (const char *str, const char *extra) +{ + return do_percent_escape (str, extra, 1); +} + +/* Same as percent_escape but return NULL instead of exiting on memory + error. */ +char * +try_percent_escape (const char *str, const char *extra) +{ + return do_percent_escape (str, extra, 0); +} + + + +static char * +do_strconcat (const char *s1, va_list arg_ptr) +{ + const char *argv[48]; + size_t argc; + size_t needed; + char *buffer, *p; + + argc = 0; + argv[argc++] = s1; + needed = strlen (s1); + while (((argv[argc] = va_arg (arg_ptr, const char *)))) + { + needed += strlen (argv[argc]); + if (argc >= DIM (argv)-1) + { + errno = EINVAL; + return NULL; + } + argc++; + } + needed++; + buffer = jnlib_malloc (needed); + if (buffer) + { + for (p = buffer, argc=0; argv[argc]; argc++) + p = stpcpy (p, argv[argc]); + } + return buffer; +} + + +/* Concatenate the string S1 with all the following strings up to a + NULL. Returns a malloced buffer with the new string or NULL on a + malloc error or if too many arguments are given. */ +char * +strconcat (const char *s1, ...) +{ + va_list arg_ptr; + char *result; + + if (!s1) + result = jnlib_strdup (""); + else + { + va_start (arg_ptr, s1); + result = do_strconcat (s1, arg_ptr); + va_end (arg_ptr); + } + return result; +} + +/* Same as strconcat but terminate the process with an error message + if something goes wrong. */ +char * +xstrconcat (const char *s1, ...) +{ + va_list arg_ptr; + char *result; + + if (!s1) + result = jnlib_xstrdup (""); + else + { + va_start (arg_ptr, s1); + result = do_strconcat (s1, arg_ptr); + va_end (arg_ptr); + } + if (!result) + { + if (errno == EINVAL) + fputs ("\nfatal: too many args for xstrconcat\n", stderr); + else + fputs ("\nfatal: out of memory\n", stderr); + exit (2); + } + return result; +} + diff --git a/jnlib/stringhelp.h b/jnlib/stringhelp.h new file mode 100644 index 0000000..a560b16 --- /dev/null +++ b/jnlib/stringhelp.h @@ -0,0 +1,136 @@ +/* stringhelp.h + * Copyright (C) 1998, 1999, 2000, 2001, 2003, + * 2006, 2007, 2009 Free Software Foundation, Inc. + * + * This file is part of JNLIB. + * + * JNLIB 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 3 of + * the License, or (at your option) any later version. + * + * JNLIB 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef LIBJNLIB_STRINGHELP_H +#define LIBJNLIB_STRINGHELP_H + +#include "types.h" + +const char *memistr (const void *buf, size_t buflen, const char *sub); +char *mem2str( char *, const void *, size_t); +char *trim_spaces( char *string ); +char *trim_trailing_spaces( char *string ); +unsigned int trim_trailing_chars( unsigned char *line, unsigned len, + const char *trimchars); +unsigned int trim_trailing_ws( unsigned char *line, unsigned len ); +size_t length_sans_trailing_chars (const unsigned char *line, size_t len, + const char *trimchars ); +size_t length_sans_trailing_ws (const unsigned char *line, size_t len); + + +char *make_basename(const char *filepath, const char *inputpath); +char *make_dirname(const char *filepath); +char *make_filename( const char *first_part, ... ) GNUPG_GCC_A_SENTINEL(0); +char *make_filename_try (const char *first_part, ... ) GNUPG_GCC_A_SENTINEL(0); +int compare_filenames( const char *a, const char *b ); + +int hextobyte (const char *s); + +size_t print_sanitized_buffer (FILE *fp, const void *buffer, size_t length, + int delim); +size_t print_sanitized_buffer2 (FILE *fp, const void *buffer, size_t length, + int delim, int delim2); +size_t print_sanitized_utf8_buffer (FILE *fp, const void *buffer, + size_t length, int delim); +size_t print_sanitized_string (FILE *fp, const char *string, int delim); +size_t print_sanitized_string2 (FILE *fp, const char *string, + int delim, int delim2); +size_t print_sanitized_utf8_string (FILE *fp, const char *string, int delim); +char *sanitize_buffer (const void *p, size_t n, int delim); + + +size_t utf8_charcount (const char *s); + + +#ifdef HAVE_W32_SYSTEM +const char *w32_strerror (int ec); +#endif + + +int ascii_isupper (int c); +int ascii_islower (int c); +int ascii_toupper (int c); +int ascii_tolower (int c); +int ascii_strcasecmp( const char *a, const char *b ); +int ascii_strncasecmp (const char *a, const char *b, size_t n); +int ascii_memcasecmp( const void *a, const void *b, size_t n ); +const char *ascii_memistr ( const void *buf, size_t buflen, const char *sub); +void *ascii_memcasemem (const void *haystack, size_t nhaystack, + const void *needle, size_t nneedle); + + +#ifndef HAVE_MEMICMP +int memicmp( const char *a, const char *b, size_t n ); +#endif +#ifndef HAVE_STPCPY +char *stpcpy(char *a,const char *b); +#endif +#ifndef HAVE_STRSEP +char *strsep (char **stringp, const char *delim); +#endif +#ifndef HAVE_STRLWR +char *strlwr(char *a); +#endif +#ifndef HAVE_STRTOUL +# define strtoul(a,b,c) ((unsigned long)strtol((a),(b),(c))) +#endif +#ifndef HAVE_MEMMOVE +# define memmove(d, s, n) bcopy((s), (d), (n)) +#endif +#ifndef HAVE_STRICMP +# define stricmp(a,b) strcasecmp( (a), (b) ) +#endif +#ifndef HAVE_MEMRCHR +void *memrchr (const void *buffer, int c, size_t n); +#endif + + +#ifndef HAVE_ISASCII +static inline int +isascii (int c) +{ + return (((c) & ~0x7f) == 0); +} +#endif /* !HAVE_ISASCII */ + + +#ifndef STR +# define STR(v) #v +#endif +#define STR2(v) STR(v) + +/* Percent-escape the string STR by replacing colons with '%3a'. If + EXTRA is not NULL, also replace all characters given in EXTRA. The + "try_" variant fails with NULL if not enough memory can be + allocated. */ +char *percent_escape (const char *str, const char *extra); +char *try_percent_escape (const char *str, const char *extra); + + +/* Concatenate the string S1 with all the following strings up to a + NULL. Returns a malloced buffer with the new string or NULL on a + malloc error or if too many arguments are given. */ +char *strconcat (const char *s1, ...) GNUPG_GCC_A_SENTINEL(0); +/* Ditto, but die on error. */ +char *xstrconcat (const char *s1, ...) GNUPG_GCC_A_SENTINEL(0); + + + +#endif /*LIBJNLIB_STRINGHELP_H*/ diff --git a/jnlib/strlist.c b/jnlib/strlist.c new file mode 100644 index 0000000..d45a164 --- /dev/null +++ b/jnlib/strlist.c @@ -0,0 +1,185 @@ +/* strlist.c - string helpers + * Copyright (C) 1998, 2000, 2001, 2006 Free Software Foundation, Inc. + * + * This file is part of JNLIB. + * + * JNLIB 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 3 of + * the License, or (at your option) any later version. + * + * JNLIB 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, see <http://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <ctype.h> + +#include "libjnlib-config.h" +#include "strlist.h" +#ifdef JNLIB_NEED_UTF8CONV +#include "utf8conv.h" +#endif + +void +free_strlist( strlist_t sl ) +{ + strlist_t sl2; + + for(; sl; sl = sl2 ) { + sl2 = sl->next; + jnlib_free(sl); + } +} + + +/* Add STRING to the LIST at the front. This function terminates the + process on memory shortage. */ +strlist_t +add_to_strlist( strlist_t *list, const char *string ) +{ + strlist_t sl; + + sl = jnlib_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 UTF-8 is done. This function terminates the process on memory + shortage. */ +#ifdef JNLIB_NEED_UTF8CONV +strlist_t +add_to_strlist2( strlist_t *list, const char *string, int is_utf8 ) +{ + strlist_t sl; + + if (is_utf8) + sl = add_to_strlist( list, string ); + else + { + char *p = native_to_utf8( string ); + sl = add_to_strlist( list, p ); + jnlib_free ( p ); + } + return sl; +} +#endif /* JNLIB_NEED_UTF8CONV*/ + + +/* Add STRING to the LIST at the end. This function terminates the + process on memory shortage. */ +strlist_t +append_to_strlist( strlist_t *list, const char *string ) +{ + strlist_t r, sl; + + sl = jnlib_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; +} + + +#ifdef JNLIB_NEED_UTF8CONV +strlist_t +append_to_strlist2( strlist_t *list, const char *string, int is_utf8 ) +{ + strlist_t sl; + + if( is_utf8 ) + sl = append_to_strlist( list, string ); + else + { + char *p = native_to_utf8 (string); + sl = append_to_strlist( list, p ); + jnlib_free( p ); + } + return sl; +} +#endif /* JNLIB_NEED_UTF8CONV */ + + +/* Return a copy of LIST. This function terminates the process on + memory shortage.*/ +strlist_t +strlist_copy (strlist_t list) +{ + strlist_t newlist = NULL, sl, *last; + + last = &newlist; + for (; list; list = list->next) + { + sl = jnlib_xmalloc (sizeof *sl + strlen (list->d)); + sl->flags = list->flags; + strcpy(sl->d, list->d); + sl->next = NULL; + *last = sl; + last = &sl; + } + return newlist; +} + + + +strlist_t +strlist_prev( strlist_t head, strlist_t node ) +{ + strlist_t n; + + for(n=NULL; head && head != node; head = head->next ) + n = head; + return n; +} + +strlist_t +strlist_last( strlist_t node ) +{ + if( node ) + for( ; node->next ; node = node->next ) + ; + return node; +} + + +/* Remove the first item from LIST and return its content in an + allocated buffer. This function terminates the process on memory + shortage. */ +char * +strlist_pop (strlist_t *list) +{ + char *str=NULL; + strlist_t sl=*list; + + if(sl) + { + str=jnlib_xmalloc(strlen(sl->d)+1); + strcpy(str,sl->d); + + *list=sl->next; + jnlib_free(sl); + } + + return str; +} + diff --git a/jnlib/strlist.h b/jnlib/strlist.h new file mode 100644 index 0000000..4191624 --- /dev/null +++ b/jnlib/strlist.h @@ -0,0 +1,49 @@ +/* strlist.h + * Copyright (C) 1998, 2000, 2001, 2006 Free Software Foundation, Inc. + * + * This file is part of JNLIB. + * + * JNLIB 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 3 of + * the License, or (at your option) any later version. + * + * JNLIB 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef LIBJNLIB_STRLIST_H +#define LIBJNLIB_STRLIST_H + +struct string_list +{ + struct string_list *next; + unsigned int flags; + char d[1]; +}; +typedef struct string_list *strlist_t; + +void free_strlist (strlist_t sl); +strlist_t add_to_strlist (strlist_t *list, const char *string); + +strlist_t add_to_strlist2( strlist_t *list, const char *string, int is_utf8); + +strlist_t append_to_strlist (strlist_t *list, const char *string); +strlist_t append_to_strlist2 (strlist_t *list, const char *string, + int is_utf8); + +strlist_t strlist_copy (strlist_t list); + +strlist_t strlist_prev (strlist_t head, strlist_t node); +strlist_t strlist_last (strlist_t node); +char * strlist_pop (strlist_t *list); + +#define FREE_STRLIST(a) do { free_strlist((a)); (a) = NULL ; } while(0) + + +#endif /*LIBJNLIB_STRLIST_H*/ diff --git a/jnlib/t-stringhelp.c b/jnlib/t-stringhelp.c new file mode 100644 index 0000000..02041d3 --- /dev/null +++ b/jnlib/t-stringhelp.c @@ -0,0 +1,414 @@ +/* t-stringhelp.c - Regression tests for stringhelp.c + * Copyright (C) 2007 Free Software Foundation, Inc. + * + * This file is part of JNLIB. + * + * JNLIB 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 3 of + * the License, or (at your option) any later version. + * + * JNLIB 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, see <http://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#ifdef HAVE_PWD_H +# include <pwd.h> +#endif +#include <unistd.h> +#include <sys/types.h> + +#include "stringhelp.h" + +#include "t-support.h" + + +static char *home_buffer; + + +const char * +gethome (void) +{ + if (!home_buffer) + { + char *home = getenv("HOME"); + +#if defined(HAVE_GETPWUID) && defined(HAVE_PWD_H) + if(home) + home_buffer = xstrdup (home); + else + { + struct passwd *pwd; + + pwd = getpwuid (getuid()); + if (pwd) + home_buffer = xstrdup (pwd->pw_dir); + } +#endif + } + return home_buffer; +} + + +static void +test_percent_escape (void) +{ + char *result; + static struct { + const char *extra; + const char *value; + const char *expected; + } tests[] = + { + { NULL, "", "" }, + { NULL, "%", "%25" }, + { NULL, "%%", "%25%25" }, + { NULL, " %", " %25" }, + { NULL, ":", "%3a" }, + { NULL, " :", " %3a" }, + { NULL, ": ", "%3a " }, + { NULL, " : ", " %3a " }, + { NULL, "::", "%3a%3a" }, + { NULL, ": :", "%3a %3a" }, + { NULL, "%:", "%25%3a" }, + { NULL, ":%", "%3a%25" }, + { "\\\n:", ":%", "%3a%25" }, + { "\\\n:", "\\:%", "%5c%3a%25" }, + { "\\\n:", "\n:%", "%0a%3a%25" }, + { "\\\n:", "\xff:%", "\xff%3a%25" }, + { "\\\n:", "\xfe:%", "\xfe%3a%25" }, + { "\\\n:", "\x01:%", "\x01%3a%25" }, + { "\x01", "\x01:%", "%01%3a%25" }, + { "\xfe", "\xfe:%", "%fe%3a%25" }, + { "\xfe", "\xff:%", "\xff%3a%25" }, + + { NULL, NULL, NULL } + }; + int testno; + + result = percent_escape (NULL, NULL); + if (result) + fail (0); + for (testno=0; tests[testno].value; testno++) + { + result = percent_escape (tests[testno].value, tests[testno].extra); + if (!result) + fail (testno); + if (strcmp (result, tests[testno].expected)) + fail (testno); + xfree (result); + } + +} + + +static void +test_compare_filenames (void) +{ + struct { + const char *a; + const char *b; + int result; + } tests[] = { + { "", "", 0 }, + { "", "a", -1 }, + { "a", "", 1 }, + { "a", "a", 0 }, + { "a", "aa", -1 }, + { "aa", "a", 1 }, + { "a", "b", -1 }, + +#ifdef HAVE_W32_SYSTEM + { "a", "A", 0 }, + { "A", "a", 0 }, + { "foo/bar", "foo\\bar", 0 }, + { "foo\\bar", "foo/bar", 0 }, + { "foo\\", "foo/", 0 }, + { "foo/", "foo\\", 0 }, +#endif /*HAVE_W32_SYSTEM*/ + { NULL, NULL, 0} + }; + int testno, result; + + for (testno=0; tests[testno].a; testno++) + { + result = compare_filenames (tests[testno].a, tests[testno].b); + result = result < 0? -1 : result > 0? 1 : 0; + if (result != tests[testno].result) + fail (testno); + } +} + + +static void +test_strconcat (void) +{ + char *out; + + out = strconcat ("1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", NULL); + if (!out) + fail (0); + else + xfree (out); + out = strconcat ("1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", NULL); + if (out) + fail (0); + else if (errno != EINVAL) + fail (0); + + out = strconcat ("1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", NULL); + if (out) + fail (0); + else if (errno != EINVAL) + fail (0); + +#if __GNUC__ < 4 /* gcc 4.0 has a sentinel attribute. */ + out = strconcat (NULL); + if (!out || *out) + fail (1); +#endif + out = strconcat (NULL, NULL); + if (!out || *out) + fail (1); + out = strconcat ("", NULL); + if (!out || *out) + fail (1); + xfree (out); + + out = strconcat ("", "", NULL); + if (!out || *out) + fail (2); + xfree (out); + + out = strconcat ("a", "b", NULL); + if (!out || strcmp (out, "ab")) + fail (3); + xfree (out); + out = strconcat ("a", "b", "c", NULL); + if (!out || strcmp (out, "abc")) + fail (3); + xfree (out); + + out = strconcat ("a", "b", "cc", NULL); + if (!out || strcmp (out, "abcc")) + fail (4); + xfree (out); + out = strconcat ("a1", "b1", "c1", NULL); + if (!out || strcmp (out, "a1b1c1")) + fail (4); + xfree (out); + + out = strconcat ("", " long b ", "", "--even-longer--", NULL); + if (!out || strcmp (out, " long b --even-longer--")) + fail (5); + xfree (out); + + out = strconcat ("", " long b ", "", "--even-longer--", NULL); + if (!out || strcmp (out, " long b --even-longer--")) + fail (5); + xfree (out); +} + +static void +test_xstrconcat (void) +{ + char *out; + + out = xstrconcat ("1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", NULL); + if (!out) + fail (0); + +#if __GNUC__ < 4 /* gcc 4.0 has a sentinel attribute. */ + out = xstrconcat (NULL); + if (!out) + fail (1); +#endif + out = xstrconcat (NULL, NULL); + if (!out) + fail (1); + out = xstrconcat ("", NULL); + if (!out || *out) + fail (1); + xfree (out); + + out = xstrconcat ("", "", NULL); + if (!out || *out) + fail (2); + xfree (out); + + out = xstrconcat ("a", "b", NULL); + if (!out || strcmp (out, "ab")) + fail (3); + xfree (out); + out = xstrconcat ("a", "b", "c", NULL); + if (!out || strcmp (out, "abc")) + fail (3); + xfree (out); + + out = xstrconcat ("a", "b", "cc", NULL); + if (!out || strcmp (out, "abcc")) + fail (4); + xfree (out); + out = xstrconcat ("a1", "b1", "c1", NULL); + if (!out || strcmp (out, "a1b1c1")) + fail (4); + xfree (out); + + out = xstrconcat ("", " long b ", "", "--even-longer--", NULL); + if (!out || strcmp (out, " long b --even-longer--")) + fail (5); + xfree (out); + + out = xstrconcat ("", " long b ", "", "--even-longer--", NULL); + if (!out || strcmp (out, " long b --even-longer--")) + fail (5); + xfree (out); +} + + +static void +test_make_filename_try (void) +{ + char *out; + const char *home = gethome (); + size_t homelen = home? strlen (home):0; + + out = make_filename_try ("1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", NULL); + if (out) + fail (0); + else if (errno != EINVAL) + fail (0); + xfree (out); + out = make_filename_try ("1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", NULL); + if (out) + fail (0); + else if (errno != EINVAL) + fail (0); + xfree (out); + + out = make_filename_try ("1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", NULL); + if (!out || strcmp (out, + "1/2/3/4/5/6/7/8/9/10/" + "1/2/3/4/5/6/7/8/9/10/" + "1/2/3/4/5/6/7/8/9/10/" + "1/2")) + fail (0); + xfree (out); + + out = make_filename_try ("foo", "~/bar", "baz/cde", NULL); + if (!out || strcmp (out, "foo/~/bar/baz/cde")) + fail (1); + xfree (out); + + out = make_filename_try ("foo", "~/bar", "baz/cde/", NULL); + if (!out || strcmp (out, "foo/~/bar/baz/cde/")) + fail (1); + xfree (out); + + out = make_filename_try ("/foo", "~/bar", "baz/cde/", NULL); + if (!out || strcmp (out, "/foo/~/bar/baz/cde/")) + fail (1); + xfree (out); + + out = make_filename_try ("//foo", "~/bar", "baz/cde/", NULL); + if (!out || strcmp (out, "//foo/~/bar/baz/cde/")) + fail (1); + xfree (out); + + out = make_filename_try ("", "~/bar", "baz/cde", NULL); + if (!out || strcmp (out, "/~/bar/baz/cde")) + fail (1); + xfree (out); + + + out = make_filename_try ("~/foo", "bar", NULL); + if (!out) + fail (2); + if (home) + { + if (strlen (out) < homelen + 7) + fail (2); + if (strncmp (out, home, homelen)) + fail (2); + if (strcmp (out+homelen, "/foo/bar")) + fail (2); + } + else + { + if (strcmp (out, "~/foo/bar")) + fail (2); + } + xfree (out); + + out = make_filename_try ("~", "bar", NULL); + if (!out) + fail (2); + if (home) + { + if (strlen (out) < homelen + 3) + fail (2); + if (strncmp (out, home, homelen)) + fail (2); + if (strcmp (out+homelen, "/bar")) + fail (2); + } + else + { + if (strcmp (out, "~/bar")) + fail (2); + } + xfree (out); +} + + +int +main (int argc, char **argv) +{ + (void)argc; + (void)argv; + + test_percent_escape (); + test_compare_filenames (); + test_strconcat (); + test_xstrconcat (); + test_make_filename_try (); + + xfree (home_buffer); + return 0; +} + diff --git a/jnlib/t-support.c b/jnlib/t-support.c new file mode 100644 index 0000000..d8eba3b --- /dev/null +++ b/jnlib/t-support.c @@ -0,0 +1,143 @@ +/* t-support.c - helper functions for the regression tests. + * Copyright (C) 2007 Free Software Foundation, Inc. + * + * This file is part of JNLIB. + * + * JNLIB 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 3 of + * the License, or (at your option) any later version. + * + * JNLIB 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, see <http://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <assert.h> + +#include "t-support.h" + + +/* Replacements for the malloc functions as used here. */ + +static void +out_of_memory (void) +{ + fprintf (stderr,"error: out of core in regression tests: %s\n", + strerror (errno)); + exit (2); +} + + +void * +gcry_malloc (size_t n) +{ + return malloc (n); +} + +void * +gcry_xmalloc (size_t n) +{ + void *p = malloc (n); + if (!p) + out_of_memory (); + return p; +} + +char * +gcry_strdup (const char *string) +{ + char *p = malloc (strlen (string)+1); + if (p) + strcpy (p, string); + return p; +} + + +void * +gcry_realloc (void *a, size_t n) +{ + return realloc (a, n); +} + +void * +gcry_xrealloc (void *a, size_t n) +{ + void *p = realloc (a, n); + if (!p) + out_of_memory (); + return p; +} + + + +void * +gcry_calloc (size_t n, size_t m) +{ + return calloc (n, m); +} + +void * +gcry_xcalloc (size_t n, size_t m) +{ + void *p = calloc (n, m); + if (!p) + out_of_memory (); + return p; +} + + +char * +gcry_xstrdup (const char *string) +{ + void *p = malloc (strlen (string)+1); + if (!p) + out_of_memory (); + strcpy (p, string); + return p; +} + +void +gcry_free (void *a) +{ + if (a) + free (a); +} + + + +/* Stubs for gpg-error functions required because some compilers do + not eliminate the supposed-to-be-unused-inline-functions and thus + require functions called from these inline fucntions. Although we + do not use gpg-error, gpg-error.h may get included via gcrypt.h if + it happens to be used used in libjnlib-config.h. */ +int +gpg_err_code_from_errno (int err) +{ + (void)err; + assert (!"stub function"); + return -1; +} + + +/* Retrieve the error code directly from the ERRNO variable. This + returns GPG_ERR_UNKNOWN_ERRNO if the system error is not mapped + (report this) and GPG_ERR_MISSING_ERRNO if ERRNO has the value 0. */ +int +gpg_err_code_from_syserror (void) +{ + assert (!"stub function"); + return -1; +} + + + diff --git a/jnlib/t-support.h b/jnlib/t-support.h new file mode 100644 index 0000000..5270174 --- /dev/null +++ b/jnlib/t-support.h @@ -0,0 +1,50 @@ +/* t-support.h - Helper for the regression tests + * Copyright (C) 2007 Free Software Foundation, Inc. + * + * This file is part of JNLIB. + * + * JNLIB 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 3 of + * the License, or (at your option) any later version. + * + * JNLIB 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef LIBJNLIB_T_SUPPORT_H +#define LIBJNLIB_T_SUPPORT_H 1 + +#ifdef GCRYPT_VERSION +#error The regression tests should not include with gcrypt.h +#endif + +/* Repalcement prototypes. */ +void *gcry_xmalloc (size_t n); +void *gcry_xcalloc (size_t n, size_t m); +void *gcry_xrealloc (void *a, size_t n); +char *gcry_xstrdup (const char * a); +void gcry_free (void *a); + +/* Map the used xmalloc functions to those implemented by t-support.c */ +#define xmalloc(a) gcry_xmalloc ( (a) ) +#define xcalloc(a,b) gcry_xcalloc ( (a), (b) ) +#define xrealloc(a,n) gcry_xrealloc ( (a), (n) ) +#define xstrdup(a) gcry_xstrdup ( (a) ) +#define xfree(a) gcry_free ( (a) ) + + +/* Macros to print the result of a test. */ +#define pass() do { ; } while(0) +#define fail(a) do { fprintf (stderr, "%s:%d: test %d failed\n",\ + __FILE__,__LINE__, (a)); \ + exit (1); \ + } while(0) + + +#endif /*LIBJNLIB_T_SUPPORT_H*/ diff --git a/jnlib/types.h b/jnlib/types.h new file mode 100644 index 0000000..62fa047 --- /dev/null +++ b/jnlib/types.h @@ -0,0 +1,114 @@ +/* types.h - define some extra types + * Copyright (C) 1999, 2000, 2001, 2006 Free Software Foundation, Inc. + * + * This file is part of JNLIB. + * + * JNLIB 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 3 of + * the License, or (at your option) any later version. + * + * JNLIB 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef LIBJNLIB_TYPES_H +#define LIBJNLIB_TYPES_H + +/* The AC_CHECK_SIZEOF() in configure fails for some machines. + * we provide some fallback values here */ +#if !SIZEOF_UNSIGNED_SHORT +# undef SIZEOF_UNSIGNED_SHORT +# define SIZEOF_UNSIGNED_SHORT 2 +#endif +#if !SIZEOF_UNSIGNED_INT +# undef SIZEOF_UNSIGNED_INT +# define SIZEOF_UNSIGNED_INT 4 +#endif +#if !SIZEOF_UNSIGNED_LONG +# undef SIZEOF_UNSIGNED_LONG +# define SIZEOF_UNSIGNED_LONG 4 +#endif + + +#include <sys/types.h> + + +#ifndef HAVE_BYTE_TYPEDEF +# undef byte /* There might be a macro with this name. */ +/* Windows typedefs byte in the rpc headers. Avoid warning about + double definition. */ +#if !(defined(_WIN32) && defined(cbNDRContext)) + typedef unsigned char byte; +#endif +# define HAVE_BYTE_TYPEDEF +#endif + +#ifndef HAVE_USHORT_TYPEDEF +# undef ushort /* There might be a macro with this name. */ + typedef unsigned short ushort; +# define HAVE_USHORT_TYPEDEF +#endif + +#ifndef HAVE_ULONG_TYPEDEF +# undef ulong /* There might be a macro with this name. */ + typedef unsigned long ulong; +# define HAVE_ULONG_TYPEDEF +#endif + +#ifndef HAVE_U16_TYPEDEF +# undef u16 /* There might be a macro with this name. */ +# if SIZEOF_UNSIGNED_INT == 2 + typedef unsigned int u16; +# elif SIZEOF_UNSIGNED_SHORT == 2 + typedef unsigned short u16; +# else +# error no typedef for u16 +# endif +# define HAVE_U16_TYPEDEF +#endif + +#ifndef HAVE_U32_TYPEDEF +# undef u32 /* There might be a macro with this name. */ +# if SIZEOF_UNSIGNED_INT == 4 + typedef unsigned int u32; +# elif SIZEOF_UNSIGNED_LONG == 4 + typedef unsigned long u32; +# else +# error no typedef for u32 +# endif +# define HAVE_U32_TYPEDEF +#endif + +#ifndef HAVE_U64_TYPEDEF +# undef u64 /* There might be a macro with this name. */ +# if SIZEOF_UNSIGNED_INT == 8 + typedef unsigned int u64; +# define HAVE_U64_TYPEDEF +# elif SIZEOF_UNSIGNED_LONG == 8 + typedef unsigned long u64; +# define HAVE_U64_TYPEDEF +# elif __GNUC__ >= 2 || defined(__SUNPRO_C) + typedef unsigned long long u64; +# define HAVE_U64_TYPEDEF +# endif +#endif + + +/* Some GCC attributes. Note that we use also define some in + mischelp.h, but this header and types.h are not always included. + Should eventually be put into one file (e.g. nlib-common.h). */ +#if __GNUC__ >= 4 +# define GNUPG_GCC_A_SENTINEL(a) __attribute__ ((sentinel(a))) +#else +# define GNUPG_GCC_A_SENTINEL(a) +#endif + + + +#endif /*LIBJNLIB_TYPES_H*/ diff --git a/jnlib/utf8conv.c b/jnlib/utf8conv.c new file mode 100644 index 0000000..fee4dc6 --- /dev/null +++ b/jnlib/utf8conv.c @@ -0,0 +1,738 @@ +/* utf8conf.c - UTF8 character set conversion + * Copyright (C) 1994, 1998, 1999, 2000, 2001, + * 2003, 2006, 2008 Free Software Foundation, Inc. + * + * This file is part of JNLIB. + * + * JNLIB 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 3 of + * the License, or (at your option) any later version. + * + * JNLIB 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, see <http://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <ctype.h> +#ifdef HAVE_LANGINFO_CODESET +#include <langinfo.h> +#endif +#include <errno.h> +#ifndef HAVE_W32_SYSTEM +# include <iconv.h> +#endif + +#include "libjnlib-config.h" +#include "stringhelp.h" +#include "dynload.h" +#include "utf8conv.h" + +#ifndef MB_LEN_MAX +#define MB_LEN_MAX 16 +#endif + +static const char *active_charset_name = "iso-8859-1"; +static int no_translation; /* Set to true if we let simply pass through. */ +static int use_iconv; /* iconv comversion fucntions required. */ + + +/* Under W32 we dlopen the iconv dll and don't require any iconv + related headers at all. However we need to define some stuff. */ +#ifdef HAVE_W32_SYSTEM +typedef void *iconv_t; +#ifndef ICONV_CONST +#define ICONV_CONST const +#endif +static iconv_t (* __stdcall iconv_open) (const char *tocode, + const char *fromcode); +static size_t (* __stdcall iconv) (iconv_t cd, + const char **inbuf, size_t *inbytesleft, + char **outbuf, size_t *outbytesleft); +static int (* __stdcall iconv_close) (iconv_t cd); + +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 %s for more information\n"), + "http://www.gnupg.org/download/iconv.html"); + iconv_open = NULL; + iconv = NULL; + iconv_close = NULL; + if (handle) + dlclose (handle); + } + } + return iconv_open? 0: -1; +} +#endif /*HAVE_W32_SYSTEM*/ + + +/* Error handler for iconv failures. This is needed to not clutter the + output with repeated diagnostics about a missing conversion. */ +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; + use_iconv = 0; + } +} + + + +int +set_native_charset (const char *newset) +{ + const char *full_newset; + + if (!newset) + { +#ifdef HAVE_W32_SYSTEM + 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 /*!HAVE_W32_SYSTEM*/ + +#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 /*!HAVE_W32_SYSTEM*/ + } + + 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; + use_iconv = 0; + } + else if ( !ascii_strcasecmp (newset, "utf8" ) + || !ascii_strcasecmp(newset, "utf-8") ) + { + active_charset_name = "utf-8"; + no_translation = 1; + use_iconv = 0; + } + else + { + iconv_t cd; + +#ifdef HAVE_W32_SYSTEM + if (load_libiconv ()) + return -1; +#endif /*HAVE_W32_SYSTEM*/ + + cd = iconv_open (full_newset, "utf-8"); + if (cd == (iconv_t)-1) + { + handle_iconv_error (full_newset, "utf-8", 0); + return -1; + } + iconv_close (cd); + cd = iconv_open ("utf-8", full_newset); + if (cd == (iconv_t)-1) + { + handle_iconv_error ("utf-8", full_newset, 0); + return -1; + } + iconv_close (cd); + active_charset_name = full_newset; + no_translation = 0; + use_iconv = 1; + } + return 0; +} + +const char * +get_native_charset () +{ + return active_charset_name; +} + +/* Return true if the native charset is utf-8. */ +int +is_native_utf8 (void) +{ + return no_translation; +} + + +/* Convert string, which is in native encoding to UTF8 and return a + new allocated UTF-8 string. This function terminates the process + on memory shortage. */ +char * +native_to_utf8 (const char *orig_string) +{ + const unsigned char *string = (const unsigned char *)orig_string; + const unsigned char *s; + char *buffer; + unsigned char *p; + size_t length = 0; + + if (no_translation) + { + /* Already utf-8 encoded. */ + buffer = jnlib_xstrdup (orig_string); + } + else if (!use_iconv) + { + /* For Latin-1 we can avoid the iconv overhead. */ + for (s = string; *s; s++) + { + length++; + if (*s & 0x80) + length++; + } + buffer = jnlib_xmalloc (length + 1); + for (p = (unsigned char *)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 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 = jnlib_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); + } + return buffer; +} + + + +static char * +do_utf8_to_native (const char *string, size_t length, int delim, + int with_iconv) +{ + int nleft; + int i; + unsigned char encbuf[8]; + int encidx; + const unsigned char *s; + size_t n; + char *buffer = NULL; + char *p = NULL; + unsigned long val = 0; + size_t slen; + int resync = 0; + + /* First pass (p==NULL): count the extended utf-8 characters. */ + /* Second pass (p!=NULL): create string. */ + for (;;) + { + for (slen = length, nleft = encidx = 0, n = 0, + s = (const unsigned char *)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 utf-8 */ + { + 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; + } + else if (with_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 mess 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; + } + else /* Latin-1 case. */ + { + if (val >= 0x80 && val < 256) + { + /* We can simply print this character */ + n++; + 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 = jnlib_xmalloc (n + 1); + } + else if (with_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); + jnlib_free (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 = jnlib_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. Try again but without iconv. */ + jnlib_free (buffer); + buffer = NULL; + jnlib_free (outbuf); + outbuf = do_utf8_to_native (string, length, delim, 0); + } + else /* Success. */ + { + *outptr = 0; /* Make sure it is a string. */ + /* We could realloc the buffer now but I doubt that it + makes much sense given that it will get freed + anyway soon after. */ + jnlib_free (buffer); + } + iconv_close (cd); + return outbuf; + } + else /* Not using iconv. */ + { + *p = 0; /* Make sure it is a string. */ + return buffer; + } + } +} + +/* Convert string, which is in UTF-8 to native encoding. Replace + 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. This function + terminates the process on memory shortage. */ +char * +utf8_to_native (const char *string, size_t length, int delim) +{ + return do_utf8_to_native (string, length, delim, use_iconv); +} + + + + +/* Wrapper function for iconv_open, required for W32 as we dlopen that + library on that system. */ +jnlib_iconv_t +jnlib_iconv_open (const char *tocode, const char *fromcode) +{ +#ifdef HAVE_W32_SYSTEM + if (load_libiconv ()) + return (jnlib_iconv_t)(-1); +#endif /*HAVE_W32_SYSTEM*/ + + return (jnlib_iconv_t)iconv_open (tocode, fromcode); +} + + +/* Wrapper function for iconv, required for W32 as we dlopen that + library on that system. */ +size_t +jnlib_iconv (jnlib_iconv_t cd, + const char **inbuf, size_t *inbytesleft, + char **outbuf, size_t *outbytesleft) +{ + +#ifdef HAVE_W32_SYSTEM + if (load_libiconv ()) + return 0; +#endif /*HAVE_W32_SYSTEM*/ + + return iconv ((iconv_t)cd, (char**)inbuf, inbytesleft, outbuf, outbytesleft); +} + +/* Wrapper function for iconv_close, required for W32 as we dlopen that + library on that system. */ +int +jnlib_iconv_close (jnlib_iconv_t cd) +{ +#ifdef HAVE_W32_SYSTEM + if (load_libiconv ()) + return 0; +#endif /*HAVE_W32_SYSTEM*/ + + return iconv_close ((iconv_t)cd); +} diff --git a/jnlib/utf8conv.h b/jnlib/utf8conv.h new file mode 100644 index 0000000..e800f81 --- /dev/null +++ b/jnlib/utf8conv.h @@ -0,0 +1,41 @@ +/* utf8conf.h + * Copyright (C) 2003, 2006 Free Software Foundation, Inc. + * + * This file is part of JNLIB. + * + * JNLIB 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 3 of + * the License, or (at your option) any later version. + * + * JNLIB 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef LIBJNLIB_UTF8CONF_H +#define LIBJNLIB_UTF8CONF_H + +int set_native_charset (const char *newset); +const char *get_native_charset (void); +int is_native_utf8 (void); + +char *native_to_utf8 (const char *string); +char *utf8_to_native (const char *string, size_t length, int delim); + + +/* Silly wrappers, required for W32 portability. */ +typedef void *jnlib_iconv_t; + +jnlib_iconv_t jnlib_iconv_open (const char *tocode, const char *fromcode); +size_t jnlib_iconv (jnlib_iconv_t cd, const char **inbuf, size_t *inbytesleft, + char **outbuf, size_t *outbytesleft); +int jnlib_iconv_close (jnlib_iconv_t cd); + + + +#endif /*LIBJNLIB_UTF8CONF_H*/ diff --git a/jnlib/w32-afunix.c b/jnlib/w32-afunix.c new file mode 100644 index 0000000..6365394 --- /dev/null +++ b/jnlib/w32-afunix.c @@ -0,0 +1,137 @@ +/* w32-afunix.c - AF_UNIX emulation for Windows (Client only). + * Copyright (C) 2004, 2006 g10 Code GmbH + * + * This file is part of JNLIB. + * + * JNLIB 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 3 of + * the License, or (at your option) any later version. + * + * JNLIB 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, see <http://www.gnu.org/licenses/>. + */ + +/* Use of this code is preprecated - you better use the sockt wrappers + from libassuan. */ + +#ifdef _WIN32 +#include <stdio.h> +#include <stdlib.h> +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <io.h> +#include <errno.h> + +#include "w32-afunix.h" + + + +/* The buffer for NONCE needs to be at least 16 bytes. Returns 0 on + success. */ +static int +read_port_and_nonce (const char *fname, unsigned short *port, char *nonce) +{ + FILE *fp; + char buffer[50], *p; + size_t nread; + int aval; + + fp = fopen (fname, "rb"); + if (!fp) + return -1; + nread = fread (buffer, 1, sizeof buffer - 1, fp); + fclose (fp); + if (!nread) + { + errno = ENOFILE; + return -1; + } + buffer[nread] = 0; + aval = atoi (buffer); + if (aval < 1 || aval > 65535) + { + errno = EINVAL; + return -1; + } + *port = (unsigned int)aval; + for (p=buffer; nread && *p != '\n'; p++, nread--) + ; + if (*p != '\n' || nread != 17) + { + errno = EINVAL; + return -1; + } + p++; nread--; + memcpy (nonce, p, 16); + return 0; +} + + + +int +_w32_close (int fd) +{ + int rc = closesocket (fd); + if (rc && WSAGetLastError () == WSAENOTSOCK) + rc = close (fd); + return rc; +} + + +int +_w32_sock_new (int domain, int type, int proto) +{ + if (domain == AF_UNIX || domain == AF_LOCAL) + domain = AF_INET; + return socket (domain, type, proto); +} + + +int +_w32_sock_connect (int sockfd, struct sockaddr *addr, int addrlen) +{ + struct sockaddr_in myaddr; + struct sockaddr_un *unaddr; + unsigned short port; + char nonce[16]; + int ret; + + (void)addrlen; + + unaddr = (struct sockaddr_un *)addr; + if (read_port_and_nonce (unaddr->sun_path, &port, nonce)) + return -1; + + myaddr.sin_family = AF_INET; + myaddr.sin_port = htons (port); + myaddr.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + + /* Set return values. */ + unaddr->sun_family = myaddr.sin_family; + unaddr->sun_port = myaddr.sin_port; + unaddr->sun_addr.s_addr = myaddr.sin_addr.s_addr; + + ret = connect (sockfd, (struct sockaddr *)&myaddr, sizeof myaddr); + if (!ret) + { + /* Send the nonce. */ + ret = send (sockfd, nonce, 16, 0); + if (ret >= 0 && ret != 16) + { + errno = EIO; + ret = -1; + } + } + return ret; +} + + +#endif /*_WIN32*/ diff --git a/jnlib/w32-afunix.h b/jnlib/w32-afunix.h new file mode 100644 index 0000000..6b8f3f9 --- /dev/null +++ b/jnlib/w32-afunix.h @@ -0,0 +1,49 @@ +/* w32-afunix.h - AF_UNIX emulation for Windows + * Copyright (C) 2004, 2006 g10 Code GmbH + * + * This file is part of JNLIB. + * + * JNLIB 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 3 of + * the License, or (at your option) any later version. + * + * JNLIB 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, see <http://www.gnu.org/licenses/>. + */ + +#ifdef _WIN32 +#ifndef W32AFUNIX_DEFS_H +#define W32AFUNIX_DEFS_H + +#include <sys/types.h> +#include <windows.h> +#include <ws2tcpip.h> +#include <unistd.h> + +#define DIRSEP_C '\\' + +#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. */ +}; + + +int _w32_close (int fd); +int _w32_sock_new (int domain, int type, int proto); +int _w32_sock_connect (int sockfd, struct sockaddr *addr, int addrlen); + + +#endif /*W32AFUNIX_DEFS_H*/ +#endif /*_WIN32*/ diff --git a/jnlib/w32-gettext.c b/jnlib/w32-gettext.c new file mode 100644 index 0000000..834b2aa --- /dev/null +++ b/jnlib/w32-gettext.c @@ -0,0 +1,1703 @@ +/* w32-gettext.h - A simple gettext implementation for Windows targets. + Copyright (C) 1995, 1996, 1997, 1999, 2005, 2007, + 2008 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + This program 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, see <http://www.gnu.org/licenses/>. + */ + +#if HAVE_CONFIG_H +#include <config.h> +#endif +#if !defined (_WIN32) && !defined (__CYGWIN32__) +# error This module may only be build for Windows or Cygwin32 +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <stdint.h> +#include <locale.h> +#include <windows.h> + +#ifdef JNLIB_IN_JNLIB +#include "libjnlib-config.h" +#endif + +#ifndef jnlib_malloc +# define jnlib_malloc(a) malloc ((a)) +# define jnlib_calloc(a,b) calloc ((a), (b)) +# define jnlib_free(a) free ((a)) +# define jnlib_xstrdup(a) my_xstrdup(a) +#endif /*!jnlib_malloc*/ + + + +/* localname.c from gettext BEGIN. */ + +/* Determine the current selected locale. + Copyright (C) 1995-1999, 2000-2003 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published + by the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. */ + +/* Written by Ulrich Drepper <drepper@gnu.org>, 1995. */ +/* Win32 code written by Tor Lillqvist <tml@iki.fi>. */ +/* Renamed _nl_locale_name, removed unsed args, removed include files, + non-W32 code and changed comments <wk@gnupg.org>. */ + +/* Mingw headers don't have latest language and sublanguage codes. */ +#ifndef LANG_AFRIKAANS +#define LANG_AFRIKAANS 0x36 +#endif +#ifndef LANG_ALBANIAN +#define LANG_ALBANIAN 0x1c +#endif +#ifndef LANG_AMHARIC +#define LANG_AMHARIC 0x5e +#endif +#ifndef LANG_ARABIC +#define LANG_ARABIC 0x01 +#endif +#ifndef LANG_ARMENIAN +#define LANG_ARMENIAN 0x2b +#endif +#ifndef LANG_ASSAMESE +#define LANG_ASSAMESE 0x4d +#endif +#ifndef LANG_AZERI +#define LANG_AZERI 0x2c +#endif +#ifndef LANG_BASQUE +#define LANG_BASQUE 0x2d +#endif +#ifndef LANG_BELARUSIAN +#define LANG_BELARUSIAN 0x23 +#endif +#ifndef LANG_BENGALI +#define LANG_BENGALI 0x45 +#endif +#ifndef LANG_BURMESE +#define LANG_BURMESE 0x55 +#endif +#ifndef LANG_CAMBODIAN +#define LANG_CAMBODIAN 0x53 +#endif +#ifndef LANG_CATALAN +#define LANG_CATALAN 0x03 +#endif +#ifndef LANG_CHEROKEE +#define LANG_CHEROKEE 0x5c +#endif +#ifndef LANG_DIVEHI +#define LANG_DIVEHI 0x65 +#endif +#ifndef LANG_EDO +#define LANG_EDO 0x66 +#endif +#ifndef LANG_ESTONIAN +#define LANG_ESTONIAN 0x25 +#endif +#ifndef LANG_FAEROESE +#define LANG_FAEROESE 0x38 +#endif +#ifndef LANG_FARSI +#define LANG_FARSI 0x29 +#endif +#ifndef LANG_FRISIAN +#define LANG_FRISIAN 0x62 +#endif +#ifndef LANG_FULFULDE +#define LANG_FULFULDE 0x67 +#endif +#ifndef LANG_GAELIC +#define LANG_GAELIC 0x3c +#endif +#ifndef LANG_GALICIAN +#define LANG_GALICIAN 0x56 +#endif +#ifndef LANG_GEORGIAN +#define LANG_GEORGIAN 0x37 +#endif +#ifndef LANG_GUARANI +#define LANG_GUARANI 0x74 +#endif +#ifndef LANG_GUJARATI +#define LANG_GUJARATI 0x47 +#endif +#ifndef LANG_HAUSA +#define LANG_HAUSA 0x68 +#endif +#ifndef LANG_HAWAIIAN +#define LANG_HAWAIIAN 0x75 +#endif +#ifndef LANG_HEBREW +#define LANG_HEBREW 0x0d +#endif +#ifndef LANG_HINDI +#define LANG_HINDI 0x39 +#endif +#ifndef LANG_IBIBIO +#define LANG_IBIBIO 0x69 +#endif +#ifndef LANG_IGBO +#define LANG_IGBO 0x70 +#endif +#ifndef LANG_INDONESIAN +#define LANG_INDONESIAN 0x21 +#endif +#ifndef LANG_INUKTITUT +#define LANG_INUKTITUT 0x5d +#endif +#ifndef LANG_KANNADA +#define LANG_KANNADA 0x4b +#endif +#ifndef LANG_KANURI +#define LANG_KANURI 0x71 +#endif +#ifndef LANG_KASHMIRI +#define LANG_KASHMIRI 0x60 +#endif +#ifndef LANG_KAZAK +#define LANG_KAZAK 0x3f +#endif +#ifndef LANG_KONKANI +#define LANG_KONKANI 0x57 +#endif +#ifndef LANG_KYRGYZ +#define LANG_KYRGYZ 0x40 +#endif +#ifndef LANG_LAO +#define LANG_LAO 0x54 +#endif +#ifndef LANG_LATIN +#define LANG_LATIN 0x76 +#endif +#ifndef LANG_LATVIAN +#define LANG_LATVIAN 0x26 +#endif +#ifndef LANG_LITHUANIAN +#define LANG_LITHUANIAN 0x27 +#endif +#ifndef LANG_MACEDONIAN +#define LANG_MACEDONIAN 0x2f +#endif +#ifndef LANG_MALAY +#define LANG_MALAY 0x3e +#endif +#ifndef LANG_MALAYALAM +#define LANG_MALAYALAM 0x4c +#endif +#ifndef LANG_MALTESE +#define LANG_MALTESE 0x3a +#endif +#ifndef LANG_MANIPURI +#define LANG_MANIPURI 0x58 +#endif +#ifndef LANG_MARATHI +#define LANG_MARATHI 0x4e +#endif +#ifndef LANG_MONGOLIAN +#define LANG_MONGOLIAN 0x50 +#endif +#ifndef LANG_NEPALI +#define LANG_NEPALI 0x61 +#endif +#ifndef LANG_ORIYA +#define LANG_ORIYA 0x48 +#endif +#ifndef LANG_OROMO +#define LANG_OROMO 0x72 +#endif +#ifndef LANG_PAPIAMENTU +#define LANG_PAPIAMENTU 0x79 +#endif +#ifndef LANG_PASHTO +#define LANG_PASHTO 0x63 +#endif +#ifndef LANG_PUNJABI +#define LANG_PUNJABI 0x46 +#endif +#ifndef LANG_RHAETO_ROMANCE +#define LANG_RHAETO_ROMANCE 0x17 +#endif +#ifndef LANG_SAAMI +#define LANG_SAAMI 0x3b +#endif +#ifndef LANG_SANSKRIT +#define LANG_SANSKRIT 0x4f +#endif +#ifndef LANG_SERBIAN +#define LANG_SERBIAN 0x1a +#endif +#ifndef LANG_SINDHI +#define LANG_SINDHI 0x59 +#endif +#ifndef LANG_SINHALESE +#define LANG_SINHALESE 0x5b +#endif +#ifndef LANG_SLOVAK +#define LANG_SLOVAK 0x1b +#endif +#ifndef LANG_SOMALI +#define LANG_SOMALI 0x77 +#endif +#ifndef LANG_SORBIAN +#define LANG_SORBIAN 0x2e +#endif +#ifndef LANG_SUTU +#define LANG_SUTU 0x30 +#endif +#ifndef LANG_SWAHILI +#define LANG_SWAHILI 0x41 +#endif +#ifndef LANG_SYRIAC +#define LANG_SYRIAC 0x5a +#endif +#ifndef LANG_TAGALOG +#define LANG_TAGALOG 0x64 +#endif +#ifndef LANG_TAJIK +#define LANG_TAJIK 0x28 +#endif +#ifndef LANG_TAMAZIGHT +#define LANG_TAMAZIGHT 0x5f +#endif +#ifndef LANG_TAMIL +#define LANG_TAMIL 0x49 +#endif +#ifndef LANG_TATAR +#define LANG_TATAR 0x44 +#endif +#ifndef LANG_TELUGU +#define LANG_TELUGU 0x4a +#endif +#ifndef LANG_THAI +#define LANG_THAI 0x1e +#endif +#ifndef LANG_TIBETAN +#define LANG_TIBETAN 0x51 +#endif +#ifndef LANG_TIGRINYA +#define LANG_TIGRINYA 0x73 +#endif +#ifndef LANG_TSONGA +#define LANG_TSONGA 0x31 +#endif +#ifndef LANG_TSWANA +#define LANG_TSWANA 0x32 +#endif +#ifndef LANG_TURKMEN +#define LANG_TURKMEN 0x42 +#endif +#ifndef LANG_UKRAINIAN +#define LANG_UKRAINIAN 0x22 +#endif +#ifndef LANG_URDU +#define LANG_URDU 0x20 +#endif +#ifndef LANG_UZBEK +#define LANG_UZBEK 0x43 +#endif +#ifndef LANG_VENDA +#define LANG_VENDA 0x33 +#endif +#ifndef LANG_VIETNAMESE +#define LANG_VIETNAMESE 0x2a +#endif +#ifndef LANG_WELSH +#define LANG_WELSH 0x52 +#endif +#ifndef LANG_XHOSA +#define LANG_XHOSA 0x34 +#endif +#ifndef LANG_YI +#define LANG_YI 0x78 +#endif +#ifndef LANG_YIDDISH +#define LANG_YIDDISH 0x3d +#endif +#ifndef LANG_YORUBA +#define LANG_YORUBA 0x6a +#endif +#ifndef LANG_ZULU +#define LANG_ZULU 0x35 +#endif +#ifndef SUBLANG_ARABIC_SAUDI_ARABIA +#define SUBLANG_ARABIC_SAUDI_ARABIA 0x01 +#endif +#ifndef SUBLANG_ARABIC_IRAQ +#define SUBLANG_ARABIC_IRAQ 0x02 +#endif +#ifndef SUBLANG_ARABIC_EGYPT +#define SUBLANG_ARABIC_EGYPT 0x03 +#endif +#ifndef SUBLANG_ARABIC_LIBYA +#define SUBLANG_ARABIC_LIBYA 0x04 +#endif +#ifndef SUBLANG_ARABIC_ALGERIA +#define SUBLANG_ARABIC_ALGERIA 0x05 +#endif +#ifndef SUBLANG_ARABIC_MOROCCO +#define SUBLANG_ARABIC_MOROCCO 0x06 +#endif +#ifndef SUBLANG_ARABIC_TUNISIA +#define SUBLANG_ARABIC_TUNISIA 0x07 +#endif +#ifndef SUBLANG_ARABIC_OMAN +#define SUBLANG_ARABIC_OMAN 0x08 +#endif +#ifndef SUBLANG_ARABIC_YEMEN +#define SUBLANG_ARABIC_YEMEN 0x09 +#endif +#ifndef SUBLANG_ARABIC_SYRIA +#define SUBLANG_ARABIC_SYRIA 0x0a +#endif +#ifndef SUBLANG_ARABIC_JORDAN +#define SUBLANG_ARABIC_JORDAN 0x0b +#endif +#ifndef SUBLANG_ARABIC_LEBANON +#define SUBLANG_ARABIC_LEBANON 0x0c +#endif +#ifndef SUBLANG_ARABIC_KUWAIT +#define SUBLANG_ARABIC_KUWAIT 0x0d +#endif +#ifndef SUBLANG_ARABIC_UAE +#define SUBLANG_ARABIC_UAE 0x0e +#endif +#ifndef SUBLANG_ARABIC_BAHRAIN +#define SUBLANG_ARABIC_BAHRAIN 0x0f +#endif +#ifndef SUBLANG_ARABIC_QATAR +#define SUBLANG_ARABIC_QATAR 0x10 +#endif +#ifndef SUBLANG_AZERI_LATIN +#define SUBLANG_AZERI_LATIN 0x01 +#endif +#ifndef SUBLANG_AZERI_CYRILLIC +#define SUBLANG_AZERI_CYRILLIC 0x02 +#endif +#ifndef SUBLANG_BENGALI_INDIA +#define SUBLANG_BENGALI_INDIA 0x01 +#endif +#ifndef SUBLANG_BENGALI_BANGLADESH +#define SUBLANG_BENGALI_BANGLADESH 0x02 +#endif +#ifndef SUBLANG_CHINESE_MACAU +#define SUBLANG_CHINESE_MACAU 0x05 +#endif +#ifndef SUBLANG_ENGLISH_SOUTH_AFRICA +#define SUBLANG_ENGLISH_SOUTH_AFRICA 0x07 +#endif +#ifndef SUBLANG_ENGLISH_JAMAICA +#define SUBLANG_ENGLISH_JAMAICA 0x08 +#endif +#ifndef SUBLANG_ENGLISH_CARIBBEAN +#define SUBLANG_ENGLISH_CARIBBEAN 0x09 +#endif +#ifndef SUBLANG_ENGLISH_BELIZE +#define SUBLANG_ENGLISH_BELIZE 0x0a +#endif +#ifndef SUBLANG_ENGLISH_TRINIDAD +#define SUBLANG_ENGLISH_TRINIDAD 0x0b +#endif +#ifndef SUBLANG_ENGLISH_ZIMBABWE +#define SUBLANG_ENGLISH_ZIMBABWE 0x0c +#endif +#ifndef SUBLANG_ENGLISH_PHILIPPINES +#define SUBLANG_ENGLISH_PHILIPPINES 0x0d +#endif +#ifndef SUBLANG_ENGLISH_INDONESIA +#define SUBLANG_ENGLISH_INDONESIA 0x0e +#endif +#ifndef SUBLANG_ENGLISH_HONGKONG +#define SUBLANG_ENGLISH_HONGKONG 0x0f +#endif +#ifndef SUBLANG_ENGLISH_INDIA +#define SUBLANG_ENGLISH_INDIA 0x10 +#endif +#ifndef SUBLANG_ENGLISH_MALAYSIA +#define SUBLANG_ENGLISH_MALAYSIA 0x11 +#endif +#ifndef SUBLANG_ENGLISH_SINGAPORE +#define SUBLANG_ENGLISH_SINGAPORE 0x12 +#endif +#ifndef SUBLANG_FRENCH_LUXEMBOURG +#define SUBLANG_FRENCH_LUXEMBOURG 0x05 +#endif +#ifndef SUBLANG_FRENCH_MONACO +#define SUBLANG_FRENCH_MONACO 0x06 +#endif +#ifndef SUBLANG_FRENCH_WESTINDIES +#define SUBLANG_FRENCH_WESTINDIES 0x07 +#endif +#ifndef SUBLANG_FRENCH_REUNION +#define SUBLANG_FRENCH_REUNION 0x08 +#endif +#ifndef SUBLANG_FRENCH_CONGO +#define SUBLANG_FRENCH_CONGO 0x09 +#endif +#ifndef SUBLANG_FRENCH_SENEGAL +#define SUBLANG_FRENCH_SENEGAL 0x0a +#endif +#ifndef SUBLANG_FRENCH_CAMEROON +#define SUBLANG_FRENCH_CAMEROON 0x0b +#endif +#ifndef SUBLANG_FRENCH_COTEDIVOIRE +#define SUBLANG_FRENCH_COTEDIVOIRE 0x0c +#endif +#ifndef SUBLANG_FRENCH_MALI +#define SUBLANG_FRENCH_MALI 0x0d +#endif +#ifndef SUBLANG_FRENCH_MOROCCO +#define SUBLANG_FRENCH_MOROCCO 0x0e +#endif +#ifndef SUBLANG_FRENCH_HAITI +#define SUBLANG_FRENCH_HAITI 0x0f +#endif +#ifndef SUBLANG_GERMAN_LUXEMBOURG +#define SUBLANG_GERMAN_LUXEMBOURG 0x04 +#endif +#ifndef SUBLANG_GERMAN_LIECHTENSTEIN +#define SUBLANG_GERMAN_LIECHTENSTEIN 0x05 +#endif +#ifndef SUBLANG_KASHMIRI_INDIA +#define SUBLANG_KASHMIRI_INDIA 0x02 +#endif +#ifndef SUBLANG_MALAY_MALAYSIA +#define SUBLANG_MALAY_MALAYSIA 0x01 +#endif +#ifndef SUBLANG_MALAY_BRUNEI_DARUSSALAM +#define SUBLANG_MALAY_BRUNEI_DARUSSALAM 0x02 +#endif +#ifndef SUBLANG_NEPALI_INDIA +#define SUBLANG_NEPALI_INDIA 0x02 +#endif +#ifndef SUBLANG_PUNJABI_INDIA +#define SUBLANG_PUNJABI_INDIA 0x01 +#endif +#ifndef SUBLANG_ROMANIAN_ROMANIA +#define SUBLANG_ROMANIAN_ROMANIA 0x01 +#endif +#ifndef SUBLANG_SERBIAN_LATIN +#define SUBLANG_SERBIAN_LATIN 0x02 +#endif +#ifndef SUBLANG_SERBIAN_CYRILLIC +#define SUBLANG_SERBIAN_CYRILLIC 0x03 +#endif +#ifndef SUBLANG_SINDHI_INDIA +#define SUBLANG_SINDHI_INDIA 0x00 +#endif +#ifndef SUBLANG_SINDHI_PAKISTAN +#define SUBLANG_SINDHI_PAKISTAN 0x01 +#endif +#ifndef SUBLANG_SPANISH_GUATEMALA +#define SUBLANG_SPANISH_GUATEMALA 0x04 +#endif +#ifndef SUBLANG_SPANISH_COSTA_RICA +#define SUBLANG_SPANISH_COSTA_RICA 0x05 +#endif +#ifndef SUBLANG_SPANISH_PANAMA +#define SUBLANG_SPANISH_PANAMA 0x06 +#endif +#ifndef SUBLANG_SPANISH_DOMINICAN_REPUBLIC +#define SUBLANG_SPANISH_DOMINICAN_REPUBLIC 0x07 +#endif +#ifndef SUBLANG_SPANISH_VENEZUELA +#define SUBLANG_SPANISH_VENEZUELA 0x08 +#endif +#ifndef SUBLANG_SPANISH_COLOMBIA +#define SUBLANG_SPANISH_COLOMBIA 0x09 +#endif +#ifndef SUBLANG_SPANISH_PERU +#define SUBLANG_SPANISH_PERU 0x0a +#endif +#ifndef SUBLANG_SPANISH_ARGENTINA +#define SUBLANG_SPANISH_ARGENTINA 0x0b +#endif +#ifndef SUBLANG_SPANISH_ECUADOR +#define SUBLANG_SPANISH_ECUADOR 0x0c +#endif +#ifndef SUBLANG_SPANISH_CHILE +#define SUBLANG_SPANISH_CHILE 0x0d +#endif +#ifndef SUBLANG_SPANISH_URUGUAY +#define SUBLANG_SPANISH_URUGUAY 0x0e +#endif +#ifndef SUBLANG_SPANISH_PARAGUAY +#define SUBLANG_SPANISH_PARAGUAY 0x0f +#endif +#ifndef SUBLANG_SPANISH_BOLIVIA +#define SUBLANG_SPANISH_BOLIVIA 0x10 +#endif +#ifndef SUBLANG_SPANISH_EL_SALVADOR +#define SUBLANG_SPANISH_EL_SALVADOR 0x11 +#endif +#ifndef SUBLANG_SPANISH_HONDURAS +#define SUBLANG_SPANISH_HONDURAS 0x12 +#endif +#ifndef SUBLANG_SPANISH_NICARAGUA +#define SUBLANG_SPANISH_NICARAGUA 0x13 +#endif +#ifndef SUBLANG_SPANISH_PUERTO_RICO +#define SUBLANG_SPANISH_PUERTO_RICO 0x14 +#endif +#ifndef SUBLANG_SWEDISH_FINLAND +#define SUBLANG_SWEDISH_FINLAND 0x02 +#endif +#ifndef SUBLANG_TAMAZIGHT_ARABIC +#define SUBLANG_TAMAZIGHT_ARABIC 0x01 +#endif +#ifndef SUBLANG_TAMAZIGHT_LATIN +#define SUBLANG_TAMAZIGHT_LATIN 0x02 +#endif +#ifndef SUBLANG_TIGRINYA_ETHIOPIA +#define SUBLANG_TIGRINYA_ETHIOPIA 0x00 +#endif +#ifndef SUBLANG_TIGRINYA_ERITREA +#define SUBLANG_TIGRINYA_ERITREA 0x01 +#endif +#ifndef SUBLANG_URDU_PAKISTAN +#define SUBLANG_URDU_PAKISTAN 0x01 +#endif +#ifndef SUBLANG_URDU_INDIA +#define SUBLANG_URDU_INDIA 0x02 +#endif +#ifndef SUBLANG_UZBEK_LATIN +#define SUBLANG_UZBEK_LATIN 0x01 +#endif +#ifndef SUBLANG_UZBEK_CYRILLIC +#define SUBLANG_UZBEK_CYRILLIC 0x02 +#endif + +/* Return an XPG style locale name + language[_territory[.codeset]][@modifier]. + Don't even bother determining the codeset; it's not useful in this + context, because message catalogs are not specific to a single + codeset. The result must not be freed; it is statically + allocated. */ +static const char * +my_nl_locale_name (const char *categoryname) +{ + const char *retval; + LCID lcid; + LANGID langid; + int primary, sub; + + /* Let the user override the system settings through environment + variables, as on POSIX systems. */ + retval = getenv ("LC_ALL"); + if (retval != NULL && retval[0] != '\0') + return retval; + retval = getenv (categoryname); + if (retval != NULL && retval[0] != '\0') + return retval; + retval = getenv ("LANG"); + if (retval != NULL && retval[0] != '\0') + return retval; + + /* Use native Win32 API locale ID. */ + lcid = GetThreadLocale (); + + /* Strip off the sorting rules, keep only the language part. */ + langid = LANGIDFROMLCID (lcid); + + /* Split into language and territory part. */ + primary = PRIMARYLANGID (langid); + sub = SUBLANGID (langid); + + /* Dispatch on language. + See also http://www.unicode.org/unicode/onlinedat/languages.html . + For details about languages, see http://www.ethnologue.com/ . */ + switch (primary) + { + case LANG_AFRIKAANS: return "af_ZA"; + case LANG_ALBANIAN: return "sq_AL"; + case LANG_AMHARIC: return "am_ET"; + case LANG_ARABIC: + switch (sub) + { + case SUBLANG_ARABIC_SAUDI_ARABIA: return "ar_SA"; + case SUBLANG_ARABIC_IRAQ: return "ar_IQ"; + case SUBLANG_ARABIC_EGYPT: return "ar_EG"; + case SUBLANG_ARABIC_LIBYA: return "ar_LY"; + case SUBLANG_ARABIC_ALGERIA: return "ar_DZ"; + case SUBLANG_ARABIC_MOROCCO: return "ar_MA"; + case SUBLANG_ARABIC_TUNISIA: return "ar_TN"; + case SUBLANG_ARABIC_OMAN: return "ar_OM"; + case SUBLANG_ARABIC_YEMEN: return "ar_YE"; + case SUBLANG_ARABIC_SYRIA: return "ar_SY"; + case SUBLANG_ARABIC_JORDAN: return "ar_JO"; + case SUBLANG_ARABIC_LEBANON: return "ar_LB"; + case SUBLANG_ARABIC_KUWAIT: return "ar_KW"; + case SUBLANG_ARABIC_UAE: return "ar_AE"; + case SUBLANG_ARABIC_BAHRAIN: return "ar_BH"; + case SUBLANG_ARABIC_QATAR: return "ar_QA"; + } + return "ar"; + case LANG_ARMENIAN: return "hy_AM"; + case LANG_ASSAMESE: return "as_IN"; + case LANG_AZERI: + switch (sub) + { + /* FIXME: Adjust this when Azerbaijani locales appear on Unix. */ + case SUBLANG_AZERI_LATIN: return "az_AZ@latin"; + case SUBLANG_AZERI_CYRILLIC: return "az_AZ@cyrillic"; + } + return "az"; + case LANG_BASQUE: + return "eu"; /* Ambiguous: could be "eu_ES" or "eu_FR". */ + case LANG_BELARUSIAN: return "be_BY"; + case LANG_BENGALI: + switch (sub) + { + case SUBLANG_BENGALI_INDIA: return "bn_IN"; + case SUBLANG_BENGALI_BANGLADESH: return "bn_BD"; + } + return "bn"; + case LANG_BULGARIAN: return "bg_BG"; + case LANG_BURMESE: return "my_MM"; + case LANG_CAMBODIAN: return "km_KH"; + case LANG_CATALAN: return "ca_ES"; + case LANG_CHEROKEE: return "chr_US"; + case LANG_CHINESE: + switch (sub) + { + case SUBLANG_CHINESE_TRADITIONAL: return "zh_TW"; + case SUBLANG_CHINESE_SIMPLIFIED: return "zh_CN"; + case SUBLANG_CHINESE_HONGKONG: return "zh_HK"; + case SUBLANG_CHINESE_SINGAPORE: return "zh_SG"; + case SUBLANG_CHINESE_MACAU: return "zh_MO"; + } + return "zh"; + case LANG_CROATIAN: /* LANG_CROATIAN == LANG_SERBIAN + * What used to be called Serbo-Croatian + * should really now be two separate + * languages because of political reasons. + * (Says tml, who knows nothing about Serbian + * or Croatian.) + * (I can feel those flames coming already.) + */ + switch (sub) + { + case SUBLANG_DEFAULT: return "hr_HR"; + case SUBLANG_SERBIAN_LATIN: return "sr_CS"; + case SUBLANG_SERBIAN_CYRILLIC: return "sr_CS@cyrillic"; + } + return "hr"; + case LANG_CZECH: return "cs_CZ"; + case LANG_DANISH: return "da_DK"; + case LANG_DIVEHI: return "div_MV"; + case LANG_DUTCH: + switch (sub) + { + case SUBLANG_DUTCH: return "nl_NL"; + case SUBLANG_DUTCH_BELGIAN: /* FLEMISH, VLAAMS */ return "nl_BE"; + } + return "nl"; + case LANG_EDO: return "bin_NG"; + case LANG_ENGLISH: + switch (sub) + { + /* SUBLANG_ENGLISH_US == SUBLANG_DEFAULT. Heh. I thought + * English was the language spoken in England. + * Oh well. + */ + case SUBLANG_ENGLISH_US: return "en_US"; + case SUBLANG_ENGLISH_UK: return "en_GB"; + case SUBLANG_ENGLISH_AUS: return "en_AU"; + case SUBLANG_ENGLISH_CAN: return "en_CA"; + case SUBLANG_ENGLISH_NZ: return "en_NZ"; + case SUBLANG_ENGLISH_EIRE: return "en_IE"; + case SUBLANG_ENGLISH_SOUTH_AFRICA: return "en_ZA"; + case SUBLANG_ENGLISH_JAMAICA: return "en_JM"; + case SUBLANG_ENGLISH_CARIBBEAN: return "en_GD"; /* Grenada? */ + case SUBLANG_ENGLISH_BELIZE: return "en_BZ"; + case SUBLANG_ENGLISH_TRINIDAD: return "en_TT"; + case SUBLANG_ENGLISH_ZIMBABWE: return "en_ZW"; + case SUBLANG_ENGLISH_PHILIPPINES: return "en_PH"; + case SUBLANG_ENGLISH_INDONESIA: return "en_ID"; + case SUBLANG_ENGLISH_HONGKONG: return "en_HK"; + case SUBLANG_ENGLISH_INDIA: return "en_IN"; + case SUBLANG_ENGLISH_MALAYSIA: return "en_MY"; + case SUBLANG_ENGLISH_SINGAPORE: return "en_SG"; + } + return "en"; + case LANG_ESTONIAN: return "et_EE"; + case LANG_FAEROESE: return "fo_FO"; + case LANG_FARSI: return "fa_IR"; + case LANG_FINNISH: return "fi_FI"; + case LANG_FRENCH: + switch (sub) + { + case SUBLANG_FRENCH: return "fr_FR"; + case SUBLANG_FRENCH_BELGIAN: /* WALLOON */ return "fr_BE"; + case SUBLANG_FRENCH_CANADIAN: return "fr_CA"; + case SUBLANG_FRENCH_SWISS: return "fr_CH"; + case SUBLANG_FRENCH_LUXEMBOURG: return "fr_LU"; + case SUBLANG_FRENCH_MONACO: return "fr_MC"; + case SUBLANG_FRENCH_WESTINDIES: return "fr"; /* Caribbean? */ + case SUBLANG_FRENCH_REUNION: return "fr_RE"; + case SUBLANG_FRENCH_CONGO: return "fr_CG"; + case SUBLANG_FRENCH_SENEGAL: return "fr_SN"; + case SUBLANG_FRENCH_CAMEROON: return "fr_CM"; + case SUBLANG_FRENCH_COTEDIVOIRE: return "fr_CI"; + case SUBLANG_FRENCH_MALI: return "fr_ML"; + case SUBLANG_FRENCH_MOROCCO: return "fr_MA"; + case SUBLANG_FRENCH_HAITI: return "fr_HT"; + } + return "fr"; + case LANG_FRISIAN: return "fy_NL"; + case LANG_FULFULDE: return "ful_NG"; + case LANG_GAELIC: + switch (sub) + { + case 0x01: /* SCOTTISH */ return "gd_GB"; + case 0x02: /* IRISH */ return "ga_IE"; + } + return "C"; + case LANG_GALICIAN: return "gl_ES"; + case LANG_GEORGIAN: return "ka_GE"; + case LANG_GERMAN: + switch (sub) + { + case SUBLANG_GERMAN: return "de_DE"; + case SUBLANG_GERMAN_SWISS: return "de_CH"; + case SUBLANG_GERMAN_AUSTRIAN: return "de_AT"; + case SUBLANG_GERMAN_LUXEMBOURG: return "de_LU"; + case SUBLANG_GERMAN_LIECHTENSTEIN: return "de_LI"; + } + return "de"; + case LANG_GREEK: return "el_GR"; + case LANG_GUARANI: return "gn_PY"; + case LANG_GUJARATI: return "gu_IN"; + case LANG_HAUSA: return "ha_NG"; + case LANG_HAWAIIAN: + /* FIXME: Do they mean Hawaiian ("haw_US", 1000 speakers) + or Hawaii Creole English ("cpe_US", 600000 speakers)? */ + return "cpe_US"; + case LANG_HEBREW: return "he_IL"; + case LANG_HINDI: return "hi_IN"; + case LANG_HUNGARIAN: return "hu_HU"; + case LANG_IBIBIO: return "nic_NG"; + case LANG_ICELANDIC: return "is_IS"; + case LANG_IGBO: return "ibo_NG"; + case LANG_INDONESIAN: return "id_ID"; + case LANG_INUKTITUT: return "iu_CA"; + case LANG_ITALIAN: + switch (sub) + { + case SUBLANG_ITALIAN: return "it_IT"; + case SUBLANG_ITALIAN_SWISS: return "it_CH"; + } + return "it"; + case LANG_JAPANESE: return "ja_JP"; + case LANG_KANNADA: return "kn_IN"; + case LANG_KANURI: return "kau_NG"; + case LANG_KASHMIRI: + switch (sub) + { + case SUBLANG_DEFAULT: return "ks_PK"; + case SUBLANG_KASHMIRI_INDIA: return "ks_IN"; + } + return "ks"; + case LANG_KAZAK: return "kk_KZ"; + case LANG_KONKANI: + /* FIXME: Adjust this when such locales appear on Unix. */ + return "kok_IN"; + case LANG_KOREAN: return "ko_KR"; + case LANG_KYRGYZ: return "ky_KG"; + case LANG_LAO: return "lo_LA"; + case LANG_LATIN: return "la_VA"; + case LANG_LATVIAN: return "lv_LV"; + case LANG_LITHUANIAN: return "lt_LT"; + case LANG_MACEDONIAN: return "mk_MK"; + case LANG_MALAY: + switch (sub) + { + case SUBLANG_MALAY_MALAYSIA: return "ms_MY"; + case SUBLANG_MALAY_BRUNEI_DARUSSALAM: return "ms_BN"; + } + return "ms"; + case LANG_MALAYALAM: return "ml_IN"; + case LANG_MALTESE: return "mt_MT"; + case LANG_MANIPURI: + /* FIXME: Adjust this when such locales appear on Unix. */ + return "mni_IN"; + case LANG_MARATHI: return "mr_IN"; + case LANG_MONGOLIAN: + return "mn"; /* Ambiguous: could be "mn_CN" or "mn_MN". */ + case LANG_NEPALI: + switch (sub) + { + case SUBLANG_DEFAULT: return "ne_NP"; + case SUBLANG_NEPALI_INDIA: return "ne_IN"; + } + return "ne"; + case LANG_NORWEGIAN: + switch (sub) + { + case SUBLANG_NORWEGIAN_BOKMAL: return "no_NO"; + case SUBLANG_NORWEGIAN_NYNORSK: return "nn_NO"; + } + return "no"; + case LANG_ORIYA: return "or_IN"; + case LANG_OROMO: return "om_ET"; + case LANG_PAPIAMENTU: return "pap_AN"; + case LANG_PASHTO: + return "ps"; /* Ambiguous: could be "ps_PK" or "ps_AF". */ + case LANG_POLISH: return "pl_PL"; + case LANG_PORTUGUESE: + switch (sub) + { + case SUBLANG_PORTUGUESE: return "pt_PT"; + /* Hmm. SUBLANG_PORTUGUESE_BRAZILIAN == SUBLANG_DEFAULT. + Same phenomenon as SUBLANG_ENGLISH_US == SUBLANG_DEFAULT. */ + case SUBLANG_PORTUGUESE_BRAZILIAN: return "pt_BR"; + } + return "pt"; + case LANG_PUNJABI: + switch (sub) + { + case SUBLANG_PUNJABI_INDIA: return "pa_IN"; /* Gurmukhi script */ + } + return "pa"; + case LANG_RHAETO_ROMANCE: return "rm_CH"; + case LANG_ROMANIAN: + switch (sub) + { + case SUBLANG_ROMANIAN_ROMANIA: return "ro_RO"; + } + return "ro"; + case LANG_RUSSIAN: + return "ru"; /* Ambiguous: could be "ru_RU" or "ru_UA" or "ru_MD". */ + case LANG_SAAMI: /* actually Northern Sami */ return "se_NO"; + case LANG_SANSKRIT: return "sa_IN"; + case LANG_SINDHI: + switch (sub) + { + case SUBLANG_SINDHI_INDIA: return "sd_IN"; + case SUBLANG_SINDHI_PAKISTAN: return "sd_PK"; + } + return "sd"; + case LANG_SINHALESE: return "si_LK"; + case LANG_SLOVAK: return "sk_SK"; + case LANG_SLOVENIAN: return "sl_SI"; + case LANG_SOMALI: return "so_SO"; + case LANG_SORBIAN: + /* FIXME: Adjust this when such locales appear on Unix. */ + return "wen_DE"; + case LANG_SPANISH: + switch (sub) + { + case SUBLANG_SPANISH: return "es_ES"; + case SUBLANG_SPANISH_MEXICAN: return "es_MX"; + case SUBLANG_SPANISH_MODERN: + return "es_ES@modern"; /* not seen on Unix */ + case SUBLANG_SPANISH_GUATEMALA: return "es_GT"; + case SUBLANG_SPANISH_COSTA_RICA: return "es_CR"; + case SUBLANG_SPANISH_PANAMA: return "es_PA"; + case SUBLANG_SPANISH_DOMINICAN_REPUBLIC: return "es_DO"; + case SUBLANG_SPANISH_VENEZUELA: return "es_VE"; + case SUBLANG_SPANISH_COLOMBIA: return "es_CO"; + case SUBLANG_SPANISH_PERU: return "es_PE"; + case SUBLANG_SPANISH_ARGENTINA: return "es_AR"; + case SUBLANG_SPANISH_ECUADOR: return "es_EC"; + case SUBLANG_SPANISH_CHILE: return "es_CL"; + case SUBLANG_SPANISH_URUGUAY: return "es_UY"; + case SUBLANG_SPANISH_PARAGUAY: return "es_PY"; + case SUBLANG_SPANISH_BOLIVIA: return "es_BO"; + case SUBLANG_SPANISH_EL_SALVADOR: return "es_SV"; + case SUBLANG_SPANISH_HONDURAS: return "es_HN"; + case SUBLANG_SPANISH_NICARAGUA: return "es_NI"; + case SUBLANG_SPANISH_PUERTO_RICO: return "es_PR"; + } + return "es"; + case LANG_SUTU: return "bnt_TZ"; /* or "st_LS" or "nso_ZA"? */ + case LANG_SWAHILI: return "sw_KE"; + case LANG_SWEDISH: + switch (sub) + { + case SUBLANG_DEFAULT: return "sv_SE"; + case SUBLANG_SWEDISH_FINLAND: return "sv_FI"; + } + return "sv"; + case LANG_SYRIAC: return "syr_TR"; /* An extinct language. */ + case LANG_TAGALOG: return "tl_PH"; + case LANG_TAJIK: return "tg_TJ"; + case LANG_TAMAZIGHT: + switch (sub) + { + /* FIXME: Adjust this when Tamazight locales appear on Unix. */ + case SUBLANG_TAMAZIGHT_ARABIC: return "ber_MA@arabic"; + case SUBLANG_TAMAZIGHT_LATIN: return "ber_MA@latin"; + } + return "ber_MA"; + case LANG_TAMIL: + return "ta"; /* Ambiguous: could be "ta_IN" or "ta_LK" or "ta_SG". */ + case LANG_TATAR: return "tt_RU"; + case LANG_TELUGU: return "te_IN"; + case LANG_THAI: return "th_TH"; + case LANG_TIBETAN: return "bo_CN"; + case LANG_TIGRINYA: + switch (sub) + { + case SUBLANG_TIGRINYA_ETHIOPIA: return "ti_ET"; + case SUBLANG_TIGRINYA_ERITREA: return "ti_ER"; + } + return "ti"; + case LANG_TSONGA: return "ts_ZA"; + case LANG_TSWANA: return "tn_BW"; + case LANG_TURKISH: return "tr_TR"; + case LANG_TURKMEN: return "tk_TM"; + case LANG_UKRAINIAN: return "uk_UA"; + case LANG_URDU: + switch (sub) + { + case SUBLANG_URDU_PAKISTAN: return "ur_PK"; + case SUBLANG_URDU_INDIA: return "ur_IN"; + } + return "ur"; + case LANG_UZBEK: + switch (sub) + { + case SUBLANG_UZBEK_LATIN: return "uz_UZ"; + case SUBLANG_UZBEK_CYRILLIC: return "uz_UZ@cyrillic"; + } + return "uz"; + case LANG_VENDA: + /* FIXME: It's not clear whether Venda has the ISO 639-2 two-letter code + "ve" or not. + http://www.loc.gov/standards/iso639-2/englangn.html has it, but + http://lcweb.loc.gov/standards/iso639-2/codechanges.html doesn't, */ + return "ven_ZA"; /* or "ve_ZA"? */ + case LANG_VIETNAMESE: return "vi_VN"; + case LANG_WELSH: return "cy_GB"; + case LANG_XHOSA: return "xh_ZA"; + case LANG_YI: return "sit_CN"; + case LANG_YIDDISH: return "yi_IL"; + case LANG_YORUBA: return "yo_NG"; + case LANG_ZULU: return "zu_ZA"; + default: return "C"; + } +} + +/* localname.c from gettext END. */ + + + +/* Support functions. */ + +static __inline__ uint32_t +do_swap_u32 (uint32_t 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__ unsigned long +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 char * */ +/* my_xstrdup (const char *s) */ +/* { */ +/* size_t n = strlen (s) + 1; */ +/* char *p = jnlib_malloc (n); */ +/* if (!p) */ +/* abort (); */ +/* strcpy (p, s); */ +/* return p; */ +/* } */ + + + +/* Generic message catalog and gettext stuff. */ + +/* 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. */ + uint32_t magic; + /* The revision number of the file format. */ + uint32_t revision; + /* The number of strings pairs. */ + uint32_t nstrings; + /* Offset of table with start offsets of original strings. */ + uint32_t orig_tab_offset; + /* Offset of table with start offsets of translation strings. */ + uint32_t trans_tab_offset; + /* Size of hashing table. */ + uint32_t hash_tab_size; + /* Offset of first hashing entry. */ + uint32_t hash_tab_offset; +}; + + +struct string_desc +{ + /* Length of addressed string. */ + uint32_t length; + /* Offset of string in file. */ + uint32_t offset; +}; + + +struct overflow_space_s +{ + struct overflow_space_s *next; + uint32_t idx; + uint32_t length; + char d[1]; +}; + +struct loaded_domain +{ + char *data; + char *data_native; /* Data mapped to the native version of the + string. (Allocated along with DATA). */ + int must_swap; + uint32_t nstrings; + uint32_t *mapped; /* 0 := Not mapped (original utf8). + 1 := Mapped to native encoding in overflow space. + >=2 := Mapped to native encoding. The values + gives the length of the mapped string. + becuase the 0 is included and an empty + string is not allowed we will enver get + values 0 and 1. */ + struct overflow_space_s *overflow_space; + struct string_desc *orig_tab; + struct string_desc *trans_tab; + uint32_t hash_size; + uint32_t *hash_tab; +}; + + +/* The domain we use. We only support one domain at this point. This + is why this implementation can not be shared. Bindtextdomain and + dgettext will simply cheat and always use this one domain. */ +static struct loaded_domain *the_domain; + +/* Global flag to switch gettext into an utf8 mode. */ +static int want_utf8; + + + +/* Free the domain data. */ +static void +free_domain (struct loaded_domain *domain) +{ + struct overflow_space_s *os, *os2; + + jnlib_free (domain->data); + jnlib_free (domain->mapped); + for (os = domain->overflow_space; os; os = os2) + { + os2 = os->next; + jnlib_free (os); + } + jnlib_free (domain); +} + + +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; + + /* Determine the file size. */ + 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 = (2*size <= size)? NULL : jnlib_malloc (2*size); + if (!data) + { + fclose (fp); + return NULL; + } + + to_read = size; + read_ptr = (char *) data; + do + { + long int nb = fread (read_ptr, 1, to_read, fp); + if (nb < to_read) + { + fclose (fp); + jnlib_free (data); + return NULL; + } + 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. */ + jnlib_free (data); + return NULL; + } + + domain = jnlib_calloc (1, sizeof *domain); + if (!domain) + { + jnlib_free (data); + return NULL; + } + domain->data = (char *) data; + domain->data_native = (char *) data + size; + domain->must_swap = data->magic != MAGIC; + + /* Fill in the information about the available tables. */ + switch (SWAPIT (domain->must_swap, data->revision)) + { + case MO_REVISION_NUMBER: + 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 = (uint32_t *) + ((char *) data + SWAPIT (domain->must_swap, data->hash_tab_offset)); + break; + + default: + /* This is an invalid revision. */ + jnlib_free (data); + jnlib_free (domain); + return NULL; + } + + /* Allocate an array to keep track of code page mappings. */ + domain->mapped = jnlib_calloc (domain->nstrings, sizeof *domain->mapped); + if (!domain->mapped) + { + jnlib_free (data); + jnlib_free (domain); + return NULL; + } + + return domain; +} + + +/* Return a malloced wide char string from an UTF-8 encoded input + string STRING. Caller must free this value. On failure returns + NULL. The result of calling this function with STRING set to NULL + is not defined. */ +static wchar_t * +utf8_to_wchar (const char *string, size_t length, size_t *retlen) +{ + int n; + wchar_t *result; + size_t nbytes; + + n = MultiByteToWideChar (CP_UTF8, 0, string, length, NULL, 0); + if (n < 0 || (n+1) <= 0) + return NULL; + + nbytes = (size_t)(n+1) * sizeof(*result); + if (nbytes / sizeof(*result) != (n+1)) + { + errno = ENOMEM; + return NULL; + } + result = jnlib_malloc (nbytes); + if (!result) + return NULL; + + n = MultiByteToWideChar (CP_UTF8, 0, string, length, result, n); + if (n < 0) + { + jnlib_free (result); + return NULL; + } + *retlen = n; + return result; +} + + +/* Return a malloced string encoded in UTF-8 from the wide char input + string STRING. Caller must free this value. On failure returns + NULL. The result of calling this function with STRING set to NULL + is not defined. */ +static char * +wchar_to_native (const wchar_t *string, size_t length, size_t *retlen) +{ + int n; + char *result; + + n = WideCharToMultiByte (CP_ACP, 0, string, length, NULL, 0, NULL, NULL); + if (n < 0 || (n+1) <= 0) + return NULL; + + result = jnlib_malloc (n+1); + if (!result) + return NULL; + + n = WideCharToMultiByte (CP_ACP, 0, string, length, result, n, NULL, NULL); + if (n < 0) + { + jnlib_free (result); + return NULL; + } + *retlen = n; + return result; +} + + +/* Convert UTF8 to the native codepage. Caller must free the return value. */ +static char * +utf8_to_native (const char *string, size_t length, size_t *retlen) +{ + wchar_t *wstring; + char *result; + size_t newlen; + + wstring = utf8_to_wchar (string, length, &newlen); + if (wstring) + { + result = wchar_to_native (wstring, newlen, &newlen); + jnlib_free (wstring); + } + else + result = NULL; + *retlen = result? newlen : 0; + return result; +} + + + + +/* Specify that the DOMAINNAME message catalog will be found + in DIRNAME rather than in the system locale data base. */ +char * +bindtextdomain (const char *domainname, const char *dirname) +{ + struct loaded_domain *domain = NULL; + const char *catval_full; + char *catval; + char *fname; + + /* DOMAINNAME is ignored. We only support one domain. */ + + /* DIRNAME is "$INSTALLDIR\share\locale". */ + + /* First find out the category value. */ + catval = NULL; + catval_full = my_nl_locale_name ("LC_MESSAGES"); + + /* Normally, we would have to loop over all returned locales, and + search for the right file. See gettext intl/dcigettext.c for all + the gory details. Here, we only support the basic category, and + ignore everything else. */ + if (catval_full) + { + char *p; + + catval = jnlib_malloc (strlen (catval_full) + 1); + if (catval) + { + strcpy (catval, catval_full); + p = strchr (catval, '_'); + if (p) + *p = '\0'; + } + } + if (!catval) + return NULL; + + /* Now build the filename string. The complete filename is this: + DIRNAME + \ + CATVAL + \LC_MESSAGES\ + DOMAINNAME + .mo */ + { + int len = strlen (dirname) + 1 + strlen (catval) + 13 + + strlen (domainname) + 3 + 1; + char *p; + + fname = jnlib_malloc (len); + if (!fname) + { + jnlib_free (catval); + return NULL; + } + + p = fname; + strcpy (p, dirname); + p += strlen (dirname); + *(p++) = '\\'; + strcpy (p, catval); + p += strlen (catval); + strcpy (p, "\\LC_MESSAGES\\"); + p += 13; + strcpy (p, domainname); + p += strlen (domainname); + strcpy (p, ".mo"); + } + + domain = load_domain (fname); + jnlib_free (catval); + jnlib_free (fname); + + /* We should not be invoked twice, but this is how you would do + it if it happened. */ + if (the_domain) + free_domain (the_domain); + the_domain = domain; + + /* For historic reasons we are not allowed to return a const char*. */ + return (char*)dirname; +} + + + + +static const char * +get_plural (const char *data, size_t datalen, unsigned long nplural) +{ + const char *p; + int idx; + + /* We only support the Germanic rule. */ + idx = (nplural == 1? 0 : 1); + + for (; idx; idx--) + { + p = strchr (data, 0) + 1; + if (p >= data+datalen) + return "ERROR in GETTEXT (bad plural entry)"; + datalen -= (p-data); + data = p; + } + return data; +} + + +static const char* +get_string (struct loaded_domain *domain, uint32_t idx, + int use_plural, unsigned long nplural) +{ + struct overflow_space_s *os; + const char *trans; /* Pointer to the translated entry. */ + size_t translen; /* Length of that entry. */ + + if (want_utf8) + { + trans = (domain->data + + SWAPIT(domain->must_swap, domain->trans_tab[idx].offset)); + translen = SWAPIT(domain->must_swap, domain->trans_tab[idx].length); + } + else if (!domain->mapped[idx]) + { + /* Not yet mapped. Map from utf-8 to native encoding now. */ + const char *p_utf8; + size_t plen_utf8, buflen; + char *buf; + + p_utf8 = (domain->data + + SWAPIT(domain->must_swap, domain->trans_tab[idx].offset)); + plen_utf8 = SWAPIT(domain->must_swap, domain->trans_tab[idx].length); + + buf = utf8_to_native (p_utf8, plen_utf8, &buflen); + if (!buf) + { + trans = "ERROR in GETTEXT MALLOC"; + translen = 0; + } + else if (buflen <= plen_utf8 && buflen > 1) + { + /* Copy into the DATA_NATIVE area. */ + char *p_tmp; + + p_tmp = (domain->data_native + + SWAPIT(domain->must_swap, domain->trans_tab[idx].offset)); + memcpy (p_tmp, buf, buflen); + domain->mapped[idx] = buflen; + trans = p_tmp; + translen = buflen; + } + else + { + /* There is not enough space for the translation (or for + whatever reason an empry string is used): Store it in the + overflow_space and mark that in the mapped array. + Because UTF-8 strings are in general longer than the + Windows 2 byte encodings, we expect that this won't + happen too often (if at all) and thus we use a linked + list to manage this space. */ + os = jnlib_malloc (sizeof *os + buflen); + if (os) + { + os->idx = idx; + memcpy (os->d, buf, buflen); + os->length = buflen; + os->next = domain->overflow_space; + domain->overflow_space = os; + domain->mapped[idx] = 1; + trans = os->d; + translen = os->length; + } + else + { + trans = "ERROR in GETTEXT MALLOC"; + translen = 0; + } + } + jnlib_free (buf); + } + else if (domain->mapped[idx] == 1) + { + /* The translated string is in the overflow_space. */ + for (os=domain->overflow_space; os; os = os->next) + if (os->idx == idx) + break; + if (os) + { + trans = os->d; + translen = os->length; + } + else + { + trans = "ERROR in GETTEXT (overflow space)\n"; + translen = 0; + } + } + else + { + trans = (domain->data_native + + SWAPIT(domain->must_swap, domain->trans_tab[idx].offset)); + translen = domain->mapped[idx]; + } + + if (use_plural && translen) + return get_plural (trans, translen, nplural); + else + return trans; +} + + +static const char * +do_gettext (const char *msgid, const char *msgid2, unsigned long nplural) +{ + struct loaded_domain *domain; + uint32_t top, bottom, nstr; + + if (!(domain = the_domain)) + goto not_found; + + /* First try to use the hash table. */ + if (domain->hash_size > 2 && domain->hash_tab) + { + /* Use the hashing table. */ + uint32_t len = strlen (msgid); + uint32_t hash_val = hash_string (msgid); + uint32_t idx = hash_val % domain->hash_size; + uint32_t incr = 1 + (hash_val % (domain->hash_size - 2)); + + while ( (nstr = SWAPIT (domain->must_swap, domain->hash_tab[idx])) ) + { + nstr--; + if (nstr < domain->nstrings + && SWAPIT(domain->must_swap, + domain->orig_tab[nstr].length) >= len + && !strcmp (msgid, (domain->data + + SWAPIT(domain->must_swap, + domain->orig_tab[nstr].offset)))) + { + return get_string (domain, nstr, !!msgid2, nplural); + } + + if (idx >= domain->hash_size - incr) + idx -= domain->hash_size - incr; + else + idx += incr; + } + } + + /* 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; + + nstr = (bottom + top) / 2; + cmp_val = strcmp (msgid, (domain->data + + SWAPIT(domain->must_swap, + domain->orig_tab[nstr].offset))); + if (cmp_val < 0) + top = nstr; + else if (cmp_val > 0) + bottom = nstr + 1; + else + return get_string (domain, nstr, !!msgid2, nplural); + } + + not_found: + /* We use the standard Germanic rule if plural has been requested. */ + return msgid2? (nplural == 1? msgid : msgid2) : msgid; +} + + +char * +textdomain (const char *domainname) +{ + /* For now, support only one domain. */ + return (char*)domainname; +} + + +const char * +gettext (const char *msgid) +{ + return do_gettext (msgid, NULL, 0); +} + +char * +dgettext (const char *domainname, const char *msgid) +{ + (void)domainname; + + /* For now, support only one domain. */ + return (char*)do_gettext (msgid, NULL, 0); +} + +const char * +ngettext (const char *msgid1, const char *msgid2, unsigned long int n) +{ + /* We use the simple Germanic plural rule. */ + return do_gettext (msgid1, msgid2, n); +} + + +/* Return the locale name as used by gettext. The return value will + never be NULL. */ +const char * +gettext_localename (void) +{ + const char *s; + + s = my_nl_locale_name ("LC_MESSAGES"); + return s? s:""; +} + +void +gettext_select_utf8 (int value) +{ + want_utf8 = value; +} + + +#ifdef TEST +int +main (int argc, char **argv) +{ + const char atext1[] = + "Warning: You have entered an insecure passphrase.%%0A" + "A passphrase should be at least %u character long."; + const char atext2[] = + "Warning: You have entered an insecure passphrase.%%0A" + "A passphrase should be at least %u characters long."; + + if (argc) + { + argc--; + argv++; + } + + bindtextdomain ("gnupg2", "c:/programme/gnu/gnupg/share/locale"); + + printf ("locale is `%s'\n", gettext_localename ()); + fputs ("text with N=1:\n", stdout); + fputs (ngettext (atext1, atext2, 1), stdout); + fputs ("\n\ntext with N=2:\n", stdout); + fputs (ngettext (atext1, atext2, 2), stdout); + fputs ("\nready\n", stdout); + + return 0; +} +/* + * Local Variables: + * compile-command: "i586-mingw32msvc-gcc -DTEST -Wall -g w32-gettext.c" + * End: + */ +#endif /*TEST*/ diff --git a/jnlib/w32-reg.c b/jnlib/w32-reg.c new file mode 100644 index 0000000..e6bd911 --- /dev/null +++ b/jnlib/w32-reg.c @@ -0,0 +1,182 @@ +/* w32-reg.c - MS-Windows Registry access + * Copyright (C) 1999, 2002, 2007 Free Software Foundation, Inc. + * + * This file is part of JNLIB. + * + * JNLIB 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 3 of + * the License, or (at your option) any later version. + * + * JNLIB 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, see <http://www.gnu.org/licenses/>. + */ + +#include <config.h> +#ifdef HAVE_W32_SYSTEM + /* This module is only used in this environment */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <windows.h> + +#include "libjnlib-config.h" +#include "w32help.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. */ +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 immediately. */ + /* 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. */ + } + + nbytes = 1; + if (RegQueryValueEx( key_handle, name, 0, NULL, NULL, &nbytes ) ) + goto leave; + result = jnlib_malloc ((n1=nbytes+1)); + if (!result) + goto leave; + if (RegQueryValueEx( key_handle, name, 0, &type, result, &n1 )) + { + jnlib_free (result); + result = NULL; + goto leave; + } + result[nbytes] = 0; /* Make sure it is a string. */ + if (type == REG_EXPAND_SZ && strchr (result, '%')) + { + char *tmp; + + n1 += 1000; + tmp = jnlib_malloc (n1+1); + if (!tmp) + goto leave; + nbytes = ExpandEnvironmentStrings (result, tmp, n1); + if (nbytes && nbytes > n1) + { + jnlib_free (tmp); + n1 = nbytes; + tmp = jnlib_malloc (n1 + 1); + if (!tmp) + goto leave; + nbytes = ExpandEnvironmentStrings (result, tmp, n1); + if (nbytes && nbytes > n1) + { + /* Oops - truncated, better don't expand at all. */ + jnlib_free (tmp); + goto leave; + } + tmp[nbytes] = 0; + jnlib_free (result); + result = tmp; + } + else if (nbytes) + { + /* Okay, reduce the length. */ + tmp[nbytes] = 0; + jnlib_free (result); + result = jnlib_malloc (strlen (tmp)+1); + if (!result) + result = tmp; + else + { + strcpy (result, tmp); + jnlib_free (tmp); + } + } + else + { + /* Error - don't expand. */ + jnlib_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 /*HAVE_W32_SYSTEM*/ diff --git a/jnlib/w32help.h b/jnlib/w32help.h new file mode 100644 index 0000000..c503ad2 --- /dev/null +++ b/jnlib/w32help.h @@ -0,0 +1,41 @@ +/* w32help.h - W32 speicif functions + * Copyright (C) 2007 Free Software Foundation, Inc. + * + * This file is part of JNLIB. + * + * JNLIB 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 3 of + * the License, or (at your option) any later version. + * + * JNLIB 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef LIBJNLIB_W32HELP_H +#define LIBJNLIB_W32HELP_H +#ifdef HAVE_W32_SYSTEM + +/*-- w32-reg.c --*/ +char *read_w32_registry_string (const char *root, + const char *dir, const char *name ); +int write_w32_registry_string (const char *root, const char *dir, + const char *name, const char *value); + +#ifdef USE_SIMPLE_GETTEXT +char *bindtextdomain (const char *domainname, const char *dirname); +const char *gettext (const char *msgid ); +const char *ngettext (const char *msgid1, const char *msgid2, + unsigned long int n); +const char *gettext_localename (void); +void gettext_select_utf8 (int value); +#endif /*USE_SIMPLE_GETTEXT*/ + + +#endif /*HAVE_W32_SYSTEM*/ +#endif /*LIBJNLIB_MISCHELP_H*/ |