diff --git a/common/ChangeLog b/common/ChangeLog index 6f4b70b30..5fd8b03a9 100644 --- a/common/ChangeLog +++ b/common/ChangeLog @@ -1,1667 +1,1689 @@ +2010-03-10 Werner Koch + + * estream.c (es_func_fp_read, es_func_fp_write, es_func_fp_seek) + (es_func_fp_destroy): Allow a NULL FP to implement a dummy stream. + (do_fpopen): Ditto. + (es_vfprintf_unlocked): New. + (es_fprintf_unlocked): Make public. + (es_fputs_unlocked): New. + + * logging.h: Replace FILE* by estream_t. + * logging.c: Remove USE_FUNWRITER cpp conditional because we now + use estream. + (my_funopen_hook_ret_t, my_funopen_hook_size_t): Replace by + ssize_t. + (log_get_stream): Change to return an estream_t. + (set_file_fd): Always close the log stream because it can't be + assigned to stderr or stdout directly. Use a dummy estream as + last resort log stream. + (log_test_fd, log_get_fd): Use es_fileno. + (log_get_stream): Assert that we have a log stream. + (do_logv): Use estream functions and lock the output. + 2010-03-10 Werner Koch * util.h: Replace jnlib path part by common. (snprintf): Use the replacement macro on all platforms. * Makefile.am (jnlib_sources): New. (libcommon_a_SOURCES, libcommonpth_a_SOURCES): Add jnlib_sources. (jnlib_tests): New. (noinst_PROGRAMS, TESTS): Add jnlib_tests. (t_common_ldadd): Remove libjnlib.a. * README.jnlib, ChangeLog.jnlib, libjnlib-config.h, argparse.c * argparse.h, dotlock.c, dotlock.h, dynload.h, logging.c * logging.h, mischelp.c, mischelp.h, stringhelp.c, stringhelp.h * strlist.c, strlist.h, types.h, utf8conv.c, utf8conv.h * w32-afunix.c, w32-afunix.h, w32-reg.c, w32help.h, xmalloc.c * xmalloc.h, t-stringhelp.c, t-support.c, t-support.h * t-timestuff.c, t-w32-reg.c: Move from jnlib to here. * init.c: Remove "estream.h". * util.h: Include "estream.h". * xasprintf.c, ttyio.c: Remove "estream-printf.h". 2010-03-08 Werner Koch * exechelp.c [!HAVE_SIGNAL_H]: Do not include signal.h. (DETACHED_PROCESS, CREATE_NEW_PROCESS_GROUP) [W32CE]: Provide stubs. * iobuf.h (iobuf_ioctl_t): New. Use the new macros instead of the hard wired values. * iobuf.c (iobuf_append): Remove. (iobuf_fdopen): Factor code out to ... (do_iobuf_fdopen): ... new. (iobuf_fdopen_nc): New. (iobuf_open_fd_or_name): Implement using iobuf_fdopen_nc. * iobuf.c (INVALID_FD): Replace by GNUPG_INVALID_FD. (fp_or_fd_t): Replace by gnupg_fd_t. (my_fileno): Replace by the FD2INT macro. (FILEP_OR_FD_FOR_STDIN, FILEP_OR_FD_FOR_STDOUT): Rename to FD_FOR_STDIN, FD_FOR_STDOUT. (file_filter): Make full use of FD_FOR_STDIN. (USE_SETMODE): Remove. Not needed without stdio. (my_fopen_ro, my_fopen): Replace unneeded macros. * iobuf.c [FILE_FILTER_USES_STDIO]: Remove all code. It has not been used for a long time. * exechelp.h: Include "estream.h". * exechelp.c (gnupg_spawn_process): Change OUTFILE to an estream_t. 2010-03-02 Werner Koch * estream.c, estream.h, estream-printf.c, estream-printf.h: Update from libestream. 2010-03-01 Werner Koch * signal.c [!HAVE_SIGNAL_H]: Don't include signal.h. * iobuf.c (direct_open) [W32CE]: Make filename to wchar_t. (iobuf_cancel) [W32CE]: Use DeleteFile. * gettime.c (dump_isotime): Use "%s" to print "none". * homedir.c (standard_homedir) [W32CE]: Use wchar_t to create the directory. (w32_rootdir) [W32CE]: Likewise. * sysutils.c (translate_sys2libc_fd) [W32CE]: Add support. (gnupg_tmpfile) [W32CE]: Ditto. (_gnupg_getenv) [W32CE]: New. * util.h (getpid, getenv) [W32CE]: New. * i18n.c (i18n_switchto_utf8) (i18n_switchback) [USE_SIMPLE_GETTEXT]: Use new function from libgpg-error which supports proper restoring. * sysutils.c (get_session_marker): Simplified by using gcrypt. 2009-12-08 Marcus Brinkmann * Makefile.am (audit-events.h, status.h) [!MAINTAINER_MODE]: No longer include these rules if not in maintainer mode. 2009-12-08 Werner Koch * userids.h, userids.c: New. (classify_user_id): Merged from similar fucntions in sm/ and g10/. * dns-cert.c (get_dns_cert): Add support for ADNS. 2009-12-08 Marcus Brinkmann * asshelp.c (start_new_gpg_agent): Convert posix FD to assuan FD. * asshelp.c (start_new_gpg_agent) [HAVE_W32_SYSTEM]: Add missing argument in assuan_socket_connect invocation. * iobuf.c (iobuf_open_fd_or_name): Fix type of FD in function declaration. 2009-12-07 Werner Koch * pka.c (get_pka_info): Add support for ADNS. * src.v (getsrv): Add support for ADNS. * srv.c (getsrv): s/xrealloc/xtryrealloc/. 2009-12-04 Werner Koch * Makefile.am (audit-events.h, status-codes.h): Create files in the source dir. Fixes bug#1164. 2009-12-02 Werner Koch * audit.c (proc_type_decrypt, proc_type_sign): Implemented. (proc_type_verify): Print hash algo infos. * audit.h (AUDIT_DATA_CIPHER_ALGO, AUDIT_BAD_DATA_CIPHER_ALSO) (AUDIT_NEW_RECP, AUDIT_DECRYPTION_RESULT, AUDIT_RECP_RESULT) (AUDIT_ATTR_HASH_ALGO, AUDIT_SIGNED_BY, AUDIT_SIGNING_DONE): 2009-11-05 Marcus Brinkmann * asshelp.c (start_new_gpg_agent): Update use of assuan_socket_connect and assuan_pipe_connect. 2009-11-02 Marcus Brinkmann * get-passphrase.c (default_inq_cb, membuf_data_cb): Change return type to gpg_error_t. 2009-10-28 Werner Koch * status.h (STATUS_MOUNTPOINT): New. 2009-10-16 Marcus Brinkmann * Makefile.am (libcommon_a_CFLAGS): Use LIBASSUAN_CFLAGS instead of LIBASSUAN_PTH_CFLAGS. 2009-10-13 Werner Koch * exechelp.c (gnupg_kill_process): New. 2009-09-29 Werner Koch * exechelp.c (create_inheritable_pipe): Rename to create_inheritable_pipe_w. (create_inheritable_pipe_r): New. (gnupg_create_outbound_pipe): New. * iobuf.h: Include "sysutils.h" * iobuf.c (iobuf_open_fd_or_name): New. (iobuf_get_fname_nonnull): New. 2009-09-23 Marcus Brinkmann * asshelp.c (start_new_gpg_agent): Allocate assuan context before starting server. 2009-09-03 Werner Koch Update from libestream: * estream-printf.c: Include stdint.h only if HAVE_STDINT_H is defined. * estream-printf.c: Remove all test code. Use macro DEBUG instead of TEST for debugging. * estream-printf.c (pr_float): Make buffer larger for silly high numbers. 2009-08-11 David Shaw * ttyio.h, ttyio.c (tty_enable_completion): Some ifdefs around HAVE_LIBREADLINE to allow building when readline isn't available. 2009-08-06 Werner Koch * status.h (STATUS_INV_SGNR, STATUS_NO_SGNR): New. * status.c (get_inv_recpsgnr_code): New. 2009-07-23 David Shaw * srv.c (getsrv): Fix type-punning warning. 2009-07-23 Werner Koch * util.h (GPG_ERR_NOT_ENABLED): New. * audit.h (enum): Add AUDIT_CRL_CHECK. * audit.c (proc_type_verify): Show CRL check result. 2009-07-06 Werner Koch * get-passphrase.c (struct agentargs): Add SESSION_ENV and remove obsolete args. (gnupg_prepare_get_passphrase): Ditto. * session-env.c, session-env.h: New. * t-session-env.c: New. * Makefile.am (common_sources, module_tests): Add them. * asshelp.h: Include "session-env.h" * asshelp.c (send_one_option): Add arg PUTENV. (send_pinentry_environment): Replace most args by SESSION_ENV and rewrite fucntion. (start_new_gpg_agent): Likewise. * t-exechelp.c (test_close_all_fds): Remove debug code. 2009-07-01 Werner Koch * sexputil.c (get_pk_algo_from_canon_sexp): New. 2009-06-29 Werner Koch * estream.c (BUFFER_ROUND_TO_BLOCK): Remove unused macro. (es_func_mem_write): Rewrite reallocation part. * estream.c (es_write_sanitized_utf8_buffer): Typo typo fix. 2009-06-25 Werner Koch * estream.c (es_write_sanitized_utf8_buffer): Typo fix. 2009-06-24 Werner Koch * estream.c (es_read_line): In the malloc error case, set MAX_LENGTH to 0 only if requested. * xreadline.c (read_line): Ditto. * estream.c (es_write_sanitized_utf8_buffer): Pass on error from es_fputs. * sexputil.c (get_rsa_pk_from_canon_sexp): Check for error after the loop. Reported by Fabian Keil. 2009-06-22 Werner Koch * estream.c (es_pth_read, es_pth_write) [W32]: New. (ESTREAM_SYS_READ, ESTREAM_SYS_WRITE) [HAVE_PTH]: Use them. 2009-06-03 Werner Koch * estream.c (es_convert_mode): Rewrite and support the "x" flag. 2009-05-28 David Shaw From 1.4: * http.h, http.c (send_request) Pass in a STRLIST for additional headers. Change all callers. 2009-05-27 David Shaw From 1.4: * http.h, http.c (send_request): Pass in srvtag and make its presence sufficient to turn the feature on. (http_open): From here. (http_document): And here. * srv.c (getsrv): Raise maximum packet size to 2048, as PACKETSZ is too small these days. 2009-05-22 Werner Koch * ttyio.c (tty_cleanup_after_signal): New. 2009-05-19 Werner Koch * simple-pwquery.c (agent_open): Use SUN_LEN (JNLIB_NEED_AFLOCAL): Define and include mischelp.h. 2009-05-07 Werner Koch * sexputil.c (get_rsa_pk_from_canon_sexp): New. * t-sexputil.c (test_make_canon_sexp_from_rsa_pk): Extend the test. 2009-04-28 Werner Koch * sexputil.c (make_canon_sexp_from_rsa_pk): New. * t-sexputil.c (test_make_canon_sexp_from_rsa_pk): New. 2009-04-01 Werner Koch * iobuf.c: Port David's changes from 1.4: (fd_cache_invalidate): Pass return code from close back. (direct_open, iobuf_ioctl): Check that return value. (fd_cache_synchronize): New. (iobuf_ioctl): Add new sub command 4 (fsync). * iobuf.c (fd_cache_strcmp): New. Taken from 1.4. (fd_cache_invalidate, fd_cache_close, fd_cache_open): Use it. * exechelp.c (gnupg_spawn_process): Implement new flag bit 6. * sysutils.c (gnupg_allow_set_foregound_window): Allow the use of ASFW_ANY. * membuf.c (put_membuf, get_membuf): Wipe memory on out of core. 2009-03-31 Werner Koch * percent.c (percent_unescape, percent_plus_unescape): New. (percent_plus_unescape_inplace, percent_unescape_inplace): New. (do_plus_or_plain_unescape, count_unescape, do_unescape): New. (do_unescape_inplace): New. * t-percent.c (test_percent_plus_escape): Test percent_plus_unescape. * get-passphrase.c, get-passphrase.h: New. * Makefile.am (without_pth_sources): New. 2009-03-18 Werner Koch * exechelp.c: Include sys/resource.h and sys/stat.h. (get_max_open_fds): New. (do_exec): Use it. (get_all_open_fds): New. (close_all_fds): New. (do_exec): Use close_all_fds. * t-exechelp.c: New. 2009-03-13 David Shaw * http.c (do_parse_uri): Properly handle IPv6 literal addresses as per RFC-2732. Adapted from patch by Phil Pennock. 2009-03-12 Werner Koch * gettime.c: Include i18n.h. (dump_isotime): New. 2009-03-06 Werner Koch * sexputil.c (make_canon_sexp): New. 2009-03-03 Werner Koch * exechelp.c (do_exec): Make sure that /dev/null connected FDs are not closed. 2009-01-19 Werner Koch * audit.c (writeout_li): Translate a few more result strings. Fixes bug#970. * convert.c (hex2str): Fix optimization to append a nul character. 2008-12-05 Werner Koch * percent.c, t-percent.c: New. * exechelp.c (gnupg_spawn_process, gnupg_spawn_process_fd) (gnupg_spawn_process_detached) [W32]: Remove debug output. 2008-11-20 Werner Koch * audit.c (writeout_li): Translate OKTEXT. 2008-11-04 Werner Koch * i18n.c (i18n_init) [USE_SIMPLE_GETTEXT]: Adjust for changed w32-gettext.c. * homedir.c (gnupg_localedir): New. 2008-10-20 Werner Koch * http.c (http_register_tls_callback) [!HTTP_USE_GNUTLS]: Mark unused arg. * localename.c (do_nl_locale_name): Ditto. * audit.c (event2str): Silent gcc warning. * sysutils.c (translate_sys2libc_fd): Mark unused arg. (translate_sys2libc_fd_int): Ditto. * iobuf.c (translate_file_handle): Ditto. * asshelp.c (send_one_option): Ditto. * exechelp.c (gnupg_spawn_process): Ditto. * signal.c (got_usr_signal): Ditto * estream.c (es_func_fd_create) [!W32]: Ditto. (es_func_fp_create) [!W32]: Ditto. (es_write_hexstring): Ditto. (dummy_mutex_call_void, dummy_mutex_call_int) [HAVE_PTH]: New. (ESTREAM_MUTEX_LOCK, ESTREAM_MUTEX_UNLOCK, ESTREAM_MUTEX_TRYLOCK) (ESTREAM_MUTEX_INITIALIZE) [HAVE_PTH]: Use dummy calls so to mark unused arg. 2008-10-19 Werner Koch * estream-printf.c (estream_vsnprintf): Fix return value. (check_snprintf): Add a new test. (one_test) [W32]: Disable test. 2008-10-17 Werner Koch * util.h (snprintf) [W32]: Redefine to estream_snprintf. 2008-09-03 Werner Koch * convert.c (hex2str): New. (hex2str_alloc): New. * t-convert.c (test_hex2str): New. 2008-08-19 Werner Koch * iobuf.c: Avoid passing a NULL (iobuf_t)->desc to the log function. Should in general never be NULL, but well. Reported by M. Heneka. 2008-06-26 Werner Koch * estream.c (es_write_sanitized): 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-25 Marcus Brinkmann Revert last three changes related to handle translation. * sysutils.c: (FD_TRANSLATE_MAX, fd_translate, fd_translate_len) (translate_table_init, translate_table_lookup): Removed. * iobuf.c (check_special_filename): Do not use translate_table_lookup. * sysutils.h (translate_table_init, translate_table_lookup): Remove prototypes. 2008-06-19 Werner Koch * sysutils.c: Remove . (fd_translate_max): Use macro for the size. (translate_table_init): Protect read against EINTR and replace isspace by spacep. 2008-06-18 Marcus Brinkmann * sysutils.c (TRANS_MAX): Bump up to 350 to be on the safe side. * sysutils.h (translate_table_init, translate_table_lookup): New prototypes. * sysutils.c: Include . (FD_TRANSLATE_MAX): New macro. (fd_translate, fd_translate_len): New static variables. (translate_table_init, translate_table_lookup): New functions. (translate_sys2libc_fd_int): Translate file descriptor. * iobuf.c (check_special_filename): Translate handle values from special filenames. 2008-06-16 Werner Koch * homedir.c (w32_commondir): New. (gnupg_sysconfdir): Use it. 2008-06-09 Werner Koch * b64dec.c: New. 2008-06-05 Werner Koch * util.h (gnupg_copy_time): Replace strcpy by memcpy. 2008-05-26 Werner Koch * asshelp.c (send_one_option, send_pinentry_environment): use xfree and xtrystrdup. * i18n.c (i18n_switchto_utf8) [USE_SIMPLE_GETTEXT]: Return NULL. * homedir.c (gnupg_module_name): Add GNUPG_MODULE_NAME_CONNECT_AGENT and GNUPG_MODULE_NAME_GPGCONF. 2008-04-21 Werner Koch * http.c (http_wait_response) [W32]: Use DuplicateHandle because it is a socket. (cookie_read) [W32]: Use recv in place of read. 2008-04-08 Werner Koch * i18n.c (i18n_switchto_utf8, i18n_switchback) [USE_SIMPLE_GETTEXT]: Implement. 2008-04-07 Werner Koch * b64enc.c (b64enc_start): Detect PGP mode. (b64enc_finish): Write PGP CRC. * util.h (struct b64state): Add field CRC. * t-b64.c: New. * pka.c (get_pka_info): Use xtrymalloc and check result. 2008-03-25 Werner Koch * localename.c: Strip all W32 code. Include w32help.h. (gnupg_messages_locale_name) [W32]: Use the gettext_localename. 2008-03-17 Werner Koch * iobuf.c (IOBUF_BUFFER_SIZE): Actually use this macro. * simple-pwquery.c (agent_send_all_options): Fix last change. 2008-03-06 Werner Koch * simple-pwquery.c (agent_send_all_options): Add support for XAUTHORITY and PINENTRY_USER_DATA. 2008-02-15 Marcus Brinkmann * exechelp.c (gnupg_spawn_process_fd): Add flag DETACHED_PROCESS unconditionally (required for all callers at the moment). 2008-02-14 Werner Koch * sysutils.c (gnupg_allow_set_foregound_window): New. (WINVER) [W32]: Define. 2008-01-31 Werner Koch * audit.c (audit_print_result): Make sure that the output is always UTF8. 2008-01-27 Werner Koch * exechelp.c (gnupg_spawn_process): Add arg FLAGS and changed all callers to pass 0 for it. 2007-12-13 Werner Koch * sexputil.c (hash_algo_from_sigval): New. * t-sexputil.c: New. * Makefile.am (module_tests): Add it. 2007-12-11 Werner Koch * asshelp.c (send_pinentry_environment): Allow using of old gpg-agents not capabale of the xauthority and pinentry_user_data options. 2007-12-04 Werner Koch * Makefile.am (t_helpfile_LDADD, module_maint_tests): New. * t-helpfile.c: New. * helpfile.c: New. * membuf.h (is_membuf_ready, MEMBUF_ZERO): New. * localename.c: New. Taken from gettext with modifications as done for GpgOL. Export one new function. * util.h (gnupg_messages_locale_name, gnupg_get_help_string): Added. * sysutils.c (gnupg_reopen_std): New. Taken from ../g10/gpg.c. 2007-11-27 Werner Koch * Makefile.am (CLEANFILES): New. * homedir.c (dirmngr_socket_name): Use CSIDL_WINDOWS. 2007-11-15 Werner Koch * asshelp.c (send_pinentry_environment): Add args XAUTHORITY and PINENTRY_USER_DATA. (start_new_gpg_agent): Ditto. 2007-11-07 Werner Koch * status.h: New. * errors.h: Remove. 2007-11-05 Werner Koch * audit.c, audit.h: New. * Makefile.am: Add rules to build audit-events.h. * exaudit.awk: New. * mkstrtable.awk: New. Taken from libgpg-error. 2007-10-19 Werner Koch * i18n.c (i18n_switchto_utf8, i18n_switchback): New. 2007-10-01 Werner Koch * sysutils.h (FD2INT, INT2FD): New. 2007-09-21 Werner Koch * homedir.c (default_homedir): Make registry work. Reported by Marc Mutz. 2007-08-29 Werner Koch * exechelp.c (gnupg_wait_process): Add arg EXITCODE. Changed all callers. (gnupg_create_inbound_pipe): New. * util.h (GNUPG_MODULE_NAME_GPGSM, GNUPG_MODULE_NAME_GPG): New. * homedir.c (gnupg_module_name): Add them 2007-08-28 Werner Koch * gettime.c (check_isotime, add_isotime): New. Originally written for DirMngr by me. (add_days_to_isotime): New. (date2jd, jd2date, days_per_month, days_per_year): New. Taken from my ancient (1988) code used in Wedit (time2.c). 2007-08-27 Werner Koch * util.h (GNUPG_MODULE_NAME_CHECK_PATTERN): New. * homedir.c (gnupg_module_name): Add it. * exechelp.c (w32_fd_or_null) [W32]: New. (gnupg_spawn_process_fd): New. (gnupg_wait_process) [W32]: Close the handle after if the process has returned. 2007-08-22 Werner Koch Updated estream from libestream. * estream.c (mem_malloc, mem_realloc, mem_free): New. Use them instead of the ES_MEM_foo. * estream.c (estream_cookie_mem): Remove members DONT_FREE, APPEND_ZERO, PTR and SIZE. Add MEMORY_LIMIT. Put GROW into a new FLAGS struct. (es_func_mem_create): Remove APPEND_ZERO, DONT_FREE, PTR and SIZE. Add MEMORY_LIMIT. (es_func_mem_write, es_func_mem_seek, es_func_mem_destroy): Revamp. (es_open_memstream): Change API to just take a memory limit and a mode argument. Rename to .. (es_fopenmem): .. this. (HAVE_W32_SYSTEM) [_WIN32]: Define if not defined. (tmpfd) [W32]: Implement directly using the W32 API. (es_fgets): Rewrite without using doreadline. 2007-08-21 Werner Koch * sysutils.c (gnupg_tmpfile): New. * t-sysutils.c: New. * Makefile.am (module_tests): Add t-sysutils. 2007-08-20 Werner Koch * exechelp.c [W32]: Redefine X_OK to F_OK. 2007-08-16 Werner Koch * Makefile.am (t_convert_DEPENDENCIES): Remove ($(PROGRAMS)): Remove. (t_common_ldadd): Use libcommon.a and not the macro. 2007-08-14 Werner Koch * homedir.c (dirmngr_socket_name): New. 2007-08-07 Werner Koch * tlv.c, tlv.h: Move from ../scd/. * tlv.c (parse_sexp, parse_ber_header): Add ERRSOURCE arg and prefix name with a _. * tlv.h: Use macro to convey ERRSOURCE. 2007-08-02 Werner Koch * gc-opt-flags.h: New. 2007-08-01 Werner Koch * estream-printf.c (read_dummy_value): Removed as it is useless now. (read_values): Remove check on !vaargs which is not anymore needed and anyway not portable. Reported by Peter O'Gorman. 2007-07-16 Werner Koch * estream.c (es_func_file_create): Clear NO_CLOSE flag. 2007-07-12 Werner Koch * sysutils.h (gnupg_fd_t): New. * sysutils.c (translate_sys2libc_fd): Use that type instead of int. (translate_sys2libc_fd_int): New. 2007-07-09 Werner Koch * t-gettime.c (test_isotime2epoch): Use time_t and not u32. 2007-07-05 Werner Koch * t-gettime.c: New. * gettime.c (isotime2epoch, epoch2isotime): New. 2007-07-04 Werner Koch * estream.c (es_init_do): Do not throw an error if pth has already been initialized. 2007-06-26 Werner Koch * Makefile.am ($(PROGRAMS)): New. * util.h (init_common_subsystems): Moved to .. * init.h: .. New. * util.h: Include init.h. * homedir.c (standard_homedir): New. (default_homedir) [W32]: Reimplemented in terms of standard_homedir. Fixed memory leak. 2007-06-25 Werner Koch * iobuf.c: Add more documentation and slighly restructured macro defintion for better readability. (FILEP_OR_FD): Rename to fp_or_fd_t. (CLOSE_CACHE): Rename to close_cache_t. * sysutils.c (translate_sys2libc_fd): New using the code from iobuf.c. * iobuf.c: Include sysutils.h. (iobuf_translate_file_handle): Remove. (translate_file_handle): Use new function. * estream-printf.c [TEST]: Header including fixes. (do_format): Do not append a trailing Nul. This avoids spurious Nuls in the es_printf output. (estream_vsnprintf, estream_vasprintf): Take this in account. * estream.h (struct es__stream): Change FLAGS to a bit structure. (ES__FLAG_WRITING): Replace by a bit from FLAGS. * estream.c (struct estream_internal): Rename FLAGS to MODEFLAGS so that they are not confused with the estream flags. (es_initialize, es_create): Add arg MODEFLAGS so that we can setup the intial writemode. Changed all callers to pass them. (es_convert_mode): Set O_BINARY. (es_func_fd_create, es_func_fp_create, es_func_file_create) [W32]: Call setmode if requested. 2007-06-24 Werner Koch * estream.c (do_fpopen, es_fpopen, es_fpopen_nc): New. (es_func_fp_create, es_func_fp_read, es_func_fp_write) (es_func_fp_seek, es_func_fp_destroy): New. 2007-06-22 Werner Koch * estream.c (es_fdopen): Factored code out to.. (do_fdopen): .. new. (es_fdopen_nc): New. (estream_cookie_fd): Add field NO_CLOSE. (es_func_fd_create): Add arg NO_CLOSE and changed all callers. (es_func_fd_destroy): Handle the new flag. * homedir.c (gnupg_libexecdir) [W32]: Factor code out to .. (w32_rootdir): .. new. (gnupg_sysconfdir, gnupg_libdir, gnupg_datadir) [W32]: Return name based on w32_rootdir(). 2007-06-21 Werner Koch * membuf.h (get_membuf_len): New. * membuf.c (init_membuf_secure): Really allocate in secure memory. (put_membuf_str): New. * ttyio.c (tty_getf): New. * util.h (ctrl_t): Declare it here. * asshelp.c (start_new_gpg_agent): New. Based on code from ../sm/call-agent.c 2007-06-20 Werner Koch * sysutils.c (gnupg_sleep): New. * sysutils.h [W32]: Remove _sleep wrapper. Changed all callers to use gnupg_sleep. * exechelp.c (build_w32_commandline_copy): New. (build_w32_commandline): Factored some code out to new function and correctly process a PGMNAME with spaces. (gnupg_spawn_process_detached) [W32]: Implement. 2007-06-14 Werner Koch * simple-pwquery.h (MAP_SPWQ_ERROR_IMPL): New. (SPWQ_NO_PIN_ENTRY): New. * simple-pwquery.c (simple_pw_set_socket): New. (agent_open): Use it if GPG_AGENT_INFO is not set. (simple_pwquery): Extended to allow returning of otehyr error codes. * util.h (GNUPG_MODULE_NAME_AGENT, GNUPG_MODULE_NAME_PINENTRY) (GNUPG_MODULE_NAME_SCDAEMON, GNUPG_MODULE_NAME_DIRMNGR) (GNUPG_MODULE_NAME_PROTECT_TOOL): New. * homedir.c (gnupg_module_name): New. (gnupg_bindir): New. 2007-06-12 Werner Koch * homedir.c (gnupg_sysconfdir): New. (gnupg_libexecdir): New. Taken from g10/misc.c:get_libexecdir. (gnupg_datadir): New. (gnupg_libdir): New. * http.c (connect_server) [W32]: Do not call init_sockets if HTTP_NO_WSASTARTUP is defined. * init.c: New. * estream.c (es_init_do): Init stream lock here because we can't use a static initialization with W32pth. 2007-06-11 Werner Koch * Makefile.am (t_common_ldadd): Use libcommonstd macro. 2007-06-06 Werner Koch * Makefile.am: Include am/cmacros.am. * sysutils.h [W32]: Remove prototypes for the registry access. * w32reg.c: Move to ../jnlib/w32-reg.c. * i18n.c (i18n_init): New. * simple-gettext.c: Remove. * iobuf.c (iobuf_get_filelength): Rename SIZE to EXSIZE to silent shadowing warning. 2007-06-04 Werner Koch * http.c [W32]: Include unistd.h also in this case. (write_server) [W32]: Fixed error code. (init_sockets): Fixed syntax error. (cookie_close): Replace close by sock_close macro. * estream.c [w32]: Do not init Mutex. * Makefile.am (common_sources) [USE_SNS_SRV]: Build srv.c only when needed. * ttyio.c (init_ttyfp) [W32]: Do not use TTYFP. * util.h: Include ../jnlib/dynload.h. * dynload.h: Move to ../jnlib. 2007-05-30 Werner Koch * estream.c (MEM_FREE, MEM_ALLOC, MEM_REALLOC): Prefix with ES_ as windows.h also has such definitions, 2007-05-15 Werner Koch * util.h: Do not include gnulib's vasprintf. Redefine asprintf and vasprintf. * xasprintf.c (xasprintf, xtryasprintf): Use estream_vasprintf. * estream-printf.h, estream-printf.c: New. Taken from current libestream SVN. * Makefile.am (common_sources): Add them. 2007-05-14 Werner Koch * sexp-parse.h (smklen): New. * sexputil.c: Include sexp-parse.h. (make_simple_sexp_from_hexstr): Replace sprintf by smklen. 2007-05-07 Werner Koch * signal.c (got_fatal_signal): Protect SIG from being clobbered by a faulty signal implementaion. Suggested by James Juran. 2007-04-25 Werner Koch * i18n.h (ngettext): New. * simple-gettext.c (ngettext): New. 2007-04-20 Werner Koch * miscellaneous.c (my_gcry_logger, my_gcry_outofcore_handler): Moved from gpg-agent to here. (my_gcry_fatalerror_handler): new. (setup_libgcrypt_logging): New. 2007-03-19 Werner Koch * miscellaneous.c (print_hexstring): New. * estream.c (es_fprintf_unlocked): New. (es_write_sanitized): New. (es_write_hexstring): New. (es_write_sanitized_utf8_buffer) [GNUPG_MAJOR_VERSION]: New. 2007-03-09 David Shaw From STABLE-BRANCH-1-4 * http.c (do_parse_uri): Remove the hkp port 11371 detection. We implement hkp in the keyserver handler, and the support here makes it appear like a bad hkp request actually succeeded. 2007-01-31 Werner Koch * Makefile.am (t_common_ldadd): Add LIBINCONV and LIBINTL. 2007-01-25 Werner Koch * simple-pwquery.c (simple_pwquery): New arg OPT_CHECK. 2006-12-13 David Shaw * Makefile.am (AM_CPPFLAGS): Include intl/ so we can reference the built-in headers. 2006-11-23 Werner Koch * http.c: Include i18n.h 2006-11-21 Werner Koch * estream.c: Remove explicit Pth soft mapping diabling becuase it is now done in config.h. 2006-11-15 Werner Koch * estream.c: Disabled Pth soft mapping. (my_funopen_hook_ret_t): New. (print_fun_writer): Use it here. * iobuf.c (fd_cache_close): Use %d instead of %p for debug output. 2006-11-03 Werner Koch * Makefile.am (t_convert_DEPENDENCIES): Add libcommon. From Gentoo. 2006-10-24 Marcus Brinkmann * Makefile.am (libcommon_a_CFLAGS): Add $(LIBASSUAN_CFLAGS). (libsimple_pwquery_a_CFLAGS): New variable. 2006-10-20 Werner Koch * convert.c (hex2bin): New. 2006-10-17 Werner Koch * estream.c (struct estream_internal, es_initialize) (es_deinitialize, print_fun_writer, es_print): New and modified functions to avoid tempfiles for printf style printing. * Makefile.am (libcommonpth_a_SOURCES): New. We now build a secon version of the library with explicit Pth support. * exechelp.c, estream.c: Make use of WITHOUT_GNU_PTH. 2006-10-08 Werner Koch * gpgrlhelp.c: Trun all functions into dummies if readline is not available. 2006-10-06 Werner Koch * Makefile.am (AM_CFLAGS): Use PTH version of libassuan. * util.h (GNUPG_GCC_A_SENTINEL): Defined for gcc >= 4. 2006-10-04 David Shaw * gpgrlhelp.c: readline requires stdio.h. 2006-10-04 Werner Koch * membuf.c (init_membuf_secure): New. (put_membuf): Make sure that ERRNO is set even if the underlying malloc code does not work properly. (get_membuf): Set ERRNO on error. (get_membuf): Allow to pass LEN as NULL. 2006-10-02 Werner Koch * iobuf.c (iobuf_unread): Removed. This code is not required. Also removed the entire unget buffer stuff. 2006-09-27 Werner Koch * util.h: Do not include strsep.h and strpbrk.h. (isascii): Removed as it is now in jnlib. * iobuf.c (pop_filter, underflow, iobuf_close): Free the unget buffer. 2006-09-27 Florian Weimer (wk) * iobuf.c (iobuf_unread): New. 2006-09-22 Werner Koch * i18n.h: Changed license to an all permissive one. * ttyio.c (tty_get): We need to use readline too. Added two more hooks. 2006-09-21 Werner Koch * ttyio.c (tty_private_set_rl_hooks): New. (tty_enable_completion, tty_disable_completion): Use a hook to enable readline support. Now always available. (tty_cleanup_rl_after_signal): New. * ttyio.h: Removed readline specific stuff. Included util.h. * common-defs.h: New. 2006-09-15 Werner Koch * convert.c: New. (hexcolon2bin): New. (bin2hex, bin2hexcolon, do_binhex): New. * t-convert.c: New 2006-09-14 Werner Koch * util.h (out_of_core): Use new gpg_error_from_syserror function. * http.c (init_sockets): Changed it to require 2.2 unless it is build within gnupg 1 where we require 1.1 (and not anymore allow for 1.0). 2006-09-07 Werner Koch * exechelp.c (gnupg_spawn_process): Factor out post fork code to .. (do_exec): .. new function. Allow passing of -1 for the fds. (gnupg_spawn_process): Terminate gcrypt's secure memory in the child. (gnupg_spawn_process_detached): New. 2006-09-06 Werner Koch * maperror.c: Removed. * util.h (out_of_core): New. 2006-09-04 Werner Koch * http.c (http_get_header): New. (capitalize_header_name, store_header): New. (parse_response): Store headers away. (send_request): Return GPG_ERR_NOT_FOUND if connect_server failed. * http.h: New flag HTTP_FLAG_NEED_HEADER. 2006-08-21 Werner Koch * Makefile.am (libcommon_a_SOURCES): Added keyserver.h * openpgpdefs.h: New. Stripped from ..g10/packet.h. 2006-08-16 Werner Koch * keyserver.h: Moved from ../include to here. * http.c: Include srv.h. * srv.c, srv.h: New. Taken from GnuPG 1.4 2006-08-14 Werner Koch * http.h (struct http_context_s): Moved to implementation. * http.c (http_open): Changed call to return a context. (http_open_document): Ditto. (http_get_read_ptr, http_get_read_ptr, http_get_status_code): New. (do_parse_uri): Replaced strlwr by straight code to ease standalone use of this file. (http_wait_response): Removed arg STATUS_CODE as it is available through an accessor function. Adjusted caller. (http_escape_string): New. * estream.c (es_read_line): Renamed to .. (doreadline): .. this. Changed all callers. (es_read_line): New. This is theusual limited getline variabnt as used at several places. Here taken and adjusted from xreadline.c (es_free): New. 2006-08-11 Werner Koch * http.c: Major internal changes to optionallly support GNUTLS and ESTREAM. (http_open): Move initialization of the stream ... (send_request): .. here. (http_register_tls_callback): New. * estream.c (es_writen): Try to seek only is a seek function has been registered. 2006-08-09 Werner Koch * http.c, http.h: New. Taken from gnupg 1.4.5, merged with changes done for the Dirmngr project (by g10 Code) and cleaned up some stuff. (make_header_line): New. Change all caller to make user of the new * Makefile.am (libcommon_a_SOURCES): Added http.c and http.h. 2006-05-23 Werner Koch * gettime.c (isotimestamp): New. * ttyio.c (tty_get_ttyname): Posixly correct usage of ctermid. * dns-cert.c: New. Taken from 1.4.3's util/cert.c. * dns-cert.h: New. 2006-05-22 Werner Koch * pka.c: New. Taked from 1.4.3. * pka.h: New. * Makefile.am: Added pka. 2006-05-19 Werner Koch * yesno.c (answer_is_yes_no_default, answer_is_yes_no_quit): Updated from 1.4.3. (answer_is_okay_cancel): new. From 1.4.3. * miscellaneous.c (match_multistr): New. Taken from 1.4.3. * ttyio.c (tty_enable_completion, tty_disable_completion): New dummy functions. * ttyio.h: Add prototypes and stubs. 2006-04-19 Werner Koch * iobuf.c (iobuf_get_fd): New. Taken from 1.4.3. (iobuf_is_pipe_filename): New. (pop_filter): Made static. (iobuf_skip_rest): New. Orginal patch by Florian Weimer. Added new argument PARTIAL. (block_filter): Remove the old gpg indeterminate length mode. (block_filter): Properly handle a partial body stream that ends with a 5-byte length that happens to be zero. (iobuf_set_block_mode, iobuf_in_block_mode): Removed as superfluous. (iobuf_get_filelength): New arg OVERFLOW. (iobuf_get_filelength) [W32]: Use GetFileSizeEx if available * miscellaneous.c (is_file_compressed): Take care of OVERFLOW. 2006-04-18 Werner Koch * homedir.c (w32_shgetfolderpath): New. Taken from gpg 1.4.3. (default_homedir): Use it. 2005-10-08 Marcus Brinkmann * signal.c (get_signal_name): Check value of HAVE_DECL_SYS_SIGLIST instead of just if it is defined. 2005-09-28 Marcus Brinkmann * Makefile.am (AM_CFLAGS): Add $(LIBASSUAN_CFLAGS). 2005-07-04 Marcus Brinkmann * simple-pwquery.h (simple_pwclear): New prototype. * simple-pwquery.c (simple_pwclear): New function. 2005-06-15 Werner Koch * miscellaneous.c (make_printable_string): Made P a void*. * sexputil.c (keygrip_from_canon_sexp, cmp_simple_canon_sexp): Fixed signed/unsigned pointer mismatch. (make_simple_sexp_from_hexstr): Ditto. This is all too ugly; I wonder why gcc-4's default is to warn about them and forcing us to use cast the warning away. * iobuf.c (block_filter): Ditto. (iobuf_flush): Ditto. (iobuf_read_line): Ditto. (iobuf_read): Make BUFFER a void *. (iobuf_write): Make BUFFER a const void *. * ttyio.c (tty_print_utf8_string2): Ditto. * estream.c (estream_cookie_mem): Make MEMORY unsigned char*. (es_write): Make BUFFER a void *. (es_writen): Ditto. (es_func_fd_read, es_func_fd_write, es_func_mem_read) (es_func_mem_write): Ditto. (es_read, es_readn): Ditto. (es_func_mem_write): Made MEMORY_NEW an unsigned char *. * estream.h (es_cookie_read_function_t) (es_cookie_write_function_t): Changed buffer arg to void*. 2005-06-03 Werner Koch * estream.c: Use HAVE_CONFIG_H and not USE_CONFIG_H! (es_func_fd_read, es_func_fd_write): Protect against EINTR. 2005-06-01 Werner Koch * Makefile.am (AM_CPPFLAGS): Added. * util.h: Add some includes for gnulib. (ttyname, isascii): Define them inline. * fseeko.c, ftello.c: Removed. * strsep.c, mkdtemp.c: Removed. * ttyname.c, isascii.c: Removed. 2005-05-31 Werner Koch * dynload.h: s/__inline__/inline/. 2005-05-13 Werner Koch * signal.c (got_fatal_signal): Print the signal number if we can't get a name for it. (get_signal_name): Return NULL if no name is available. Fixed conditional for sys_siglist to the correct one. 2005-04-17 Werner Koch * sexputil.c (cmp_simple_canon_sexp): New. (make_simple_sexp_from_hexstr): New. 2005-04-07 Werner Koch * sexputil.c: New. 2005-04-11 Marcus Brinkmann * simple-pwquery.c (simple_pwquery): Use spwq_secure_free. 2005-03-03 Werner Koch * Makefile.am (AM_CFLAGS): Added PTH_CFLAGS. Noted by Kazu Yamamoto. 2005-02-25 Werner Koch * xasprintf.c (xtryasprintf): New. 2005-01-26 Moritz Schulte * Makefile.am (libcommon_a_SOURCES): New source files: estream.c, estream.h. * estream.c, estream.h: New files. 2005-01-03 Werner Koch * asshelp.c (send_pinentry_environment): Fixed changed from 2004-12-18; cut+paste error for lc-messages. 2004-12-21 Werner Koch * simple-pwquery.c (agent_open) [W32]: Implement for W32. (readline) [W32]: Use recv instead of read. (writen) [W32]: Use send instead of write. (my_stpcpy): Define a stpcpy replacement so that this file continues to be self-contained. (agent_send_all_options) [W32]: Don't call ttyname. 2004-12-21 Marcus Brinkmann * simple-pwquery.h (simple_query): Add prototype. * simple-pwquery.c (simple_query): New function. 2004-12-21 Werner Koch * signal.c (got_fatal_signal, got_usr_signal) (got_fatal_signal) [DOSISH]: Don't build. * simple-gettext.c: Include sysutils.h * homedir.c: New. Use CSIDL_APPDATA for W32 as the default home directory. * Makefile.am (libcommon_a_SOURCES): Add it. (EXTRA_DIST): Removed mkerror and mkerrtok. 2004-12-20 Werner Koch * sysutils.h [W32]: Define sleep. * util.h: Add prototype for mkdtemp. * membuf.c (put_membuf): Wipe out buffer after a failed realloc. 2004-12-19 Werner Koch * maperror.c (map_assuan_err_with_source): Oops, args were swapped. 2004-12-18 Werner Koch * maperror.c (map_assuan_err): Renamed to .. (map_assuan_err_with_source): .. this and add arg SOURCE.c * asshelp.c (send_pinentry_environment, send_one_option): Add arg ERRSOURCE. 2004-12-15 Werner Koch * sysutils.h [W32]: Prototypes for registry functions. * w32reg.c: Include sysutils.h * simple-pwquery.c [W32]: Dummy code to allow a build. * exechelp.c [W32]: Implemented for W32 . * ttyname.c: New. * asshelp.c (send_one_option): New. (send_pinentry_environment): Cleaned up and made sure that empty values are not send. 2004-12-07 Werner Koch * asshelp.c (send_pinentry_environment) [W32]: Do not use ttyname. 2004-12-06 Werner Koch * exechelp.h, exechelp.c: New. Based on code from ../sm/import.c. 2004-12-03 Werner Koch * strsep.c: Fixed copyright comments. 2004-11-26 Werner Koch * simple-gettext.c: New taken from gnupg 1.3.x * simple-pwquery.c [_WIN32]: Include winsock2.h. (agent_open): Disable it until we have our AF_UNIX implementation ready. * fseeko.c, ftello.c: Include sys/types for the sake of W32. 2004-11-23 Werner Koch * b64enc.c: Include stdio.h and string.h 2004-08-18 Werner Koch * simple-pwquery.c (simple_pwquery): Handle gpg-error style return code for canceled. 2004-07-20 Werner Koch * maperror.c: Removed header ksba.h. Not required anymore. 2004-06-14 Werner Koch * xreadline.c: New. Based on the iobuf_read_line function. 2004-05-12 Werner Koch * util.h (xtrycalloc_secure,xtrymalloc_secure): New. 2004-05-11 Werner Koch * sysutils.c (disable_core_dumps): Only set the current limit. (enable_core_dumps): New. 2004-04-13 Werner Koch * simple-pwquery.c (copy_and_escape): Relaxed quoting. 2004-04-05 Werner Koch * errors.h (STATUS_NEWSIG): New. 2004-03-11 Werner Koch * dynload.h [__MINGW32__]: Define RTLD_LAZY. 2004-03-09 Werner Koch * maperror.c (map_assuan_err): Map the Locale_Problem item. 2004-03-03 Werner Koch * asshelp.c, asshelp.h: New. (send_pinentry_environment): New. Code taken from ../sm/call-agent.c. 2004-02-19 Werner Koch * simple-pwquery.c (agent_open): Don't mangle INFOSTR. 2004-02-17 Werner Koch * simple-pwquery.c (agent_open): Ignore an empty GPG_AGENT_INFO. * errors.h: Added STATUS_IMPORT_OK. 2004-02-10 Werner Koch * b64enc.c: New. Based on code from ../sm/base64.c. 2004-01-30 Marcus Brinkmann * Makefile.am (libcommon_a_SOURCES): Add xasprintf.c. * miscellaneous.c (xasprintf): Moved to ... * xasprintf (xasprintf): ... here. New file. This allows to use xasprintf without sucking in gpg-error. 2004-01-27 Werner Koch * sexp-parse.h: New; moved from../agent. * util.h (xtoi_4): New. 2003-12-23 Werner Koch * maperror.c (map_assuan_err): Prepared for a new error code. 2003-12-17 Werner Koch * gettime.c (asctimestamp): Add a note on a non-avoidable gcc warning. * util.h [!HAVE_VASPRINTF]: Add printf format attribute to the replacement function. * miscellaneous.c (xasprintf): New. 2003-11-14 Werner Koch * mkdtemp.c (mkdtemp): Use gcry_create_nonce. * cryptmiss.c: Removed. 2003-11-13 Werner Koch * util.h (vasprintf): Also fixed the prototype. * vasprintf.c (vasprintf): ARGS should not be a pointer. Fixed segv on Solaris. Reported by Andrew J. Schorr. 2003-11-12 Werner Koch * maperror.c (map_ksba_err, map_gcry_err, map_kbx_err): Removed. 2003-10-31 Werner Koch * util.h (gnupg_isotime_t): New. (gnupg_copy_time): New. * gettime.c (gnupg_get_isotime): New. 2003-09-23 Werner Koch * iobuf.c (check_special_filename): Replaced is isdigit by digitp to avoid passing negative values and potential locale problems. Problem noted by Christian Biere. * util.h (ascii_isspace): New. 2003-09-18 Werner Koch * ttyio.c (tty_fprintf): New. (tty_print_string, tty_print_utf8_string2) (tty_print_utf8_string): Made P argument const byte*. 2003-08-20 Marcus Brinkmann * maperror.c (map_ksba_err): Map -1. Use gpg_err_make to set the error source. 2003-08-14 Timo Schulz * dynload.h. New. W32 wrapper around the dynload mechanism. 2003-07-15 Werner Koch * simple-pwquery.c, simple-pwquery.h: New; moved from ../agent. * Makefile.am (libsimple_pwquery_a_LIBADD): New. 2003-06-25 Werner Koch * maperror.c (map_to_assuan_status): Directly map 0 to 0. 2003-06-17 Werner Koch * gettime.c (scan_isodatestr,add_days_to_timestamp,strtimevalue) (strtimestamp,asctimestamp): New. Code taken from gnupg 1.3.2 mischelp.c. * yesno.c: New. Code taken from gnupg 1.3.2 mischelp.c * miscellaneous.c: New. * util.h: Include utf8conf.h 2003-06-16 Werner Koch * gettime.c (make_timestamp): New. * ttyio.c: New. Taken from gnupg 1.2. * ttyio.h: Move from ../include. 2003-06-13 Werner Koch * util.h (seterr): Removed macro. (xmalloc_secure,xcalloc_secure): New. 2003-06-11 Werner Koch * iobuf.c (iobuf_writebyte,iobuf_write): Return error code from iobuf_flush. (iobuf_writestr): Ditto. 2003-06-10 Werner Koch * iobuf.c, iobuf.h: New. Taken from current gnupg 1.3 CVS. Run indent on it and adjusted error handling to libgpg-error style. Replaced IOBUF by iobuf_t. Renamed malloc functions. 2003-06-04 Werner Koch * errors.h: Removed all error codes. We keep the status codes for now. * Makefile.am: Do not create errors.c anymore; remove it from the sources. * maperror.c: Don't include error.h. Change all error codes to libgpg-error style. (map_assuan_err): Changed to new Assuan error code convention. (map_to_assuan_status): Likewise. (map_gcry_err,map_kbx_err): Not needed. For now dummy functions. * membuf.c, membuf.h: New. Code taken from ../sm/call-agent.h. * Makefile.am: Added above. 2003-04-29 Werner Koch * util.h (fopencokokie): Removed prototype and struct. * fopencookie.c: Removed. * maperror.c: Use system assuan.h 2002-10-31 Neal H. Walfield * isascii.c: New file. * putc_unlocked.c: Likewise. 2002-10-28 Neal H. Walfield * signal.c (caught_fatal_sig): Remove superfluous zero initializer. (caught_sigusr1): Likewise. 2002-09-04 Neal H. Walfield * vasprintf.c (vasprintf) [va_copy]: Use va_copy. [!va_copy && __va_copy]: Use __va_copy. [!va_copy && !__va_copy]: Only now fall back to using memcpy. 2002-08-21 Werner Koch * errors.h: Added STATUS_IMPORT_PROBLEM. 2002-08-20 Werner Koch * vasprintf.c: Hack to handle NULL for %s. 2002-08-09 Werner Koch * signal.c: New. Taken from GnuPG 1.1.91. 2002-07-23 Werner Koch * util.h (_IO_cookie_io_functions_t): Fixed typo. Noted by Richard Lefebvre. 2002-07-22 Werner Koch * fseeko.c, ftello.c: New. 2002-06-28 Werner Koch * maperror.c (map_to_assuan_status): Map more errorcodes to Bad Certificate. 2002-06-26 Werner Koch * maperror.c (map_to_assuan_status): Map EOF to No_Data_Available. 2002-06-10 Werner Koch * errors.h (gnupg_error_token): Add new prototype. (STATUS_ERROR): New. * mkerrtok: New. * Makefile.am: Use it to create the new error token function. 2002-06-04 Werner Koch * maperror.c (map_to_assuan_status): Map Bad_CA_Certificate. 2002-05-23 Werner Koch * no-pth.c, Makefile.am: Removed. 2002-05-22 Werner Koch * mkdtemp.c: Replaced byte by unsigned char because it is no longer defined in gcrypt.h. 2002-05-21 Werner Koch * maperror.c (map_gcry_err): Add libgcrypt's new S-expression errors. (map_ksba_err): Add a few mappings. 2002-05-14 Werner Koch * gettime.c: New. 2002-05-03 Werner Koch * errors.h: Added STARUS_EXPSIG and STATUS_EXPKEYSIG. 2002-04-15 Werner Koch * cryptmiss.c: New. 2002-02-14 Werner Koch * maperror.c: Add more assuan<->gnupg mappings. 2002-02-12 Werner Koch * fopencookie.c: Dummy function. * vasprintf.c: New. Taken from binutils-2.9.1 and dropped all non ANSI-C stuff. Merged with asprintf version. * no-pth.c: New. 2002-01-23 Werner Koch * mkdtemp.c: Copied from gnupg-1.0.6c and changed to use libgcrypt. 2002-01-19 Werner Koch * sysutils.c: New. This is the misc.c file from gnupg 1.0.6 with the OpenPGP stuff removed. * sysutils.h: New. 2002-01-15 Werner Koch * maperror.c: Add mapping for Not_Trusted. 2002-01-11 Werner Koch * maperror.c (map_assuan_err): Codes for CRL 2002-01-08 Werner Koch * util.h (spacep): New. 2002-01-02 Werner Koch * maperror.c (map_to_assuan_status): New. Merged from ../agent and ../sm. 2001-12-20 Werner Koch * maperror.c (map_gcry_err): Add some mappings. 2001-12-18 Werner Koch * Makefile.am (AM_CPPFLAGS): Include flags for gcrypt and ksba 2001-12-14 Werner Koch * util.h (digitp, hexdigitp): New ctype like macros. (atoi_1,atoi_2,atoi_4,xtoi_1,xtoi_2): New. Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 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/common/estream.c b/common/estream.c index af7da7052..dfa2de4aa 100644 --- a/common/estream.c +++ b/common/estream.c @@ -1,3371 +1,3412 @@ /* estream.c - Extended Stream I/O Library * Copyright (C) 2004, 2005, 2006, 2007, 2009, 2010 g10 Code GmbH * * This file is part of Libestream. * * Libestream is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation; either version 2 of the License, * or (at your option) any later version. * * Libestream is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Libestream; if not, see . * * ALTERNATIVELY, Libestream may be distributed under the terms of the * following license, in which case the provisions of this license are * required INSTEAD OF the GNU General Public License. If you wish to * allow use of your version of this file only under the terms of the * GNU General Public License, and not to allow others to use your * version of this file under the terms of the following license, * indicate your decision by deleting this paragraph and the license * below. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, and the entire permission notice in its entirety, * including the disclaimer of warranties. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote * products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef USE_ESTREAM_SUPPORT_H # include #endif #ifdef HAVE_CONFIG_H # include #endif #if defined(_WIN32) && !defined(HAVE_W32_SYSTEM) # define HAVE_W32_SYSTEM 1 # if defined(__MINGW32CE__) && !defined (HAVE_W32CE_SYSTEM) # define HAVE_W32CE_SYSTEM # endif #endif #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_W32_SYSTEM # include #endif #ifdef HAVE_W32CE_SYSTEM # include /* ERRNO replacement. */ #endif #ifdef WITHOUT_GNU_PTH /* Give the Makefile a chance to build without Pth. */ # undef HAVE_PTH # undef USE_GNU_PTH #endif #ifdef HAVE_PTH # include #endif /* This is for the special hack to use estream.c in GnuPG. */ #ifdef GNUPG_MAJOR_VERSION # include "../common/util.h" #endif #ifndef HAVE_MKSTEMP int mkstemp (char *template); #endif #ifndef HAVE_MEMRCHR void *memrchr (const void *block, int c, size_t size); #endif #include #include #ifndef O_BINARY #define O_BINARY 0 #endif #ifdef HAVE_W32CE_SYSTEM # define _set_errno(a) gpg_err_set_errno ((a)) #else # define _set_errno(a) do { errno = (a); } while (0) #endif /* Generally used types. */ typedef void *(*func_realloc_t) (void *mem, size_t size); typedef void (*func_free_t) (void *mem); /* Buffer management layer. */ #define BUFFER_BLOCK_SIZE BUFSIZ #define BUFFER_UNREAD_SIZE 16 /* Locking. */ #ifdef HAVE_PTH typedef pth_mutex_t estream_mutex_t; # define ESTREAM_MUTEX_INITIALIZER PTH_MUTEX_INIT # define ESTREAM_MUTEX_LOCK(mutex) \ pth_mutex_acquire (&(mutex), 0, NULL) # define ESTREAM_MUTEX_UNLOCK(mutex) \ pth_mutex_release (&(mutex)) # define ESTREAM_MUTEX_TRYLOCK(mutex) \ ((pth_mutex_acquire (&(mutex), 1, NULL) == TRUE) ? 0 : -1) # define ESTREAM_MUTEX_INITIALIZE(mutex) \ pth_mutex_init (&(mutex)) #else typedef void *estream_mutex_t; static inline void dummy_mutex_call_void (estream_mutex_t mutex) { (void)mutex; } static inline int dummy_mutex_call_int (estream_mutex_t mutex) { (void)mutex; return 0; } # define ESTREAM_MUTEX_INITIALIZER NULL # define ESTREAM_MUTEX_LOCK(mutex) dummy_mutex_call_void ((mutex)) # define ESTREAM_MUTEX_UNLOCK(mutex) dummy_mutex_call_void ((mutex)) # define ESTREAM_MUTEX_TRYLOCK(mutex) dummy_mutex_call_int ((mutex)) # define ESTREAM_MUTEX_INITIALIZE(mutex) dummy_mutex_call_void ((mutex)) #endif /* Primitive system I/O. */ #ifdef HAVE_PTH # define ESTREAM_SYS_READ es_pth_read # define ESTREAM_SYS_WRITE es_pth_write #else # define ESTREAM_SYS_READ read # define ESTREAM_SYS_WRITE write #endif /* Misc definitions. */ #define ES_DEFAULT_OPEN_MODE (S_IRUSR | S_IWUSR) /* An internal stream object. */ struct estream_internal { unsigned char buffer[BUFFER_BLOCK_SIZE]; unsigned char unread_buffer[BUFFER_UNREAD_SIZE]; estream_mutex_t lock; /* Lock. */ void *cookie; /* Cookie. */ void *opaque; /* Opaque data. */ unsigned int modeflags; /* Flags for the backend. */ off_t offset; es_cookie_read_function_t func_read; es_cookie_write_function_t func_write; es_cookie_seek_function_t func_seek; es_cookie_close_function_t func_close; int strategy; int fd; struct { unsigned int err: 1; unsigned int eof: 1; } indicators; unsigned int deallocate_buffer: 1; unsigned int print_err: 1; /* Error in print_fun_writer. */ int print_errno; /* Errno from print_fun_writer. */ size_t print_ntotal; /* Bytes written from in print_fun_writer. */ FILE *print_fp; /* Stdio stream used by print_fun_writer. */ }; typedef struct estream_internal *estream_internal_t; #define ESTREAM_LOCK(stream) ESTREAM_MUTEX_LOCK (stream->intern->lock) #define ESTREAM_UNLOCK(stream) ESTREAM_MUTEX_UNLOCK (stream->intern->lock) #define ESTREAM_TRYLOCK(stream) ESTREAM_MUTEX_TRYLOCK (stream->intern->lock) /* Stream list. */ typedef struct estream_list *estream_list_t; struct estream_list { estream_t car; estream_list_t cdr; estream_list_t *prev_cdr; }; static estream_list_t estream_list; static estream_mutex_t estream_list_lock; #define ESTREAM_LIST_LOCK ESTREAM_MUTEX_LOCK (estream_list_lock) #define ESTREAM_LIST_UNLOCK ESTREAM_MUTEX_UNLOCK (estream_list_lock) #ifndef EOPNOTSUPP # define EOPNOTSUPP ENOSYS #endif /* Macros. */ /* Calculate array dimension. */ #ifndef DIM #define DIM(array) (sizeof (array) / sizeof (*array)) #endif #define tohex(n) ((n) < 10 ? ((n) + '0') : (((n) - 10) + 'A')) /* Evaluate EXPRESSION, setting VARIABLE to the return code, if VARIABLE is zero. */ #define SET_UNLESS_NONZERO(variable, tmp_variable, expression) \ do \ { \ tmp_variable = expression; \ if ((! variable) && tmp_variable) \ variable = tmp_variable; \ } \ while (0) /* Malloc wrappers to overcome problems on some older OSes. */ static void * mem_alloc (size_t n) { if (!n) n++; return malloc (n); } static void * mem_realloc (void *p, size_t n) { if (!p) return mem_alloc (n); return realloc (p, n); } static void mem_free (void *p) { if (p) free (p); } /* * List manipulation. */ /* Add STREAM to the list of registered stream objects. */ static int es_list_add (estream_t stream) { estream_list_t list_obj; int ret; list_obj = mem_alloc (sizeof (*list_obj)); if (! list_obj) ret = -1; else { ESTREAM_LIST_LOCK; list_obj->car = stream; list_obj->cdr = estream_list; list_obj->prev_cdr = &estream_list; if (estream_list) estream_list->prev_cdr = &list_obj->cdr; estream_list = list_obj; ESTREAM_LIST_UNLOCK; ret = 0; } return ret; } /* Remove STREAM from the list of registered stream objects. */ static void es_list_remove (estream_t stream) { estream_list_t list_obj; ESTREAM_LIST_LOCK; for (list_obj = estream_list; list_obj; list_obj = list_obj->cdr) if (list_obj->car == stream) { *list_obj->prev_cdr = list_obj->cdr; if (list_obj->cdr) list_obj->cdr->prev_cdr = list_obj->prev_cdr; mem_free (list_obj); break; } ESTREAM_LIST_UNLOCK; } /* Type of an stream-iterator-function. */ typedef int (*estream_iterator_t) (estream_t stream); /* Iterate over list of registered streams, calling ITERATOR for each of them. */ static int es_list_iterate (estream_iterator_t iterator) { estream_list_t list_obj; int ret = 0; ESTREAM_LIST_LOCK; for (list_obj = estream_list; list_obj; list_obj = list_obj->cdr) ret |= (*iterator) (list_obj->car); ESTREAM_LIST_UNLOCK; return ret; } /* * I/O Helper * * Unfortunately our Pth emulation for Windows expects system handles * for pth_read and pth_write. We use a simple approach to fix this: * If the function returns an error we fall back to a vanilla read or * write, assuming that we do I/O on a plain file where the operation * can't block. */ #ifdef HAVE_PTH static int es_pth_read (int fd, void *buffer, size_t size) { # ifdef HAVE_W32_SYSTEM int rc = pth_read (fd, buffer, size); if (rc == -1 && errno == EINVAL) rc = read (fd, buffer, size); return rc; # else /*!HAVE_W32_SYSTEM*/ return pth_read (fd, buffer, size); # endif /* !HAVE_W32_SYSTEM*/ } static int es_pth_write (int fd, const void *buffer, size_t size) { # ifdef HAVE_W32_SYSTEM int rc = pth_write (fd, buffer, size); if (rc == -1 && errno == EINVAL) rc = write (fd, buffer, size); return rc; # else /*!HAVE_W32_SYSTEM*/ return pth_write (fd, buffer, size); # endif /* !HAVE_W32_SYSTEM*/ } #endif /*HAVE_PTH*/ /* * Initialization. */ static int es_init_do (void) { #ifdef HAVE_PTH static int initialized; if (!initialized) { if (!pth_init () && errno != EPERM ) return -1; if (pth_mutex_init (&estream_list_lock)) initialized = 1; } #endif return 0; } /* * I/O methods. */ /* Implementation of Memory I/O. */ /* Cookie for memory objects. */ typedef struct estream_cookie_mem { unsigned int modeflags; /* Open flags. */ unsigned char *memory; /* Allocated data buffer. */ size_t memory_size; /* Allocated size of MEMORY. */ size_t memory_limit; /* Caller supplied maximum allowed allocation size or 0 for no limit. */ size_t offset; /* Current offset in MEMORY. */ size_t data_len; /* Used length of data in MEMORY. */ size_t block_size; /* Block size. */ struct { unsigned int grow: 1; /* MEMORY is allowed to grow. */ } flags; func_realloc_t func_realloc; func_free_t func_free; } *estream_cookie_mem_t; /* Create function for memory objects. DATA is either NULL or a user supplied buffer with the initial conetnt of the memory buffer. If DATA is NULL, DATA_N and DATA_LEN need to be 0 as well. If DATA is not NULL, DATA_N gives the allocated size of DATA and DATA_LEN the used length in DATA. */ static int es_func_mem_create (void *ES__RESTRICT *ES__RESTRICT cookie, unsigned char *ES__RESTRICT data, size_t data_n, size_t data_len, size_t block_size, unsigned int grow, func_realloc_t func_realloc, func_free_t func_free, unsigned int modeflags, size_t memory_limit) { estream_cookie_mem_t mem_cookie; int err; if (!data && (data_n || data_len)) { _set_errno (EINVAL); return -1; } mem_cookie = mem_alloc (sizeof (*mem_cookie)); if (!mem_cookie) err = -1; else { mem_cookie->modeflags = modeflags; mem_cookie->memory = data; mem_cookie->memory_size = data_n; mem_cookie->memory_limit = memory_limit; mem_cookie->offset = 0; mem_cookie->data_len = data_len; mem_cookie->block_size = block_size; mem_cookie->flags.grow = !!grow; mem_cookie->func_realloc = func_realloc ? func_realloc : mem_realloc; mem_cookie->func_free = func_free ? func_free : mem_free; *cookie = mem_cookie; err = 0; } return err; } /* Read function for memory objects. */ static ssize_t es_func_mem_read (void *cookie, void *buffer, size_t size) { estream_cookie_mem_t mem_cookie = cookie; ssize_t ret; if (size > mem_cookie->data_len - mem_cookie->offset) size = mem_cookie->data_len - mem_cookie->offset; if (size) { memcpy (buffer, mem_cookie->memory + mem_cookie->offset, size); mem_cookie->offset += size; } ret = size; return ret; } /* Write function for memory objects. */ static ssize_t es_func_mem_write (void *cookie, const void *buffer, size_t size) { estream_cookie_mem_t mem_cookie = cookie; ssize_t ret; size_t nleft; if (!size) return 0; /* A flush is a NOP for memory objects. */ if (mem_cookie->modeflags & O_APPEND) { /* Append to data. */ mem_cookie->offset = mem_cookie->data_len; } assert (mem_cookie->memory_size >= mem_cookie->offset); nleft = mem_cookie->memory_size - mem_cookie->offset; /* If we are not allowed to grow limit the size to the left space. */ if (!mem_cookie->flags.grow && size > nleft) size = nleft; /* Enlarge the memory buffer if needed. */ if (size > nleft) { unsigned char *newbuf; size_t newsize; if (!mem_cookie->memory_size) newsize = size; /* Not yet allocated. */ else newsize = mem_cookie->memory_size + (nleft - size); if (newsize < mem_cookie->offset) { _set_errno (EINVAL); return -1; } /* Round up to the next block length. BLOCK_SIZE should always be set; we check anyway. */ if (mem_cookie->block_size) { newsize += mem_cookie->block_size - 1; if (newsize < mem_cookie->offset) { _set_errno (EINVAL); return -1; } newsize /= mem_cookie->block_size; newsize *= mem_cookie->block_size; } /* Check for a total limit. */ if (mem_cookie->memory_limit && newsize > mem_cookie->memory_limit) { _set_errno (ENOSPC); return -1; } newbuf = mem_cookie->func_realloc (mem_cookie->memory, newsize); if (!newbuf) return -1; mem_cookie->memory = newbuf; mem_cookie->memory_size = newsize; assert (mem_cookie->memory_size >= mem_cookie->offset); nleft = mem_cookie->memory_size - mem_cookie->offset; assert (size <= nleft); } memcpy (mem_cookie->memory + mem_cookie->offset, buffer, size); if (mem_cookie->offset + size > mem_cookie->data_len) mem_cookie->data_len = mem_cookie->offset + size; mem_cookie->offset += size; ret = size; return ret; } /* Seek function for memory objects. */ static int es_func_mem_seek (void *cookie, off_t *offset, int whence) { estream_cookie_mem_t mem_cookie = cookie; off_t pos_new; switch (whence) { case SEEK_SET: pos_new = *offset; break; case SEEK_CUR: pos_new = mem_cookie->offset += *offset; break; case SEEK_END: pos_new = mem_cookie->data_len += *offset; break; default: _set_errno (EINVAL); return -1; } if (pos_new > mem_cookie->memory_size) { size_t newsize; void *newbuf; if (!mem_cookie->flags.grow) { _set_errno (ENOSPC); return -1; } newsize = pos_new + mem_cookie->block_size - 1; if (newsize < pos_new) { _set_errno (EINVAL); return -1; } newsize /= mem_cookie->block_size; newsize *= mem_cookie->block_size; if (mem_cookie->memory_limit && newsize > mem_cookie->memory_limit) { _set_errno (ENOSPC); return -1; } newbuf = mem_cookie->func_realloc (mem_cookie->memory, newsize); if (!newbuf) return -1; mem_cookie->memory = newbuf; mem_cookie->memory_size = newsize; } if (pos_new > mem_cookie->data_len) { /* Fill spare space with zeroes. */ memset (mem_cookie->memory + mem_cookie->data_len, 0, pos_new - mem_cookie->data_len); mem_cookie->data_len = pos_new; } mem_cookie->offset = pos_new; *offset = pos_new; return 0; } /* Destroy function for memory objects. */ static int es_func_mem_destroy (void *cookie) { estream_cookie_mem_t mem_cookie = cookie; if (cookie) { mem_cookie->func_free (mem_cookie->memory); mem_free (mem_cookie); } return 0; } static es_cookie_io_functions_t estream_functions_mem = { es_func_mem_read, es_func_mem_write, es_func_mem_seek, es_func_mem_destroy }; /* Implementation of fd I/O. */ /* Cookie for fd objects. */ typedef struct estream_cookie_fd { int fd; /* The file descriptor we are using for actual output. */ int no_close; /* If set we won't close the file descriptor. */ } *estream_cookie_fd_t; /* Create function for fd objects. */ static int es_func_fd_create (void **cookie, int fd, unsigned int modeflags, int no_close) { estream_cookie_fd_t fd_cookie; int err; fd_cookie = mem_alloc (sizeof (*fd_cookie)); if (! fd_cookie) err = -1; else { #ifdef HAVE_DOSISH_SYSTEM /* Make sure it is in binary mode if requested. */ if ( (modeflags & O_BINARY) ) setmode (fd, O_BINARY); #else (void)modeflags; #endif fd_cookie->fd = fd; fd_cookie->no_close = no_close; *cookie = fd_cookie; err = 0; } return err; } /* Read function for fd objects. */ static ssize_t es_func_fd_read (void *cookie, void *buffer, size_t size) { estream_cookie_fd_t file_cookie = cookie; ssize_t bytes_read; do bytes_read = ESTREAM_SYS_READ (file_cookie->fd, buffer, size); while (bytes_read == -1 && errno == EINTR); return bytes_read; } /* Write function for fd objects. */ static ssize_t es_func_fd_write (void *cookie, const void *buffer, size_t size) { estream_cookie_fd_t file_cookie = cookie; ssize_t bytes_written; do bytes_written = ESTREAM_SYS_WRITE (file_cookie->fd, buffer, size); while (bytes_written == -1 && errno == EINTR); return bytes_written; } /* Seek function for fd objects. */ static int es_func_fd_seek (void *cookie, off_t *offset, int whence) { estream_cookie_fd_t file_cookie = cookie; off_t offset_new; int err; offset_new = lseek (file_cookie->fd, *offset, whence); if (offset_new == -1) err = -1; else { *offset = offset_new; err = 0; } return err; } /* Destroy function for fd objects. */ static int es_func_fd_destroy (void *cookie) { estream_cookie_fd_t fd_cookie = cookie; int err; if (fd_cookie) { err = fd_cookie->no_close? 0 : close (fd_cookie->fd); mem_free (fd_cookie); } else err = 0; return err; } static es_cookie_io_functions_t estream_functions_fd = { es_func_fd_read, es_func_fd_write, es_func_fd_seek, es_func_fd_destroy }; /* Implementation of FILE* I/O. */ /* Cookie for fp objects. */ typedef struct estream_cookie_fp { FILE *fp; /* The file pointer we are using for actual output. */ int no_close; /* If set we won't close the file pointer. */ } *estream_cookie_fp_t; /* Create function for fd objects. */ static int es_func_fp_create (void **cookie, FILE *fp, unsigned int modeflags, int no_close) { estream_cookie_fp_t fp_cookie; int err; fp_cookie = mem_alloc (sizeof *fp_cookie); if (!fp_cookie) err = -1; else { #ifdef HAVE_DOSISH_SYSTEM /* Make sure it is in binary mode if requested. */ if ( (modeflags & O_BINARY) ) setmode (fileno (fp), O_BINARY); #else (void)modeflags; #endif fp_cookie->fp = fp; fp_cookie->no_close = no_close; *cookie = fp_cookie; err = 0; } return err; } /* Read function for FILE* objects. */ static ssize_t es_func_fp_read (void *cookie, void *buffer, size_t size) { estream_cookie_fp_t file_cookie = cookie; ssize_t bytes_read; - bytes_read = fread (buffer, 1, size, file_cookie->fp); + if (file_cookie->fp) + bytes_read = fread (buffer, 1, size, file_cookie->fp); + else + bytes_read = 0; if (!bytes_read && ferror (file_cookie->fp)) return -1; return bytes_read; } /* Write function for FILE* objects. */ static ssize_t es_func_fp_write (void *cookie, const void *buffer, size_t size) { estream_cookie_fp_t file_cookie = cookie; size_t bytes_written; - bytes_written = fwrite (buffer, 1, size, file_cookie->fp); + + if (file_cookie->fp) + bytes_written = fwrite (buffer, 1, size, file_cookie->fp); + else + bytes_written = size; /* Successfully written to the bit bucket. */ if (bytes_written != size) return -1; return bytes_written; } /* Seek function for FILE* objects. */ static int es_func_fp_seek (void *cookie, off_t *offset, int whence) { estream_cookie_fp_t file_cookie = cookie; long int offset_new; + if (!file_cookie->fp) + { + _set_errno (ESPIPE); + return -1; + } + if ( fseek (file_cookie->fp, (long int)*offset, whence) ) { - fprintf (stderr, "\nfseek failed: errno=%d (%s)\n", errno,strerror (errno)); - return -1; + /* fprintf (stderr, "\nfseek failed: errno=%d (%s)\n", */ + /* errno,strerror (errno)); */ + return -1; } offset_new = ftell (file_cookie->fp); if (offset_new == -1) { - fprintf (stderr, "\nftell failed: errno=%d (%s)\n", errno,strerror (errno)); - return -1; + /* fprintf (stderr, "\nftell failed: errno=%d (%s)\n", */ + /* errno,strerror (errno)); */ + return -1; } *offset = offset_new; return 0; } /* Destroy function for fd objects. */ static int es_func_fp_destroy (void *cookie) { estream_cookie_fp_t fp_cookie = cookie; int err; if (fp_cookie) { - fflush (fp_cookie->fp); - err = fp_cookie->no_close? 0 : fclose (fp_cookie->fp); + if (fp_cookie->fp) + { + fflush (fp_cookie->fp); + err = fp_cookie->no_close? 0 : fclose (fp_cookie->fp); + } + else + err = 0; mem_free (fp_cookie); } else err = 0; return err; } static es_cookie_io_functions_t estream_functions_fp = { es_func_fp_read, es_func_fp_write, es_func_fp_seek, es_func_fp_destroy }; /* Implementation of file I/O. */ /* Create function for file objects. */ static int es_func_file_create (void **cookie, int *filedes, const char *path, unsigned int modeflags) { estream_cookie_fd_t file_cookie; int err; int fd; err = 0; fd = -1; file_cookie = mem_alloc (sizeof (*file_cookie)); if (! file_cookie) { err = -1; goto out; } fd = open (path, modeflags, ES_DEFAULT_OPEN_MODE); if (fd == -1) { err = -1; goto out; } #ifdef HAVE_DOSISH_SYSTEM /* Make sure it is in binary mode if requested. */ if ( (modeflags & O_BINARY) ) setmode (fd, O_BINARY); #endif file_cookie->fd = fd; file_cookie->no_close = 0; *cookie = file_cookie; *filedes = fd; out: if (err) mem_free (file_cookie); return err; } static es_cookie_io_functions_t estream_functions_file = { es_func_fd_read, es_func_fd_write, es_func_fd_seek, es_func_fd_destroy }; static int es_convert_mode (const char *mode, unsigned int *modeflags) { unsigned int omode, oflags; switch (*mode) { case 'r': omode = O_RDONLY; oflags = 0; break; case 'w': omode = O_WRONLY; oflags = O_TRUNC | O_CREAT; break; case 'a': omode = O_WRONLY; oflags = O_APPEND | O_CREAT; break; default: _set_errno (EINVAL); return -1; } for (mode++; *mode; mode++) { switch (*mode) { case '+': omode = O_RDWR; break; case 'b': oflags |= O_BINARY; break; case 'x': oflags |= O_EXCL; break; default: /* Ignore unknown flags. */ break; } } *modeflags = (omode | oflags); return 0; } /* * Low level stream functionality. */ static int es_fill (estream_t stream) { size_t bytes_read = 0; int err; if (!stream->intern->func_read) { _set_errno (EOPNOTSUPP); err = -1; } else { es_cookie_read_function_t func_read = stream->intern->func_read; ssize_t ret; ret = (*func_read) (stream->intern->cookie, stream->buffer, stream->buffer_size); if (ret == -1) { bytes_read = 0; err = -1; } else { bytes_read = ret; err = 0; } } if (err) stream->intern->indicators.err = 1; else if (!bytes_read) stream->intern->indicators.eof = 1; stream->intern->offset += stream->data_len; stream->data_len = bytes_read; stream->data_offset = 0; return err; } static int es_flush (estream_t stream) { es_cookie_write_function_t func_write = stream->intern->func_write; int err; assert (stream->flags.writing); if (stream->data_offset) { size_t bytes_written; size_t data_flushed; ssize_t ret; if (! func_write) { err = EOPNOTSUPP; goto out; } /* Note: to prevent an endless loop caused by user-provided write-functions that pretend to have written more bytes than they were asked to write, we have to check for "(stream->data_offset - data_flushed) > 0" instead of "stream->data_offset - data_flushed". */ data_flushed = 0; err = 0; while ((((ssize_t) (stream->data_offset - data_flushed)) > 0) && (! err)) { ret = (*func_write) (stream->intern->cookie, stream->buffer + data_flushed, stream->data_offset - data_flushed); if (ret == -1) { bytes_written = 0; err = -1; } else bytes_written = ret; data_flushed += bytes_written; if (err) break; } stream->data_flushed += data_flushed; if (stream->data_offset == data_flushed) { stream->intern->offset += stream->data_offset; stream->data_offset = 0; stream->data_flushed = 0; /* Propagate flush event. */ (*func_write) (stream->intern->cookie, NULL, 0); } } else err = 0; out: if (err) stream->intern->indicators.err = 1; return err; } /* Discard buffered data for STREAM. */ static void es_empty (estream_t stream) { assert (!stream->flags.writing); stream->data_len = 0; stream->data_offset = 0; stream->unread_data_len = 0; } /* Initialize STREAM. */ static void es_initialize (estream_t stream, void *cookie, int fd, es_cookie_io_functions_t functions, unsigned int modeflags) { stream->intern->cookie = cookie; stream->intern->opaque = NULL; stream->intern->offset = 0; stream->intern->func_read = functions.func_read; stream->intern->func_write = functions.func_write; stream->intern->func_seek = functions.func_seek; stream->intern->func_close = functions.func_close; stream->intern->strategy = _IOFBF; stream->intern->fd = fd; stream->intern->print_err = 0; stream->intern->print_errno = 0; stream->intern->print_ntotal = 0; stream->intern->print_fp = NULL; stream->intern->indicators.err = 0; stream->intern->indicators.eof = 0; stream->intern->deallocate_buffer = 0; stream->data_len = 0; stream->data_offset = 0; stream->data_flushed = 0; stream->unread_data_len = 0; /* Depending on the modeflags we set whether we start in writing or reading mode. This is required in case we are working on a wronly stream which is not seeekable (like stdout). Without this pre-initialization we would do a seek at the first write call and as this will fail no utput will be delivered. */ if ((modeflags & O_WRONLY) || (modeflags & O_RDWR) ) stream->flags.writing = 1; else stream->flags.writing = 0; } /* Deinitialize STREAM. */ static int es_deinitialize (estream_t stream) { es_cookie_close_function_t func_close; int err, tmp_err; if (stream->intern->print_fp) { int save_errno = errno; fclose (stream->intern->print_fp); stream->intern->print_fp = NULL; _set_errno (save_errno); } func_close = stream->intern->func_close; err = 0; if (stream->flags.writing) SET_UNLESS_NONZERO (err, tmp_err, es_flush (stream)); if (func_close) SET_UNLESS_NONZERO (err, tmp_err, (*func_close) (stream->intern->cookie)); return err; } /* Create a new stream object, initialize it. */ static int es_create (estream_t *stream, void *cookie, int fd, es_cookie_io_functions_t functions, unsigned int modeflags) { estream_internal_t stream_internal_new; estream_t stream_new; int err; stream_new = NULL; stream_internal_new = NULL; stream_new = mem_alloc (sizeof (*stream_new)); if (! stream_new) { err = -1; goto out; } stream_internal_new = mem_alloc (sizeof (*stream_internal_new)); if (! stream_internal_new) { err = -1; goto out; } stream_new->buffer = stream_internal_new->buffer; stream_new->buffer_size = sizeof (stream_internal_new->buffer); stream_new->unread_buffer = stream_internal_new->unread_buffer; stream_new->unread_buffer_size = sizeof (stream_internal_new->unread_buffer); stream_new->intern = stream_internal_new; ESTREAM_MUTEX_INITIALIZE (stream_new->intern->lock); es_initialize (stream_new, cookie, fd, functions, modeflags); err = es_list_add (stream_new); if (err) goto out; *stream = stream_new; out: if (err) { if (stream_new) { es_deinitialize (stream_new); mem_free (stream_new); } } return err; } /* Deinitialize a stream object and destroy it. */ static int es_destroy (estream_t stream) { int err = 0; if (stream) { es_list_remove (stream); err = es_deinitialize (stream); mem_free (stream->intern); mem_free (stream); } return err; } /* Try to read BYTES_TO_READ bytes FROM STREAM into BUFFER in unbuffered-mode, storing the amount of bytes read in *BYTES_READ. */ static int es_read_nbf (estream_t ES__RESTRICT stream, unsigned char *ES__RESTRICT buffer, size_t bytes_to_read, size_t *ES__RESTRICT bytes_read) { es_cookie_read_function_t func_read = stream->intern->func_read; size_t data_read; ssize_t ret; int err; data_read = 0; err = 0; while (bytes_to_read - data_read) { ret = (*func_read) (stream->intern->cookie, buffer + data_read, bytes_to_read - data_read); if (ret == -1) { err = -1; break; } else if (ret) data_read += ret; else break; } stream->intern->offset += data_read; *bytes_read = data_read; return err; } /* Try to read BYTES_TO_READ bytes FROM STREAM into BUFFER in fully-buffered-mode, storing the amount of bytes read in *BYTES_READ. */ static int es_read_fbf (estream_t ES__RESTRICT stream, unsigned char *ES__RESTRICT buffer, size_t bytes_to_read, size_t *ES__RESTRICT bytes_read) { size_t data_available; size_t data_to_read; size_t data_read; int err; data_read = 0; err = 0; while ((bytes_to_read - data_read) && (! err)) { if (stream->data_offset == stream->data_len) { /* Nothing more to read in current container, try to fill container with new data. */ err = es_fill (stream); if (! err) if (! stream->data_len) /* Filling did not result in any data read. */ break; } if (! err) { /* Filling resulted in some new data. */ data_to_read = bytes_to_read - data_read; data_available = stream->data_len - stream->data_offset; if (data_to_read > data_available) data_to_read = data_available; memcpy (buffer + data_read, stream->buffer + stream->data_offset, data_to_read); stream->data_offset += data_to_read; data_read += data_to_read; } } *bytes_read = data_read; return err; } /* Try to read BYTES_TO_READ bytes FROM STREAM into BUFFER in line-buffered-mode, storing the amount of bytes read in *BYTES_READ. */ static int es_read_lbf (estream_t ES__RESTRICT stream, unsigned char *ES__RESTRICT buffer, size_t bytes_to_read, size_t *ES__RESTRICT bytes_read) { int err; err = es_read_fbf (stream, buffer, bytes_to_read, bytes_read); return err; } /* Try to read BYTES_TO_READ bytes FROM STREAM into BUFFER, storing *the amount of bytes read in BYTES_READ. */ static int es_readn (estream_t ES__RESTRICT stream, void *ES__RESTRICT buffer_arg, size_t bytes_to_read, size_t *ES__RESTRICT bytes_read) { unsigned char *buffer = (unsigned char *)buffer_arg; size_t data_read_unread, data_read; int err; data_read_unread = 0; data_read = 0; err = 0; if (stream->flags.writing) { /* Switching to reading mode -> flush output. */ err = es_flush (stream); if (err) goto out; stream->flags.writing = 0; } /* Read unread data first. */ while ((bytes_to_read - data_read_unread) && stream->unread_data_len) { buffer[data_read_unread] = stream->unread_buffer[stream->unread_data_len - 1]; stream->unread_data_len--; data_read_unread++; } switch (stream->intern->strategy) { case _IONBF: err = es_read_nbf (stream, buffer + data_read_unread, bytes_to_read - data_read_unread, &data_read); break; case _IOLBF: err = es_read_lbf (stream, buffer + data_read_unread, bytes_to_read - data_read_unread, &data_read); break; case _IOFBF: err = es_read_fbf (stream, buffer + data_read_unread, bytes_to_read - data_read_unread, &data_read); break; } out: if (bytes_read) *bytes_read = data_read_unread + data_read; return err; } /* Try to unread DATA_N bytes from DATA into STREAM, storing the amount of bytes successfully unread in *BYTES_UNREAD. */ static void es_unreadn (estream_t ES__RESTRICT stream, const unsigned char *ES__RESTRICT data, size_t data_n, size_t *ES__RESTRICT bytes_unread) { size_t space_left; space_left = stream->unread_buffer_size - stream->unread_data_len; if (data_n > space_left) data_n = space_left; if (! data_n) goto out; memcpy (stream->unread_buffer + stream->unread_data_len, data, data_n); stream->unread_data_len += data_n; stream->intern->indicators.eof = 0; out: if (bytes_unread) *bytes_unread = data_n; } /* Seek in STREAM. */ static int es_seek (estream_t ES__RESTRICT stream, off_t offset, int whence, off_t *ES__RESTRICT offset_new) { es_cookie_seek_function_t func_seek = stream->intern->func_seek; int err, ret; off_t off; if (! func_seek) { _set_errno (EOPNOTSUPP); err = -1; goto out; } if (stream->flags.writing) { /* Flush data first in order to prevent flushing it to the wrong offset. */ err = es_flush (stream); if (err) goto out; stream->flags.writing = 0; } off = offset; if (whence == SEEK_CUR) { off = off - stream->data_len + stream->data_offset; off -= stream->unread_data_len; } ret = (*func_seek) (stream->intern->cookie, &off, whence); if (ret == -1) { err = -1; goto out; } err = 0; es_empty (stream); if (offset_new) *offset_new = off; stream->intern->indicators.eof = 0; stream->intern->offset = off; out: if (err) stream->intern->indicators.err = 1; return err; } /* Write BYTES_TO_WRITE bytes from BUFFER into STREAM in unbuffered-mode, storing the amount of bytes written in *BYTES_WRITTEN. */ static int es_write_nbf (estream_t ES__RESTRICT stream, const unsigned char *ES__RESTRICT buffer, size_t bytes_to_write, size_t *ES__RESTRICT bytes_written) { es_cookie_write_function_t func_write = stream->intern->func_write; size_t data_written; ssize_t ret; int err; if (bytes_to_write && (! func_write)) { err = EOPNOTSUPP; goto out; } data_written = 0; err = 0; while (bytes_to_write - data_written) { ret = (*func_write) (stream->intern->cookie, buffer + data_written, bytes_to_write - data_written); if (ret == -1) { err = -1; break; } else data_written += ret; } stream->intern->offset += data_written; *bytes_written = data_written; out: return err; } /* Write BYTES_TO_WRITE bytes from BUFFER into STREAM in fully-buffered-mode, storing the amount of bytes written in *BYTES_WRITTEN. */ static int es_write_fbf (estream_t ES__RESTRICT stream, const unsigned char *ES__RESTRICT buffer, size_t bytes_to_write, size_t *ES__RESTRICT bytes_written) { size_t space_available; size_t data_to_write; size_t data_written; int err; data_written = 0; err = 0; while ((bytes_to_write - data_written) && (! err)) { if (stream->data_offset == stream->buffer_size) /* Container full, flush buffer. */ err = es_flush (stream); if (! err) { /* Flushing resulted in empty container. */ data_to_write = bytes_to_write - data_written; space_available = stream->buffer_size - stream->data_offset; if (data_to_write > space_available) data_to_write = space_available; memcpy (stream->buffer + stream->data_offset, buffer + data_written, data_to_write); stream->data_offset += data_to_write; data_written += data_to_write; } } *bytes_written = data_written; return err; } /* Write BYTES_TO_WRITE bytes from BUFFER into STREAM in line-buffered-mode, storing the amount of bytes written in *BYTES_WRITTEN. */ static int es_write_lbf (estream_t ES__RESTRICT stream, const unsigned char *ES__RESTRICT buffer, size_t bytes_to_write, size_t *ES__RESTRICT bytes_written) { size_t data_flushed = 0; size_t data_buffered = 0; unsigned char *nlp; int err = 0; nlp = memrchr (buffer, '\n', bytes_to_write); if (nlp) { /* Found a newline, directly write up to (including) this character. */ err = es_flush (stream); if (!err) err = es_write_nbf (stream, buffer, nlp - buffer + 1, &data_flushed); } if (!err) { /* Write remaining data fully buffered. */ err = es_write_fbf (stream, buffer + data_flushed, bytes_to_write - data_flushed, &data_buffered); } *bytes_written = data_flushed + data_buffered; return err; } /* Write BYTES_TO_WRITE bytes from BUFFER into STREAM in, storing the amount of bytes written in BYTES_WRITTEN. */ static int es_writen (estream_t ES__RESTRICT stream, const void *ES__RESTRICT buffer, size_t bytes_to_write, size_t *ES__RESTRICT bytes_written) { size_t data_written; int err; data_written = 0; err = 0; if (!stream->flags.writing) { /* Switching to writing mode -> discard input data and seek to position at which reading has stopped. We can do this only if a seek function has been registered. */ if (stream->intern->func_seek) { err = es_seek (stream, 0, SEEK_CUR, NULL); if (err) { if (errno == ESPIPE) err = 0; else goto out; } } } switch (stream->intern->strategy) { case _IONBF: err = es_write_nbf (stream, buffer, bytes_to_write, &data_written); break; case _IOLBF: err = es_write_lbf (stream, buffer, bytes_to_write, &data_written); break; case _IOFBF: err = es_write_fbf (stream, buffer, bytes_to_write, &data_written); break; } out: if (bytes_written) *bytes_written = data_written; if (data_written) if (!stream->flags.writing) stream->flags.writing = 1; return err; } static int es_peek (estream_t ES__RESTRICT stream, unsigned char **ES__RESTRICT data, size_t *ES__RESTRICT data_len) { int err; if (stream->flags.writing) { /* Switching to reading mode -> flush output. */ err = es_flush (stream); if (err) goto out; stream->flags.writing = 0; } if (stream->data_offset == stream->data_len) { /* Refill container. */ err = es_fill (stream); if (err) goto out; } if (data) *data = stream->buffer + stream->data_offset; if (data_len) *data_len = stream->data_len - stream->data_offset; err = 0; out: return err; } /* Skip SIZE bytes of input data contained in buffer. */ static int es_skip (estream_t stream, size_t size) { int err; if (stream->data_offset + size > stream->data_len) { _set_errno (EINVAL); err = -1; } else { stream->data_offset += size; err = 0; } return err; } static int doreadline (estream_t ES__RESTRICT stream, size_t max_length, char *ES__RESTRICT *ES__RESTRICT line, size_t *ES__RESTRICT line_length) { size_t space_left; size_t line_size; estream_t line_stream; char *line_new; void *line_stream_cookie; char *newline; unsigned char *data; size_t data_len; int err; line_new = NULL; line_stream = NULL; line_stream_cookie = NULL; err = es_func_mem_create (&line_stream_cookie, NULL, 0, 0, BUFFER_BLOCK_SIZE, 1, mem_realloc, mem_free, O_RDWR, 0); if (err) goto out; err = es_create (&line_stream, line_stream_cookie, -1, estream_functions_mem, O_RDWR); if (err) goto out; space_left = max_length; line_size = 0; while (1) { if (max_length && (space_left == 1)) break; err = es_peek (stream, &data, &data_len); if (err || (! data_len)) break; if (data_len > (space_left - 1)) data_len = space_left - 1; newline = memchr (data, '\n', data_len); if (newline) { data_len = (newline - (char *) data) + 1; err = es_write (line_stream, data, data_len, NULL); if (! err) { space_left -= data_len; line_size += data_len; es_skip (stream, data_len); break; } } else { err = es_write (line_stream, data, data_len, NULL); if (! err) { space_left -= data_len; line_size += data_len; es_skip (stream, data_len); } } if (err) break; } if (err) goto out; /* Complete line has been written to line_stream. */ if ((max_length > 1) && (! line_size)) { stream->intern->indicators.eof = 1; goto out; } err = es_seek (line_stream, 0, SEEK_SET, NULL); if (err) goto out; if (! *line) { line_new = mem_alloc (line_size + 1); if (! line_new) { err = -1; goto out; } } else line_new = *line; err = es_read (line_stream, line_new, line_size, NULL); if (err) goto out; line_new[line_size] = '\0'; if (! *line) *line = line_new; if (line_length) *line_length = line_size; out: if (line_stream) es_destroy (line_stream); else if (line_stream_cookie) es_func_mem_destroy (line_stream_cookie); if (err) { if (! *line) mem_free (line_new); stream->intern->indicators.err = 1; } return err; } /* Output fucntion used for estream_format. */ static int print_writer (void *outfncarg, const char *buf, size_t buflen) { estream_t stream = outfncarg; size_t nwritten; int rc; nwritten = 0; rc = es_writen (stream, buf, buflen, &nwritten); stream->intern->print_ntotal += nwritten; return rc; } /* The core of our printf function. This is called in locked state. */ static int es_print (estream_t ES__RESTRICT stream, const char *ES__RESTRICT format, va_list ap) { int rc; stream->intern->print_ntotal = 0; rc = estream_format (print_writer, stream, format, ap); if (rc) return -1; return (int)stream->intern->print_ntotal; } static void es_set_indicators (estream_t stream, int ind_err, int ind_eof) { if (ind_err != -1) stream->intern->indicators.err = ind_err ? 1 : 0; if (ind_eof != -1) stream->intern->indicators.eof = ind_eof ? 1 : 0; } static int es_get_indicator (estream_t stream, int ind_err, int ind_eof) { int ret = 0; if (ind_err) ret = stream->intern->indicators.err; else if (ind_eof) ret = stream->intern->indicators.eof; return ret; } static int es_set_buffering (estream_t ES__RESTRICT stream, char *ES__RESTRICT buffer, int mode, size_t size) { int err; /* Flush or empty buffer depending on mode. */ if (stream->flags.writing) { err = es_flush (stream); if (err) goto out; } else es_empty (stream); es_set_indicators (stream, -1, 0); /* Free old buffer in case that was allocated by this function. */ if (stream->intern->deallocate_buffer) { stream->intern->deallocate_buffer = 0; mem_free (stream->buffer); stream->buffer = NULL; } if (mode == _IONBF) stream->buffer_size = 0; else { void *buffer_new; if (buffer) buffer_new = buffer; else { buffer_new = mem_alloc (size); if (! buffer_new) { err = -1; goto out; } } stream->buffer = buffer_new; stream->buffer_size = size; if (! buffer) stream->intern->deallocate_buffer = 1; } stream->intern->strategy = mode; err = 0; out: return err; } static off_t es_offset_calculate (estream_t stream) { off_t offset; offset = stream->intern->offset + stream->data_offset; if (offset < stream->unread_data_len) /* Offset undefined. */ offset = 0; else offset -= stream->unread_data_len; return offset; } static void es_opaque_ctrl (estream_t ES__RESTRICT stream, void *ES__RESTRICT opaque_new, void **ES__RESTRICT opaque_old) { if (opaque_old) *opaque_old = stream->intern->opaque; if (opaque_new) stream->intern->opaque = opaque_new; } static int es_get_fd (estream_t stream) { return stream->intern->fd; } /* API. */ int es_init (void) { int err; err = es_init_do (); return err; } estream_t es_fopen (const char *ES__RESTRICT path, const char *ES__RESTRICT mode) { unsigned int modeflags; int create_called; estream_t stream; void *cookie; int err; int fd; stream = NULL; cookie = NULL; create_called = 0; err = es_convert_mode (mode, &modeflags); if (err) goto out; err = es_func_file_create (&cookie, &fd, path, modeflags); if (err) goto out; create_called = 1; err = es_create (&stream, cookie, fd, estream_functions_file, modeflags); if (err) goto out; out: if (err && create_called) (*estream_functions_file.func_close) (cookie); return stream; } estream_t es_mopen (unsigned char *ES__RESTRICT data, size_t data_n, size_t data_len, unsigned int grow, func_realloc_t func_realloc, func_free_t func_free, const char *ES__RESTRICT mode) { unsigned int modeflags; int create_called; estream_t stream; void *cookie; int err; cookie = 0; stream = NULL; create_called = 0; err = es_convert_mode (mode, &modeflags); if (err) goto out; err = es_func_mem_create (&cookie, data, data_n, data_len, BUFFER_BLOCK_SIZE, grow, func_realloc, func_free, modeflags, 0); if (err) goto out; create_called = 1; err = es_create (&stream, cookie, -1, estream_functions_mem, modeflags); out: if (err && create_called) (*estream_functions_mem.func_close) (cookie); return stream; } estream_t es_fopenmem (size_t memlimit, const char *ES__RESTRICT mode) { unsigned int modeflags; estream_t stream = NULL; void *cookie = NULL; /* Memory streams are always read/write. We use MODE only to get the append flag. */ if (es_convert_mode (mode, &modeflags)) return NULL; modeflags |= O_RDWR; if (es_func_mem_create (&cookie, NULL, 0, 0, BUFFER_BLOCK_SIZE, 1, mem_realloc, mem_free, modeflags, memlimit)) return NULL; if (es_create (&stream, cookie, -1, estream_functions_mem, modeflags)) (*estream_functions_mem.func_close) (cookie); return stream; } estream_t es_fopencookie (void *ES__RESTRICT cookie, const char *ES__RESTRICT mode, es_cookie_io_functions_t functions) { unsigned int modeflags; estream_t stream; int err; stream = NULL; modeflags = 0; err = es_convert_mode (mode, &modeflags); if (err) goto out; err = es_create (&stream, cookie, -1, functions, modeflags); if (err) goto out; out: return stream; } estream_t do_fdopen (int filedes, const char *mode, int no_close) { unsigned int modeflags; int create_called; estream_t stream; void *cookie; int err; stream = NULL; cookie = NULL; create_called = 0; err = es_convert_mode (mode, &modeflags); if (err) goto out; err = es_func_fd_create (&cookie, filedes, modeflags, no_close); if (err) goto out; create_called = 1; err = es_create (&stream, cookie, filedes, estream_functions_fd, modeflags); out: if (err && create_called) (*estream_functions_fd.func_close) (cookie); return stream; } estream_t es_fdopen (int filedes, const char *mode) { return do_fdopen (filedes, mode, 0); } /* A variant of es_fdopen which does not close FILEDES at the end. */ estream_t es_fdopen_nc (int filedes, const char *mode) { return do_fdopen (filedes, mode, 1); } estream_t do_fpopen (FILE *fp, const char *mode, int no_close) { unsigned int modeflags; int create_called; estream_t stream; void *cookie; int err; stream = NULL; cookie = NULL; create_called = 0; err = es_convert_mode (mode, &modeflags); if (err) goto out; - fflush (fp); + if (fp) + fflush (fp); err = es_func_fp_create (&cookie, fp, modeflags, no_close); if (err) goto out; - + create_called = 1; - err = es_create (&stream, cookie, fileno (fp), estream_functions_fp, + err = es_create (&stream, cookie, fp? fileno (fp):-1, estream_functions_fp, modeflags); out: if (err && create_called) (*estream_functions_fp.func_close) (cookie); return stream; } /* Create an estream from the stdio stream FP. This mechanism is useful in case the stdio streams have special properties and may not be mixed with fd based functions. This is for example the case under Windows where the 3 standard streams are associated with the console whereas a duped and fd-opened stream of one of this stream won't be associated with the console. As this messes things up it is easier to keep on using the standard I/O stream as a backend for estream. */ estream_t es_fpopen (FILE *fp, const char *mode) { return do_fpopen (fp, mode, 0); } /* Same as es_fpopen but does not close FP at the end. */ estream_t es_fpopen_nc (FILE *fp, const char *mode) { return do_fpopen (fp, mode, 1); } estream_t es_freopen (const char *ES__RESTRICT path, const char *ES__RESTRICT mode, estream_t ES__RESTRICT stream) { int err; if (path) { unsigned int modeflags; int create_called; void *cookie; int fd; cookie = NULL; create_called = 0; ESTREAM_LOCK (stream); es_deinitialize (stream); err = es_convert_mode (mode, &modeflags); if (err) goto leave; err = es_func_file_create (&cookie, &fd, path, modeflags); if (err) goto leave; create_called = 1; es_initialize (stream, cookie, fd, estream_functions_file, modeflags); leave: if (err) { if (create_called) es_func_fd_destroy (cookie); es_destroy (stream); stream = NULL; } else ESTREAM_UNLOCK (stream); } else { /* FIXME? We don't support re-opening at the moment. */ _set_errno (EINVAL); es_deinitialize (stream); es_destroy (stream); stream = NULL; } return stream; } int es_fclose (estream_t stream) { int err; err = es_destroy (stream); return err; } int es_fileno_unlocked (estream_t stream) { return es_get_fd (stream); } void es_flockfile (estream_t stream) { ESTREAM_LOCK (stream); } int es_ftrylockfile (estream_t stream) { return ESTREAM_TRYLOCK (stream); } void es_funlockfile (estream_t stream) { ESTREAM_UNLOCK (stream); } int es_fileno (estream_t stream) { int ret; ESTREAM_LOCK (stream); ret = es_fileno_unlocked (stream); ESTREAM_UNLOCK (stream); return ret; } int es_feof_unlocked (estream_t stream) { return es_get_indicator (stream, 0, 1); } int es_feof (estream_t stream) { int ret; ESTREAM_LOCK (stream); ret = es_feof_unlocked (stream); ESTREAM_UNLOCK (stream); return ret; } int es_ferror_unlocked (estream_t stream) { return es_get_indicator (stream, 1, 0); } int es_ferror (estream_t stream) { int ret; ESTREAM_LOCK (stream); ret = es_ferror_unlocked (stream); ESTREAM_UNLOCK (stream); return ret; } void es_clearerr_unlocked (estream_t stream) { es_set_indicators (stream, 0, 0); } void es_clearerr (estream_t stream) { ESTREAM_LOCK (stream); es_clearerr_unlocked (stream); ESTREAM_UNLOCK (stream); } int es_fflush (estream_t stream) { int err; if (stream) { ESTREAM_LOCK (stream); if (stream->flags.writing) err = es_flush (stream); else { es_empty (stream); err = 0; } ESTREAM_UNLOCK (stream); } else err = es_list_iterate (es_fflush); return err ? EOF : 0; } int es_fseek (estream_t stream, long int offset, int whence) { int err; ESTREAM_LOCK (stream); err = es_seek (stream, offset, whence, NULL); ESTREAM_UNLOCK (stream); return err; } int es_fseeko (estream_t stream, off_t offset, int whence) { int err; ESTREAM_LOCK (stream); err = es_seek (stream, offset, whence, NULL); ESTREAM_UNLOCK (stream); return err; } long int es_ftell (estream_t stream) { long int ret; ESTREAM_LOCK (stream); ret = es_offset_calculate (stream); ESTREAM_UNLOCK (stream); return ret; } off_t es_ftello (estream_t stream) { off_t ret = -1; ESTREAM_LOCK (stream); ret = es_offset_calculate (stream); ESTREAM_UNLOCK (stream); return ret; } void es_rewind (estream_t stream) { ESTREAM_LOCK (stream); es_seek (stream, 0L, SEEK_SET, NULL); es_set_indicators (stream, 0, -1); ESTREAM_UNLOCK (stream); } int _es_getc_underflow (estream_t stream) { int err; unsigned char c; size_t bytes_read; err = es_readn (stream, &c, 1, &bytes_read); return (err || (! bytes_read)) ? EOF : c; } int _es_putc_overflow (int c, estream_t stream) { unsigned char d = c; int err; err = es_writen (stream, &d, 1, NULL); return err ? EOF : c; } int es_fgetc (estream_t stream) { int ret; ESTREAM_LOCK (stream); ret = es_getc_unlocked (stream); ESTREAM_UNLOCK (stream); return ret; } int es_fputc (int c, estream_t stream) { int ret; ESTREAM_LOCK (stream); ret = es_putc_unlocked (c, stream); ESTREAM_UNLOCK (stream); return ret; } int es_ungetc (int c, estream_t stream) { unsigned char data = (unsigned char) c; size_t data_unread; ESTREAM_LOCK (stream); es_unreadn (stream, &data, 1, &data_unread); ESTREAM_UNLOCK (stream); return data_unread ? c : EOF; } int es_read (estream_t ES__RESTRICT stream, void *ES__RESTRICT buffer, size_t bytes_to_read, size_t *ES__RESTRICT bytes_read) { int err; if (bytes_to_read) { ESTREAM_LOCK (stream); err = es_readn (stream, buffer, bytes_to_read, bytes_read); ESTREAM_UNLOCK (stream); } else err = 0; return err; } int es_write (estream_t ES__RESTRICT stream, const void *ES__RESTRICT buffer, size_t bytes_to_write, size_t *ES__RESTRICT bytes_written) { int err; if (bytes_to_write) { ESTREAM_LOCK (stream); err = es_writen (stream, buffer, bytes_to_write, bytes_written); ESTREAM_UNLOCK (stream); } else err = 0; return err; } size_t es_fread (void *ES__RESTRICT ptr, size_t size, size_t nitems, estream_t ES__RESTRICT stream) { size_t ret, bytes; int err; if (size * nitems) { ESTREAM_LOCK (stream); err = es_readn (stream, ptr, size * nitems, &bytes); ESTREAM_UNLOCK (stream); ret = bytes / size; } else ret = 0; return ret; } size_t es_fwrite (const void *ES__RESTRICT ptr, size_t size, size_t nitems, estream_t ES__RESTRICT stream) { size_t ret, bytes; int err; if (size * nitems) { ESTREAM_LOCK (stream); err = es_writen (stream, ptr, size * nitems, &bytes); ESTREAM_UNLOCK (stream); ret = bytes / size; } else ret = 0; return ret; } char * es_fgets (char *ES__RESTRICT buffer, int length, estream_t ES__RESTRICT stream) { unsigned char *s = (unsigned char*)buffer; int c; if (!length) return NULL; c = EOF; ESTREAM_LOCK (stream); while (length > 1 && (c = es_getc_unlocked (stream)) != EOF && c != '\n') { *s++ = c; length--; } ESTREAM_UNLOCK (stream); if (c == EOF && s == (unsigned char*)buffer) return NULL; /* Nothing read. */ if (c != EOF && length > 1) *s++ = c; *s = 0; return buffer; } +int +es_fputs_unlocked (const char *ES__RESTRICT s, estream_t ES__RESTRICT stream) +{ + size_t length; + int err; + + length = strlen (s); + err = es_writen (stream, s, length, NULL); + return err ? EOF : 0; +} + int es_fputs (const char *ES__RESTRICT s, estream_t ES__RESTRICT stream) { size_t length; int err; length = strlen (s); ESTREAM_LOCK (stream); err = es_writen (stream, s, length, NULL); ESTREAM_UNLOCK (stream); return err ? EOF : 0; } ssize_t es_getline (char *ES__RESTRICT *ES__RESTRICT lineptr, size_t *ES__RESTRICT n, estream_t ES__RESTRICT stream) { char *line = NULL; size_t line_n = 0; int err; ESTREAM_LOCK (stream); err = doreadline (stream, 0, &line, &line_n); ESTREAM_UNLOCK (stream); if (err) goto out; if (*n) { /* Caller wants us to use his buffer. */ if (*n < (line_n + 1)) { /* Provided buffer is too small -> resize. */ void *p; p = mem_realloc (*lineptr, line_n + 1); if (! p) err = -1; else { if (*lineptr != p) *lineptr = p; } } if (! err) { memcpy (*lineptr, line, line_n + 1); if (*n != line_n) *n = line_n; } mem_free (line); } else { /* Caller wants new buffers. */ *lineptr = line; *n = line_n; } out: return err ? err : (ssize_t)line_n; } /* Same as fgets() but if the provided buffer is too short a larger one will be allocated. This is similar to getline. A line is considered a byte stream ending in a LF. If MAX_LENGTH is not NULL, it shall point to a value with the maximum allowed allocation. Returns the length of the line. EOF is indicated by a line of length zero. A truncated line is indicated my setting the value at MAX_LENGTH to 0. If the returned value is less then 0 not enough memory was enable or another error occurred; ERRNO is then set accordingly. If a line has been truncated, the file pointer is moved forward to the end of the line so that the next read starts with the next line. Note that MAX_LENGTH must be re-initialzied in this case. The caller initially needs to provide the address of a variable, initialized to NULL, at ADDR_OF_BUFFER and don't change this value anymore with the following invocations. LENGTH_OF_BUFFER should be the address of a variable, initialized to 0, which is also maintained by this function. Thus, both paramaters should be considered the state of this function. Note: The returned buffer is allocated with enough extra space to allow the caller to append a CR,LF,Nul. The buffer should be released using es_free. */ ssize_t es_read_line (estream_t stream, char **addr_of_buffer, size_t *length_of_buffer, size_t *max_length) { int c; char *buffer = *addr_of_buffer; size_t length = *length_of_buffer; size_t nbytes = 0; size_t maxlen = max_length? *max_length : 0; char *p; if (!buffer) { /* No buffer given - allocate a new one. */ length = 256; buffer = mem_alloc (length); *addr_of_buffer = buffer; if (!buffer) { *length_of_buffer = 0; if (max_length) *max_length = 0; return -1; } *length_of_buffer = length; } if (length < 4) { /* This should never happen. If it does, the function has been called with wrong arguments. */ _set_errno (EINVAL); return -1; } length -= 3; /* Reserve 3 bytes for CR,LF,EOL. */ ESTREAM_LOCK (stream); p = buffer; while ((c = es_getc_unlocked (stream)) != EOF) { if (nbytes == length) { /* Enlarge the buffer. */ if (maxlen && length > maxlen) { /* We are beyond our limit: Skip the rest of the line. */ while (c != '\n' && (c=es_getc_unlocked (stream)) != EOF) ; *p++ = '\n'; /* Always append a LF (we reserved some space). */ nbytes++; if (max_length) *max_length = 0; /* Indicate truncation. */ break; /* the while loop. */ } length += 3; /* Adjust for the reserved bytes. */ length += length < 1024? 256 : 1024; *addr_of_buffer = mem_realloc (buffer, length); if (!*addr_of_buffer) { int save_errno = errno; mem_free (buffer); *length_of_buffer = 0; if (max_length) *max_length = 0; ESTREAM_UNLOCK (stream); _set_errno (save_errno); return -1; } buffer = *addr_of_buffer; *length_of_buffer = length; length -= 3; p = buffer + nbytes; } *p++ = c; nbytes++; if (c == '\n') break; } *p = 0; /* Make sure the line is a string. */ ESTREAM_UNLOCK (stream); return nbytes; } /* Wrapper around free() to match the memory allocation system used by estream. Should be used for all buffers returned to the caller by libestream. */ void es_free (void *a) { mem_free (a); } +int +es_vfprintf_unlocked (estream_t ES__RESTRICT stream, + const char *ES__RESTRICT format, + va_list ap) +{ + return es_print (stream, format, ap); +} + + int es_vfprintf (estream_t ES__RESTRICT stream, const char *ES__RESTRICT format, va_list ap) { int ret; ESTREAM_LOCK (stream); ret = es_print (stream, format, ap); ESTREAM_UNLOCK (stream); return ret; } -static int +int es_fprintf_unlocked (estream_t ES__RESTRICT stream, - const char *ES__RESTRICT format, ...) + const char *ES__RESTRICT format, ...) { int ret; va_list ap; va_start (ap, format); ret = es_print (stream, format, ap); va_end (ap); return ret; } int es_fprintf (estream_t ES__RESTRICT stream, const char *ES__RESTRICT format, ...) { int ret; va_list ap; va_start (ap, format); ESTREAM_LOCK (stream); ret = es_print (stream, format, ap); ESTREAM_UNLOCK (stream); va_end (ap); return ret; } /* A variant of asprintf. The function returns the allocated buffer or NULL on error; ERRNO is set in the error case. The caller should use es_free to release the buffer. This function actually belongs into estream-printf but we put it here as a convenience and because es_free is required anyway. */ char * es_asprintf (const char *ES__RESTRICT format, ...) { int rc; va_list ap; char *buf; va_start (ap, format); rc = estream_vasprintf (&buf, format, ap); va_end (ap); if (rc < 0) return NULL; return buf; } /* A variant of vasprintf. The function returns the allocated buffer or NULL on error; ERRNO is set in the error case. The caller should use es_free to release the buffer. This function actually belongs into estream-printf but we put it here as a convenience and because es_free is required anyway. */ char * es_vasprintf (const char *ES__RESTRICT format, va_list ap) { int rc; char *buf; rc = estream_vasprintf (&buf, format, ap); if (rc < 0) return NULL; return buf; } static int tmpfd (void) { #ifdef HAVE_W32_SYSTEM int attempts, n; #ifdef HAVE_W32CE_SYSTEM wchar_t buffer[MAX_PATH+9+12+1]; # define mystrlen(a) wcslen (a) wchar_t *name, *p; #else char buffer[MAX_PATH+9+12+1]; # define mystrlen(a) strlen (a) char *name, *p; #endif HANDLE file; int pid = GetCurrentProcessId (); unsigned int value; int i; n = GetTempPath (MAX_PATH+1, buffer); if (!n || n > MAX_PATH || mystrlen (buffer) > MAX_PATH) { _set_errno (ENOENT); return -1; } p = buffer + mystrlen (buffer); #ifdef HAVE_W32CE_SYSTEM wcscpy (p, L"_estream"); #else strcpy (p, "_estream"); #endif p += 8; /* We try to create the directory but don't care about an error as it may already exist and the CreateFile would throw an error anyway. */ CreateDirectory (buffer, NULL); *p++ = '\\'; name = p; for (attempts=0; attempts < 10; attempts++) { p = name; value = (GetTickCount () ^ ((pid<<16) & 0xffff0000)); for (i=0; i < 8; i++) { *p++ = tohex (((value >> 28) & 0x0f)); value <<= 4; } #ifdef HAVE_W32CE_SYSTEM wcscpy (p, L".tmp"); #else strcpy (p, ".tmp"); #endif file = CreateFile (buffer, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL); if (file != INVALID_HANDLE_VALUE) { #ifdef HAVE_W32CE_SYSTEM int fd = (int)file; #else int fd = _open_osfhandle ((long)file, 0); if (fd == -1) { CloseHandle (file); return -1; } #endif return fd; } Sleep (1); /* One ms as this is the granularity of GetTickCount. */ } _set_errno (ENOENT); return -1; #else /*!HAVE_W32_SYSTEM*/ FILE *fp; int fp_fd; int fd; fp = NULL; fd = -1; fp = tmpfile (); if (! fp) goto out; fp_fd = fileno (fp); fd = dup (fp_fd); out: if (fp) fclose (fp); return fd; #endif /*!HAVE_W32_SYSTEM*/ } estream_t es_tmpfile (void) { unsigned int modeflags; int create_called; estream_t stream; void *cookie; int err; int fd; create_called = 0; stream = NULL; modeflags = O_RDWR | O_TRUNC | O_CREAT; cookie = NULL; fd = tmpfd (); if (fd == -1) { err = -1; goto out; } err = es_func_fd_create (&cookie, fd, modeflags, 0); if (err) goto out; create_called = 1; err = es_create (&stream, cookie, fd, estream_functions_fd, modeflags); out: if (err) { if (create_called) es_func_fd_destroy (cookie); else if (fd != -1) close (fd); stream = NULL; } return stream; } int es_setvbuf (estream_t ES__RESTRICT stream, char *ES__RESTRICT buf, int type, size_t size) { int err; if (((type == _IOFBF) || (type == _IOLBF) || (type == _IONBF)) && (! ((! size) && (type != _IONBF)))) { ESTREAM_LOCK (stream); err = es_set_buffering (stream, buf, type, size); ESTREAM_UNLOCK (stream); } else { _set_errno (EINVAL); err = -1; } return err; } void es_setbuf (estream_t ES__RESTRICT stream, char *ES__RESTRICT buf) { ESTREAM_LOCK (stream); es_set_buffering (stream, buf, buf ? _IOFBF : _IONBF, BUFSIZ); ESTREAM_UNLOCK (stream); } void es_opaque_set (estream_t stream, void *opaque) { ESTREAM_LOCK (stream); es_opaque_ctrl (stream, opaque, NULL); ESTREAM_UNLOCK (stream); } void * es_opaque_get (estream_t stream) { void *opaque; ESTREAM_LOCK (stream); es_opaque_ctrl (stream, NULL, &opaque); ESTREAM_UNLOCK (stream); return opaque; } /* Print a BUFFER to STREAM while replacing all control characters and the characters in DELIMITERS by standard C escape sequences. Returns 0 on success or -1 on error. If BYTES_WRITTEN is not NULL the number of bytes actually written are stored at this address. */ int es_write_sanitized (estream_t ES__RESTRICT stream, const void * ES__RESTRICT buffer, size_t length, const char * delimiters, size_t * ES__RESTRICT bytes_written) { const unsigned char *p = buffer; size_t count = 0; int ret; ESTREAM_LOCK (stream); for (; length; length--, p++, count++) { if (*p < 0x20 || *p == 0x7f || (delimiters && (strchr (delimiters, *p) || *p == '\\'))) { es_putc_unlocked ('\\', stream); count++; if (*p == '\n') { es_putc_unlocked ('n', stream); count++; } else if (*p == '\r') { es_putc_unlocked ('r', stream); count++; } else if (*p == '\f') { es_putc_unlocked ('f', stream); count++; } else if (*p == '\v') { es_putc_unlocked ('v', stream); count++; } else if (*p == '\b') { es_putc_unlocked ('b', stream); count++; } else if (!*p) { es_putc_unlocked('0', stream); count++; } else { es_fprintf_unlocked (stream, "x%02x", *p); count += 3; } } else { es_putc_unlocked (*p, stream); count++; } } if (bytes_written) *bytes_written = count; ret = es_ferror_unlocked (stream)? -1 : 0; ESTREAM_UNLOCK (stream); return ret; } /* Write LENGTH bytes of BUFFER to STREAM as a hex encoded string. RESERVED must be 0. Returns 0 on success or -1 on error. If BYTES_WRITTEN is not NULL the number of bytes actually written are stored at this address. */ int es_write_hexstring (estream_t ES__RESTRICT stream, const void *ES__RESTRICT buffer, size_t length, int reserved, size_t *ES__RESTRICT bytes_written ) { int ret; const unsigned char *s; size_t count = 0; (void)reserved; #define tohex(n) ((n) < 10 ? ((n) + '0') : (((n) - 10) + 'A')) if (!length) return 0; ESTREAM_LOCK (stream); for (s = buffer; length; s++, length--) { es_putc_unlocked ( tohex ((*s>>4)&15), stream); es_putc_unlocked ( tohex (*s&15), stream); count += 2; } if (bytes_written) *bytes_written = count; ret = es_ferror_unlocked (stream)? -1 : 0; ESTREAM_UNLOCK (stream); return ret; #undef tohex } #ifdef GNUPG_MAJOR_VERSION /* Special estream function to print an UTF8 string in the native encoding. The interface is the same as es_write_sanitized, however only one delimiter may be supported. THIS IS NOT A STANDARD ESTREAM FUNCTION AND ONLY USED BY GNUPG!. */ int es_write_sanitized_utf8_buffer (estream_t stream, const void *buffer, size_t length, const char *delimiters, size_t *bytes_written) { 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) { int delim = delimiters? *delimiters : 0; char *buf; int ret; /*(utf8 conversion already does the control character quoting). */ buf = utf8_to_native (p, length, delim); if (bytes_written) *bytes_written = strlen (buf); ret = es_fputs (buf, stream); xfree (buf); return ret == EOF? ret : (int)i; } else return es_write_sanitized (stream, p, length, delimiters, bytes_written); } #endif /*GNUPG_MAJOR_VERSION*/ diff --git a/common/estream.h b/common/estream.h index b22c5de43..477aface8 100644 --- a/common/estream.h +++ b/common/estream.h @@ -1,350 +1,363 @@ /* estream.h - Extended stream I/O Library * Copyright (C) 2004, 2005, 2006, 2007, 2010 g10 Code GmbH * * This file is part of Libestream. * * Libestream is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation; either version 2 of the License, * or (at your option) any later version. * * Libestream is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Libestream; if not, see . * * ALTERNATIVELY, Libestream may be distributed under the terms of the * following license, in which case the provisions of this license are * required INSTEAD OF the GNU General Public License. If you wish to * allow use of your version of this file only under the terms of the * GNU General Public License, and not to allow others to use your * version of this file under the terms of the following license, * indicate your decision by deleting this paragraph and the license * below. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, and the entire permission notice in its entirety, * including the disclaimer of warranties. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote * products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef ESTREAM_H #define ESTREAM_H #include #include #include /* To use this file with libraries the following macro is useful: #define _ESTREAM_EXT_SYM_PREFIX _foo_ This prefixes all external symbols with "_foo_". */ #ifdef _ESTREAM_EXT_SYM_PREFIX #ifndef _ESTREAM_PREFIX #define _ESTREAM_PREFIX1(x,y) x ## y #define _ESTREAM_PREFIX2(x,y) _ESTREAM_PREFIX1(x,y) #define _ESTREAM_PREFIX(x) _ESTREAM_PREFIX2(_ESTREAM_EXT_SYM_PREFIX,x) #endif /*_ESTREAM_PREFIX*/ #define es_fopen _ESTREAM_PREFIX(es_fopen) #define es_mopen _ESTREAM_PREFIX(es_mopen) #define es_fopenmem _ESTREAM_PREFIX(es_fopenmem) #define es_fdopen _ESTREAM_PREFIX(es_fdopen) #define es_fdopen_nc _ESTREAM_PREFIX(es_fdopen_nc) #define es_fpopen _ESTREAM_PREFIX(es_fpopen) #define es_fpopen_nc _ESTREAM_PREFIX(es_fpopen_nc) #define es_freopen _ESTREAM_PREFIX(es_freopen) #define es_fopencookie _ESTREAM_PREFIX(es_fopencookie) #define es_fclose _ESTREAM_PREFIX(es_fclose) #define es_fileno _ESTREAM_PREFIX(es_fileno) #define es_fileno_unlocked _ESTREAM_PREFIX(es_fileno_unlocked) #define es_flockfile _ESTREAM_PREFIX(es_flockfile) #define es_ftrylockfile _ESTREAM_PREFIX(es_ftrylockfile) #define es_funlockfile _ESTREAM_PREFIX(es_funlockfile) #define es_feof _ESTREAM_PREFIX(es_feof) #define es_feof_unlocked _ESTREAM_PREFIX(es_feof_unlocked) #define es_ferror _ESTREAM_PREFIX(es_ferror) #define es_ferror_unlocked _ESTREAM_PREFIX(es_ferror_unlocked) #define es_clearerr _ESTREAM_PREFIX(es_clearerr) #define es_clearerr_unlocked _ESTREAM_PREFIX(es_clearerr_unlocked) #define es_fflush _ESTREAM_PREFIX(es_fflush) #define es_fseek _ESTREAM_PREFIX(es_fseek) #define es_fseeko _ESTREAM_PREFIX(es_fseeko) #define es_ftell _ESTREAM_PREFIX(es_ftell) #define es_ftello _ESTREAM_PREFIX(es_ftello) #define es_rewind _ESTREAM_PREFIX(es_rewind) #define es_fgetc _ESTREAM_PREFIX(es_fgetc) #define es_fputc _ESTREAM_PREFIX(es_fputc) #define _es_getc_underflow _ESTREAM_PREFIX(_es_getc_underflow) #define _es_putc_overflow _ESTREAM_PREFIX(_es_putc_overflow) #define es_ungetc _ESTREAM_PREFIX(es_ungetc) #define es_read _ESTREAM_PREFIX(es_read) #define es_write _ESTREAM_PREFIX(es_write) #define es_write_sanitized _ESTREAM_PREFIX(es_write_sanitized) #define es_write_hexstring _ESTREAM_PREFIX(es_write_hexstring) #define es_fread _ESTREAM_PREFIX(es_fread) #define es_fwrite _ESTREAM_PREFIX(es_fwrite) #define es_fgets _ESTREAM_PREFIX(es_fgets) #define es_fputs _ESTREAM_PREFIX(es_fputs) +#define es_fputs_unlocked _ESTREAM_PREFIX(es_fputs_unlocked) #define es_getline _ESTREAM_PREFIX(es_getline) #define es_read_line _ESTREAM_PREFIX(es_read_line) #define es_free _ESTREAM_PREFIX(es_free) -#define es_fprf _ESTREAM_PREFIX(es_fprf) -#define es_vfprf _ESTREAM_PREFIX(es_vfprf) +#define es_fprintf _ESTREAM_PREFIX(es_fprintf) +#define es_fprintf_unlocked _ESTREAM_PREFIX(es_fprintf_unlocked) +#define es_vfprintf _ESTREAM_PREFIX(es_vfprint) +#define es_vfprintf_unlocked _ESTREAM_PREFIX(es_vfprint_unlocked) #define es_setvbuf _ESTREAM_PREFIX(es_setvbuf) #define es_setbuf _ESTREAM_PREFIX(es_setbuf) #define es_tmpfile _ESTREAM_PREFIX(es_tmpfile) #define es_opaque_set _ESTREAM_PREFIX(es_opaque_set) #define es_opaque_get _ESTREAM_PREFIX(es_opaque_get) #define es_write_sanitized_utf8_buffer \ _ESTREAM_PREFIX(es_write_sanitized_utf8_buffer) #endif /*_ESTREAM_EXT_SYM_PREFIX*/ #ifdef __cplusplus extern "C" { #if 0 } #endif #endif /* Forward declaration for the (opaque) internal type. */ struct estream_internal; /* The definition of this struct is entirely private. You must not use it for anything. It is only here so some functions can be implemented as macros. */ struct es__stream { /* The layout of this struct must never change. It may be grown, but only if all functions which access the new members are versioned. */ /* A pointer to the stream buffer. */ unsigned char *buffer; /* The size of the buffer in bytes. */ size_t buffer_size; /* The length of the usable data in the buffer, only valid when in read mode (see flags). */ size_t data_len; /* The current position of the offset pointer, valid in read and write mode. */ size_t data_offset; size_t data_flushed; unsigned char *unread_buffer; size_t unread_buffer_size; /* The number of unread bytes. */ size_t unread_data_len; /* Various flags. */ struct { unsigned int writing: 1; unsigned int reserved: 7; } flags; /* A pointer to our internal data for this stream. */ struct estream_internal *intern; }; /* The opaque type for an estream. */ typedef struct es__stream *estream_t; typedef ssize_t (*es_cookie_read_function_t) (void *cookie, void *buffer, size_t size); typedef ssize_t (*es_cookie_write_function_t) (void *cookie, const void *buffer, size_t size); typedef int (*es_cookie_seek_function_t) (void *cookie, off_t *pos, int whence); typedef int (*es_cookie_close_function_t) (void *cookie); typedef struct es_cookie_io_functions { es_cookie_read_function_t func_read; es_cookie_write_function_t func_write; es_cookie_seek_function_t func_seek; es_cookie_close_function_t func_close; } es_cookie_io_functions_t; #ifndef _ESTREAM_GCC_A_PRINTF #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5 ) # define _ESTREAM_GCC_A_PRINTF( f, a ) __attribute__ ((format (printf,f,a))) #else # define _ESTREAM_GCC_A_PRINTF( f, a ) #endif #endif /*_ESTREAM_GCC_A_PRINTF*/ #ifndef ES__RESTRICT # if defined __GNUC__ && defined __GNUC_MINOR__ # if (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 92)) # define ES__RESTRICT __restrict__ # endif # endif #endif #ifndef ES__RESTRICT # define ES__RESTRICT #endif int es_init (void); estream_t es_fopen (const char *ES__RESTRICT path, const char *ES__RESTRICT mode); estream_t es_mopen (unsigned char *ES__RESTRICT data, size_t data_n, size_t data_len, unsigned int grow, void *(*func_realloc) (void *mem, size_t size), void (*func_free) (void *mem), const char *ES__RESTRICT mode); estream_t es_fopenmem (size_t memlimit, const char *ES__RESTRICT mode); estream_t es_fdopen (int filedes, const char *mode); estream_t es_fdopen_nc (int filedes, const char *mode); estream_t es_fpopen (FILE *fp, const char *mode); estream_t es_fpopen_nc (FILE *fp, const char *mode); estream_t es_freopen (const char *ES__RESTRICT path, const char *ES__RESTRICT mode, estream_t ES__RESTRICT stream); estream_t es_fopencookie (void *ES__RESTRICT cookie, const char *ES__RESTRICT mode, es_cookie_io_functions_t functions); int es_fclose (estream_t stream); int es_fileno (estream_t stream); int es_fileno_unlocked (estream_t stream); void es_flockfile (estream_t stream); int es_ftrylockfile (estream_t stream); void es_funlockfile (estream_t stream); int es_feof (estream_t stream); int es_feof_unlocked (estream_t stream); int es_ferror (estream_t stream); int es_ferror_unlocked (estream_t stream); void es_clearerr (estream_t stream); void es_clearerr_unlocked (estream_t stream); int es_fflush (estream_t stream); int es_fseek (estream_t stream, long int offset, int whence); int es_fseeko (estream_t stream, off_t offset, int whence); long int es_ftell (estream_t stream); off_t es_ftello (estream_t stream); void es_rewind (estream_t stream); int es_fgetc (estream_t stream); int es_fputc (int c, estream_t stream); int _es_getc_underflow (estream_t stream); int _es_putc_overflow (int c, estream_t stream); #define es_getc_unlocked(stream) \ (((!(stream)->flags.writing) \ && ((stream)->data_offset < (stream)->data_len) \ && (! (stream)->unread_data_len)) \ ? ((int) (stream)->buffer[((stream)->data_offset)++]) \ : _es_getc_underflow ((stream))) #define es_putc_unlocked(c, stream) \ (((stream)->flags.writing \ && ((stream)->data_offset < (stream)->buffer_size) \ && (c != '\n')) \ ? ((int) ((stream)->buffer[((stream)->data_offset)++] = (c))) \ : _es_putc_overflow ((c), (stream))) #define es_getc(stream) es_fgetc (stream) #define es_putc(c, stream) es_fputc (c, stream) int es_ungetc (int c, estream_t stream); int es_read (estream_t ES__RESTRICT stream, void *ES__RESTRICT buffer, size_t bytes_to_read, size_t *ES__RESTRICT bytes_read); int es_write (estream_t ES__RESTRICT stream, const void *ES__RESTRICT buffer, size_t bytes_to_write, size_t *ES__RESTRICT bytes_written); int es_write_sanitized (estream_t ES__RESTRICT stream, const void *ES__RESTRICT buffer, size_t length, const char *delimiters, size_t *ES__RESTRICT bytes_written); int es_write_hexstring (estream_t ES__RESTRICT stream, const void *ES__RESTRICT buffer, size_t length, int reserved, size_t *ES__RESTRICT bytes_written); size_t es_fread (void *ES__RESTRICT ptr, size_t size, size_t nitems, estream_t ES__RESTRICT stream); size_t es_fwrite (const void *ES__RESTRICT ptr, size_t size, size_t memb, estream_t ES__RESTRICT stream); char *es_fgets (char *ES__RESTRICT s, int n, estream_t ES__RESTRICT stream); int es_fputs (const char *ES__RESTRICT s, estream_t ES__RESTRICT stream); +int es_fputs_unlocked (const char *ES__RESTRICT s, + estream_t ES__RESTRICT stream); ssize_t es_getline (char *ES__RESTRICT *ES__RESTRICT lineptr, size_t *ES__RESTRICT n, estream_t stream); ssize_t es_read_line (estream_t stream, char **addr_of_buffer, size_t *length_of_buffer, size_t *max_length); void es_free (void *a); int es_fprintf (estream_t ES__RESTRICT stream, const char *ES__RESTRICT format, ...) _ESTREAM_GCC_A_PRINTF(2,3); +int es_fprintf_unlocked (estream_t ES__RESTRICT stream, + const char *ES__RESTRICT format, ...) + _ESTREAM_GCC_A_PRINTF(2,3); + int es_vfprintf (estream_t ES__RESTRICT stream, const char *ES__RESTRICT format, va_list ap) _ESTREAM_GCC_A_PRINTF(2,0); +int es_vfprintf_unlocked (estream_t ES__RESTRICT stream, + const char *ES__RESTRICT format, va_list ap) + _ESTREAM_GCC_A_PRINTF(2,0); + int es_setvbuf (estream_t ES__RESTRICT stream, char *ES__RESTRICT buf, int mode, size_t size); void es_setbuf (estream_t ES__RESTRICT stream, char *ES__RESTRICT buf); estream_t es_tmpfile (void); void es_opaque_set (estream_t ES__RESTRICT stream, void *ES__RESTRICT opaque); void *es_opaque_get (estream_t stream); #ifdef GNUPG_MAJOR_VERSION int es_write_sanitized_utf8_buffer (estream_t stream, const void *buffer, size_t length, const char *delimiters, size_t *bytes_written); #endif /*GNUPG_MAJOR_VERSION*/ #ifdef __cplusplus } #endif #endif /*ESTREAM_H*/ diff --git a/common/logging.c b/common/logging.c index 028697b78..c0e01da11 100644 --- a/common/logging.c +++ b/common/logging.c @@ -1,651 +1,626 @@ /* logging.c - Useful logging functions - * Copyright (C) 1998, 1999, 2000, 2001, 2003, - * 2004, 2005, 2006, 2009 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, + * 2009, 2010 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 . */ #include #include #include #include #include #include #include #include #include #include #ifndef HAVE_W32_SYSTEM #include #include #endif /*!HAVE_W32_SYSTEM*/ #include #include #include #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 estream_t 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 { +/* The following 3 functions are used by es_fopencookie to write logs + to a socket. */ +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) +static ssize_t +fun_writer (void *cookie_arg, const void *buffer, 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. */ + return (ssize_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; + return (ssize_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; + estream_t 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); + es_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; + es_cookie_io_functions_t io = { NULL }; + io.func_write = fun_writer; + io.func_close = fun_closer; - fp = fopencookie (cookie, "w", io); + fp = es_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. */ + /* On error default to a stderr based estream. */ if (!fp) { - if (name) - fprintf (stderr, "failed to open log file `%s': %s\n", - name, strerror(errno)); + fp = es_fpopen (stderr, "a"); + if (fp) + { + if (name) + es_fprintf (fp, "failed to open log file `%s': %s\n", + name, strerror (errno)); + else + es_fprintf (fp, "failed to fdopen file descriptor %d: %s\n", + fd, 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; + { + fprintf (stderr, "failed to use stderr as log stream: %s\n", + strerror (errno)); + /* No way to log something. Create a dummy estream so that + there is something we can use. */ + fp = es_fpopen (NULL, "a"); + if (!fp) + { + fprintf (stderr, "fatal: failed to open dummy stream: %s\n", + strerror (errno)); + abort(); + } + } } - else - setvbuf (fp, NULL, _IOLBF, 0); + + es_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. */ + it allows the logging code to use more then one file descriptor. */ int log_test_fd (int fd) { if (logstream) { - int tmp = fileno (logstream); + int tmp = es_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); + return logstream? es_fileno(logstream) : -1; } -FILE * +estream_t 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; + assert (logstream); + return logstream; } 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); } + es_flockfile (logstream); if (missing_lf && level != JNLIB_LOG_CONT) - putc('\n', logstream ); + es_putc_unlocked ('\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 ); + es_fprintf_unlocked (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); + es_fputs_unlocked (prefix_buffer, logstream); if (with_pid || force_prefixes) { if (get_tid_callback) - fprintf (logstream, "[%u.%lx]", - (unsigned int)getpid (), get_tid_callback ()); + es_fprintf_unlocked (logstream, "[%u.%lx]", + (unsigned int)getpid (), get_tid_callback ()); else - fprintf (logstream, "[%u]", (unsigned int)getpid ()); + es_fprintf_unlocked (logstream, "[%u]", (unsigned int)getpid ()); } if (!with_time || force_prefixes) - putc (':', logstream); + es_putc_unlocked (':', 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); + es_putc_unlocked (' ', 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; + case JNLIB_LOG_FATAL: es_fputs_unlocked ("Fatal: ",logstream ); break; + case JNLIB_LOG_BUG: es_fputs_unlocked ("Ohhhh jeeee: ", logstream); break; + case JNLIB_LOG_DEBUG: es_fputs_unlocked ("DBG: ", logstream ); break; + default: + es_fprintf_unlocked (logstream,"[Unknown log level %d]: ", level); + break; } - if (fmt) { - vfprintf(logstream,fmt,arg_ptr) ; + es_vfprintf_unlocked (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); + es_putc_unlocked ('\n', logstream); + es_funlockfile (logstream); + exit (2); } - if (level == JNLIB_LOG_BUG) + else if (level == JNLIB_LOG_BUG) { if (missing_lf) - putc('\n', logstream ); - abort(); + es_putc_unlocked ('\n', logstream ); + es_funlockfile (logstream); + abort (); } + else + es_funlockfile (logstream); } + 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/common/logging.h b/common/logging.h index 0b96108a8..7f595273d 100644 --- a/common/logging.h +++ b/common/logging.h @@ -1,88 +1,90 @@ /* logging.h - * Copyright (C) 1999, 2000, 2001, 2004, 2006 Free Software Foundation, Inc. + * Copyright (C) 1999, 2000, 2001, 2004, 2006, + * 2010 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 . */ #ifndef LIBJNLIB_LOGGING_H #define LIBJNLIB_LOGGING_H #include +#include "estream.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); +estream_t 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 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*/