diff --git a/NEWS b/NEWS
index bd4f975..4779bc0 100644
--- a/NEWS
+++ b/NEWS
@@ -1,905 +1,910 @@
Noteworthy changes in version 1.34 (unreleased) [C25/A25/R_]
-----------------------------------------------
+ * Interface changes relative to the 1.33 release:
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ gpgrt_abort NEW.
+ gpgrt_add_emergency_cleanup NEW.
+
Noteworthy changes in version 1.33 (2018-12-07) [C25/A25/R0]
-----------------------------------------------
* New unified config script gpgrt-config which can now be used by all
GnuPG related packages.
* Support for ARC and arm64ilp32.
* The log functions now sanitize strings printed with the "%s" format
specifier. All control characters are C-escaped in the output.
Users of that function may want to remove their own escaping to
avoid doubling of backslashes.
* New fprintf style function to apply a custom filter for string
arguments.
* New function to compare version strings.
* Interface changes relative to the 1.28 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
gpgrt_cmp_version New.
gpgrt_string_filter_t New.
gpgrt_fprintf_sf New.
gpgrt_fprintf_sf_unlocked New.
gpgrt_ftruncate New but limited functionality.
gpgrt_w32_override_locale New.
Release-info: https://dev.gnupg.org/T4205
Noteworthy changes in version 1.32 (2018-07-12) [C24/A24/R3]
-----------------------------------------------
* Fixes a problem with gpgrt_fflush and gpgrt_fopencookie. [#4069]
* Fixes a problem with the C11 header stdnoreturn.h. [#4002]
* The yat2m tool can now also be build on Windows.
* Updates translations for Spanish, Russian and Ukrainian.
Noteworthy changes in version 1.31 (2018-05-02) [C24/A24/R2]
-----------------------------------------------
* Fixes another problem with gpgrt_poll under Windows. [#3937]
* New translation for Spanish.
Noteworthy changes in version 1.30 (2018-04-30) [C24/A24/R1]
-----------------------------------------------
* Fixes a hang on Windows when using gpgrt_poll under nPth.
* Build fix for Solaris. [#3869]
Noteworthy changes in version 1.29 (2018-04-11) [C24/A24/R0]
-----------------------------------------------
* The yat2m tool is during cross-compile now also installed on the
host platform.
* New option parser and associated functions similar to the one used
by GnuPG.
* New Base-64 encoder.
* Fixes regression in 1.28 for arm64 and w64 builds.
* Interface changes relative to the 1.28 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
gpgrt_argparse New.
gpgrt_usage New.
gpgrt_strusage New.
gpgrt_set_strusage New.
gpgrt_set_usage_outfnc New.
gpgrt_set_fixed_string_mapper New.
GPGRT_ENABLE_ARGPARSE_MACROS New macro.
gpgrt_b64enc_start New.
gpgrt_b64enc_write New.
gpgrt_b64enc_finish New.
Noteworthy changes in version 1.28 (2018-03-13) [C23/A23/R0]
-----------------------------------------------
* The formerly internal yat2m tool is now installed for a native
build.
* The new files gpgrt.m4 and gpgrt-config are now installed. They
can be used instead of gpg-error.m4 and gpg-error-config.
* New logging functions similar to those used by GnuPG.
* New helper functions for platform abstraction.
* Interface changes relative to the 1.27 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
gpgrt_get_errorcount New API.
gpgrt_inc_errorcount New API.
gpgrt_log_set_sink New API.
gpgrt_log_set_socket_dir_cb New API.
gpgrt_log_set_pid_suffix_cb New API.
gpgrt_log_set_prefix New API.
gpgrt_log_get_prefix New API.
gpgrt_log_test_fd New API.
gpgrt_log_get_fd New API.
gpgrt_log_get_stream New API.
gpgrt_log New API.
gpgrt_logv New API.
gpgrt_logv_prefix New API.
gpgrt_log_string New API.
gpgrt_log_info New API.
gpgrt_log_error New API.
gpgrt_log_fatal New API.
gpgrt_log_bug New API.
gpgrt_log_debug New API.
gpgrt_log_debug_string New API.
gpgrt_log_printf New API.
gpgrt_log_flush New API.
gpgrt_log_printhex New API.
gpgrt_log_clock New API.
gpgrt_assert New macro.
_gpgrt_log_assert New internal API.
GPGRT_LOGLVL_BEGIN New const.
GPGRT_LOGLVL_CONT New const.
GPGRT_LOGLVL_INFO New const.
GPGRT_LOGLVL_WARN New const.
GPGRT_LOGLVL_ERROR New const.
GPGRT_LOGLVL_FATAL New const.
GPGRT_LOGLVL_BUG New const.
GPGRT_LOGLVL_DEBUG New const.
gpgrt_realloc New API.
gpgrt_malloc New API.
gpgrt_calloc New API.
gpgrt_strdup New API.
gpgrt_strconcat New API.
gpgrt_w32_reg_query_string New API.
gpgrt_getenv New API.
gpgrt_setenv New API.
gpgrt_mkdir New API.
gpgrt_chdir New API.
gpgrt_getcwd New API.
Noteworthy changes in version 1.27 (2017-02-28) [C22/A22/R0]
-----------------------------------------------
* Added a Base64 decoder.
* Added support for the sh3 architecture.
* Added header gpgrt.h as an alias for gpg-error.h.
* Fixed macro GPGRT_GCC_VERSION.
* Fixed a race in non-blocking I/O on Windows.
* Interface changes relative to the 1.26 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
gpgrt_b64state_t NEW type.
gpgrt_b64dec_start NEW.
gpgrt_b64dec_proc NEW.
gpgrt_b64dec_finish NEW.
GPG_ERR_WRONG_NAME NEW.
gpgrt.h NEW header.
Noteworthy changes in version 1.26 (2016-12-21) [C21/A21/R0]
-----------------------------------------------
* New option --desc for gpg-error.
* Interface changes relative to the 1.25 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
GPG_ERR_UNKNOWN_FLAG NEW.
GPG_ERR_INV_ORDER NEW.
GPG_ERR_ALREADY_FETCHED NEW.
GPG_ERR_TRY_LATER NEW.
GPG_ERR_SYSTEM_BUG NEW.
GPG_ERR_DNS_UNKNOWN NEW.
GPG_ERR_DNS_SECTION NEW.
GPG_ERR_DNS_ADDRESS NEW.
GPG_ERR_DNS_NO_QUERY NEW.
GPG_ERR_DNS_NO_ANSWER NEW.
GPG_ERR_DNS_CLOSED NEW.
GPG_ERR_DNS_VERIFY NEW.
GPG_ERR_DNS_TIMEOUT NEW.
Noteworthy changes in version 1.25 (2016-11-14) [C20/A20/R0]
-----------------------------------------------
* New interface gpgrt_get_syscall_clamp to allow libaries to make use
of Libgpg-error's system call wrapper functions.
* gpgrt_poll does now work under Windows.
* Fixed bug in the locking code when used with the nPth threading
library.
* Added support for {i686,x86_64}-apple-darwin.
* Added new error codes.
* Interface changes relative to the 1.23 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
gpgrt_get_syscall_clamp NEW.
GPG_ERR_ENGINE_TOO_OLD NEW.
GPG_ERR_WINDOW_TOO_SMALL NEW.
GPG_ERR_WINDOW_TOO_LARGE NEW.
GPG_ERR_MISSING_ENVVAR NEW.
GPG_ERR_USER_ID_EXISTS NEW.
GPG_ERR_NAME_EXISTS NEW.
GPG_ERR_DUP_NAME NEW.
GPG_ERR_TOO_OLD NEW.
GPG_ERR_TOO_YOUNG NEW.
Noteworthy changes in version 1.24 (2016-07-14) [C19/A19/R1]
-----------------------------------------------
* Fixes a bug in es_fclose_snatch when used used after es_fseek.
* Fixes building without thread support.
* New configure option --disable-tests.
Noteworthy changes in version 1.23 (2016-06-15) [C19/A19/R0]
-----------------------------------------------
* Fixes an assertion failure due to es_flush on read/write streams.
* Fixes a bug with a too short memory limit is es_fopenmen.
* Cross-build support for powerpc-unknown-linux-gnuspe and
tilegx-unknown-linux-gnu architectures.
* Interface changes relative to the 1.22 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
GPG_ERR_SUBKEYS_EXP_OR_REV NEW.
Noteworthy changes in version 1.22 (2016-04-25) [C18/A18/R0]
-----------------------------------------------
* New functions and macros to to provide iconv(3) on Windows.
* Support for LeakSanitizer with the gpgrt_annotate_leaked_object
inline function.
* Interface changes relative to the 1.21 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
GPG_ERR_DB_CORRUPTED NEW.
gpgrt_annotate_leaked_object NEW inline func.
GPGRT_ENABLE_W32_ICONV_MACROS NEW.
gpgrt_w32_iconv_open NEW.
gpgrt_w32_iconv_close NEW.
gpgrt_w32_iconv NEW.
Noteworthy changes in version 1.21 (2015-12-12) [C17/A17/R0]
-----------------------------------------------
* New functions gpgrt_poll and gpgrt_set_nonblock. For now only
pipes and sockets on Unix are supported.
* Fixes gettext output encoding problems on Windows.
* Interface changes relative to the 1.20 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
gpgrt_set_nonblock NEW.
gpgrt_get_nonblock NEW.
gpgrt_poll NEW.
gpgrt_poll_t NEW type.
es_poll_t NEW type.
es_set_nonblock NEW macro.
es_get_nonblock NEW macro.
es_poll NEW macro.
GPG_ERR_TRUE NEW.
GPG_ERR_FALSE NEW.
GPG_ERR_NO_NAME NEW.
GPG_ERR_NO_KEY NEW.
GPG_ERR_SERVER_FAILED NEW.
Noteworthy changes in version 1.20 (2015-08-26) [C16/A16/R0]
-----------------------------------------------
* New macros for GCC attributes.
* Make es_set_binary actually work for Windows.
* Allow building without thread support.
* Build without a build timestamp by default.
* Interface changes relative to the 1.19 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
GPGRT_VERSION NEW macro.
GPGRT_VERSION_NUMBER NEW macro.
GPGRT_INLINE NEW macro.
GPGRT_GCC_VERSION NEW macro.
GPGRT_ATTR_NORETURN NEW macro.
GPGRT_ATTR_PRINTF NEW macro.
GPGRT_ATTR_NR_PRINTF NEW macro.
GPGRT_ATTR_FORMAT_ARG NEW macro.
GPGRT_ATTR_SENTINEL NEW macro.
GPGRT_ATTR_USED NEW macro.
GPGRT_ATTR_UNUSED NEW macro.
GPGRT_ATTR_DEPRECATED NEW macro.
GPGRT_ATTR_PURE NEW macro.
GPGRT_ATTR_MALLOC NEW macro.
GPGRT_HAVE_MACRO_FUNCTION NEW macro.
GPGRT_HAVE_PRAGMA_GCC_PUSH NEW macro.
Noteworthy changes in version 1.19 (2015-04-10) [C15/A15/R0]
-----------------------------------------------
* New set of error codes for use with LDAP.
* New options --help and --defines for gpg-error.
* Allow building with gcc 5.
* Interface changes relative to the 1.18 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
GPG_ERR_LDAP_* NEW.
Noteworthy changes in version 1.18 (2015-01-26) [C14/A14/R0]
-----------------------------------------------
* New translations for Hungarian, Portuguese, Russian, and
traditional Chinese. Updated other translations.
* New error codes.
* Interface changes relative to the 1.17 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
GPG_ERR_FORBIDDEN NEW.
GPG_ERR_OBJ_TERM_STATE NEW.
GPG_ERR_REQUEST_TOO_SHORT NEW.
GPG_ERR_REQUEST_TOO_LONG NEW.
GPG_ERR_LEGACY_KEY NEW.
Noteworthy changes in version 1.17 (2014-10-15) [C13/A13/R0]
-----------------------------------------------
* New error codes for TLS protocol libraries.
* New configure option --enable-build-timestamp.
* New man page for gpg-error-config.
* Interface changes relative to the 1.16 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
GPG_ERR_SOURCE_TLS NEW.
GPG_ERR_NO_CERT_CHAIN NEW.
GPG_ERR_CERT_TOO_LARGE NEW.
GPG_ERR_INV_RECORD NEW.
GPG_ERR_BAD_MAC NEW.
GPG_ERR_UNEXPECTED_MSG NEW.
GPG_ERR_COMPR_FAILED NEW.
GPG_ERR_WOULD_WRAP NEW.
GPG_ERR_FATAL_ALERT NEW.
GPG_ERR_NO_CIPHER NEW.
GPG_ERR_MISSING_CLIENT_CERT NEW.
GPG_ERR_CLOSE_NOTIFY NEW.
GPG_ERR_TICKET_EXPIRED NEW.
GPG_ERR_BAD_TICKET NEW.
GPG_ERR_UNKNOWN_IDENTITY NEW.
GPG_ERR_BAD_HS_CERT NEW.
GPG_ERR_BAD_HS_CERT_REQ NEW.
GPG_ERR_BAD_HS_CERT_VER NEW.
GPG_ERR_BAD_HS_CHANGE_CIPHER NEW.
GPG_ERR_BAD_HS_CLIENT_HELLO NEW.
GPG_ERR_BAD_HS_SERVER_HELLO NEW.
GPG_ERR_BAD_HS_SERVER_HELLO_DONE NEW.
GPG_ERR_BAD_HS_FINISHED NEW.
GPG_ERR_BAD_HS_SERVER_KEX NEW.
GPG_ERR_BAD_HS_CLIENT_KEX NEW.
GPG_ERR_BOGUS_STRING NEW.
gpgrt_pending NEW.
gpgrt_pending_unlocked NEW.
Noteworthy changes in version 1.16 (2014-09-18) [C12/A12/R2]
-----------------------------------------------
* Support building for iOS.
* Fixed a prototype mismatch.
* Fix es_fclose for streams opened with "samethread".
Noteworthy changes in version 1.15 (2014-09-11) [C12/A12/R1]
-----------------------------------------------
* This releases fixes problems with the use of off_t and ssize_t by
the estream functions introduced with 1.14. Although this is
technically an ABI break on some platforms, we take this as a
simple bug fix for 1.14. The new functions are very unlikely in
use by any code and thus no breakage should happen. The 1.14
tarball will be removed from the archive.
* Add type gpgrt_off_t which is guaranteed to be 64 bit.
* Add type gpgrt_ssize_t to make use on Windows easier. On Unix
platforms this is an alias for ssize_t.
Noteworthy changes in version 1.14 (2014-09-08) [C12/A12/R0]
-----------------------------------------------
* Added gpgrt_lock_trylock.
* Added the estream library under the name gpgrt and a set of macros
to use them with their "es_" names.
* Interface changes relative to the 1.13 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
GPG_ERR_KEY_DISABLED NEW.
gpgrt_init NEW macro.
gpgrt_check_version NEW.
gpgrt_lock_trylock NEW.
gpgrt_set_syscall_clamp NEW.
gpgrt_set_alloc_func NEW.
gpgrt_stream_t NEW.
gpgrt_cookie_io_functions_t NEW.
gpgrt_syshd_t NEW.
GPGRT_SYSHD_NONE NEW.
GPGRT_SYSHD_FD NEW.
GPGRT_SYSHD_SOCK NEW.
GPGRT_SYSHD_RVID NEW.
GPGRT_SYSHD_HANDLE NEW.
gpgrt_stdin NEW macro.
gpgrt_stdout NEW macro.
gpgrt_stderr NEW macro.
gpgrt_fopen NEW.
gpgrt_mopen NEW.
gpgrt_fopenmem NEW.
gpgrt_fopenmem_init NEW.
gpgrt_fdopen NEW.
gpgrt_fdopen_nc NEW.
gpgrt_sysopen NEW.
gpgrt_sysopen_nc NEW.
gpgrt_fpopen NEW.
gpgrt_fpopen_nc NEW.
gpgrt_freopen NEW.
gpgrt_fopencookie NEW.
gpgrt_fclose NEW.
gpgrt_fclose_snatch NEW.
gpgrt_onclose NEW.
gpgrt_fileno NEW.
gpgrt_fileno_unlocked NEW.
gpgrt_syshd NEW.
gpgrt_syshd_unlocked NEW.
gpgrt_flockfile NEW.
gpgrt_ftrylockfile NEW.
gpgrt_funlockfile NEW.
gpgrt_feof NEW.
gpgrt_feof_unlocked NEW.
gpgrt_ferror NEW.
gpgrt_ferror_unlocked NEW.
gpgrt_clearerr NEW.
gpgrt_clearerr_unlocked NEW.
gpgrt_fflush NEW.
gpgrt_fseek NEW.
gpgrt_fseeko NEW.
gpgrt_ftell NEW.
gpgrt_ftello NEW.
gpgrt_rewind NEW.
gpgrt_getc NEW macro.
gpgrt_getc_unlocked NEW macro.
gpgrt_fgetc NEW.
gpgrt_fputc NEW.
gpgrt_ungetc NEW.
gpgrt_read NEW.
gpgrt_write NEW.
gpgrt_write_sanitized NEW.
gpgrt_write_hexstring NEW.
gpgrt_fread NEW.
gpgrt_fwrite NEW.
gpgrt_fgets NEW.
gpgrt_putc NEW macro.
gpgrt_putc_unlocked NEW macro.
gpgrt_fputs NEW.
gpgrt_fputs_unlocked NEW.
gpgrt_getline NEW.
gpgrt_read_line NEW.
gpgrt_free NEW.
gpgrt_fprintf NEW.
gpgrt_fprintf_unlocked NEW.
gpgrt_printf NEW.
gpgrt_printf_unlocked NEW.
gpgrt_vfprintf NEW.
gpgrt_vfprintf_unlocked NEW.
gpgrt_setvbuf NEW.
gpgrt_setbuf NEW.
gpgrt_set_binary NEW.
gpgrt_tmpfile NEW.
gpgrt_opaque_set NEW.
gpgrt_opaque_get NEW.
gpgrt_fname_set NEW.
gpgrt_fname_get NEW.
gpgrt_asprintf NEW.
gpgrt_vasprintf NEW.
gpgrt_bsprintf NEW.
gpgrt_vbsprintf NEW.
gpgrt_snprintf NEW.
gpgrt_vsnprintf NEW.
Noteworthy changes in version 1.13 (2014-04-15) [C11/A11/R0]
-----------------------------------------------
* Added a portable mutex API.
* The AM_PATH_GPG_ERROR macro now defines GPG_ERROR_MT_CFLAGS and
GPG_ERROR_MT_LIBS autoconf output variables for use by programs
which need gpgrt based thread support. gpg-error-config has a new
option --mt.
* Interface changes relative to the 1.12 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
GPG_ERR_KEY_ON_CARD NEW.
GPG_ERR_MAC_ALGO NEW.
GPG_ERR_INV_LOCK_OBJ NEW.
gpgrt_lock_t NEW.
GPGRT_LOCK_INITIALIZER NEW.
GPGRT_LOCK_DEFINE NEW.
gpgrt_lock_init NEW.
gpgrt_lock_lock NEW.
gpgrt_lock_unlock NEW.
gpgrt_lock_destroy NEW.
gpgrt_yield NEW.
Noteworthy changes in version 1.12 (2013-06-24)
-----------------------------------------------
* Add support for 64 bit Windows (use ./autogen.sh --build-w64).
* Fixed parsing and installing of the Windows .def file.
* Interface changes relative to the 1.11 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
GPG_ERR_NO_CRYPT_CTX NEW.
GPG_ERR_WRONG_CRYPT_CTX NEW.
GPG_ERR_BAD_CRYPT_CTX NEW.
GPG_ERR_CRYPT_CTX_CONFLICT NEW.
GPG_ERR_BROKEN_PUBKEY NEW.
GPG_ERR_BROKEN_SECKEY NEW.
Noteworthy changes in version 1.11 (2013-02-25)
-----------------------------------------------
* New error source GPG_ERR_SOURCE_ASSUAN for Libassuan related
errors.
* New macros GPG_ERROR_VERSION and GPG_ERROR_VERSION_NUMBER. New
function gpg_error_check_version.
* Interface changes relative to the 1.10 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
GPG_ERR_NO_KEYSERVER NEW.
GPG_ERR_INV_CURVE NEW.
GPG_ERR_UNKNOWN_CURVE NEW.
GPG_ERR_DUP_KEY NEW.
GPG_ERR_AMBIGUOUS NEW.
GPG_ERR_SOURCE_ASSUAN NEW.
gpg_error_check_version NEW.
GPG_ERROR_VERSION NEW.
GPG_ERROR_VERSION_NUMBER NEW.
Noteworthy changes in version 1.10 (2010-10-26)
-----------------------------------------------
* Using a static library on W32 does now work.
* Interface changes relative to the 1.9 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
GPG_ERR_NOT_INITIALIZED NEW.
GPG_ERR_MISSING_ISSUER_CERT NEW.
GPG_ERR_FULLY_CANCELED NEW.
Noteworthy changes in version 1.9 (2010-07-21)
----------------------------------------------
* New function gpg_err_deinit.
* Fix building of static lib under W32.
* Interface changes relative to the 1.8 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
GPG_ERR_MISSING_KEY NEW.
GPG_ERR_TOO_MANY NEW.
GPG_ERR_LIMIT_REACHED NEW.
gpg_err_deinit NEW.
Noteworthy changes in version 1.8 (2010-05-06)
----------------------------------------------
* Support for WindowsCE.
* New option --list for gpg-error.
* Interface changes relative to the 1.7 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
GPG_ERR_NOT_ENABLED NEW.
GPG_ERR_SOURCE_G13 NEW.
GPG_ERR_NO_ENGINE NEW.
gpg_err_set_errno NEW.
Noteworthy changes in version 1.7 (2008-11-26)
----------------------------------------------
* Minor fixes and a few new error codes.
* Interface changes relative to the 1.6 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
GPG_ERR_NOT_OPERATIONAL NEW
GPG_ERR_NO_PASSPHRASE NEW
GPG_ERR_NO_PIN NEW
Noteworthy changes in version 1.6 (2007-10-29)
----------------------------------------------
* Fixed a build problem under Windows.
* Interface changes relative to the 1.4 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
GPG_ERR_UNFINISHED NEW
GPG_ERR_SOURCE_GPA NEW
GPG_ERR_SOURCE_KLEO NEW
Noteworthy changes in version 1.5 (2006-11-30)
----------------------------------------------
* Minor build system fixes.
* Updated gettext. Removed included gettext copy.
* gpg-error has a new option --version.
Noteworthy changes in version 1.4 (2006-09-14)
----------------------------------------------
* Support for Common Lisp is included.
* New error codes for the Assuan IPC library.
* New error code GPG_ERR_MISSING_ERRNO to be used in cases when a
system accidentally does not set errno but a system error
definitely occurred.
* New error source GPG_ERR_SOURCE_ANY to allow proper use of
libgpg-error even if a specific source is not available.
* New convenience functions gpg_err_code_from_syserror and
gpg_error_from_syserror which make sure never to return 0.
* Interface changes relative to the 1.2 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
gpg_err_code_from_syserror NEW
gpg_error_from_syserror NEW
GPG_ERR_SOURCE_ANY NEW
GPG_ERR_MISSING_ERRNO NEW
GPG_ERR_UNKNOWN_OPTION NEW
GPG_ERR_UNKNOWN_COMMAND NEW
GPG_ERR_ASS_GENERAL NEW
GPG_ERR_ASS_ACCEPT_FAILED NEW
GPG_ERR_ASS_CONNECT_FAILED NEW
GPG_ERR_ASS_INV_RESPONSE NEW
GPG_ERR_ASS_INV_VALUE NEW
GPG_ERR_ASS_INCOMPLETE_LINE NEW
GPG_ERR_ASS_LINE_TOO_LONG NEW
GPG_ERR_ASS_NESTED_COMMANDS NEW
GPG_ERR_ASS_NO_DATA_CB NEW
GPG_ERR_ASS_NO_INQUIRE_CB NEW
GPG_ERR_ASS_NOT_A_SERVER NEW
GPG_ERR_ASS_NOT_A_CLIENT NEW
GPG_ERR_ASS_SERVER_START NEW
GPG_ERR_ASS_READ_ERROR NEW
GPG_ERR_ASS_WRITE_ERROR NEW
GPG_ERR_ASS_TOO_MUCH_DATA NEW
GPG_ERR_ASS_UNEXPECTED_CMD NEW
GPG_ERR_ASS_UNKNOWN_CMD NEW
GPG_ERR_ASS_SYNTAX NEW
GPG_ERR_ASS_CANCELED NEW
GPG_ERR_ASS_NO_INPUT NEW
GPG_ERR_ASS_NO_OUTPUT NEW
GPG_ERR_ASS_PARAMETER NEW
GPG_ERR_ASS_UNKNOWN_INQUIRE NEW
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Noteworthy changes in version 1.3 (2006-03-14)
----------------------------------------------
* GNU gettext is included for systems that do not provide it.
Noteworthy changes in version 1.2 (2006-03-03)
----------------------------------------------
* New function gpg_err_init, which binds the locale directory to
the text domain. This function is a constructor on GCC targets,
so it does not need to be called explicitely. The header file
defines GPG_ERR_INITIALIZED in this case. This is experimental for
now.
* "./autogen.sh --build-w32" does now also build a DLL for W32.
Translations are not yet provided for this platform.
* New error codes GPG_ERR_UNKNOWN_EXTN and GPG_ERR_UNKNOWN_CRIT_EXTN.
* New error code GPG_ERR_LOCKED.
* New translations included for France, Romania, and Vietnamese.
* Interface changes relative to the 1.1 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
GPG_ERR_UNKNOWN_EXTN NEW
GPG_ERR_UNKNOWN_CRIT_EXTN NEW
GPG_ERR_LOCKED NEW
gpg_err_init NEW
GPG_ERR_INITIALIZED NEW
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Noteworthy changes in version 1.1 (2005-06-20)
----------------------------------------------
* Bug fixes.
Noteworthy changes in version 1.0 (2004-07-30)
----------------------------------------------
* Ported to Solaris 2.8.
* Added a new error source GPG_ERR_SOURCE_GSTI, and new error
codes GPG_ERR_PROTOCOL_VIOLATION and GPG_ERR_INV_MAC for this
source.
* Interface changes relative to the 0.7 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
GPG_ERR_SOURCE_GSTI NEW
GPG_ERR_PROTOCOL_VIOLATION NEW
GPG_ERR_INV_MAC NEW
GPG_ERR_INV_REQUEST NEW
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Noteworthy changes in version 0.7 (2004-03-07)
----------------------------------------------
* libgpg-error can be built on systems where the errno macros do not
evaluate to plain numbers, but expressions. If you want to
cross-compile, you might have to set CC_FOR_BUILD, though.
* A new tool gpg-error to convert error numbers into symbols into
strings is provided.
* Interface changes relative to the 0.6 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
GPG_ERR_LOCALE_PROBLEM NEW
GPG_ERR_NOT_LOCKED NEW
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Noteworthy changes in version 0.6 (2003-11-14)
----------------------------------------------
* German translation included.
* It is now possible to use the inline functions even for non C99
compliant compilers by given e.g. -DGPG_ERR_INLINE=inline when
compiling an application using this library. Note, that gcc will
use inline anyway.
* Interface changes relative to the 0.5 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
GPG_ERR_SOURCE_KSBA NEW
GPG_ERR_SOURCE_DIRMNGR NEW
GPG_ERR_TRUNCATED NEW
GPG_ERR_NO_ENCODING_METHOD NEW
GPG_ERR_NO_ENCRYPTION_SCHEME NEW
GPG_ERR_NO_SIGNATURE_SCHEME NEW
GPG_ERR_INV_ATTR NEW
GPG_ERR_NO_VALUE NEW
GPG_ERR_NOT_FOUND NEW
GPG_ERR_VALUE_NOT_FOUND NEW
GPG_ERR_SYNTAX NEW
GPG_ERR_INV_CRL NEW
GPG_ERR_BAD_BER NEW
GPG_ERR_INV_BER NEW
GPG_ERR_ELEMENT_NOT_FOUND NEW
GPG_ERR_IDENTIFIER_NOT_FOUND NEW
GPG_ERR_INV_TAG NEW
GPG_ERR_INV_LENGTH NEW
GPG_ERR_INV_KEYINFO NEW
GPG_ERR_UNEXPECTED_TAG NEW
GPG_ERR_NOT_DER_ENCODED, NEW
GPG_ERR_NO_CMS_OBJ NEW
GPG_ERR_INV_CMS_OBJ NEW
GPG_ERR_UNKNOWN_CMS_OBJ, NEW
GPG_ERR_UNSUPPORTED_CMS_OBJ NEW
GPG_ERR_UNSUPPORTED_ENCODING, NEW
GPG_ERR_UNSUPPORTED_CMS_VERSION NEW
GPG_ERR_UNKNOWN_ALGORITHM NEW
GPG_ERR_ENCODING_PROBLEM NEW
GPG_ERR_INV_STATE NEW
GPG_ERR_DUP_VALUE, NEW
GPG_ERR_MISSING_ACTION NEW
GPG_ERR_MODULE_NOT_FOUND NEW
GPG_ERR_INV_OID_STRING NEW
GPG_ERR_INV_TIME NEW
GPG_ERR_INV_CRL_OBJ NEW
GPG_ERR_UNSUPPORTED_CRL_VERSION NEW
GPG_ERR_INV_CERT_OBJ NEW
GPG_ERR_UNKNOWN_NAME NEW
GPG_ERR_BUFFER_TOO_SHORT. NEW
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Noteworthy changes in version 0.5 (2003-10-06)
----------------------------------------------
* New thread safe interface gpg_strerror_r.
* New error code GPG_ERR_PIN_NOT_SYNCED has been added.
* Interface changes relative to the 0.4 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
gpg_strerror_r NEW
GPG_ERR_PIN_NOT_SYNCED NEW
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Noteworthy changes in version 0.4 (2003-09-03)
----------------------------------------------
* Fixed another bug that prevented that system errors were created
correctly in the first place.
* Use inline in public header file only on C99 compilers.
Noteworthy changes in version 0.3 (2003-07-31)
----------------------------------------------
* Fixed bug that prevented that system errors were mapped to error
strings correctly.
Noteworthy changes in version 0.2 (2003-07-30)
----------------------------------------------
* Value of the error code GPG_ERR_CANCELED was fixed.
* New error codes GPG_ERR_WRONG_CARD, GPG_ERR_HARDWARE,
GPG_ERR_PIN_BLOCKED and GPG_ERR_USE_CONDITIONS have been added.
* The header file has been made C++ clean.
* AM_PATH_GPG_ERR has been fixed to work without explicit version
number.
* The header file now uses inline instead __inline__ for non-GNU
compilers.
Noteworthy changes in version 0.1 (2003-06-06)
----------------------------------------------
* Initial release.
Copyright 2003, 2004, 2005, 2010 g10 Code GmbH
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/gpgscm/ffi.c b/gpgscm/ffi.c
index 578c68d..2067b0d 100644
--- a/gpgscm/ffi.c
+++ b/gpgscm/ffi.c
@@ -1,1476 +1,1475 @@
/* FFI interface for TinySCHEME.
* Copyright (C) 2016 g10 Code GmbH
*
* This file is part of Libgpg-error.
*
* Libgpg-error is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* Libgpg-error 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
#include
#include
#include
#include
#if HAVE_LIBREADLINE
#define GNUPG_LIBREADLINE_H_INCLUDED
#include
#include
#endif
/* #include "../../common/util.h" */
/* #include "../../common/exechelp.h" */
/* #include "../../common/sysutils.h" */
#include "private.h"
#include "ffi.h"
#include "ffi-private.h"
/* For use in nice error messages. */
static const char *
ordinal_suffix (int n)
{
switch (n)
{
case 1: return "st";
case 2: return "nd";
case 3: return "rd";
default: return "th";
}
assert (! "reached");
}
int
ffi_bool_value (scheme *sc, pointer p)
{
return ! (p == sc->F);
}
static pointer
do_logand (scheme *sc, pointer args)
{
FFI_PROLOG ();
unsigned int v, acc = ~0;
while (args != sc->NIL)
{
FFI_ARG_OR_RETURN (sc, unsigned int, v, number, args);
acc &= v;
}
FFI_RETURN_INT (sc, acc);
}
static pointer
do_logior (scheme *sc, pointer args)
{
FFI_PROLOG ();
unsigned int v, acc = 0;
while (args != sc->NIL)
{
FFI_ARG_OR_RETURN (sc, unsigned int, v, number, args);
acc |= v;
}
FFI_RETURN_INT (sc, acc);
}
static pointer
do_logxor (scheme *sc, pointer args)
{
FFI_PROLOG ();
unsigned int v, acc = 0;
while (args != sc->NIL)
{
FFI_ARG_OR_RETURN (sc, unsigned int, v, number, args);
acc ^= v;
}
FFI_RETURN_INT (sc, acc);
}
static pointer
do_lognot (scheme *sc, pointer args)
{
FFI_PROLOG ();
unsigned int v;
FFI_ARG_OR_RETURN (sc, unsigned int, v, number, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
FFI_RETURN_INT (sc, ~v);
}
/* User interface. */
static pointer
do_flush_stdio (scheme *sc, pointer args)
{
FFI_PROLOG ();
FFI_ARGS_DONE_OR_RETURN (sc, args);
fflush (stdout);
fflush (stderr);
FFI_RETURN (sc);
}
int use_libreadline;
/* Read a string, and return a pointer to it. Returns NULL on EOF. */
char *
rl_gets (const char *prompt)
{
static char *line = NULL;
char *p;
xfree (line);
#if HAVE_LIBREADLINE
{
line = readline (prompt);
if (line && *line)
add_history (line);
}
#else
{
size_t max_size = 0xff;
printf ("%s", prompt);
fflush (stdout);
line = xtrymalloc (max_size);
if (line != NULL)
fgets (line, max_size, stdin);
}
#endif
/* Strip trailing whitespace. */
if (line && strlen (line) > 0)
for (p = &line[strlen (line) - 1]; isspace (*p); p--)
*p = 0;
return line;
}
static pointer
do_prompt (scheme *sc, pointer args)
{
FFI_PROLOG ();
const char *prompt;
const char *line;
FFI_ARG_OR_RETURN (sc, const char *, prompt, string, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
line = rl_gets (prompt);
if (! line)
FFI_RETURN_POINTER (sc, sc->EOF_OBJ);
FFI_RETURN_STRING (sc, line);
}
static pointer
do_sleep (scheme *sc, pointer args)
{
FFI_PROLOG ();
unsigned int seconds;
FFI_ARG_OR_RETURN (sc, unsigned int, seconds, number, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
sleep (seconds);
FFI_RETURN (sc);
}
static pointer
do_usleep (scheme *sc, pointer args)
{
FFI_PROLOG ();
useconds_t microseconds;
FFI_ARG_OR_RETURN (sc, useconds_t, microseconds, number, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
usleep (microseconds);
FFI_RETURN (sc);
}
static pointer
do_chdir (scheme *sc, pointer args)
{
FFI_PROLOG ();
char *name;
FFI_ARG_OR_RETURN (sc, char *, name, path, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
if (chdir (name))
FFI_RETURN_ERR (sc, errno);
FFI_RETURN (sc);
}
static pointer
do_strerror (scheme *sc, pointer args)
{
FFI_PROLOG ();
int error;
FFI_ARG_OR_RETURN (sc, int, error, number, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
FFI_RETURN_STRING (sc, gpg_strerror (error));
}
static pointer
do_getenv (scheme *sc, pointer args)
{
FFI_PROLOG ();
char *name;
char *value;
FFI_ARG_OR_RETURN (sc, char *, name, string, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
value = gpgrt_getenv (name);
FFI_RETURN_STRING (sc, value ? value : "");
}
static pointer
do_setenv (scheme *sc, pointer args)
{
FFI_PROLOG ();
gpg_err_code_t ec;
char *name;
char *value;
int overwrite;
FFI_ARG_OR_RETURN (sc, char *, name, string, args);
FFI_ARG_OR_RETURN (sc, char *, value, string, args);
FFI_ARG_OR_RETURN (sc, int, overwrite, bool, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
if ((ec = gpgrt_setenv (name, value, overwrite)))
FFI_RETURN_ERR (sc, ec);
FFI_RETURN (sc);
}
static pointer
do_exit (scheme *sc, pointer args)
{
FFI_PROLOG ();
int retcode;
FFI_ARG_OR_RETURN (sc, int, retcode, number, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
exit (retcode);
}
/* XXX: use gnupgs variant b/c mode as string */
static pointer
do_open (scheme *sc, pointer args)
{
FFI_PROLOG ();
int fd;
char *pathname;
int flags;
mode_t mode = 0;
FFI_ARG_OR_RETURN (sc, char *, pathname, path, args);
FFI_ARG_OR_RETURN (sc, int, flags, number, args);
if (args != sc->NIL)
FFI_ARG_OR_RETURN (sc, mode_t, mode, number, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
fd = open (pathname, flags, mode);
if (fd == -1)
FFI_RETURN_ERR (sc, gpg_error_from_syserror ());
FFI_RETURN_INT (sc, fd);
}
static pointer
do_fdopen (scheme *sc, pointer args)
{
FFI_PROLOG ();
FILE *stream;
int fd;
char *mode;
int kind;
FFI_ARG_OR_RETURN (sc, int, fd, number, args);
FFI_ARG_OR_RETURN (sc, char *, mode, string, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
stream = fdopen (fd, mode);
if (stream == NULL)
FFI_RETURN_ERR (sc, gpg_error_from_syserror ());
if (setvbuf (stream, NULL, _IONBF, 0) != 0)
FFI_RETURN_ERR (sc, gpg_error_from_syserror ());
kind = 0;
if (strchr (mode, 'r'))
kind |= port_input;
if (strchr (mode, 'w'))
kind |= port_output;
FFI_RETURN_POINTER (sc, sc->vptr->mk_port_from_file (sc, stream, kind));
}
static pointer
do_close (scheme *sc, pointer args)
{
FFI_PROLOG ();
int fd;
FFI_ARG_OR_RETURN (sc, int, fd, number, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
FFI_RETURN_ERR (sc, close (fd) == 0 ? 0 : gpg_error_from_syserror ());
}
static pointer
do_seek (scheme *sc, pointer args)
{
FFI_PROLOG ();
int fd;
off_t offset;
int whence;
FFI_ARG_OR_RETURN (sc, int, fd, number, args);
FFI_ARG_OR_RETURN (sc, off_t, offset, number, args);
FFI_ARG_OR_RETURN (sc, int, whence, number, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
FFI_RETURN_ERR (sc, lseek (fd, offset, whence) == (off_t) -1
? gpg_error_from_syserror () : 0);
}
static pointer
do_get_temp_path (scheme *sc, pointer args)
{
FFI_PROLOG ();
#ifdef HAVE_W32_SYSTEM
char buffer[MAX_PATH+1];
#endif
FFI_ARGS_DONE_OR_RETURN (sc, args);
#ifdef HAVE_W32_SYSTEM
if (GetTempPath (MAX_PATH+1, buffer) == 0)
FFI_RETURN_STRING (sc, "/temp");
FFI_RETURN_STRING (sc, buffer);
#else
FFI_RETURN_STRING (sc, "/tmp");
#endif
}
static pointer
do_mkdtemp (scheme *sc, pointer args)
{
FFI_PROLOG ();
char *template;
#ifdef PATH_MAX
char buffer[PATH_MAX];
#else
char buffer[1024];
#endif
char *name;
FFI_ARG_OR_RETURN (sc, char *, template, string, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
if (strlen (template) > sizeof buffer - 1)
FFI_RETURN_ERR (sc, EINVAL);
strncpy (buffer, template, sizeof buffer);
name = NULL; /*gnupg_mkdtemp (buffer);*/
if (name == NULL)
FFI_RETURN_ERR (sc, gpg_error_from_syserror ());
FFI_RETURN_STRING (sc, name);
}
static pointer
do_unlink (scheme *sc, pointer args)
{
FFI_PROLOG ();
char *name;
FFI_ARG_OR_RETURN (sc, char *, name, string, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
if (unlink (name) == -1)
FFI_RETURN_ERR (sc, gpg_error_from_syserror ());
FFI_RETURN (sc);
}
static gpg_error_t
unlink_recursively (const char *name)
{
gpg_error_t err = 0;
struct stat st;
if (stat (name, &st) == -1)
return gpg_error_from_syserror ();
if (S_ISDIR (st.st_mode))
{
DIR *dir;
struct dirent *dent;
dir = opendir (name);
if (dir == NULL)
return gpg_error_from_syserror ();
while ((dent = readdir (dir)))
{
char *child;
if (strcmp (dent->d_name, ".") == 0
|| strcmp (dent->d_name, "..") == 0)
continue;
child = gpgrt_bsprintf ("%s/%s", name, dent->d_name);
if (!child)
{
err = gpg_error_from_syserror ();
goto leave;
}
err = unlink_recursively (child);
xfree (child);
if (err == gpg_error_from_errno (ENOENT))
err = 0;
if (err)
goto leave;
}
leave:
closedir (dir);
if (! err)
rmdir (name);
return err;
}
else
if (unlink (name) == -1)
return gpg_error_from_syserror ();
return 0;
}
static pointer
do_unlink_recursively (scheme *sc, pointer args)
{
FFI_PROLOG ();
char *name;
FFI_ARG_OR_RETURN (sc, char *, name, string, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
err = unlink_recursively (name);
FFI_RETURN (sc);
}
static pointer
do_rename (scheme *sc, pointer args)
{
FFI_PROLOG ();
char *old;
char *new;
FFI_ARG_OR_RETURN (sc, char *, old, string, args);
FFI_ARG_OR_RETURN (sc, char *, new, string, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
if (rename (old, new) == -1)
FFI_RETURN_ERR (sc, gpg_error_from_syserror ());
FFI_RETURN (sc);
}
static pointer
do_getcwd (scheme *sc, pointer args)
{
FFI_PROLOG ();
pointer result;
char *cwd;
FFI_ARGS_DONE_OR_RETURN (sc, args);
cwd = gpgrt_getcwd ();
if (cwd == NULL)
FFI_RETURN_ERR (sc, gpg_error_from_syserror ());
result = sc->vptr->mk_string (sc, cwd);
xfree (cwd);
FFI_RETURN_POINTER (sc, result);
}
static pointer
do_mkdir (scheme *sc, pointer args)
{
FFI_PROLOG ();
gpg_err_code_t ec;
char *name;
char *mode;
FFI_ARG_OR_RETURN (sc, char *, name, string, args);
FFI_ARG_OR_RETURN (sc, char *, mode, string, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
ec = gpgrt_mkdir (name, mode);
if (ec)
FFI_RETURN_ERR (sc, ec);
FFI_RETURN (sc);
}
static pointer
do_rmdir (scheme *sc, pointer args)
{
FFI_PROLOG ();
char *name;
FFI_ARG_OR_RETURN (sc, char *, name, string, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
if (rmdir (name) == -1)
FFI_RETURN_ERR (sc, gpg_error_from_syserror ());
FFI_RETURN (sc);
}
static pointer
do_get_isotime (scheme *sc, pointer args)
{
FFI_PROLOG ();
/* gnupg_isotime_t timebuf; */
char timebuf[15];
FFI_ARGS_DONE_OR_RETURN (sc, args);
*timebuf = 0; /*gnupg_get_isotime (timebuf);*/
FFI_RETURN_STRING (sc, timebuf);
}
static pointer
do_get_time (scheme *sc, pointer args)
{
FFI_PROLOG ();
time_t current;
FFI_ARGS_DONE_OR_RETURN (sc, args);
current = time (NULL);
if (current == (time_t)(-1))
gpgrt_log_fatal ("time() failed\n");
FFI_RETURN_INT (sc, current);
}
static pointer
do_getpid (scheme *sc, pointer args)
{
FFI_PROLOG ();
FFI_ARGS_DONE_OR_RETURN (sc, args);
FFI_RETURN_INT (sc, getpid ());
}
static pointer
do_srandom (scheme *sc, pointer args)
{
FFI_PROLOG ();
int seed;
FFI_ARG_OR_RETURN (sc, int, seed, number, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
srand (seed);
FFI_RETURN (sc);
}
static int
random_scaled (int scale)
{
int v;
#ifdef HAVE_RAND
v = rand ();
#else
v = random ();
#endif
#ifndef RAND_MAX /* for SunOS */
#define RAND_MAX 32767
#endif
return ((int) (1 + (int) ((float) scale * v / (RAND_MAX + 1.0))) - 1);
}
static pointer
do_random (scheme *sc, pointer args)
{
FFI_PROLOG ();
int scale;
FFI_ARG_OR_RETURN (sc, int, scale, number, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
FFI_RETURN_INT (sc, random_scaled (scale));
}
static pointer
do_make_random_string (scheme *sc, pointer args)
{
FFI_PROLOG ();
int size;
pointer chunk;
char *p;
FFI_ARG_OR_RETURN (sc, int, size, number, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
if (size < 0)
return ffi_sprintf (sc, "size must be positive");
chunk = sc->vptr->mk_counted_string (sc, NULL, size);
if (sc->no_memory)
FFI_RETURN_ERR (sc, ENOMEM);
for (p = sc->vptr->string_value (chunk); size; p++, size--)
*p = (char) random_scaled (256);
FFI_RETURN_POINTER (sc, chunk);
}
/* estream functions. */
struct es_object_box
{
estream_t stream;
int closed;
};
static void
es_object_finalize (scheme *sc, void *data)
{
struct es_object_box *box = data;
(void) sc;
if (! box->closed)
es_fclose (box->stream);
xfree (box);
}
static void
es_object_to_string (scheme *sc, char *out, size_t size, void *data)
{
struct es_object_box *box = data;
(void) sc;
snprintf (out, size, "#estream %p", box->stream);
}
static struct foreign_object_vtable es_object_vtable =
{
es_object_finalize,
es_object_to_string,
};
static pointer
es_wrap (scheme *sc, estream_t stream)
{
struct es_object_box *box = xmalloc (sizeof *box);
if (box == NULL)
return sc->NIL;
box->stream = stream;
box->closed = 0;
return sc->vptr->mk_foreign_object (sc, &es_object_vtable, box);
}
static struct es_object_box *
es_unwrap (scheme *sc, pointer object)
{
(void) sc;
if (! is_foreign_object (object))
return NULL;
if (sc->vptr->get_foreign_object_vtable (object) != &es_object_vtable)
return NULL;
return sc->vptr->get_foreign_object_data (object);
}
#define CONVERSION_estream(SC, X) es_unwrap (SC, X)
#define IS_A_estream(SC, X) es_unwrap (SC, X)
static pointer
do_es_fclose (scheme *sc, pointer args)
{
FFI_PROLOG ();
struct es_object_box *box;
FFI_ARG_OR_RETURN (sc, struct es_object_box *, box, estream, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
err = es_fclose (box->stream);
if (! err)
box->closed = 1;
FFI_RETURN (sc);
}
static pointer
do_es_read (scheme *sc, pointer args)
{
FFI_PROLOG ();
struct es_object_box *box;
size_t bytes_to_read;
pointer result;
void *buffer;
size_t bytes_read;
FFI_ARG_OR_RETURN (sc, struct es_object_box *, box, estream, args);
FFI_ARG_OR_RETURN (sc, size_t, bytes_to_read, number, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
buffer = xtrymalloc (bytes_to_read);
if (buffer == NULL)
FFI_RETURN_ERR (sc, ENOMEM);
err = es_read (box->stream, buffer, bytes_to_read, &bytes_read);
if (err)
FFI_RETURN_ERR (sc, err);
result = sc->vptr->mk_counted_string (sc, buffer, bytes_read);
xfree (buffer);
FFI_RETURN_POINTER (sc, result);
}
static pointer
do_es_feof (scheme *sc, pointer args)
{
FFI_PROLOG ();
struct es_object_box *box;
FFI_ARG_OR_RETURN (sc, struct es_object_box *, box, estream, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
FFI_RETURN_POINTER (sc, es_feof (box->stream) ? sc->T : sc->F);
}
static pointer
do_es_write (scheme *sc, pointer args)
{
FFI_PROLOG ();
struct es_object_box *box;
const char *buffer;
size_t bytes_to_write, bytes_written;
FFI_ARG_OR_RETURN (sc, struct es_object_box *, box, estream, args);
/* XXX how to get the length of the string buffer? scheme strings
may contain \0. */
FFI_ARG_OR_RETURN (sc, const char *, buffer, string, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
bytes_to_write = strlen (buffer);
while (bytes_to_write > 0)
{
err = es_write (box->stream, buffer, bytes_to_write, &bytes_written);
if (err)
break;
bytes_to_write -= bytes_written;
buffer += bytes_written;
}
FFI_RETURN (sc);
}
/* Process handling. */
static pointer
do_spawn_process (scheme *sc, pointer args)
{
FFI_PROLOG ();
pointer arguments;
char **argv;
size_t len;
unsigned int flags;
estream_t infp;
estream_t outfp;
estream_t errfp;
pid_t pid;
FFI_ARG_OR_RETURN (sc, pointer, arguments, list, args);
FFI_ARG_OR_RETURN (sc, unsigned int, flags, number, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
err = ffi_list2argv (sc, arguments, &argv, &len);
if (err == gpg_error (GPG_ERR_INV_VALUE))
return ffi_sprintf (sc, "%luth element of first argument is "
"neither string nor symbol",
(unsigned long) len);
if (err)
FFI_RETURN_ERR (sc, err);
if (verbose > 1)
{
char **p;
fprintf (stderr, "Executing:");
for (p = argv; *p; p++)
fprintf (stderr, " '%s'", *p);
fprintf (stderr, "\n");
}
err = gpgrt_spawn_process (argv[0], (const char **) &argv[1],
NULL,
NULL,
flags,
&infp, &outfp, &errfp, &pid);
xfree (argv);
#define IMC(A, B) \
_cons (sc, sc->vptr->mk_integer (sc, (unsigned long) (A)), (B), 1)
#define IMS(A, B) \
_cons (sc, es_wrap (sc, (A)), (B), 1)
FFI_RETURN_POINTER (sc, IMS (infp,
IMS (outfp,
IMS (errfp,
IMC (pid, sc->NIL)))));
#undef IMS
#undef IMC
}
static pointer
do_spawn_process_fd (scheme *sc, pointer args)
{
FFI_PROLOG ();
pointer arguments;
char **argv;
size_t len;
int infd, outfd, errfd;
pid_t pid;
FFI_ARG_OR_RETURN (sc, pointer, arguments, list, args);
FFI_ARG_OR_RETURN (sc, int, infd, number, args);
FFI_ARG_OR_RETURN (sc, int, outfd, number, args);
FFI_ARG_OR_RETURN (sc, int, errfd, number, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
err = ffi_list2argv (sc, arguments, &argv, &len);
if (err == gpg_error (GPG_ERR_INV_VALUE))
return ffi_sprintf (sc, "%luth element of first argument is "
"neither string nor symbol",
(unsigned long) len);
if (err)
FFI_RETURN_ERR (sc, err);
if (verbose > 1)
{
char **p;
fprintf (stderr, "Executing:");
for (p = argv; *p; p++)
fprintf (stderr, " '%s'", *p);
fprintf (stderr, "\n");
}
err = gpgrt_spawn_process_fd (argv[0], (const char **) &argv[1],
infd, outfd, errfd, &pid);
xfree (argv);
FFI_RETURN_INT (sc, pid);
}
static pointer
do_wait_process (scheme *sc, pointer args)
{
FFI_PROLOG ();
const char *name;
pid_t pid;
int hang;
int retcode;
FFI_ARG_OR_RETURN (sc, const char *, name, string, args);
FFI_ARG_OR_RETURN (sc, pid_t, pid, number, args);
FFI_ARG_OR_RETURN (sc, int, hang, bool, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
err = gpgrt_wait_process (name, pid, hang, &retcode);
if (err == GPG_ERR_GENERAL)
err = 0; /* Let the return code speak for itself. */
FFI_RETURN_INT (sc, retcode);
}
static pointer
do_wait_processes (scheme *sc, pointer args)
{
FFI_PROLOG ();
pointer list_names;
char **names;
pointer list_pids;
size_t i, count;
pid_t *pids;
int hang;
int *retcodes;
pointer retcodes_list = sc->NIL;
FFI_ARG_OR_RETURN (sc, pointer, list_names, list, args);
FFI_ARG_OR_RETURN (sc, pointer, list_pids, list, args);
FFI_ARG_OR_RETURN (sc, int, hang, bool, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
if (sc->vptr->list_length (sc, list_names)
!= sc->vptr->list_length (sc, list_pids))
return
sc->vptr->mk_string (sc, "length of first two arguments must match");
err = ffi_list2argv (sc, list_names, &names, &count);
if (err == gpg_error (GPG_ERR_INV_VALUE))
return ffi_sprintf (sc, "%lu%s element of first argument is "
"neither string nor symbol",
(unsigned long) count,
ordinal_suffix ((int) count));
if (err)
FFI_RETURN_ERR (sc, err);
err = ffi_list2intv (sc, list_pids, (int **) &pids, &count);
if (err == gpg_error (GPG_ERR_INV_VALUE))
return ffi_sprintf (sc, "%lu%s element of second argument is "
"not a number",
(unsigned long) count,
ordinal_suffix ((int) count));
if (err)
FFI_RETURN_ERR (sc, err);
retcodes = xtrycalloc (sizeof *retcodes, count);
if (retcodes == NULL)
{
xfree (names);
xfree (pids);
FFI_RETURN_ERR (sc, gpg_error_from_syserror ());
}
err = gpgrt_wait_processes ((const char **) names, pids, count, hang,
retcodes);
if (err == GPG_ERR_GENERAL)
err = 0; /* Let the return codes speak. */
if (err == GPG_ERR_TIMEOUT)
err = 0; /* We may have got some results. */
for (i = 0; i < count; i++)
retcodes_list =
(sc->vptr->cons) (sc,
sc->vptr->mk_integer (sc,
(long) retcodes[count-1-i]),
retcodes_list);
xfree (names);
xfree (pids);
xfree (retcodes);
FFI_RETURN_POINTER (sc, retcodes_list);
}
static pointer
do_pipe (scheme *sc, pointer args)
{
FFI_PROLOG ();
int filedes[2];
FFI_ARGS_DONE_OR_RETURN (sc, args);
err = gpgrt_create_pipe (filedes);
#define IMC(A, B) \
_cons (sc, sc->vptr->mk_integer (sc, (unsigned long) (A)), (B), 1)
FFI_RETURN_POINTER (sc, IMC (filedes[0],
IMC (filedes[1], sc->NIL)));
#undef IMC
}
static pointer
do_inbound_pipe (scheme *sc, pointer args)
{
FFI_PROLOG ();
int filedes[2];
FFI_ARGS_DONE_OR_RETURN (sc, args);
err = gpgrt_create_inbound_pipe (filedes, NULL, 0);
#define IMC(A, B) \
_cons (sc, sc->vptr->mk_integer (sc, (unsigned long) (A)), (B), 1)
FFI_RETURN_POINTER (sc, IMC (filedes[0],
IMC (filedes[1], sc->NIL)));
#undef IMC
}
static pointer
do_outbound_pipe (scheme *sc, pointer args)
{
FFI_PROLOG ();
int filedes[2];
FFI_ARGS_DONE_OR_RETURN (sc, args);
err = gpgrt_create_outbound_pipe (filedes, NULL, 0);
#define IMC(A, B) \
_cons (sc, sc->vptr->mk_integer (sc, (unsigned long) (A)), (B), 1)
FFI_RETURN_POINTER (sc, IMC (filedes[0],
IMC (filedes[1], sc->NIL)));
#undef IMC
}
/* Test helper functions. */
static pointer
do_file_equal (scheme *sc, pointer args)
{
FFI_PROLOG ();
pointer result = sc->F;
char *a_name, *b_name;
int binary;
const char *mode;
FILE *a_stream = NULL, *b_stream = NULL;
struct stat a_stat, b_stat;
#define BUFFER_SIZE 1024
char a_buf[BUFFER_SIZE], b_buf[BUFFER_SIZE];
#undef BUFFER_SIZE
size_t chunk;
FFI_ARG_OR_RETURN (sc, char *, a_name, string, args);
FFI_ARG_OR_RETURN (sc, char *, b_name, string, args);
FFI_ARG_OR_RETURN (sc, int, binary, bool, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
mode = binary ? "rb" : "r";
a_stream = fopen (a_name, mode);
if (a_stream == NULL)
goto errout;
b_stream = fopen (b_name, mode);
if (b_stream == NULL)
goto errout;
if (fstat (fileno (a_stream), &a_stat) < 0)
goto errout;
if (fstat (fileno (b_stream), &b_stat) < 0)
goto errout;
if (binary && a_stat.st_size != b_stat.st_size)
{
if (verbose)
fprintf (stderr, "Files %s and %s differ in size %lu != %lu\n",
a_name, b_name, (unsigned long) a_stat.st_size,
(unsigned long) b_stat.st_size);
goto out;
}
while (! feof (a_stream))
{
chunk = sizeof a_buf;
chunk = fread (a_buf, 1, chunk, a_stream);
if (chunk == 0 && ferror (a_stream))
goto errout; /* some error */
if (fread (b_buf, 1, chunk, b_stream) < chunk)
{
if (feof (b_stream))
goto out; /* short read */
goto errout; /* some error */
}
if (chunk > 0 && memcmp (a_buf, b_buf, chunk) != 0)
goto out;
}
fread (b_buf, 1, 1, b_stream);
if (! feof (b_stream))
goto out; /* b is longer */
/* They match. */
result = sc->T;
out:
if (a_stream)
fclose (a_stream);
if (b_stream)
fclose (b_stream);
FFI_RETURN_POINTER (sc, result);
errout:
err = gpg_error_from_syserror ();
goto out;
}
static pointer
do_splice (scheme *sc, pointer args)
{
FFI_PROLOG ();
int source;
char buffer[1024];
ssize_t bytes_read;
pointer sinks, sink;
FFI_ARG_OR_RETURN (sc, int, source, number, args);
sinks = args;
if (sinks == sc->NIL)
return ffi_sprintf (sc, "need at least one sink");
for (sink = sinks; sink != sc->NIL; sink = pair_cdr (sink), ffi_arg_index++)
if (! sc->vptr->is_number (pair_car (sink)))
return ffi_sprintf (sc, "%d%s argument is not a number",
ffi_arg_index, ordinal_suffix (ffi_arg_index));
while (1)
{
bytes_read = read (source, buffer, sizeof buffer);
if (bytes_read == 0)
break;
if (bytes_read < 0)
FFI_RETURN_ERR (sc, gpg_error_from_syserror ());
for (sink = sinks; sink != sc->NIL; sink = pair_cdr (sink))
{
int fd = sc->vptr->ivalue (pair_car (sink));
char *p = buffer;
ssize_t left = bytes_read;
while (left)
{
ssize_t written = write (fd, p, left);
if (written < 0)
FFI_RETURN_ERR (sc, gpg_error_from_syserror ());
assert (written <= left);
left -= written;
p += written;
}
}
}
FFI_RETURN (sc);
}
static pointer
do_string_index (scheme *sc, pointer args)
{
FFI_PROLOG ();
char *haystack;
char needle;
ssize_t offset = 0;
char *position;
FFI_ARG_OR_RETURN (sc, char *, haystack, string, args);
FFI_ARG_OR_RETURN (sc, char, needle, character, args);
if (args != sc->NIL)
{
FFI_ARG_OR_RETURN (sc, ssize_t, offset, number, args);
if (offset < 0)
return ffi_sprintf (sc, "offset must be positive");
if (offset > strlen (haystack))
return ffi_sprintf (sc, "offset exceeds haystack");
}
FFI_ARGS_DONE_OR_RETURN (sc, args);
position = strchr (haystack+offset, needle);
if (position)
FFI_RETURN_INT (sc, position - haystack);
else
FFI_RETURN_POINTER (sc, sc->F);
}
static pointer
do_string_rindex (scheme *sc, pointer args)
{
FFI_PROLOG ();
char *haystack;
char needle;
ssize_t offset = 0;
char *position;
FFI_ARG_OR_RETURN (sc, char *, haystack, string, args);
FFI_ARG_OR_RETURN (sc, char, needle, character, args);
if (args != sc->NIL)
{
FFI_ARG_OR_RETURN (sc, ssize_t, offset, number, args);
if (offset < 0)
return ffi_sprintf (sc, "offset must be positive");
if (offset > strlen (haystack))
return ffi_sprintf (sc, "offset exceeds haystack");
}
FFI_ARGS_DONE_OR_RETURN (sc, args);
position = strrchr (haystack+offset, needle);
if (position)
FFI_RETURN_INT (sc, position - haystack);
else
FFI_RETURN_POINTER (sc, sc->F);
}
static pointer
do_string_contains (scheme *sc, pointer args)
{
FFI_PROLOG ();
char *haystack;
char *needle;
FFI_ARG_OR_RETURN (sc, char *, haystack, string, args);
FFI_ARG_OR_RETURN (sc, char *, needle, string, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
FFI_RETURN_POINTER (sc, strstr (haystack, needle) ? sc->T : sc->F);
}
static pointer
do_get_verbose (scheme *sc, pointer args)
{
FFI_PROLOG ();
FFI_ARGS_DONE_OR_RETURN (sc, args);
FFI_RETURN_INT (sc, verbose);
}
static pointer
do_set_verbose (scheme *sc, pointer args)
{
FFI_PROLOG ();
int new_verbosity, old;
FFI_ARG_OR_RETURN (sc, int, new_verbosity, number, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
old = verbose;
verbose = new_verbosity;
FFI_RETURN_INT (sc, old);
}
gpg_error_t
ffi_list2argv (scheme *sc, pointer list, char ***argv, size_t *len)
{
int i;
*len = sc->vptr->list_length (sc, list);
*argv = xtrycalloc (*len + 1, sizeof **argv);
if (*argv == NULL)
return gpg_error_from_syserror ();
for (i = 0; sc->vptr->is_pair (list); list = sc->vptr->pair_cdr (list))
{
if (sc->vptr->is_string (sc->vptr->pair_car (list)))
(*argv)[i++] = sc->vptr->string_value (sc->vptr->pair_car (list));
else if (sc->vptr->is_symbol (sc->vptr->pair_car (list)))
(*argv)[i++] = sc->vptr->symname (sc->vptr->pair_car (list));
else
{
xfree (*argv);
*argv = NULL;
*len = i;
return gpg_error (GPG_ERR_INV_VALUE);
}
}
(*argv)[i] = NULL;
return 0;
}
gpg_error_t
ffi_list2intv (scheme *sc, pointer list, int **intv, size_t *len)
{
int i;
*len = sc->vptr->list_length (sc, list);
*intv = xtrycalloc (*len, sizeof **intv);
if (*intv == NULL)
return gpg_error_from_syserror ();
for (i = 0; sc->vptr->is_pair (list); list = sc->vptr->pair_cdr (list))
{
if (sc->vptr->is_number (sc->vptr->pair_car (list)))
(*intv)[i++] = sc->vptr->ivalue (sc->vptr->pair_car (list));
else
{
xfree (*intv);
*intv = NULL;
*len = i;
return gpg_error (GPG_ERR_INV_VALUE);
}
}
return 0;
}
char *
ffi_schemify_name (const char *s, int macro)
{
/* Fixme: We should use xtrystrdup and return NULL. However, this
* requires a lot more changes. Simply returning S as done
* originally is not an option. */
char *n = xstrdup (s), *p;
/* if (n == NULL) */
/* return s; */
for (p = n; *p; p++)
{
*p = (char) tolower (*p);
/* We convert _ to - in identifiers. We allow, however, for
function names to start with a leading _. The functions in
this namespace are not yet finalized and might change or
vanish without warning. Use them with care. */
if (! macro
&& p != n
&& *p == '_')
*p = '-';
}
return n;
}
pointer
ffi_sprintf (scheme *sc, const char *format, ...)
{
pointer result;
va_list listp;
char *expression;
int size, written;
va_start (listp, format);
size = vsnprintf (NULL, 0, format, listp);
va_end (listp);
expression = xtrymalloc (size + 1);
if (expression == NULL)
return NULL;
va_start (listp, format);
written = vsnprintf (expression, size + 1, format, listp);
va_end (listp);
assert (size == written);
result = sc->vptr->mk_string (sc, expression);
xfree (expression);
return result;
}
void
ffi_scheme_eval (scheme *sc, const char *format, ...)
{
va_list listp;
char *expression;
int size, written;
va_start (listp, format);
size = vsnprintf (NULL, 0, format, listp);
va_end (listp);
expression = xtrymalloc (size + 1);
if (expression == NULL)
return;
va_start (listp, format);
written = vsnprintf (expression, size + 1, format, listp);
va_end (listp);
assert (size == written);
sc->vptr->load_string (sc, expression);
xfree (expression);
}
gpg_error_t
ffi_init (scheme *sc, const char *argv0, const char *scriptname,
int argc, const char **argv)
{
int i;
pointer args = sc->NIL;
/* bitwise arithmetic */
ffi_define_function (sc, logand);
ffi_define_function (sc, logior);
ffi_define_function (sc, logxor);
ffi_define_function (sc, lognot);
/* libc. */
ffi_define_constant (sc, O_RDONLY);
ffi_define_constant (sc, O_WRONLY);
ffi_define_constant (sc, O_RDWR);
ffi_define_constant (sc, O_CREAT);
ffi_define_constant (sc, O_APPEND);
#ifndef O_BINARY
# define O_BINARY 0
#endif
#ifndef O_TEXT
# define O_TEXT 0
#endif
ffi_define_constant (sc, O_BINARY);
ffi_define_constant (sc, O_TEXT);
ffi_define_constant (sc, STDIN_FILENO);
ffi_define_constant (sc, STDOUT_FILENO);
ffi_define_constant (sc, STDERR_FILENO);
ffi_define_constant (sc, SEEK_SET);
ffi_define_constant (sc, SEEK_CUR);
ffi_define_constant (sc, SEEK_END);
ffi_define_function (sc, sleep);
ffi_define_function (sc, usleep);
ffi_define_function (sc, chdir);
ffi_define_function (sc, strerror);
ffi_define_function (sc, getenv);
ffi_define_function (sc, setenv);
ffi_define_function_name (sc, "_exit", exit);
ffi_define_function (sc, open);
ffi_define_function (sc, fdopen);
ffi_define_function (sc, close);
ffi_define_function (sc, seek);
ffi_define_function (sc, get_temp_path);
ffi_define_function_name (sc, "_mkdtemp", mkdtemp);
ffi_define_function (sc, unlink);
ffi_define_function (sc, unlink_recursively);
ffi_define_function (sc, rename);
ffi_define_function (sc, getcwd);
ffi_define_function (sc, mkdir);
ffi_define_function (sc, rmdir);
ffi_define_function (sc, get_isotime);
ffi_define_function (sc, get_time);
ffi_define_function (sc, getpid);
/* Random numbers. */
ffi_define_function (sc, srandom);
ffi_define_function (sc, random);
ffi_define_function (sc, make_random_string);
/* Process management. */
ffi_define_function (sc, spawn_process);
ffi_define_function (sc, spawn_process_fd);
ffi_define_function (sc, wait_process);
ffi_define_function (sc, wait_processes);
ffi_define_function (sc, pipe);
ffi_define_function (sc, inbound_pipe);
ffi_define_function (sc, outbound_pipe);
/* estream functions. */
ffi_define_function_name (sc, "es-fclose", es_fclose);
ffi_define_function_name (sc, "es-read", es_read);
ffi_define_function_name (sc, "es-feof", es_feof);
ffi_define_function_name (sc, "es-write", es_write);
/* Test helper functions. */
ffi_define_function (sc, file_equal);
ffi_define_function (sc, splice);
ffi_define_function (sc, string_index);
ffi_define_function (sc, string_rindex);
ffi_define_function_name (sc, "string-contains?", string_contains);
/* User interface. */
ffi_define_function (sc, flush_stdio);
ffi_define_function (sc, prompt);
/* Configuration. */
ffi_define_function_name (sc, "*verbose*", get_verbose);
ffi_define_function_name (sc, "*set-verbose!*", set_verbose);
ffi_define (sc, "*argv0*", sc->vptr->mk_string (sc, argv0));
ffi_define (sc, "*scriptname*", sc->vptr->mk_string (sc, scriptname));
for (i = argc - 1; i >= 0; i--)
{
pointer value = sc->vptr->mk_string (sc, argv[i]);
args = (sc->vptr->cons) (sc, value, args);
}
ffi_define (sc, "*args*", args);
#if _WIN32
ffi_define (sc, "*pathsep*", sc->vptr->mk_character (sc, ';'));
#else
ffi_define (sc, "*pathsep*", sc->vptr->mk_character (sc, ':'));
#endif
ffi_define (sc, "*win32*",
#if _WIN32
sc->T
#else
sc->F
#endif
);
ffi_define (sc, "*maintainer-mode*",
#if MAINTAINER_MODE
sc->T
#else
sc->F
#endif
);
ffi_define (sc, "*run-all-tests*",
#if RUN_ALL_TESTS
sc->T
#else
sc->F
#endif
);
ffi_define (sc, "*stdin*",
sc->vptr->mk_port_from_file (sc, stdin, port_input));
ffi_define (sc, "*stdout*",
sc->vptr->mk_port_from_file (sc, stdout, port_output));
ffi_define (sc, "*stderr*",
sc->vptr->mk_port_from_file (sc, stderr, port_output));
return 0;
}
diff --git a/gpgscm/main.c b/gpgscm/main.c
index 22c7c98..07d8e07 100644
--- a/gpgscm/main.c
+++ b/gpgscm/main.c
@@ -1,352 +1,351 @@
/* TinyScheme-based test driver.
* Copyright (C) 2016 g10 Code GmbH
*
* This file is part of Libgpg-error.
*
* Libgpg-error is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* Libgpg-error 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
#include
#if HAVE_MMAP
#include
#endif
#include "private.h"
#include "scheme.h"
#include "scheme-private.h"
#include "ffi.h"
/* The TinyScheme banner. Unfortunately, it isn't in the header
file. */
#define ts_banner "TinyScheme 1.41"
int verbose;
/* Constants to identify the commands and options. */
enum cmd_and_opt_values
{
aNull = 0,
oVerbose = 'v',
};
/* The list of commands and options. */
/* static ARGPARSE_OPTS opts[] = */
/* { */
/* ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")), */
/* ARGPARSE_end (), */
/* }; */
char *scmpath = "";
size_t scmpath_len = 0;
/* Command line parsing. */
/* static void */
/* parse_arguments (ARGPARSE_ARGS *pargs, ARGPARSE_OPTS *popts) */
/* { */
/* int no_more_options = 0; */
/* while (!no_more_options && optfile_parse (NULL, NULL, NULL, pargs, popts)) */
/* { */
/* switch (pargs->r_opt) */
/* { */
/* case oVerbose: */
/* verbose++; */
/* break; */
/* default: */
/* pargs->err = 2; */
/* break; */
/* } */
/* } */
/* } */
/* Print usage information and provide strings for help. */
static const char *
my_strusage( int level )
{
const char *p;
switch (level)
{
case 11: p = "gpgscm";
break;
case 13: p = VERSION; break;
case 19: p = "Please report bugs to <@EMAIL@>.\n"; break;
case 1:
case 40:
p = "Usage: gpgscm [options] [file] (-h for help)";
break;
case 41:
p = "Syntax: gpgscm [options] [file]\n"
"Execute the given Scheme program, or spawn interactive shell.\n";
break;
default: p = NULL; break;
}
return p;
}
static int
path_absolute_p (const char *p)
{
#if _WIN32
return ((strlen (p) > 2 && p[1] == ':' && (p[2] == '\\' || p[2] == '/'))
|| p[0] == '\\' || p[0] == '/');
#else
return p[0] == '/';
#endif
}
/* Load the Scheme program from FILE_NAME. If FILE_NAME is not an
absolute path, and LOOKUP_IN_PATH is given, then it is qualified
with the values in scmpath until the file is found. */
static gpg_error_t
load (scheme *sc, char *file_name,
int lookup_in_cwd, int lookup_in_path)
{
gpg_error_t err = 0;
size_t n;
const char *directory;
char *qualified_name = file_name;
int use_path;
FILE *h = NULL;
use_path =
lookup_in_path && ! (path_absolute_p (file_name) || scmpath_len == 0);
if (path_absolute_p (file_name) || lookup_in_cwd || scmpath_len == 0)
{
h = fopen (file_name, "r");
if (! h)
err = gpg_error_from_syserror ();
}
if (h == NULL && use_path)
for (directory = scmpath, n = scmpath_len; n;
directory += strlen (directory) + 1, n--)
{
if (asprintf (&qualified_name, "%s/%s", directory, file_name) < 0)
return gpg_error_from_syserror ();
h = fopen (qualified_name, "r");
if (h)
{
err = 0;
break;
}
if (n > 1)
{
free (qualified_name);
continue; /* Try again! */
}
err = gpg_error_from_syserror ();
}
if (h == NULL)
{
/* Failed and no more elements in scmpath to try. */
fprintf (stderr, "Could not read %s: %s.\n",
qualified_name, gpg_strerror (err));
if (lookup_in_path)
fprintf (stderr,
"Consider using GPGSCM_PATH to specify the location "
"of the Scheme library.\n");
goto leave;
}
if (verbose > 2)
fprintf (stderr, "Loading %s...\n", qualified_name);
#if HAVE_MMAP
/* Always try to mmap the file. This allows the pages to be shared
* between processes. If anything fails, we fall back to using
* buffered streams. */
if (1)
{
struct stat st;
void *map;
size_t len;
int fd = fileno (h);
if (fd < 0)
goto fallback;
if (fstat (fd, &st))
goto fallback;
len = (size_t) st.st_size;
if ((off_t) len != st.st_size)
goto fallback; /* Truncated. */
map = mmap (NULL, len, PROT_READ, MAP_SHARED, fd, 0);
if (map == MAP_FAILED)
goto fallback;
scheme_load_memory (sc, map, len, qualified_name);
munmap (map, len);
}
else
fallback:
#endif
scheme_load_named_file (sc, h, qualified_name);
fclose (h);
if (sc->retcode && sc->nesting)
{
fprintf (stderr, "%s: Unbalanced parenthesis\n", qualified_name);
err = gpg_error (GPG_ERR_GENERAL);
}
leave:
if (file_name != qualified_name)
free (qualified_name);
return err;
}
int
main (int argc, char **argv)
{
int retcode;
gpg_error_t err;
char *argv0;
/* ARGPARSE_ARGS pargs; */
scheme *sc;
char *p;
#if _WIN32
char pathsep = ';';
#else
char pathsep = ':';
#endif
char *script = NULL;
/* Save argv[0] so that we can re-exec. */
argv0 = argv[0];
if (!gpgrt_check_version (PACKAGE_VERSION))
{
fprintf (stderr, _("%s is too old (need %s, have %s)\n"), "libgpg-error",
PACKAGE_VERSION, gpgrt_check_version (NULL));
exit (2);
}
/* Parse path. */
if (getenv ("GPGSCM_PATH"))
scmpath = getenv ("GPGSCM_PATH");
p = scmpath = strdup (scmpath);
if (p == NULL)
return 2;
if (*p)
scmpath_len++;
for (; *p; p++)
if (*p == pathsep)
*p = 0, scmpath_len++;
/* set_strusage (my_strusage); */
gpgrt_log_set_prefix ("gpgscm", GPGRT_LOG_WITH_PREFIX);
/* Make sure that our subsystems are ready. */
/* i18n_init (); */
/* init_common_subsystems (&argc, &argv); */
/* Parse the command line. */
/* pargs.argc = &argc; */
/* pargs.argv = &argv; */
/* pargs.flags = 0; */
/* parse_arguments (&pargs, opts); */
if (gpgrt_get_errorcount (0))
exit (2);
sc = scheme_init_new_custom_alloc (gpgrt_malloc, gpgrt_free);
if (!sc)
{
fprintf (stderr, "Could not initialize TinyScheme!\n");
return 2;
}
scheme_set_input_port_file (sc, stdin);
scheme_set_output_port_file (sc, stderr);
if (argc)
{
script = argv[0];
argc--, argv++;
}
err = load (sc, "init.scm", 0, 1);
if (! err)
err = load (sc, "ffi.scm", 0, 1);
if (! err)
err = ffi_init (sc, argv0, script ? script : "interactive",
argc, (const char **) argv);
if (! err)
err = load (sc, "lib.scm", 0, 1);
if (! err)
err = load (sc, "repl.scm", 0, 1);
if (! err)
err = load (sc, "xml.scm", 0, 1);
if (! err)
err = load (sc, "tests.scm", 0, 1);
if (! err)
err = load (sc, "gnupg.scm", 0, 1);
if (err)
{
fprintf (stderr, "Error initializing gpgscm: %s.\n",
gpg_strerror (err));
exit (2);
}
if (script == NULL)
{
/* Interactive shell. */
fprintf (stderr, "gpgscm/"ts_banner".\n");
scheme_load_string (sc, "(interactive-repl)");
}
else
{
err = load (sc, script, 1, 1);
if (err)
gpgrt_log_fatal ("%s: %s", script, gpg_strerror (err));
}
retcode = sc->retcode;
scheme_load_string (sc, "(*run-atexit-handlers*)");
scheme_deinit (sc);
xfree (sc);
return retcode;
}
diff --git a/gpgscm/scheme.c b/gpgscm/scheme.c
index 906e563..1b489e4 100644
--- a/gpgscm/scheme.c
+++ b/gpgscm/scheme.c
@@ -1,6029 +1,6028 @@
/* T I N Y S C H E M E 1 . 4 1
* Dimitrios Souflis (dsouflis@acm.org)
* Based on MiniScheme (original credits follow)
* (MINISCM) coded by Atsushi Moriwaki (11/5/1989)
* (MINISCM) E-MAIL : moriwaki@kurims.kurims.kyoto-u.ac.jp
* (MINISCM) This version has been modified by R.C. Secrist.
* (MINISCM)
* (MINISCM) Mini-Scheme is now maintained by Akira KIDA.
* (MINISCM)
* (MINISCM) This is a revised and modified version by Akira KIDA.
* (MINISCM) current version is 0.85k4 (15 May 1994)
*
* This is a revised and modified version by g10 Code GmbH
* written by Justus Winter for use in GnuPG.
*/
#ifdef HAVE_CONFIG_H
# include
#endif
#define _SCHEME_SOURCE
#include "scheme-private.h"
#ifndef WIN32
# include
#endif
#ifdef WIN32
#define snprintf _snprintf
#endif
#if USE_DL
# include "dynload.h"
#endif
#if USE_MATH
# include
#endif
-#include
#include
#include
#include
#include
#if USE_STRCASECMP
#include
# ifndef __APPLE__
# define stricmp strcasecmp
# endif
#endif
/* Used for documentation purposes, to signal functions in 'interface' */
#define INTERFACE
#define TOK_EOF (-1)
#define TOK_LPAREN 0
#define TOK_RPAREN 1
#define TOK_DOT 2
#define TOK_ATOM 3
#define TOK_QUOTE 4
#define TOK_COMMENT 5
#define TOK_DQUOTE 6
#define TOK_BQUOTE 7
#define TOK_COMMA 8
#define TOK_ATMARK 9
#define TOK_SHARP 10
#define TOK_SHARP_CONST 11
#define TOK_VEC 12
#define BACKQUOTE '`'
#define DELIMITERS "()\";\f\t\v\n\r "
/*
* Basic memory allocation units
*/
#define banner "TinyScheme 1.41"
#include
#include
#include
#ifdef __APPLE__
static int stricmp(const char *s1, const char *s2)
{
unsigned char c1, c2;
do {
c1 = tolower(*s1);
c2 = tolower(*s2);
if (c1 < c2)
return -1;
else if (c1 > c2)
return 1;
s1++, s2++;
} while (c1 != 0);
return 0;
}
#endif /* __APPLE__ */
#if USE_STRLWR && !defined(HAVE_STRLWR)
static const char *strlwr(char *s) {
const char *p=s;
while(*s) {
*s=tolower(*s);
s++;
}
return p;
}
#endif
#ifndef prompt
# define prompt "ts> "
#endif
#ifndef InitFile
# define InitFile "init.scm"
#endif
#ifndef FIRST_CELLSEGS
# define FIRST_CELLSEGS 3
#endif
/* All types have the LSB set. The garbage collector takes advantage
* of that to identify types. */
enum scheme_types {
T_STRING = 1 << 1 | 1,
T_NUMBER = 2 << 1 | 1,
T_SYMBOL = 3 << 1 | 1,
T_PROC = 4 << 1 | 1,
T_PAIR = 5 << 1 | 1,
T_CLOSURE = 6 << 1 | 1,
T_CONTINUATION = 7 << 1 | 1,
T_FOREIGN = 8 << 1 | 1,
T_CHARACTER = 9 << 1 | 1,
T_PORT = 10 << 1 | 1,
T_VECTOR = 11 << 1 | 1,
T_MACRO = 12 << 1 | 1,
T_PROMISE = 13 << 1 | 1,
T_ENVIRONMENT = 14 << 1 | 1,
T_FOREIGN_OBJECT = 15 << 1 | 1,
T_BOOLEAN = 16 << 1 | 1,
T_NIL = 17 << 1 | 1,
T_EOF_OBJ = 18 << 1 | 1,
T_SINK = 19 << 1 | 1,
T_FRAME = 20 << 1 | 1,
T_LAST_SYSTEM_TYPE = 20 << 1 | 1
};
static const char *
type_to_string (enum scheme_types typ)
{
switch (typ)
{
case T_STRING: return "string";
case T_NUMBER: return "number";
case T_SYMBOL: return "symbol";
case T_PROC: return "proc";
case T_PAIR: return "pair";
case T_CLOSURE: return "closure";
case T_CONTINUATION: return "continuation";
case T_FOREIGN: return "foreign";
case T_CHARACTER: return "character";
case T_PORT: return "port";
case T_VECTOR: return "vector";
case T_MACRO: return "macro";
case T_PROMISE: return "promise";
case T_ENVIRONMENT: return "environment";
case T_FOREIGN_OBJECT: return "foreign object";
case T_BOOLEAN: return "boolean";
case T_NIL: return "nil";
case T_EOF_OBJ: return "eof object";
case T_SINK: return "sink";
case T_FRAME: return "frame";
}
assert (! "not reached");
}
/* ADJ is enough slack to align cells in a TYPE_BITS-bit boundary */
#define TYPE_BITS 6
#define ADJ (1 << TYPE_BITS)
#define T_MASKTYPE (ADJ - 1)
/* 0000000000111111 */
#define T_TAGGED 1024 /* 0000010000000000 */
#define T_FINALIZE 2048 /* 0000100000000000 */
#define T_SYNTAX 4096 /* 0001000000000000 */
#define T_IMMUTABLE 8192 /* 0010000000000000 */
#define T_ATOM 16384 /* 0100000000000000 */ /* only for gc */
#define CLRATOM 49151 /* 1011111111111111 */ /* only for gc */
#define MARK 32768 /* 1000000000000000 */
#define UNMARK 32767 /* 0111111111111111 */
static num num_add(num a, num b);
static num num_mul(num a, num b);
static num num_div(num a, num b);
static num num_intdiv(num a, num b);
static num num_sub(num a, num b);
static num num_rem(num a, num b);
static num num_mod(num a, num b);
static int num_eq(num a, num b);
static int num_gt(num a, num b);
static int num_ge(num a, num b);
static int num_lt(num a, num b);
static int num_le(num a, num b);
#if USE_MATH
static double round_per_R5RS(double x);
#endif
static int is_zero_double(double x);
static INLINE int num_is_integer(pointer p) {
return ((p)->_object._number.is_fixnum);
}
static const struct num num_zero = { 1, {0} };
static const struct num num_one = { 1, {1} };
/* macros for cell operations */
#define typeflag(p) ((p)->_flag)
#define type(p) (typeflag(p)&T_MASKTYPE)
#define settype(p, typ) (typeflag(p) = (typeflag(p) & ~T_MASKTYPE) | (typ))
INTERFACE INLINE int is_string(pointer p) { return (type(p)==T_STRING); }
#define strvalue(p) ((p)->_object._string._svalue)
#define strlength(p) ((p)->_object._string._length)
INTERFACE static int is_list(scheme *sc, pointer p);
INTERFACE INLINE int is_vector(pointer p) { return (type(p)==T_VECTOR); }
/* Given a vector, return it's length. */
#define vector_length(v) (v)->_object._vector._length
/* Given a vector length, compute the amount of cells required to
* represent it. */
#define vector_size(len) (1 + ((len) - 1 + 2) / 3)
INTERFACE static void fill_vector(pointer vec, pointer obj);
INTERFACE static pointer *vector_elem_slot(pointer vec, int ielem);
INTERFACE static pointer vector_elem(pointer vec, int ielem);
INTERFACE static pointer set_vector_elem(pointer vec, int ielem, pointer a);
INTERFACE INLINE int is_number(pointer p) { return (type(p)==T_NUMBER); }
INTERFACE INLINE int is_integer(pointer p) {
if (!is_number(p))
return 0;
if (num_is_integer(p) || (double)ivalue(p) == rvalue(p))
return 1;
return 0;
}
INTERFACE INLINE int is_real(pointer p) {
return is_number(p) && (!(p)->_object._number.is_fixnum);
}
INTERFACE INLINE int is_character(pointer p) { return (type(p)==T_CHARACTER); }
INTERFACE INLINE char *string_value(pointer p) { return strvalue(p); }
INLINE num nvalue(pointer p) { return ((p)->_object._number); }
INTERFACE long ivalue(pointer p) { return (num_is_integer(p)?(p)->_object._number.value.ivalue:(long)(p)->_object._number.value.rvalue); }
INTERFACE double rvalue(pointer p) { return (!num_is_integer(p)?(p)->_object._number.value.rvalue:(double)(p)->_object._number.value.ivalue); }
#define ivalue_unchecked(p) ((p)->_object._number.value.ivalue)
#define rvalue_unchecked(p) ((p)->_object._number.value.rvalue)
#define set_num_integer(p) (p)->_object._number.is_fixnum=1;
#define set_num_real(p) (p)->_object._number.is_fixnum=0;
INTERFACE long charvalue(pointer p) { return ivalue_unchecked(p); }
INTERFACE INLINE int is_port(pointer p) { return (type(p)==T_PORT); }
INTERFACE INLINE int is_inport(pointer p) { return is_port(p) && p->_object._port->kind & port_input; }
INTERFACE INLINE int is_outport(pointer p) { return is_port(p) && p->_object._port->kind & port_output; }
INTERFACE INLINE int is_pair(pointer p) { return (type(p)==T_PAIR); }
#define car(p) ((p)->_object._cons._car)
#define cdr(p) ((p)->_object._cons._cdr)
INTERFACE pointer pair_car(pointer p) { return car(p); }
INTERFACE pointer pair_cdr(pointer p) { return cdr(p); }
INTERFACE pointer set_car(pointer p, pointer q) { return car(p)=q; }
INTERFACE pointer set_cdr(pointer p, pointer q) { return cdr(p)=q; }
INTERFACE INLINE int is_symbol(pointer p) { return (type(p)==T_SYMBOL); }
INTERFACE INLINE char *symname(pointer p) { return strvalue(car(p)); }
#if USE_PLIST
SCHEME_EXPORT INLINE int hasprop(pointer p) { return (is_symbol(p)); }
#define symprop(p) cdr(p)
#endif
INTERFACE INLINE int is_syntax(pointer p) { return (typeflag(p)&T_SYNTAX); }
INTERFACE INLINE int is_proc(pointer p) { return (type(p)==T_PROC); }
INTERFACE INLINE int is_foreign(pointer p) { return (type(p)==T_FOREIGN); }
INTERFACE INLINE char *syntaxname(pointer p) { return strvalue(car(p)); }
#define procnum(p) ivalue_unchecked(p)
static const char *procname(pointer x);
INTERFACE INLINE int is_closure(pointer p) { return (type(p)==T_CLOSURE); }
INTERFACE INLINE int is_macro(pointer p) { return (type(p)==T_MACRO); }
INTERFACE INLINE pointer closure_code(pointer p) { return car(p); }
INTERFACE INLINE pointer closure_env(pointer p) { return cdr(p); }
INTERFACE INLINE int is_continuation(pointer p) { return (type(p)==T_CONTINUATION); }
#define cont_dump(p) cdr(p)
INTERFACE INLINE int is_foreign_object(pointer p) { return (type(p)==T_FOREIGN_OBJECT); }
INTERFACE const foreign_object_vtable *get_foreign_object_vtable(pointer p) {
return p->_object._foreign_object._vtable;
}
INTERFACE void *get_foreign_object_data(pointer p) {
return p->_object._foreign_object._data;
}
/* To do: promise should be forced ONCE only */
INTERFACE INLINE int is_promise(pointer p) { return (type(p)==T_PROMISE); }
INTERFACE INLINE int is_environment(pointer p) { return (type(p)==T_ENVIRONMENT); }
#define setenvironment(p) typeflag(p) = T_ENVIRONMENT
INTERFACE INLINE int is_frame(pointer p) { return (type(p) == T_FRAME); }
#define setframe(p) settype(p, T_FRAME)
#define is_atom(p) (typeflag(p)&T_ATOM)
#define setatom(p) typeflag(p) |= T_ATOM
#define clratom(p) typeflag(p) &= CLRATOM
#define is_mark(p) (typeflag(p)&MARK)
#define setmark(p) typeflag(p) |= MARK
#define clrmark(p) typeflag(p) &= UNMARK
INTERFACE INLINE int is_immutable(pointer p) { return (typeflag(p)&T_IMMUTABLE); }
/*#define setimmutable(p) typeflag(p) |= T_IMMUTABLE*/
INTERFACE INLINE void setimmutable(pointer p) { typeflag(p) |= T_IMMUTABLE; }
#define caar(p) car(car(p))
#define cadr(p) car(cdr(p))
#define cdar(p) cdr(car(p))
#define cddr(p) cdr(cdr(p))
#define cadar(p) car(cdr(car(p)))
#define caddr(p) car(cdr(cdr(p)))
#define cdaar(p) cdr(car(car(p)))
#define cadaar(p) car(cdr(car(car(p))))
#define cadddr(p) car(cdr(cdr(cdr(p))))
#define cddddr(p) cdr(cdr(cdr(cdr(p))))
#if USE_HISTORY
static pointer history_flatten(scheme *sc);
static void history_mark(scheme *sc);
#else
# define history_mark(SC) (void) 0
# define history_flatten(SC) (SC)->NIL
#endif
#if USE_CHAR_CLASSIFIERS
static INLINE int Cisalpha(int c) { return isascii(c) && isalpha(c); }
static INLINE int Cisdigit(int c) { return isascii(c) && isdigit(c); }
static INLINE int Cisspace(int c) { return isascii(c) && isspace(c); }
static INLINE int Cisupper(int c) { return isascii(c) && isupper(c); }
static INLINE int Cislower(int c) { return isascii(c) && islower(c); }
#endif
#if USE_ASCII_NAMES
static const char charnames[32][3]={
"nul",
"soh",
"stx",
"etx",
"eot",
"enq",
"ack",
"bel",
"bs",
"ht",
"lf",
"vt",
"ff",
"cr",
"so",
"si",
"dle",
"dc1",
"dc2",
"dc3",
"dc4",
"nak",
"syn",
"etb",
"can",
"em",
"sub",
"esc",
"fs",
"gs",
"rs",
"us"
};
static int is_ascii_name(const char *name, int *pc) {
int i;
for(i=0; i<32; i++) {
if (strncasecmp(name, charnames[i], 3) == 0) {
*pc=i;
return 1;
}
}
if (strcasecmp(name, "del") == 0) {
*pc=127;
return 1;
}
return 0;
}
#endif
static int file_push(scheme *sc, pointer fname);
static void file_pop(scheme *sc);
static int file_interactive(scheme *sc);
static INLINE int is_one_of(char *s, int c);
static int alloc_cellseg(scheme *sc, int n);
static long binary_decode(const char *s);
static INLINE pointer get_cell(scheme *sc, pointer a, pointer b);
static pointer _get_cell(scheme *sc, pointer a, pointer b);
static pointer reserve_cells(scheme *sc, int n);
static pointer get_consecutive_cells(scheme *sc, int n);
static pointer find_consecutive_cells(scheme *sc, int n);
static int finalize_cell(scheme *sc, pointer a);
static int count_consecutive_cells(pointer x, int needed);
static pointer find_slot_in_env(scheme *sc, pointer env, pointer sym, int all);
static pointer mk_number(scheme *sc, num n);
static char *store_string(scheme *sc, int len, const char *str, char fill);
static pointer mk_vector(scheme *sc, int len);
static pointer mk_atom(scheme *sc, char *q);
static pointer mk_sharp_const(scheme *sc, char *name);
static pointer mk_port(scheme *sc, port *p);
static pointer port_from_filename(scheme *sc, const char *fn, int prop);
static pointer port_from_file(scheme *sc, FILE *, int prop);
static pointer port_from_string(scheme *sc, char *start, char *past_the_end, int prop);
static port *port_rep_from_filename(scheme *sc, const char *fn, int prop);
static port *port_rep_from_file(scheme *sc, FILE *, int prop);
static port *port_rep_from_string(scheme *sc, char *start, char *past_the_end, int prop);
static void port_close(scheme *sc, pointer p, int flag);
static void mark(pointer a);
static void gc(scheme *sc, pointer a, pointer b);
static int basic_inchar(port *pt);
static int inchar(scheme *sc);
static void backchar(scheme *sc, int c);
static char *readstr_upto(scheme *sc, char *delim);
static pointer readstrexp(scheme *sc);
static INLINE int skipspace(scheme *sc);
static int token(scheme *sc);
static void printslashstring(scheme *sc, char *s, int len);
static void atom2str(scheme *sc, pointer l, int f, char **pp, int *plen);
static void printatom(scheme *sc, pointer l, int f);
static pointer mk_proc(scheme *sc, enum scheme_opcodes op);
static pointer mk_closure(scheme *sc, pointer c, pointer e);
static pointer mk_continuation(scheme *sc, pointer d);
static pointer reverse(scheme *sc, pointer term, pointer list);
static pointer reverse_in_place(scheme *sc, pointer term, pointer list);
static pointer revappend(scheme *sc, pointer a, pointer b);
static void dump_stack_preallocate_frame(scheme *sc);
static void dump_stack_mark(scheme *);
struct op_code_info {
char name[31]; /* strlen ("call-with-current-continuation") + 1 */
unsigned char min_arity;
unsigned char max_arity;
char arg_tests_encoding[3];
};
static const struct op_code_info dispatch_table[];
static int check_arguments (scheme *sc, const struct op_code_info *pcd, char *msg, size_t msg_size);
static void Eval_Cycle(scheme *sc, enum scheme_opcodes op);
static void assign_syntax(scheme *sc, enum scheme_opcodes op, char *name);
static int syntaxnum(scheme *sc, pointer p);
static void assign_proc(scheme *sc, enum scheme_opcodes, const char *name);
#define num_ivalue(n) (n.is_fixnum?(n).value.ivalue:(long)(n).value.rvalue)
#define num_rvalue(n) (!n.is_fixnum?(n).value.rvalue:(double)(n).value.ivalue)
static num num_add(num a, num b) {
num ret;
ret.is_fixnum=a.is_fixnum && b.is_fixnum;
if(ret.is_fixnum) {
ret.value.ivalue= a.value.ivalue+b.value.ivalue;
} else {
ret.value.rvalue=num_rvalue(a)+num_rvalue(b);
}
return ret;
}
static num num_mul(num a, num b) {
num ret;
ret.is_fixnum=a.is_fixnum && b.is_fixnum;
if(ret.is_fixnum) {
ret.value.ivalue= a.value.ivalue*b.value.ivalue;
} else {
ret.value.rvalue=num_rvalue(a)*num_rvalue(b);
}
return ret;
}
static num num_div(num a, num b) {
num ret;
ret.is_fixnum=a.is_fixnum && b.is_fixnum && a.value.ivalue%b.value.ivalue==0;
if(ret.is_fixnum) {
ret.value.ivalue= a.value.ivalue/b.value.ivalue;
} else {
ret.value.rvalue=num_rvalue(a)/num_rvalue(b);
}
return ret;
}
static num num_intdiv(num a, num b) {
num ret;
ret.is_fixnum=a.is_fixnum && b.is_fixnum;
if(ret.is_fixnum) {
ret.value.ivalue= a.value.ivalue/b.value.ivalue;
} else {
ret.value.rvalue=num_rvalue(a)/num_rvalue(b);
}
return ret;
}
static num num_sub(num a, num b) {
num ret;
ret.is_fixnum=a.is_fixnum && b.is_fixnum;
if(ret.is_fixnum) {
ret.value.ivalue= a.value.ivalue-b.value.ivalue;
} else {
ret.value.rvalue=num_rvalue(a)-num_rvalue(b);
}
return ret;
}
static num num_rem(num a, num b) {
num ret;
long e1, e2, res;
ret.is_fixnum=a.is_fixnum && b.is_fixnum;
e1=num_ivalue(a);
e2=num_ivalue(b);
res=e1%e2;
/* remainder should have same sign as second operand */
if (res > 0) {
if (e1 < 0) {
res -= labs(e2);
}
} else if (res < 0) {
if (e1 > 0) {
res += labs(e2);
}
}
ret.value.ivalue=res;
return ret;
}
static num num_mod(num a, num b) {
num ret;
long e1, e2, res;
ret.is_fixnum=a.is_fixnum && b.is_fixnum;
e1=num_ivalue(a);
e2=num_ivalue(b);
res=e1%e2;
/* modulo should have same sign as second operand */
if (res * e2 < 0) {
res += e2;
}
ret.value.ivalue=res;
return ret;
}
static int num_eq(num a, num b) {
int ret;
int is_fixnum=a.is_fixnum && b.is_fixnum;
if(is_fixnum) {
ret= a.value.ivalue==b.value.ivalue;
} else {
ret=num_rvalue(a)==num_rvalue(b);
}
return ret;
}
static int num_gt(num a, num b) {
int ret;
int is_fixnum=a.is_fixnum && b.is_fixnum;
if(is_fixnum) {
ret= a.value.ivalue>b.value.ivalue;
} else {
ret=num_rvalue(a)>num_rvalue(b);
}
return ret;
}
static int num_ge(num a, num b) {
return !num_lt(a,b);
}
static int num_lt(num a, num b) {
int ret;
int is_fixnum=a.is_fixnum && b.is_fixnum;
if(is_fixnum) {
ret= a.value.ivaluedce) {
return ce;
} else if(dfl-DBL_MIN;
}
static long binary_decode(const char *s) {
long x=0;
while(*s!=0 && (*s=='1' || *s=='0')) {
x<<=1;
x+=*s-'0';
s++;
}
return x;
}
/*
* Copying values.
*
* Occasionally, we need to copy a value from one location in the
* storage to another. Scheme objects are fine. Some primitive
* objects, however, require finalization, usually to free resources.
*
* For these values, we either make a copy or acquire a reference.
*/
/*
* Copy SRC to DST.
*
* Copies the representation of SRC to DST. This makes SRC
* indistinguishable from DST from the perspective of a Scheme
* expression modulo the fact that they reside at a different location
* in the store.
*
* Conditions:
*
* - SRC must not be a vector.
* - Caller must ensure that any resources associated with the
* value currently stored in DST is accounted for.
*/
static void
copy_value(scheme *sc, pointer dst, pointer src)
{
memcpy(dst, src, sizeof *src);
/* We may need to make a copy or acquire a reference. */
if (typeflag(dst) & T_FINALIZE)
switch (type(dst)) {
case T_STRING:
strvalue(dst) = store_string(sc, strlength(dst), strvalue(dst), 0);
break;
case T_PORT:
/* XXX acquire reference */
assert (!"implemented");
break;
case T_FOREIGN_OBJECT:
/* XXX acquire reference */
assert (!"implemented");
break;
case T_VECTOR:
assert (!"vectors cannot be copied");
}
}
/* Tags are like property lists, but can be attached to arbitrary
* values. */
static pointer
mk_tagged_value(scheme *sc, pointer v, pointer tag_car, pointer tag_cdr)
{
pointer r, t;
assert(! is_vector(v));
r = get_consecutive_cells(sc, 2);
if (r == sc->sink)
return sc->sink;
copy_value(sc, r, v);
typeflag(r) |= T_TAGGED;
t = r + 1;
typeflag(t) = T_PAIR;
car(t) = tag_car;
cdr(t) = tag_cdr;
return r;
}
static INLINE int
has_tag(pointer v)
{
return !! (typeflag(v) & T_TAGGED);
}
static INLINE pointer
get_tag(scheme *sc, pointer v)
{
if (has_tag(v))
return v + 1;
return sc->NIL;
}
/* Low-level allocator.
*
* Memory is allocated in segments. Every segment holds a fixed
* number of cells. Segments are linked into a list, sorted in
* reverse address order (i.e. those with a higher address first).
* This is used in the garbage collector to build the freelist in
* address order.
*/
struct cell_segment
{
struct cell_segment *next;
void *alloc;
pointer cells;
size_t cells_len;
};
/* Allocate a new cell segment but do not make it available yet. */
static int
_alloc_cellseg(scheme *sc, size_t len, struct cell_segment **segment)
{
int adj = ADJ;
void *cp;
if (adj < sizeof(struct cell))
adj = sizeof(struct cell);
/* The segment header is conveniently allocated with the cells. */
cp = sc->malloc(sizeof **segment + len * sizeof(struct cell) + adj);
if (cp == NULL)
return 1;
*segment = cp;
(*segment)->next = NULL;
(*segment)->alloc = cp;
cp = (void *) ((uintptr_t) cp + sizeof **segment);
/* adjust in TYPE_BITS-bit boundary */
if (((uintptr_t) cp) % adj != 0)
cp = (void *) (adj * ((uintptr_t) cp / adj + 1));
(*segment)->cells = cp;
(*segment)->cells_len = len;
return 0;
}
/* Deallocate a cell segment. Returns the next cell segment.
* Convenient for deallocation in a loop. */
static struct cell_segment *
_dealloc_cellseg(scheme *sc, struct cell_segment *segment)
{
struct cell_segment *next;
if (segment == NULL)
return NULL;
next = segment->next;
sc->free(segment->alloc);
return next;
}
/* allocate new cell segment */
static int alloc_cellseg(scheme *sc, int n) {
pointer last;
pointer p;
int k;
for (k = 0; k < n; k++) {
struct cell_segment *new, **s;
if (_alloc_cellseg(sc, CELL_SEGSIZE, &new)) {
return k;
}
/* insert new segment in reverse address order */
for (s = &sc->cell_segments;
*s && (uintptr_t) (*s)->alloc > (uintptr_t) new->alloc;
s = &(*s)->next) {
/* walk */
}
new->next = *s;
*s = new;
sc->fcells += new->cells_len;
last = new->cells + new->cells_len - 1;
for (p = new->cells; p <= last; p++) {
typeflag(p) = 0;
cdr(p) = p + 1;
car(p) = sc->NIL;
}
/* insert new cells in address order on free list */
if (sc->free_cell == sc->NIL || p < sc->free_cell) {
cdr(last) = sc->free_cell;
sc->free_cell = new->cells;
} else {
p = sc->free_cell;
while (cdr(p) != sc->NIL && (uintptr_t) new->cells > (uintptr_t) cdr(p))
p = cdr(p);
cdr(last) = cdr(p);
cdr(p) = new->cells;
}
}
return n;
}
/* Controlling the garbage collector.
*
* Every time a cell is allocated, the interpreter may run out of free
* cells and do a garbage collection. This is problematic because it
* might garbage collect objects that have been allocated, but are not
* yet made available to the interpreter.
*
* Previously, we would plug such newly allocated cells into the list
* of newly allocated objects rooted at car(sc->sink), but that
* requires allocating yet another cell increasing pressure on the
* memory management system.
*
* A faster alternative is to preallocate the cells needed for an
* operation and make sure the garbage collection is not run until all
* allocated objects are plugged in. This can be done with gc_disable
* and gc_enable.
*/
/* The garbage collector is enabled if the inhibit counter is
* zero. */
#define GC_ENABLED 0
/* For now we provide a way to disable this optimization for
* benchmarking and because it produces slightly smaller code. */
#ifndef USE_GC_LOCKING
# define USE_GC_LOCKING 1
#endif
/* To facilitate nested calls to gc_disable, functions that allocate
* more than one cell may define a macro, e.g. foo_allocates. This
* macro can be used to compute the amount of preallocation at the
* call site with the help of this macro. */
#define gc_reservations(fn) fn ## _allocates
#if USE_GC_LOCKING
/* Report a shortage in reserved cells, and terminate the program. */
static void
gc_reservation_failure(struct scheme *sc)
{
#ifdef NDEBUG
fprintf(stderr,
"insufficient reservation\n")
#else
fprintf(stderr,
"insufficient %s reservation in line %d\n",
sc->frame_freelist == sc->NIL ? "frame" : "cell",
sc->reserved_lineno);
#endif
- abort();
+ _gpgrt_abort();
}
/* Disable the garbage collection and reserve the given number of
* cells. gc_disable may be nested, but the enclosing reservation
* must include the reservations of all nested calls. Note: You must
* re-enable the gc before calling Error_X. */
static void
_gc_disable(struct scheme *sc, size_t reserve, int lineno)
{
if (sc->inhibit_gc == 0) {
reserve_cells(sc, (reserve));
sc->reserved_cells = (reserve);
#ifdef NDEBUG
(void) lineno;
#else
sc->reserved_lineno = lineno;
#endif
} else if (sc->reserved_cells < (reserve))
gc_reservation_failure (sc);
sc->inhibit_gc += 1;
}
#define gc_disable(sc, reserve) \
do { \
if (sc->frame_freelist == sc->NIL) { \
if (gc_enabled(sc)) \
dump_stack_preallocate_frame(sc); \
else \
gc_reservation_failure(sc); \
} \
_gc_disable (sc, reserve, __LINE__); \
} while (0)
/* Enable the garbage collector. */
#define gc_enable(sc) \
do { \
assert(sc->inhibit_gc); \
sc->inhibit_gc -= 1; \
} while (0)
/* Test whether the garbage collector is enabled. */
#define gc_enabled(sc) \
(sc->inhibit_gc == GC_ENABLED)
/* Consume a reserved cell. */
#define gc_consume(sc) \
do { \
assert(! gc_enabled (sc)); \
if (sc->reserved_cells == 0) \
gc_reservation_failure (sc); \
sc->reserved_cells -= 1; \
} while (0)
#else /* USE_GC_LOCKING */
#define gc_reservation_failure(sc) (void) 0
#define gc_disable(sc, reserve) \
do { \
if (sc->frame_freelist == sc->NIL) \
dump_stack_preallocate_frame(sc); \
} while (0)
#define gc_enable(sc) (void) 0
#define gc_enabled(sc) 1
#define gc_consume(sc) (void) 0
#endif /* USE_GC_LOCKING */
static INLINE pointer get_cell_x(scheme *sc, pointer a, pointer b) {
if (! gc_enabled (sc) || sc->free_cell != sc->NIL) {
pointer x = sc->free_cell;
if (! gc_enabled (sc))
gc_consume (sc);
sc->free_cell = cdr(x);
--sc->fcells;
return (x);
}
assert (gc_enabled (sc));
return _get_cell (sc, a, b);
}
/* get new cell. parameter a, b is marked by gc. */
static pointer _get_cell(scheme *sc, pointer a, pointer b) {
pointer x;
if(sc->no_memory) {
return sc->sink;
}
assert (gc_enabled (sc));
if (sc->free_cell == sc->NIL) {
gc(sc,a, b);
if (sc->free_cell == sc->NIL) {
sc->no_memory=1;
return sc->sink;
}
}
x = sc->free_cell;
sc->free_cell = cdr(x);
--sc->fcells;
return (x);
}
/* make sure that there is a given number of cells free */
static pointer reserve_cells(scheme *sc, int n) {
if(sc->no_memory) {
return sc->NIL;
}
/* Are there enough cells available? */
if (sc->fcells < n) {
/* If not, try gc'ing some */
gc(sc, sc->NIL, sc->NIL);
if (sc->fcells < n) {
/* If there still aren't, try getting more heap */
if (!alloc_cellseg(sc,1)) {
sc->no_memory=1;
return sc->NIL;
}
}
if (sc->fcells < n) {
/* If all fail, report failure */
sc->no_memory=1;
return sc->NIL;
}
}
return (sc->T);
}
static pointer get_consecutive_cells(scheme *sc, int n) {
pointer x;
if(sc->no_memory) { return sc->sink; }
/* Are there any cells available? */
x=find_consecutive_cells(sc,n);
if (x != sc->NIL) { return x; }
/* If not, try gc'ing some */
gc(sc, sc->NIL, sc->NIL);
x=find_consecutive_cells(sc,n);
if (x != sc->NIL) { return x; }
/* If there still aren't, try getting more heap */
if (!alloc_cellseg(sc,1))
{
sc->no_memory=1;
return sc->sink;
}
x=find_consecutive_cells(sc,n);
if (x != sc->NIL) { return x; }
/* If all fail, report failure */
sc->no_memory=1;
return sc->sink;
}
static int count_consecutive_cells(pointer x, int needed) {
int n=1;
while(cdr(x)==x+1) {
x=cdr(x);
n++;
if(n>needed) return n;
}
return n;
}
static pointer find_consecutive_cells(scheme *sc, int n) {
pointer *pp;
int cnt;
pp=&sc->free_cell;
while(*pp!=sc->NIL) {
cnt=count_consecutive_cells(*pp,n);
if(cnt>=n) {
pointer x=*pp;
*pp=cdr(*pp+n-1);
sc->fcells -= n;
return x;
}
pp=&cdr(*pp+cnt-1);
}
return sc->NIL;
}
/* Free a cell. This is dangerous. Only free cells that are not
* referenced. */
static INLINE void
free_cell(scheme *sc, pointer a)
{
cdr(a) = sc->free_cell;
sc->free_cell = a;
sc->fcells += 1;
}
/* Free a cell and retrieve its content. This is dangerous. Only
* free cells that are not referenced. */
static INLINE void
free_cons(scheme *sc, pointer a, pointer *r_car, pointer *r_cdr)
{
*r_car = car(a);
*r_cdr = cdr(a);
free_cell(sc, a);
}
/* To retain recent allocs before interpreter knows about them -
Tehom */
static void push_recent_alloc(scheme *sc, pointer recent, pointer extra)
{
pointer holder = get_cell_x(sc, recent, extra);
typeflag(holder) = T_PAIR | T_IMMUTABLE;
car(holder) = recent;
cdr(holder) = car(sc->sink);
car(sc->sink) = holder;
}
static INLINE void ok_to_freely_gc(scheme *sc)
{
pointer a = car(sc->sink), next;
car(sc->sink) = sc->NIL;
while (a != sc->NIL)
{
next = cdr(a);
free_cell(sc, a);
a = next;
}
}
static pointer get_cell(scheme *sc, pointer a, pointer b)
{
pointer cell = get_cell_x(sc, a, b);
/* For right now, include "a" and "b" in "cell" so that gc doesn't
think they are garbage. */
/* Tentatively record it as a pair so gc understands it. */
typeflag(cell) = T_PAIR;
car(cell) = a;
cdr(cell) = b;
if (gc_enabled (sc))
push_recent_alloc(sc, cell, sc->NIL);
return cell;
}
static pointer get_vector_object(scheme *sc, int len, pointer init)
{
pointer cells = get_consecutive_cells(sc, vector_size(len));
int i;
int alloc_len = 1 + 3 * (vector_size(len) - 1);
if(sc->no_memory) { return sc->sink; }
/* Record it as a vector so that gc understands it. */
typeflag(cells) = (T_VECTOR | T_ATOM | T_FINALIZE);
vector_length(cells) = len;
fill_vector(cells,init);
/* Initialize the unused slots at the end. */
assert (alloc_len - len < 3);
for (i = len; i < alloc_len; i++)
cells->_object._vector._elements[i] = sc->NIL;
if (gc_enabled (sc))
push_recent_alloc(sc, cells, sc->NIL);
return cells;
}
/* Medium level cell allocation */
/* get new cons cell */
pointer _cons(scheme *sc, pointer a, pointer b, int immutable) {
pointer x = get_cell(sc,a, b);
typeflag(x) = T_PAIR;
if(immutable) {
setimmutable(x);
}
car(x) = a;
cdr(x) = b;
return (x);
}
/* ========== oblist implementation ========== */
#ifndef USE_OBJECT_LIST
static int hash_fn(const char *key, int table_size);
static pointer oblist_initial_value(scheme *sc)
{
/* There are about 768 symbols used after loading the
* interpreter. */
return mk_vector(sc, 1009);
}
/* Lookup the symbol NAME. Returns the symbol, or NIL if it does not
* exist. In that case, SLOT points to the point where the new symbol
* is to be inserted. */
static INLINE pointer
oblist_find_by_name(scheme *sc, const char *name, pointer **slot)
{
int location;
pointer x;
char *s;
int d;
location = hash_fn(name, vector_length(sc->oblist));
for (*slot = vector_elem_slot(sc->oblist, location), x = **slot;
x != sc->NIL; *slot = &cdr(x), x = **slot) {
s = symname(car(x));
/* case-insensitive, per R5RS section 2. */
d = stricmp(name, s);
if (d == 0)
return car(x); /* Hit. */
else if (d > 0)
break; /* Miss. */
}
return sc->NIL;
}
static pointer oblist_all_symbols(scheme *sc)
{
int i;
pointer x;
pointer ob_list = sc->NIL;
for (i = 0; i < vector_length(sc->oblist); i++) {
for (x = vector_elem(sc->oblist, i); x != sc->NIL; x = cdr(x)) {
ob_list = cons(sc, x, ob_list);
}
}
return ob_list;
}
#else
static pointer oblist_initial_value(scheme *sc)
{
return sc->NIL;
}
/* Lookup the symbol NAME. Returns the symbol, or NIL if it does not
* exist. In that case, SLOT points to the point where the new symbol
* is to be inserted. */
static INLINE pointer
oblist_find_by_name(scheme *sc, const char *name, pointer **slot)
{
pointer x;
char *s;
int d;
for (*slot = &sc->oblist, x = **slot; x != sc->NIL; *slot = &cdr(x), x = **slot) {
s = symname(car(x));
/* case-insensitive, per R5RS section 2. */
d = stricmp(name, s);
if (d == 0)
return car(x); /* Hit. */
else if (d > 0)
break; /* Miss. */
}
return sc->NIL;
}
static pointer oblist_all_symbols(scheme *sc)
{
return sc->oblist;
}
#endif
/* Add a new symbol NAME at SLOT. SLOT must be obtained using
* oblist_find_by_name, and no insertion must be done between
* obtaining the SLOT and calling this function. Returns the new
* symbol. */
static pointer oblist_add_by_name(scheme *sc, const char *name, pointer *slot)
{
#define oblist_add_by_name_allocates 3
pointer x;
gc_disable(sc, gc_reservations (oblist_add_by_name));
x = immutable_cons(sc, mk_string(sc, name), sc->NIL);
typeflag(x) = T_SYMBOL;
setimmutable(car(x));
*slot = immutable_cons(sc, x, *slot);
gc_enable(sc);
return x;
}
static pointer mk_port(scheme *sc, port *p) {
pointer x = get_cell(sc, sc->NIL, sc->NIL);
typeflag(x) = T_PORT|T_ATOM|T_FINALIZE;
x->_object._port=p;
return (x);
}
pointer mk_foreign_func(scheme *sc, foreign_func f) {
pointer x = get_cell(sc, sc->NIL, sc->NIL);
typeflag(x) = (T_FOREIGN | T_ATOM);
x->_object._ff=f;
return (x);
}
pointer mk_foreign_object(scheme *sc, const foreign_object_vtable *vtable, void *data) {
pointer x = get_cell(sc, sc->NIL, sc->NIL);
typeflag(x) = (T_FOREIGN_OBJECT | T_ATOM | T_FINALIZE);
x->_object._foreign_object._vtable=vtable;
x->_object._foreign_object._data = data;
return (x);
}
INTERFACE pointer mk_character(scheme *sc, int c) {
pointer x = get_cell(sc,sc->NIL, sc->NIL);
typeflag(x) = (T_CHARACTER | T_ATOM);
ivalue_unchecked(x)= c;
set_num_integer(x);
return (x);
}
#if USE_SMALL_INTEGERS
static const struct cell small_integers[] = {
#define DEFINE_INTEGER(n) { T_NUMBER | T_ATOM | MARK, {{ 1, {n}}}},
#include "small-integers.h"
#undef DEFINE_INTEGER
{0}
};
#define MAX_SMALL_INTEGER (sizeof small_integers / sizeof *small_integers - 1)
static INLINE pointer
mk_small_integer(scheme *sc, long n)
{
#define mk_small_integer_allocates 0
(void) sc;
assert(0 <= n && n < MAX_SMALL_INTEGER);
return (pointer) &small_integers[n];
}
#else
#define mk_small_integer_allocates 1
#define mk_small_integer mk_integer
#endif
/* get number atom (integer) */
INTERFACE pointer mk_integer(scheme *sc, long n) {
pointer x;
#if USE_SMALL_INTEGERS
if (0 <= n && n < MAX_SMALL_INTEGER)
return mk_small_integer(sc, n);
#endif
x = get_cell(sc,sc->NIL, sc->NIL);
typeflag(x) = (T_NUMBER | T_ATOM);
ivalue_unchecked(x)= n;
set_num_integer(x);
return (x);
}
INTERFACE pointer mk_real(scheme *sc, double n) {
pointer x = get_cell(sc,sc->NIL, sc->NIL);
typeflag(x) = (T_NUMBER | T_ATOM);
rvalue_unchecked(x)= n;
set_num_real(x);
return (x);
}
static pointer mk_number(scheme *sc, num n) {
if(n.is_fixnum) {
return mk_integer(sc,n.value.ivalue);
} else {
return mk_real(sc,n.value.rvalue);
}
}
/* allocate name to string area */
static char *store_string(scheme *sc, int len_str, const char *str, char fill) {
char *q;
q=(char*)sc->malloc(len_str+1);
if(q==0) {
sc->no_memory=1;
return sc->strbuff;
}
if(str!=0) {
memcpy (q, str, len_str);
q[len_str]=0;
} else {
memset(q, fill, len_str);
q[len_str]=0;
}
return (q);
}
/* get new string */
INTERFACE pointer mk_string(scheme *sc, const char *str) {
return mk_counted_string(sc,str,strlen(str));
}
INTERFACE pointer mk_counted_string(scheme *sc, const char *str, int len) {
pointer x = get_cell(sc, sc->NIL, sc->NIL);
typeflag(x) = (T_STRING | T_ATOM | T_FINALIZE);
strvalue(x) = store_string(sc,len,str,0);
strlength(x) = len;
return (x);
}
INTERFACE pointer mk_empty_string(scheme *sc, int len, char fill) {
pointer x = get_cell(sc, sc->NIL, sc->NIL);
typeflag(x) = (T_STRING | T_ATOM | T_FINALIZE);
strvalue(x) = store_string(sc,len,0,fill);
strlength(x) = len;
return (x);
}
INTERFACE static pointer mk_vector(scheme *sc, int len)
{ return get_vector_object(sc,len,sc->NIL); }
INTERFACE static void fill_vector(pointer vec, pointer obj) {
size_t i;
assert (is_vector (vec));
for(i = 0; i < vector_length(vec); i++) {
vec->_object._vector._elements[i] = obj;
}
}
INTERFACE static pointer *vector_elem_slot(pointer vec, int ielem) {
assert (is_vector (vec));
assert (ielem < vector_length(vec));
return &vec->_object._vector._elements[ielem];
}
INTERFACE static pointer vector_elem(pointer vec, int ielem) {
assert (is_vector (vec));
assert (ielem < vector_length(vec));
return vec->_object._vector._elements[ielem];
}
INTERFACE static pointer set_vector_elem(pointer vec, int ielem, pointer a) {
assert (is_vector (vec));
assert (ielem < vector_length(vec));
vec->_object._vector._elements[ielem] = a;
return a;
}
/* get new symbol */
INTERFACE pointer mk_symbol(scheme *sc, const char *name) {
#define mk_symbol_allocates oblist_add_by_name_allocates
pointer x;
pointer *slot;
/* first check oblist */
x = oblist_find_by_name(sc, name, &slot);
if (x != sc->NIL) {
return (x);
} else {
x = oblist_add_by_name(sc, name, slot);
return (x);
}
}
INTERFACE pointer gensym(scheme *sc) {
pointer x;
pointer *slot;
char name[40];
for(; sc->gensym_cntgensym_cnt++) {
snprintf(name,40,"gensym-%ld",sc->gensym_cnt);
/* first check oblist */
x = oblist_find_by_name(sc, name, &slot);
if (x != sc->NIL) {
continue;
} else {
x = oblist_add_by_name(sc, name, slot);
return (x);
}
}
return sc->NIL;
}
/* double the size of the string buffer */
static int expand_strbuff(scheme *sc) {
size_t new_size = sc->strbuff_size * 2;
char *new_buffer = sc->malloc(new_size);
if (new_buffer == 0) {
sc->no_memory = 1;
return 1;
}
memcpy(new_buffer, sc->strbuff, sc->strbuff_size);
sc->free(sc->strbuff);
sc->strbuff = new_buffer;
sc->strbuff_size = new_size;
return 0;
}
/* make symbol or number atom from string */
static pointer mk_atom(scheme *sc, char *q) {
char c, *p;
int has_dec_point=0;
int has_fp_exp = 0;
#if USE_COLON_HOOK
char *next;
next = p = q;
while ((next = strstr(next, "::")) != 0) {
/* Keep looking for the last occurrence. */
p = next;
next = next + 2;
}
if (p != q) {
*p=0;
return cons(sc, sc->COLON_HOOK,
cons(sc,
cons(sc,
sc->QUOTE,
cons(sc, mk_symbol(sc, strlwr(p + 2)),
sc->NIL)),
cons(sc, mk_atom(sc, q), sc->NIL)));
}
#endif
p = q;
c = *p++;
if ((c == '+') || (c == '-')) {
c = *p++;
if (c == '.') {
has_dec_point=1;
c = *p++;
}
if (!isdigit(c)) {
return (mk_symbol(sc, strlwr(q)));
}
} else if (c == '.') {
has_dec_point=1;
c = *p++;
if (!isdigit(c)) {
return (mk_symbol(sc, strlwr(q)));
}
} else if (!isdigit(c)) {
return (mk_symbol(sc, strlwr(q)));
}
for ( ; (c = *p) != 0; ++p) {
if (!isdigit(c)) {
if(c=='.') {
if(!has_dec_point) {
has_dec_point=1;
continue;
}
}
else if ((c == 'e') || (c == 'E')) {
if(!has_fp_exp) {
has_dec_point = 1; /* decimal point illegal
from now on */
p++;
if ((*p == '-') || (*p == '+') || isdigit(*p)) {
continue;
}
}
}
return (mk_symbol(sc, strlwr(q)));
}
}
if(has_dec_point) {
return mk_real(sc,atof(q));
}
return (mk_integer(sc, atol(q)));
}
/* make constant */
static pointer mk_sharp_const(scheme *sc, char *name) {
long x;
char tmp[STRBUFFSIZE];
if (!strcmp(name, "t"))
return (sc->T);
else if (!strcmp(name, "f"))
return (sc->F);
else if (*name == 'o') {/* #o (octal) */
snprintf(tmp, STRBUFFSIZE, "0%s", name+1);
sscanf(tmp, "%lo", (long unsigned *)&x);
return (mk_integer(sc, x));
} else if (*name == 'd') { /* #d (decimal) */
sscanf(name+1, "%ld", (long int *)&x);
return (mk_integer(sc, x));
} else if (*name == 'x') { /* #x (hex) */
snprintf(tmp, STRBUFFSIZE, "0x%s", name+1);
sscanf(tmp, "%lx", (long unsigned *)&x);
return (mk_integer(sc, x));
} else if (*name == 'b') { /* #b (binary) */
x = binary_decode(name+1);
return (mk_integer(sc, x));
} else if (*name == '\\') { /* #\w (character) */
int c=0;
if(stricmp(name+1,"space")==0) {
c=' ';
} else if(stricmp(name+1,"newline")==0) {
c='\n';
} else if(stricmp(name+1,"return")==0) {
c='\r';
} else if(stricmp(name+1,"tab")==0) {
c='\t';
} else if(name[1]=='x' && name[2]!=0) {
int c1=0;
if(sscanf(name+2,"%x",(unsigned int *)&c1)==1 && c1 < UCHAR_MAX) {
c=c1;
} else {
return sc->NIL;
}
#if USE_ASCII_NAMES
} else if(is_ascii_name(name+1,&c)) {
/* nothing */
#endif
} else if(name[2]==0) {
c=name[1];
} else {
return sc->NIL;
}
return mk_character(sc,c);
} else
return (sc->NIL);
}
/* ========== garbage collector ========== */
const int frame_length;
static void dump_stack_deallocate_frame(scheme *sc, pointer frame);
/*--
* We use algorithm E (Knuth, The Art of Computer Programming Vol.1,
* sec. 2.3.5), the Schorr-Deutsch-Waite link-inversion algorithm,
* for marking.
*/
static void mark(pointer a) {
pointer t, q, p;
t = (pointer) 0;
p = a;
E2: if (! is_mark(p))
setmark(p);
if (is_vector(p) || is_frame(p)) {
int i;
int len = is_vector(p) ? vector_length(p) : frame_length;
for (i = 0; i < len; i++) {
mark(p->_object._vector._elements[i]);
}
}
#if SHOW_ERROR_LINE
else if (is_port(p)) {
port *pt = p->_object._port;
mark(pt->curr_line);
mark(pt->filename);
}
#endif
/* Mark tag if p has one. */
if (has_tag(p))
mark(p + 1);
if (is_atom(p))
goto E6;
/* E4: down car */
q = car(p);
if (q && !is_mark(q)) {
setatom(p); /* a note that we have moved car */
car(p) = t;
t = p;
p = q;
goto E2;
}
E5: q = cdr(p); /* down cdr */
if (q && !is_mark(q)) {
cdr(p) = t;
t = p;
p = q;
goto E2;
}
E6: /* up. Undo the link switching from steps E4 and E5. */
if (!t)
return;
q = t;
if (is_atom(q)) {
clratom(q);
t = car(q);
car(q) = p;
p = q;
goto E5;
} else {
t = cdr(q);
cdr(q) = p;
p = q;
goto E6;
}
}
/* garbage collection. parameter a, b is marked. */
static void gc(scheme *sc, pointer a, pointer b) {
pointer p;
struct cell_segment *s;
int i;
assert (gc_enabled (sc));
if(sc->gc_verbose) {
putstr(sc, "gc...");
}
/* mark system globals */
mark(sc->oblist);
mark(sc->global_env);
/* mark current registers */
mark(sc->args);
mark(sc->envir);
mark(sc->code);
history_mark(sc);
dump_stack_mark(sc);
mark(sc->value);
mark(sc->inport);
mark(sc->save_inport);
mark(sc->outport);
mark(sc->loadport);
for (i = 0; i <= sc->file_i; i++) {
mark(sc->load_stack[i].filename);
mark(sc->load_stack[i].curr_line);
}
/* Mark recent objects the interpreter doesn't know about yet. */
mark(car(sc->sink));
/* Mark any older stuff above nested C calls */
mark(sc->c_nest);
/* mark variables a, b */
mark(a);
mark(b);
/* garbage collect */
clrmark(sc->NIL);
sc->fcells = 0;
sc->free_cell = sc->NIL;
/* free-list is kept sorted by address so as to maintain consecutive
ranges, if possible, for use with vectors. Here we scan the cells
(which are also kept sorted by address) downwards to build the
free-list in sorted order.
*/
for (s = sc->cell_segments; s; s = s->next) {
p = s->cells + s->cells_len;
while (--p >= s->cells) {
if ((typeflag(p) & 1) == 0)
/* All types have the LSB set. This is not a typeflag. */
continue;
if (is_mark(p)) {
clrmark(p);
} else {
/* reclaim cell */
if ((typeflag(p) & T_FINALIZE) == 0
|| finalize_cell(sc, p)) {
/* Reclaim cell. */
++sc->fcells;
typeflag(p) = 0;
car(p) = sc->NIL;
cdr(p) = sc->free_cell;
sc->free_cell = p;
}
}
}
}
if (sc->gc_verbose) {
char msg[80];
snprintf(msg,80,"done: %ld cells were recovered.\n", sc->fcells);
putstr(sc,msg);
}
/* if only a few recovered, get more to avoid fruitless gc's */
if (sc->fcells < CELL_MINRECOVER
&& alloc_cellseg(sc, 1) == 0)
sc->no_memory = 1;
}
/* Finalize A. Returns true if a can be added to the list of free
* cells. */
static int
finalize_cell(scheme *sc, pointer a)
{
switch (type(a)) {
case T_STRING:
sc->free(strvalue(a));
break;
case T_PORT:
if(a->_object._port->kind&port_file
&& a->_object._port->rep.stdio.closeit) {
port_close(sc,a,port_input|port_output);
} else if (a->_object._port->kind & port_srfi6) {
sc->free(a->_object._port->rep.string.start);
}
sc->free(a->_object._port);
break;
case T_FOREIGN_OBJECT:
a->_object._foreign_object._vtable->finalize(sc, a->_object._foreign_object._data);
break;
case T_VECTOR:
do {
int i;
for (i = vector_size(vector_length(a)) - 1; i > 0; i--) {
pointer p = a + i;
typeflag(p) = 0;
car(p) = sc->NIL;
cdr(p) = sc->free_cell;
sc->free_cell = p;
sc->fcells += 1;
}
} while (0);
break;
case T_FRAME:
dump_stack_deallocate_frame(sc, a);
return 0; /* Do not free cell. */
}
return 1; /* Free cell. */
}
#if SHOW_ERROR_LINE
static void
port_clear_location (scheme *sc, port *p)
{
p->curr_line = sc->NIL;
p->filename = sc->NIL;
}
static void
port_increment_current_line (scheme *sc, port *p, long delta)
{
if (delta == 0)
return;
p->curr_line =
mk_integer(sc, ivalue_unchecked(p->curr_line) + delta);
}
static void
port_init_location (scheme *sc, port *p, pointer name)
{
p->curr_line = mk_integer(sc, 0);
p->filename = name ? name : mk_string(sc, "");
}
#else
static void
port_clear_location (scheme *sc, port *p)
{
}
static void
port_increment_current_line (scheme *sc, port *p, long delta)
{
}
static void
port_init_location (scheme *sc, port *p, pointer name)
{
}
#endif
/* ========== Routines for Reading ========== */
static int file_push(scheme *sc, pointer fname) {
FILE *fin = NULL;
if (sc->file_i == MAXFIL-1)
return 0;
fin = fopen(string_value(fname), "r");
if(fin!=0) {
sc->file_i++;
sc->load_stack[sc->file_i].kind=port_file|port_input;
sc->load_stack[sc->file_i].rep.stdio.file=fin;
sc->load_stack[sc->file_i].rep.stdio.closeit=1;
sc->nesting_stack[sc->file_i]=0;
sc->loadport->_object._port=sc->load_stack+sc->file_i;
port_init_location(sc, &sc->load_stack[sc->file_i], fname);
}
return fin!=0;
}
static void file_pop(scheme *sc) {
if(sc->file_i != 0) {
sc->nesting=sc->nesting_stack[sc->file_i];
port_close(sc,sc->loadport,port_input);
port_clear_location(sc, &sc->load_stack[sc->file_i]);
sc->file_i--;
sc->loadport->_object._port=sc->load_stack+sc->file_i;
}
}
static int file_interactive(scheme *sc) {
return sc->file_i==0 && sc->load_stack[0].rep.stdio.file==stdin
&& sc->inport->_object._port->kind&port_file;
}
static port *port_rep_from_filename(scheme *sc, const char *fn, int prop) {
FILE *f;
char *rw;
port *pt;
if(prop==(port_input|port_output)) {
rw="a+";
} else if(prop==port_output) {
rw="w";
} else {
rw="r";
}
f=fopen(fn,rw);
if(f==0) {
return 0;
}
pt=port_rep_from_file(sc,f,prop);
pt->rep.stdio.closeit=1;
port_init_location(sc, pt, mk_string(sc, fn));
return pt;
}
static pointer port_from_filename(scheme *sc, const char *fn, int prop) {
port *pt;
pt=port_rep_from_filename(sc,fn,prop);
if(pt==0) {
return sc->NIL;
}
return mk_port(sc,pt);
}
static port *port_rep_from_file(scheme *sc, FILE *f, int prop)
{
port *pt;
pt = (port *)sc->malloc(sizeof *pt);
if (pt == NULL) {
return NULL;
}
pt->kind = port_file | prop;
pt->rep.stdio.file = f;
pt->rep.stdio.closeit = 0;
port_init_location(sc, pt, NULL);
return pt;
}
static pointer port_from_file(scheme *sc, FILE *f, int prop) {
port *pt;
pt=port_rep_from_file(sc,f,prop);
if(pt==0) {
return sc->NIL;
}
return mk_port(sc,pt);
}
static port *port_rep_from_string(scheme *sc, char *start, char *past_the_end, int prop) {
port *pt;
pt=(port*)sc->malloc(sizeof(port));
if(pt==0) {
return 0;
}
pt->kind=port_string|prop;
pt->rep.string.start=start;
pt->rep.string.curr=start;
pt->rep.string.past_the_end=past_the_end;
port_init_location(sc, pt, NULL);
return pt;
}
static pointer port_from_string(scheme *sc, char *start, char *past_the_end, int prop) {
port *pt;
pt=port_rep_from_string(sc,start,past_the_end,prop);
if(pt==0) {
return sc->NIL;
}
return mk_port(sc,pt);
}
#define BLOCK_SIZE 256
static port *port_rep_from_scratch(scheme *sc) {
port *pt;
char *start;
pt=(port*)sc->malloc(sizeof(port));
if(pt==0) {
return 0;
}
start=sc->malloc(BLOCK_SIZE);
if(start==0) {
return 0;
}
memset(start,' ',BLOCK_SIZE-1);
start[BLOCK_SIZE-1]='\0';
pt->kind=port_string|port_output|port_srfi6;
pt->rep.string.start=start;
pt->rep.string.curr=start;
pt->rep.string.past_the_end=start+BLOCK_SIZE-1;
port_init_location(sc, pt, NULL);
return pt;
}
static pointer port_from_scratch(scheme *sc) {
port *pt;
pt=port_rep_from_scratch(sc);
if(pt==0) {
return sc->NIL;
}
return mk_port(sc,pt);
}
static void port_close(scheme *sc, pointer p, int flag) {
port *pt=p->_object._port;
pt->kind&=~flag;
if((pt->kind & (port_input|port_output))==0) {
/* Cleanup is here so (close-*-port) functions could work too */
port_clear_location(sc, pt);
if(pt->kind&port_file) {
fclose(pt->rep.stdio.file);
}
pt->kind=port_free;
}
}
/* get new character from input file */
static int inchar(scheme *sc) {
int c;
port *pt;
pt = sc->inport->_object._port;
if(pt->kind & port_saw_EOF)
{ return EOF; }
c = basic_inchar(pt);
if(c == EOF && sc->inport == sc->loadport) {
/* Instead, set port_saw_EOF */
pt->kind |= port_saw_EOF;
/* file_pop(sc); */
return EOF;
/* NOTREACHED */
}
return c;
}
static int basic_inchar(port *pt) {
if(pt->kind & port_file) {
return fgetc(pt->rep.stdio.file);
} else {
if(*pt->rep.string.curr == 0 ||
pt->rep.string.curr == pt->rep.string.past_the_end) {
return EOF;
} else {
return *pt->rep.string.curr++;
}
}
}
/* back character to input buffer */
static void backchar(scheme *sc, int c) {
port *pt;
if(c==EOF) return;
pt=sc->inport->_object._port;
if(pt->kind&port_file) {
ungetc(c,pt->rep.stdio.file);
} else {
if(pt->rep.string.curr!=pt->rep.string.start) {
--pt->rep.string.curr;
}
}
}
static int realloc_port_string(scheme *sc, port *p)
{
char *start=p->rep.string.start;
size_t old_size = p->rep.string.past_the_end - start;
size_t new_size=p->rep.string.past_the_end-start+1+BLOCK_SIZE;
char *str=sc->malloc(new_size);
if(str) {
memset(str,' ',new_size-1);
str[new_size-1]='\0';
memcpy(str, start, old_size);
p->rep.string.start=str;
p->rep.string.past_the_end=str+new_size-1;
p->rep.string.curr-=start-str;
sc->free(start);
return 1;
} else {
return 0;
}
}
INTERFACE void putstr(scheme *sc, const char *s) {
port *pt=sc->outport->_object._port;
if(pt->kind&port_file) {
fputs(s,pt->rep.stdio.file);
} else {
for(;*s;s++) {
if(pt->rep.string.curr!=pt->rep.string.past_the_end) {
*pt->rep.string.curr++=*s;
} else if(pt->kind&port_srfi6&&realloc_port_string(sc,pt)) {
*pt->rep.string.curr++=*s;
}
}
}
}
static void putchars(scheme *sc, const char *s, int len) {
port *pt=sc->outport->_object._port;
if(pt->kind&port_file) {
fwrite(s,1,len,pt->rep.stdio.file);
} else {
for(;len;len--) {
if(pt->rep.string.curr!=pt->rep.string.past_the_end) {
*pt->rep.string.curr++=*s++;
} else if(pt->kind&port_srfi6&&realloc_port_string(sc,pt)) {
*pt->rep.string.curr++=*s++;
}
}
}
}
INTERFACE void putcharacter(scheme *sc, int c) {
port *pt=sc->outport->_object._port;
if(pt->kind&port_file) {
fputc(c,pt->rep.stdio.file);
} else {
if(pt->rep.string.curr!=pt->rep.string.past_the_end) {
*pt->rep.string.curr++=c;
} else if(pt->kind&port_srfi6&&realloc_port_string(sc,pt)) {
*pt->rep.string.curr++=c;
}
}
}
/* read characters up to delimiter, but cater to character constants */
static char *readstr_upto(scheme *sc, char *delim) {
char *p = sc->strbuff;
while ((p - sc->strbuff < sc->strbuff_size) &&
!is_one_of(delim, (*p++ = inchar(sc))));
if(p == sc->strbuff+2 && p[-2] == '\\') {
*p=0;
} else {
backchar(sc,p[-1]);
*--p = '\0';
}
return sc->strbuff;
}
/* read string expression "xxx...xxx" */
static pointer readstrexp(scheme *sc) {
char *p = sc->strbuff;
int c;
int c1=0;
enum { st_ok, st_bsl, st_x1, st_x2, st_oct1, st_oct2 } state=st_ok;
for (;;) {
c=inchar(sc);
if(c == EOF) {
return sc->F;
}
if(p-sc->strbuff > (sc->strbuff_size)-1) {
ptrdiff_t offset = p - sc->strbuff;
if (expand_strbuff(sc) != 0) {
return sc->F;
}
p = sc->strbuff + offset;
}
switch(state) {
case st_ok:
switch(c) {
case '\\':
state=st_bsl;
break;
case '"':
*p=0;
return mk_counted_string(sc,sc->strbuff,p-sc->strbuff);
default:
*p++=c;
break;
}
break;
case st_bsl:
switch(c) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
state=st_oct1;
c1=c-'0';
break;
case 'x':
case 'X':
state=st_x1;
c1=0;
break;
case 'n':
*p++='\n';
state=st_ok;
break;
case 't':
*p++='\t';
state=st_ok;
break;
case 'r':
*p++='\r';
state=st_ok;
break;
case '"':
*p++='"';
state=st_ok;
break;
default:
*p++=c;
state=st_ok;
break;
}
break;
case st_x1:
case st_x2:
c=toupper(c);
if(c>='0' && c<='F') {
if(c<='9') {
c1=(c1<<4)+c-'0';
} else {
c1=(c1<<4)+c-'A'+10;
}
if(state==st_x1) {
state=st_x2;
} else {
*p++=c1;
state=st_ok;
}
} else {
return sc->F;
}
break;
case st_oct1:
case st_oct2:
if (c < '0' || c > '7')
{
*p++=c1;
backchar(sc, c);
state=st_ok;
}
else
{
if (state==st_oct2 && c1 >= 32)
return sc->F;
c1=(c1<<3)+(c-'0');
if (state == st_oct1)
state=st_oct2;
else
{
*p++=c1;
state=st_ok;
}
}
break;
}
}
}
/* check c is in chars */
static INLINE int is_one_of(char *s, int c) {
if(c==EOF) return 1;
while (*s)
if (*s++ == c)
return (1);
return (0);
}
/* skip white characters */
static INLINE int skipspace(scheme *sc) {
int c = 0, curr_line = 0;
do {
c=inchar(sc);
#if SHOW_ERROR_LINE
if(c=='\n')
curr_line++;
#endif
} while (isspace(c));
/* record it */
port_increment_current_line(sc, &sc->load_stack[sc->file_i], curr_line);
if(c!=EOF) {
backchar(sc,c);
return 1;
}
else
{ return EOF; }
}
/* get token */
static int token(scheme *sc) {
int c;
c = skipspace(sc);
if(c == EOF) { return (TOK_EOF); }
switch (c=inchar(sc)) {
case EOF:
return (TOK_EOF);
case '(':
return (TOK_LPAREN);
case ')':
return (TOK_RPAREN);
case '.':
c=inchar(sc);
if(is_one_of(" \n\t",c)) {
return (TOK_DOT);
} else {
backchar(sc,c);
backchar(sc,'.');
return TOK_ATOM;
}
case '\'':
return (TOK_QUOTE);
case ';':
while ((c=inchar(sc)) != '\n' && c!=EOF)
;
if(c == '\n')
port_increment_current_line(sc, &sc->load_stack[sc->file_i], 1);
if(c == EOF)
{ return (TOK_EOF); }
else
{ return (token(sc));}
case '"':
return (TOK_DQUOTE);
case BACKQUOTE:
return (TOK_BQUOTE);
case ',':
if ((c=inchar(sc)) == '@') {
return (TOK_ATMARK);
} else {
backchar(sc,c);
return (TOK_COMMA);
}
case '#':
c=inchar(sc);
if (c == '(') {
return (TOK_VEC);
} else if(c == '!') {
while ((c=inchar(sc)) != '\n' && c!=EOF)
;
if(c == '\n')
port_increment_current_line(sc, &sc->load_stack[sc->file_i], 1);
if(c == EOF)
{ return (TOK_EOF); }
else
{ return (token(sc));}
} else {
backchar(sc,c);
if(is_one_of(" tfodxb\\",c)) {
return TOK_SHARP_CONST;
} else {
return (TOK_SHARP);
}
}
default:
backchar(sc,c);
return (TOK_ATOM);
}
}
/* ========== Routines for Printing ========== */
#define ok_abbrev(x) (is_pair(x) && cdr(x) == sc->NIL)
static void printslashstring(scheme *sc, char *p, int len) {
int i;
unsigned char *s=(unsigned char*)p;
putcharacter(sc,'"');
for ( i=0; iNIL) {
p = "()";
} else if (l == sc->T) {
p = "#t";
} else if (l == sc->F) {
p = "#f";
} else if (l == sc->EOF_OBJ) {
p = "#";
} else if (is_port(l)) {
p = "#";
} else if (is_number(l)) {
p = sc->strbuff;
if (f <= 1 || f == 10) /* f is the base for numbers if > 1 */ {
if(num_is_integer(l)) {
snprintf(p, STRBUFFSIZE, "%ld", ivalue_unchecked(l));
} else {
snprintf(p, STRBUFFSIZE, "%.10g", rvalue_unchecked(l));
/* r5rs says there must be a '.' (unless 'e'?) */
f = strcspn(p, ".e");
if (p[f] == 0) {
p[f] = '.'; /* not found, so add '.0' at the end */
p[f+1] = '0';
p[f+2] = 0;
}
}
} else {
long v = ivalue(l);
if (f == 16) {
if (v >= 0)
snprintf(p, STRBUFFSIZE, "%lx", v);
else
snprintf(p, STRBUFFSIZE, "-%lx", -v);
} else if (f == 8) {
if (v >= 0)
snprintf(p, STRBUFFSIZE, "%lo", v);
else
snprintf(p, STRBUFFSIZE, "-%lo", -v);
} else if (f == 2) {
unsigned long b = (v < 0) ? -v : v;
p = &p[STRBUFFSIZE-1];
*p = 0;
do { *--p = (b&1) ? '1' : '0'; b >>= 1; } while (b != 0);
if (v < 0) *--p = '-';
}
}
} else if (is_string(l)) {
if (!f) {
*pp = strvalue(l);
*plen = strlength(l);
return;
} else { /* Hack, uses the fact that printing is needed */
*pp=sc->strbuff;
*plen=0;
printslashstring(sc, strvalue(l), strlength(l));
return;
}
} else if (is_character(l)) {
int c=charvalue(l);
p = sc->strbuff;
if (!f) {
p[0]=c;
p[1]=0;
} else {
switch(c) {
case ' ':
p = "#\\space";
break;
case '\n':
p = "#\\newline";
break;
case '\r':
p = "#\\return";
break;
case '\t':
p = "#\\tab";
break;
default:
#if USE_ASCII_NAMES
if(c==127) {
p = "#\\del";
break;
} else if(c<32) {
snprintf(p,STRBUFFSIZE, "#\\%s",charnames[c]);
break;
}
#else
if(c<32) {
snprintf(p,STRBUFFSIZE,"#\\x%x",c);
break;
}
#endif
snprintf(p,STRBUFFSIZE,"#\\%c",c);
break;
}
}
} else if (is_symbol(l)) {
p = symname(l);
} else if (is_proc(l)) {
p = sc->strbuff;
snprintf(p,STRBUFFSIZE,"#<%s PROCEDURE %ld>", procname(l),procnum(l));
} else if (is_macro(l)) {
p = "#";
} else if (is_closure(l)) {
p = "#";
} else if (is_promise(l)) {
p = "#";
} else if (is_foreign(l)) {
p = sc->strbuff;
snprintf(p,STRBUFFSIZE,"#", procnum(l));
} else if (is_continuation(l)) {
p = "#";
} else if (is_foreign_object(l)) {
p = sc->strbuff;
l->_object._foreign_object._vtable->to_string(sc, p, STRBUFFSIZE, l->_object._foreign_object._data);
} else {
p = "#";
}
*pp=p;
*plen=strlen(p);
}
/* ========== Routines for Evaluation Cycle ========== */
/* make closure. c is code. e is environment */
static pointer mk_closure(scheme *sc, pointer c, pointer e) {
pointer x = get_cell(sc, c, e);
typeflag(x) = T_CLOSURE;
car(x) = c;
cdr(x) = e;
return (x);
}
/* make continuation. */
static pointer mk_continuation(scheme *sc, pointer d) {
pointer x = get_cell(sc, sc->NIL, d);
typeflag(x) = T_CONTINUATION;
cont_dump(x) = d;
return (x);
}
static pointer list_star(scheme *sc, pointer d) {
pointer p, q;
if(cdr(d)==sc->NIL) {
return car(d);
}
p=cons(sc,car(d),cdr(d));
q=p;
while(cdr(cdr(p))!=sc->NIL) {
d=cons(sc,car(p),cdr(p));
if(cdr(cdr(p))!=sc->NIL) {
p=cdr(d);
}
}
cdr(p)=car(cdr(p));
return q;
}
/* reverse list -- produce new list */
static pointer reverse(scheme *sc, pointer term, pointer list) {
/* a must be checked by gc */
pointer a = list, p = term;
for ( ; is_pair(a); a = cdr(a)) {
p = cons(sc, car(a), p);
}
return (p);
}
/* reverse list --- in-place */
static pointer reverse_in_place(scheme *sc, pointer term, pointer list) {
pointer p = list, result = term, q;
while (p != sc->NIL) {
q = cdr(p);
cdr(p) = result;
result = p;
p = q;
}
return (result);
}
/* append list -- produce new list (in reverse order) */
static pointer revappend(scheme *sc, pointer a, pointer b) {
pointer result = a;
pointer p = b;
while (is_pair(p)) {
result = cons(sc, car(p), result);
p = cdr(p);
}
if (p == sc->NIL) {
return result;
}
return sc->F; /* signal an error */
}
/* equivalence of atoms */
int eqv(pointer a, pointer b) {
if (is_string(a)) {
if (is_string(b))
return (strvalue(a) == strvalue(b));
else
return (0);
} else if (is_number(a)) {
if (is_number(b)) {
if (num_is_integer(a) == num_is_integer(b))
return num_eq(nvalue(a),nvalue(b));
}
return (0);
} else if (is_character(a)) {
if (is_character(b))
return charvalue(a)==charvalue(b);
else
return (0);
} else if (is_port(a)) {
if (is_port(b))
return a==b;
else
return (0);
} else if (is_proc(a)) {
if (is_proc(b))
return procnum(a)==procnum(b);
else
return (0);
} else {
return (a == b);
}
}
/* true or false value macro */
/* () is #t in R5RS */
#define is_true(p) ((p) != sc->F)
#define is_false(p) ((p) == sc->F)
/* ========== Environment implementation ========== */
#if !defined(USE_ALIST_ENV) || !defined(USE_OBJECT_LIST)
static int hash_fn(const char *key, int table_size)
{
unsigned int hashed = 0;
const char *c;
int bits_per_int = sizeof(unsigned int)*8;
for (c = key; *c; c++) {
/* letters have about 5 bits in them */
hashed = (hashed<<5) | (hashed>>(bits_per_int-5));
hashed ^= *c;
}
return hashed % table_size;
}
#endif
/* Compares A and B. Returns an integer less than, equal to, or
* greater than zero if A is stored at a memory location that is
* numerical less than, equal to, or greater than that of B. */
static int
pointercmp(pointer a, pointer b)
{
uintptr_t a_n = (uintptr_t) a;
uintptr_t b_n = (uintptr_t) b;
if (a_n < b_n)
return -1;
if (a_n > b_n)
return 1;
return 0;
}
#ifndef USE_ALIST_ENV
/*
* In this implementation, each frame of the environment may be
* a hash table: a vector of alists hashed by variable name.
* In practice, we use a vector only for the initial frame;
* subsequent frames are too small and transient for the lookup
* speed to out-weigh the cost of making a new vector.
*/
static void new_frame_in_env(scheme *sc, pointer old_env)
{
pointer new_frame;
/* The interaction-environment has about 480 variables in it. */
if (old_env == sc->NIL) {
new_frame = mk_vector(sc, 751);
} else {
new_frame = sc->NIL;
}
gc_disable(sc, 1);
sc->envir = immutable_cons(sc, new_frame, old_env);
gc_enable(sc);
setenvironment(sc->envir);
}
/* Find the slot in ENV under the key HDL. If ALL is given, look in
* all environments enclosing ENV. If the lookup fails, and SSLOT is
* given, the position where the new slot has to be inserted is stored
* at SSLOT. */
static pointer
find_slot_spec_in_env(scheme *sc, pointer env, pointer hdl, int all, pointer **sslot)
{
pointer x,y;
int location;
pointer *sl;
int d;
assert(is_symbol(hdl));
for (x = env; x != sc->NIL; x = cdr(x)) {
if (is_vector(car(x))) {
location = hash_fn(symname(hdl), vector_length(car(x)));
sl = vector_elem_slot(car(x), location);
} else {
sl = &car(x);
}
for (y = *sl ; y != sc->NIL; sl = &cdr(y), y = *sl) {
d = pointercmp(caar(y), hdl);
if (d == 0)
return car(y); /* Hit. */
else if (d > 0)
break; /* Miss. */
}
if (x == env && sslot)
*sslot = sl; /* Insert here. */
if (!all)
return sc->NIL; /* Miss, and stop looking. */
}
return sc->NIL; /* Not found in any environment. */
}
#else /* USE_ALIST_ENV */
static INLINE void new_frame_in_env(scheme *sc, pointer old_env)
{
sc->envir = immutable_cons(sc, sc->NIL, old_env);
setenvironment(sc->envir);
}
/* Find the slot in ENV under the key HDL. If ALL is given, look in
* all environments enclosing ENV. If the lookup fails, and SSLOT is
* given, the position where the new slot has to be inserted is stored
* at SSLOT. */
static pointer
find_slot_spec_in_env(scheme *sc, pointer env, pointer hdl, int all, pointer **sslot)
{
pointer x,y;
pointer *sl;
int d;
assert(is_symbol(hdl));
for (x = env; x != sc->NIL; x = cdr(x)) {
for (sl = &car(x), y = *sl; y != sc->NIL; sl = &cdr(y), y = *sl) {
d = pointercmp(caar(y), hdl);
if (d == 0)
return car(y); /* Hit. */
else if (d > 0)
break; /* Miss. */
}
if (x == env && sslot)
*sslot = sl; /* Insert here. */
if (!all)
return sc->NIL; /* Miss, and stop looking. */
}
return sc->NIL; /* Not found in any environment. */
}
#endif /* USE_ALIST_ENV else */
static pointer find_slot_in_env(scheme *sc, pointer env, pointer hdl, int all)
{
return find_slot_spec_in_env(sc, env, hdl, all, NULL);
}
/* Insert (VARIABLE, VALUE) at SSLOT. SSLOT must be obtained using
* find_slot_spec_in_env, and no insertion must be done between
* obtaining SSLOT and the call to this function. */
static INLINE void new_slot_spec_in_env(scheme *sc,
pointer variable, pointer value,
pointer *sslot)
{
#define new_slot_spec_in_env_allocates 2
pointer slot;
gc_disable(sc, gc_reservations (new_slot_spec_in_env));
slot = immutable_cons(sc, variable, value);
*sslot = immutable_cons(sc, slot, *sslot);
gc_enable(sc);
}
static INLINE void new_slot_in_env(scheme *sc, pointer variable, pointer value)
{
#define new_slot_in_env_allocates new_slot_spec_in_env_allocates
pointer slot;
pointer *sslot;
assert(is_symbol(variable));
slot = find_slot_spec_in_env(sc, sc->envir, variable, 0, &sslot);
assert(slot == sc->NIL);
new_slot_spec_in_env(sc, variable, value, sslot);
}
static INLINE void set_slot_in_env(scheme *sc, pointer slot, pointer value)
{
(void)sc;
cdr(slot) = value;
}
static INLINE pointer slot_value_in_env(pointer slot)
{
return cdr(slot);
}
/* ========== Evaluation Cycle ========== */
static enum scheme_opcodes
_Error_1(scheme *sc, const char *s, pointer a) {
const char *str = s;
pointer history;
#if USE_ERROR_HOOK
pointer x;
pointer hdl=sc->ERROR_HOOK;
#endif
#if SHOW_ERROR_LINE
char sbuf[STRBUFFSIZE];
#endif
history = history_flatten(sc);
#if SHOW_ERROR_LINE
/* make sure error is not in REPL */
if (((sc->load_stack[sc->file_i].kind & port_file) == 0
|| sc->load_stack[sc->file_i].rep.stdio.file != stdin)) {
pointer tag;
const char *fname;
int ln;
if (history != sc->NIL && has_tag(car(history))
&& (tag = get_tag(sc, car(history)))
&& is_string(car(tag)) && is_integer(cdr(tag))) {
fname = string_value(car(tag));
ln = ivalue_unchecked(cdr(tag));
} else {
fname = string_value(sc->load_stack[sc->file_i].filename);
ln = ivalue_unchecked(sc->load_stack[sc->file_i].curr_line);
}
/* should never happen */
if(!fname) fname = "";
/* we started from 0 */
ln++;
snprintf(sbuf, STRBUFFSIZE, "%s:%i: %s", fname, ln, s);
str = (const char*)sbuf;
}
#endif
#if USE_ERROR_HOOK
x=find_slot_in_env(sc,sc->envir,hdl,1);
if (x != sc->NIL) {
sc->code = cons(sc, cons(sc, sc->QUOTE,
cons(sc, history, sc->NIL)),
sc->NIL);
if(a!=0) {
sc->code = cons(sc, cons(sc, sc->QUOTE, cons(sc, a, sc->NIL)),
sc->code);
} else {
sc->code = cons(sc, sc->F, sc->code);
}
sc->code = cons(sc, mk_string(sc, str), sc->code);
setimmutable(car(sc->code));
sc->code = cons(sc, slot_value_in_env(x), sc->code);
return OP_EVAL;
}
#endif
if(a!=0) {
sc->args = cons(sc, (a), sc->NIL);
} else {
sc->args = sc->NIL;
}
sc->args = cons(sc, mk_string(sc, str), sc->args);
setimmutable(car(sc->args));
return OP_ERR0;
}
#define Error_1(sc,s, a) { op = _Error_1(sc,s,a); goto dispatch; }
#define Error_0(sc,s) { op = _Error_1(sc,s,0); goto dispatch; }
/* Too small to turn into function */
# define BEGIN do {
# define END } while (0)
/* Flags. The interpreter has a flags field. When the interpreter
* pushes a frame to the dump stack, it is encoded with the opcode.
* Therefore, we do not use the least significant byte. */
/* Masks used to encode and decode opcode and flags. */
#define S_OP_MASK 0x000000ff
#define S_FLAG_MASK 0xffffff00
/* Set if the interpreter evaluates an expression in a tail context
* (see R5RS, section 3.5). If a function, procedure, or continuation
* is invoked while this flag is set, the call is recorded as tail
* call in the history buffer. */
#define S_FLAG_TAIL_CONTEXT 0x00000100
/* Set flag F. */
#define s_set_flag(sc, f) \
BEGIN \
(sc)->flags |= S_FLAG_ ## f; \
END
/* Clear flag F. */
#define s_clear_flag(sc, f) \
BEGIN \
(sc)->flags &= ~ S_FLAG_ ## f; \
END
/* Check if flag F is set. */
#define s_get_flag(sc, f) \
!!((sc)->flags & S_FLAG_ ## f)
/* Bounce back to Eval_Cycle and execute A. */
#define s_goto(sc, a) { op = (a); goto dispatch; }
#if USE_THREADED_CODE
/* Do not bounce back to Eval_Cycle but execute A by jumping directly
* to it. */
#define s_thread_to(sc, a) \
BEGIN \
op = (a); \
goto a; \
END
/* Define a label OP and emit a case statement for OP. For use in the
* dispatch function. The slightly peculiar goto that is never
* executed avoids warnings about unused labels. */
#if __GNUC__ > 6
#define CASE(OP) OP: __attribute__((unused)); case OP
#else
#define CASE(OP) case OP: if (0) goto OP; OP
#endif
#else /* USE_THREADED_CODE */
#define s_thread_to(sc, a) s_goto(sc, a)
#define CASE(OP) case OP
#endif /* USE_THREADED_CODE */
#if __GNUC__ > 6
#define FALLTHROUGH __attribute__ ((fallthrough))
#else
#define FALLTHROUGH /* fallthrough */
#endif
/* Return to the previous frame on the dump stack, setting the current
* value to A. */
#define s_return(sc, a) s_goto(sc, _s_return(sc, a, 0))
/* Return to the previous frame on the dump stack, setting the current
* value to A, and re-enable the garbage collector. */
#define s_return_enable_gc(sc, a) s_goto(sc, _s_return(sc, a, 1))
static INLINE void dump_stack_reset(scheme *sc)
{
sc->dump = sc->NIL;
}
static INLINE void dump_stack_initialize(scheme *sc)
{
dump_stack_reset(sc);
sc->frame_freelist = sc->NIL;
}
static void dump_stack_free(scheme *sc)
{
dump_stack_initialize(sc);
}
const int frame_length = 4;
static pointer
dump_stack_make_frame(scheme *sc)
{
pointer frame;
frame = mk_vector(sc, frame_length);
if (! sc->no_memory)
setframe(frame);
return frame;
}
static INLINE pointer *
frame_slots(pointer frame)
{
return &frame->_object._vector._elements[0];
}
#define frame_payload vector_length
static pointer
dump_stack_allocate_frame(scheme *sc)
{
pointer frame = sc->frame_freelist;
if (frame == sc->NIL) {
if (gc_enabled(sc))
frame = dump_stack_make_frame(sc);
else
gc_reservation_failure(sc);
} else
sc->frame_freelist = *frame_slots(frame);
return frame;
}
static void
dump_stack_deallocate_frame(scheme *sc, pointer frame)
{
pointer *p = frame_slots(frame);
*p++ = sc->frame_freelist;
*p++ = sc->NIL;
*p++ = sc->NIL;
*p++ = sc->NIL;
sc->frame_freelist = frame;
}
static void
dump_stack_preallocate_frame(scheme *sc)
{
pointer frame = dump_stack_make_frame(sc);
if (! sc->no_memory)
dump_stack_deallocate_frame(sc, frame);
}
static enum scheme_opcodes
_s_return(scheme *sc, pointer a, int enable_gc) {
pointer dump = sc->dump;
pointer *p;
unsigned long v;
enum scheme_opcodes next_op;
sc->value = (a);
if (enable_gc)
gc_enable(sc);
if (dump == sc->NIL)
return OP_QUIT;
v = frame_payload(dump);
next_op = (int) (v & S_OP_MASK);
sc->flags = v & S_FLAG_MASK;
p = frame_slots(dump);
sc->args = *p++;
sc->envir = *p++;
sc->code = *p++;
sc->dump = *p++;
dump_stack_deallocate_frame(sc, dump);
return next_op;
}
static void s_save(scheme *sc, enum scheme_opcodes op, pointer args, pointer code) {
#define s_save_allocates 0
pointer dump;
pointer *p;
gc_disable(sc, gc_reservations (s_save));
dump = dump_stack_allocate_frame(sc);
frame_payload(dump) = (size_t) (sc->flags | (unsigned long) op);
p = frame_slots(dump);
*p++ = args;
*p++ = sc->envir;
*p++ = code;
*p++ = sc->dump;
sc->dump = dump;
gc_enable(sc);
}
static INLINE void dump_stack_mark(scheme *sc)
{
mark(sc->dump);
mark(sc->frame_freelist);
}
#if USE_HISTORY
static void
history_free(scheme *sc)
{
sc->free(sc->history.m);
sc->history.tailstacks = sc->NIL;
sc->history.callstack = sc->NIL;
}
static pointer
history_init(scheme *sc, size_t N, size_t M)
{
size_t i;
struct history *h = &sc->history;
h->N = N;
h->mask_N = N - 1;
h->n = N - 1;
assert ((N & h->mask_N) == 0);
h->M = M;
h->mask_M = M - 1;
assert ((M & h->mask_M) == 0);
h->callstack = mk_vector(sc, N);
if (h->callstack == sc->sink)
goto fail;
h->tailstacks = mk_vector(sc, N);
for (i = 0; i < N; i++) {
pointer tailstack = mk_vector(sc, M);
if (tailstack == sc->sink)
goto fail;
set_vector_elem(h->tailstacks, i, tailstack);
}
h->m = sc->malloc(N * sizeof *h->m);
if (h->m == NULL)
goto fail;
for (i = 0; i < N; i++)
h->m[i] = 0;
return sc->T;
fail:
history_free(sc);
return sc->F;
}
static void
history_mark(scheme *sc)
{
struct history *h = &sc->history;
mark(h->callstack);
mark(h->tailstacks);
}
#define add_mod(a, b, mask) (((a) + (b)) & (mask))
#define sub_mod(a, b, mask) add_mod(a, (mask) + 1 - (b), mask)
static INLINE void
tailstack_clear(scheme *sc, pointer v)
{
assert(is_vector(v));
/* XXX optimize */
fill_vector(v, sc->NIL);
}
static pointer
callstack_pop(scheme *sc)
{
struct history *h = &sc->history;
size_t n = h->n;
pointer item;
if (h->callstack == sc->NIL)
return sc->NIL;
item = vector_elem(h->callstack, n);
/* Clear our frame so that it can be gc'ed and we don't run into it
* when walking the history. */
set_vector_elem(h->callstack, n, sc->NIL);
tailstack_clear(sc, vector_elem(h->tailstacks, n));
/* Exit from the frame. */
h->n = sub_mod(h->n, 1, h->mask_N);
return item;
}
static void
callstack_push(scheme *sc, pointer item)
{
struct history *h = &sc->history;
size_t n = h->n;
if (h->callstack == sc->NIL)
return;
/* Enter a new frame. */
n = h->n = add_mod(n, 1, h->mask_N);
/* Initialize tail stack. */
tailstack_clear(sc, vector_elem(h->tailstacks, n));
h->m[n] = h->mask_M;
set_vector_elem(h->callstack, n, item);
}
static void
tailstack_push(scheme *sc, pointer item)
{
struct history *h = &sc->history;
size_t n = h->n;
size_t m = h->m[n];
if (h->callstack == sc->NIL)
return;
/* Enter a new tail frame. */
m = h->m[n] = add_mod(m, 1, h->mask_M);
set_vector_elem(vector_elem(h->tailstacks, n), m, item);
}
static pointer
tailstack_flatten(scheme *sc, pointer tailstack, size_t i, size_t n,
pointer acc)
{
struct history *h = &sc->history;
pointer frame;
assert(i <= h->M);
assert(n < h->M);
if (acc == sc->sink)
return sc->sink;
if (i == 0) {
/* We reached the end, but we did not see a unused frame. Signal
this using '... . */
return cons(sc, mk_symbol(sc, "..."), acc);
}
frame = vector_elem(tailstack, n);
if (frame == sc->NIL) {
/* A unused frame. We reached the end of the history. */
return acc;
}
/* Add us. */
acc = cons(sc, frame, acc);
return tailstack_flatten(sc, tailstack, i - 1, sub_mod(n, 1, h->mask_M),
acc);
}
static pointer
callstack_flatten(scheme *sc, size_t i, size_t n, pointer acc)
{
struct history *h = &sc->history;
pointer frame;
assert(i <= h->N);
assert(n < h->N);
if (acc == sc->sink)
return sc->sink;
if (i == 0) {
/* We reached the end, but we did not see a unused frame. Signal
this using '... . */
return cons(sc, mk_symbol(sc, "..."), acc);
}
frame = vector_elem(h->callstack, n);
if (frame == sc->NIL) {
/* A unused frame. We reached the end of the history. */
return acc;
}
/* First, emit the tail calls. */
acc = tailstack_flatten(sc, vector_elem(h->tailstacks, n), h->M, h->m[n],
acc);
/* Then us. */
acc = cons(sc, frame, acc);
return callstack_flatten(sc, i - 1, sub_mod(n, 1, h->mask_N), acc);
}
static pointer
history_flatten(scheme *sc)
{
struct history *h = &sc->history;
pointer history;
if (h->callstack == sc->NIL)
return sc->NIL;
history = callstack_flatten(sc, h->N, h->n, sc->NIL);
if (history == sc->sink)
return sc->sink;
return reverse_in_place(sc, sc->NIL, history);
}
#undef add_mod
#undef sub_mod
#else /* USE_HISTORY */
#define history_init(SC, A, B) (void) 0
#define history_free(SC) (void) 0
#define callstack_pop(SC) (void) 0
#define callstack_push(SC, X) (void) 0
#define tailstack_push(SC, X) (void) 0
#endif /* USE_HISTORY */
#if USE_PLIST
static pointer
get_property(scheme *sc, pointer obj, pointer key)
{
pointer x;
assert (is_symbol(obj));
assert (is_symbol(key));
for (x = symprop(obj); x != sc->NIL; x = cdr(x)) {
if (caar(x) == key)
break;
}
if (x != sc->NIL)
return cdar(x);
return sc->NIL;
}
static pointer
set_property(scheme *sc, pointer obj, pointer key, pointer value)
{
#define set_property_allocates 2
pointer x;
assert (is_symbol(obj));
assert (is_symbol(key));
for (x = symprop(obj); x != sc->NIL; x = cdr(x)) {
if (caar(x) == key)
break;
}
if (x != sc->NIL)
cdar(x) = value;
else {
gc_disable(sc, gc_reservations(set_property));
symprop(obj) = cons(sc, cons(sc, key, value), symprop(obj));
gc_enable(sc);
}
return sc->T;
}
#endif
static int is_list(scheme *sc, pointer a)
{ return list_length(sc,a) >= 0; }
/* Result is:
proper list: length
circular list: -1
not even a pair: -2
dotted list: -2 minus length before dot
*/
int list_length(scheme *sc, pointer a) {
int i=0;
pointer slow, fast;
slow = fast = a;
while (1)
{
if (fast == sc->NIL)
return i;
if (!is_pair(fast))
return -2 - i;
fast = cdr(fast);
++i;
if (fast == sc->NIL)
return i;
if (!is_pair(fast))
return -2 - i;
++i;
fast = cdr(fast);
/* Safe because we would have already returned if `fast'
encountered a non-pair. */
slow = cdr(slow);
if (fast == slow)
{
/* the fast pointer has looped back around and caught up
with the slow pointer, hence the structure is circular,
not of finite length, and therefore not a list */
return -1;
}
}
}
#define s_retbool(tf) s_return(sc,(tf) ? sc->T : sc->F)
/* kernel of this interpreter */
static void
Eval_Cycle(scheme *sc, enum scheme_opcodes op) {
for (;;) {
pointer x, y;
pointer callsite;
num v;
#if USE_MATH
double dd;
#endif
int (*comp_func)(num, num) = NULL;
const struct op_code_info *pcd;
dispatch:
pcd = &dispatch_table[op];
if (pcd->name[0] != 0) { /* if built-in function, check arguments */
char msg[STRBUFFSIZE];
if (! check_arguments (sc, pcd, msg, sizeof msg)) {
s_goto(sc, _Error_1(sc, msg, 0));
}
}
if(sc->no_memory) {
fprintf(stderr,"No memory!\n");
exit(1);
}
ok_to_freely_gc(sc);
switch (op) {
CASE(OP_LOAD): /* load */
if(file_interactive(sc)) {
fprintf(sc->outport->_object._port->rep.stdio.file,
"Loading %s\n", strvalue(car(sc->args)));
}
if (!file_push(sc, car(sc->args))) {
Error_1(sc,"unable to open", car(sc->args));
}
else
{
sc->args = mk_integer(sc,sc->file_i);
s_thread_to(sc,OP_T0LVL);
}
CASE(OP_T0LVL): /* top level */
/* If we reached the end of file, this loop is done. */
if(sc->loadport->_object._port->kind & port_saw_EOF)
{
if(sc->file_i == 0)
{
sc->args=sc->NIL;
sc->nesting = sc->nesting_stack[0];
s_thread_to(sc,OP_QUIT);
}
else
{
file_pop(sc);
s_return(sc,sc->value);
}
/* NOTREACHED */
}
/* If interactive, be nice to user. */
if(file_interactive(sc))
{
sc->envir = sc->global_env;
dump_stack_reset(sc);
putstr(sc,"\n");
putstr(sc,prompt);
}
/* Set up another iteration of REPL */
sc->nesting=0;
sc->save_inport=sc->inport;
sc->inport = sc->loadport;
s_save(sc,OP_T0LVL, sc->NIL, sc->NIL);
s_save(sc,OP_VALUEPRINT, sc->NIL, sc->NIL);
s_save(sc,OP_T1LVL, sc->NIL, sc->NIL);
s_thread_to(sc,OP_READ_INTERNAL);
CASE(OP_T1LVL): /* top level */
sc->code = sc->value;
sc->inport=sc->save_inport;
s_thread_to(sc,OP_EVAL);
CASE(OP_READ_INTERNAL): /* internal read */
sc->tok = token(sc);
if(sc->tok==TOK_EOF)
{ s_return(sc,sc->EOF_OBJ); }
s_thread_to(sc,OP_RDSEXPR);
CASE(OP_GENSYM):
s_return(sc, gensym(sc));
CASE(OP_VALUEPRINT): /* print evaluation result */
/* OP_VALUEPRINT is always pushed, because when changing from
non-interactive to interactive mode, it needs to be
already on the stack */
if(sc->tracing) {
putstr(sc,"\nGives: ");
}
if(file_interactive(sc)) {
sc->print_flag = 1;
sc->args = sc->value;
s_thread_to(sc,OP_P0LIST);
} else {
s_return(sc,sc->value);
}
CASE(OP_EVAL): /* main part of evaluation */
#if USE_TRACING
if(sc->tracing) {
/*s_save(sc,OP_VALUEPRINT,sc->NIL,sc->NIL);*/
s_save(sc,OP_REAL_EVAL,sc->args,sc->code);
sc->args=sc->code;
putstr(sc,"\nEval: ");
s_thread_to(sc,OP_P0LIST);
}
FALLTHROUGH;
CASE(OP_REAL_EVAL):
#endif
if (is_symbol(sc->code)) { /* symbol */
x=find_slot_in_env(sc,sc->envir,sc->code,1);
if (x != sc->NIL) {
s_return(sc,slot_value_in_env(x));
} else {
Error_1(sc, "eval: unbound variable", sc->code);
}
} else if (is_pair(sc->code)) {
if (is_syntax(x = car(sc->code))) { /* SYNTAX */
sc->code = cdr(sc->code);
s_goto(sc, syntaxnum(sc, x));
} else {/* first, eval top element and eval arguments */
s_save(sc,OP_E0ARGS, sc->NIL, sc->code);
/* If no macros => s_save(sc,OP_E1ARGS, sc->NIL, cdr(sc->code));*/
sc->code = car(sc->code);
s_clear_flag(sc, TAIL_CONTEXT);
s_thread_to(sc,OP_EVAL);
}
} else {
s_return(sc,sc->code);
}
CASE(OP_E0ARGS): /* eval arguments */
if (is_macro(sc->value)) { /* macro expansion */
gc_disable(sc, 1 + gc_reservations (s_save));
s_save(sc,OP_DOMACRO, sc->NIL, sc->NIL);
sc->args = cons(sc,sc->code, sc->NIL);
gc_enable(sc);
sc->code = sc->value;
s_clear_flag(sc, TAIL_CONTEXT);
s_thread_to(sc,OP_APPLY);
} else {
gc_disable(sc, 1);
sc->args = cons(sc, sc->code, sc->NIL);
gc_enable(sc);
sc->code = cdr(sc->code);
s_thread_to(sc,OP_E1ARGS);
}
CASE(OP_E1ARGS): /* eval arguments */
gc_disable(sc, 1);
sc->args = cons(sc, sc->value, sc->args);
gc_enable(sc);
if (is_pair(sc->code)) { /* continue */
s_save(sc,OP_E1ARGS, sc->args, cdr(sc->code));
sc->code = car(sc->code);
sc->args = sc->NIL;
s_clear_flag(sc, TAIL_CONTEXT);
s_thread_to(sc,OP_EVAL);
} else { /* end */
sc->args = reverse_in_place(sc, sc->NIL, sc->args);
s_thread_to(sc,OP_APPLY_CODE);
}
#if USE_TRACING
CASE(OP_TRACING): {
int tr=sc->tracing;
sc->tracing=ivalue(car(sc->args));
gc_disable(sc, 1);
s_return_enable_gc(sc, mk_integer(sc, tr));
}
#endif
#if USE_HISTORY
CASE(OP_CALLSTACK_POP): /* pop the call stack */
callstack_pop(sc);
s_return(sc, sc->value);
#endif
CASE(OP_APPLY_CODE): /* apply 'cadr(args)' to 'cddr(args)',
* record in the history as invoked from
* 'car(args)' */
free_cons(sc, sc->args, &callsite, &sc->args);
sc->code = car(sc->args);
sc->args = cdr(sc->args);
FALLTHROUGH;
CASE(OP_APPLY): /* apply 'code' to 'args' */
#if USE_TRACING
if(sc->tracing) {
s_save(sc,OP_REAL_APPLY,sc->args,sc->code);
sc->print_flag = 1;
/* sc->args=cons(sc,sc->code,sc->args);*/
putstr(sc,"\nApply to: ");
s_thread_to(sc,OP_P0LIST);
}
FALLTHROUGH;
CASE(OP_REAL_APPLY):
#endif
#if USE_HISTORY
if (op != OP_APPLY_CODE)
callsite = sc->code;
if (s_get_flag(sc, TAIL_CONTEXT)) {
/* We are evaluating a tail call. */
tailstack_push(sc, callsite);
} else {
callstack_push(sc, callsite);
s_save(sc, OP_CALLSTACK_POP, sc->NIL, sc->NIL);
}
#endif
if (is_proc(sc->code)) {
s_goto(sc,procnum(sc->code)); /* PROCEDURE */
} else if (is_foreign(sc->code))
{
/* Keep nested calls from GC'ing the arglist */
push_recent_alloc(sc,sc->args,sc->NIL);
x=sc->code->_object._ff(sc,sc->args);
s_return(sc,x);
} else if (is_closure(sc->code) || is_macro(sc->code)
|| is_promise(sc->code)) { /* CLOSURE */
/* Should not accept promise */
/* make environment */
new_frame_in_env(sc, closure_env(sc->code));
for (x = car(closure_code(sc->code)), y = sc->args;
is_pair(x); x = cdr(x), y = cdr(y)) {
if (y == sc->NIL) {
Error_1(sc, "not enough arguments, missing", x);
} else if (is_symbol(car(x))) {
new_slot_in_env(sc, car(x), car(y));
} else {
Error_1(sc, "syntax error in closure: not a symbol", car(x));
}
}
if (x == sc->NIL) {
if (y != sc->NIL) {
Error_0(sc, "too many arguments");
}
} else if (is_symbol(x))
new_slot_in_env(sc, x, y);
else {
Error_1(sc, "syntax error in closure: not a symbol", x);
}
sc->code = cdr(closure_code(sc->code));
sc->args = sc->NIL;
s_set_flag(sc, TAIL_CONTEXT);
s_thread_to(sc,OP_BEGIN);
} else if (is_continuation(sc->code)) { /* CONTINUATION */
sc->dump = cont_dump(sc->code);
s_return(sc,sc->args != sc->NIL ? car(sc->args) : sc->NIL);
} else {
Error_1(sc,"illegal function",sc->code);
}
CASE(OP_DOMACRO): /* do macro */
sc->code = sc->value;
s_thread_to(sc,OP_EVAL);
#if USE_COMPILE_HOOK
CASE(OP_LAMBDA): /* lambda */
/* If the hook is defined, apply it to sc->code, otherwise
set sc->value fall through */
{
pointer f=find_slot_in_env(sc,sc->envir,sc->COMPILE_HOOK,1);
if(f==sc->NIL) {
sc->value = sc->code;
/* Fallthru */
} else {
gc_disable(sc, 1 + gc_reservations (s_save));
s_save(sc,OP_LAMBDA1,sc->args,sc->code);
sc->args=cons(sc,sc->code,sc->NIL);
gc_enable(sc);
sc->code=slot_value_in_env(f);
s_thread_to(sc,OP_APPLY);
}
}
#else
CASE(OP_LAMBDA): /* lambda */
sc->value = sc->code;
#endif
FALLTHROUGH;
CASE(OP_LAMBDA1):
gc_disable(sc, 1);
s_return_enable_gc(sc, mk_closure(sc, sc->value, sc->envir));
CASE(OP_MKCLOSURE): /* make-closure */
x=car(sc->args);
if(car(x)==sc->LAMBDA) {
x=cdr(x);
}
if(cdr(sc->args)==sc->NIL) {
y=sc->envir;
} else {
y=cadr(sc->args);
}
gc_disable(sc, 1);
s_return_enable_gc(sc, mk_closure(sc, x, y));
CASE(OP_QUOTE): /* quote */
s_return(sc,car(sc->code));
CASE(OP_DEF0): /* define */
if(is_immutable(car(sc->code)))
Error_1(sc,"define: unable to alter immutable", car(sc->code));
if (is_pair(car(sc->code))) {
x = caar(sc->code);
gc_disable(sc, 2);
sc->code = cons(sc, sc->LAMBDA, cons(sc, cdar(sc->code), cdr(sc->code)));
gc_enable(sc);
} else {
x = car(sc->code);
sc->code = cadr(sc->code);
}
if (!is_symbol(x)) {
Error_0(sc,"variable is not a symbol");
}
s_save(sc,OP_DEF1, sc->NIL, x);
s_thread_to(sc,OP_EVAL);
CASE(OP_DEF1): { /* define */
pointer *sslot;
x = find_slot_spec_in_env(sc, sc->envir, sc->code, 0, &sslot);
if (x != sc->NIL) {
set_slot_in_env(sc, x, sc->value);
} else {
new_slot_spec_in_env(sc, sc->code, sc->value, sslot);
}
s_return(sc,sc->code);
}
CASE(OP_DEFP): /* defined? */
x=sc->envir;
if(cdr(sc->args)!=sc->NIL) {
x=cadr(sc->args);
}
s_retbool(find_slot_in_env(sc,x,car(sc->args),1)!=sc->NIL);
CASE(OP_SET0): /* set! */
if(is_immutable(car(sc->code)))
Error_1(sc,"set!: unable to alter immutable variable",car(sc->code));
s_save(sc,OP_SET1, sc->NIL, car(sc->code));
sc->code = cadr(sc->code);
s_thread_to(sc,OP_EVAL);
CASE(OP_SET1): /* set! */
y=find_slot_in_env(sc,sc->envir,sc->code,1);
if (y != sc->NIL) {
set_slot_in_env(sc, y, sc->value);
s_return(sc,sc->value);
} else {
Error_1(sc, "set!: unbound variable", sc->code);
}
CASE(OP_BEGIN): /* begin */
{
int last;
if (!is_pair(sc->code)) {
s_return(sc,sc->code);
}
last = cdr(sc->code) == sc->NIL;
if (!last) {
s_save(sc,OP_BEGIN, sc->NIL, cdr(sc->code));
}
sc->code = car(sc->code);
if (! last)
/* This is not the end of the list. This is not a tail
* position. */
s_clear_flag(sc, TAIL_CONTEXT);
s_thread_to(sc,OP_EVAL);
}
CASE(OP_IF0): /* if */
s_save(sc,OP_IF1, sc->NIL, cdr(sc->code));
sc->code = car(sc->code);
s_clear_flag(sc, TAIL_CONTEXT);
s_thread_to(sc,OP_EVAL);
CASE(OP_IF1): /* if */
if (is_true(sc->value))
sc->code = car(sc->code);
else
sc->code = cadr(sc->code); /* (if #f 1) ==> () because
* car(sc->NIL) = sc->NIL */
s_thread_to(sc,OP_EVAL);
CASE(OP_LET0): /* let */
sc->args = sc->NIL;
sc->value = sc->code;
sc->code = is_symbol(car(sc->code)) ? cadr(sc->code) : car(sc->code);
s_thread_to(sc,OP_LET1);
CASE(OP_LET1): /* let (calculate parameters) */
gc_disable(sc, 1 + (is_pair(sc->code) ? gc_reservations (s_save) : 0));
sc->args = cons(sc, sc->value, sc->args);
if (is_pair(sc->code)) { /* continue */
if (!is_pair(car(sc->code)) || !is_pair(cdar(sc->code))) {
gc_enable(sc);
Error_1(sc, "Bad syntax of binding spec in let",
car(sc->code));
}
s_save(sc,OP_LET1, sc->args, cdr(sc->code));
gc_enable(sc);
sc->code = cadar(sc->code);
sc->args = sc->NIL;
s_clear_flag(sc, TAIL_CONTEXT);
s_thread_to(sc,OP_EVAL);
} else { /* end */
gc_enable(sc);
sc->args = reverse_in_place(sc, sc->NIL, sc->args);
sc->code = car(sc->args);
sc->args = cdr(sc->args);
s_thread_to(sc,OP_LET2);
}
CASE(OP_LET2): /* let */
new_frame_in_env(sc, sc->envir);
for (x = is_symbol(car(sc->code)) ? cadr(sc->code) : car(sc->code), y = sc->args;
y != sc->NIL; x = cdr(x), y = cdr(y)) {
new_slot_in_env(sc, caar(x), car(y));
}
if (is_symbol(car(sc->code))) { /* named let */
for (x = cadr(sc->code), sc->args = sc->NIL; x != sc->NIL; x = cdr(x)) {
if (!is_pair(x))
Error_1(sc, "Bad syntax of binding in let", x);
if (!is_list(sc, car(x)))
Error_1(sc, "Bad syntax of binding in let", car(x));
gc_disable(sc, 1);
sc->args = cons(sc, caar(x), sc->args);
gc_enable(sc);
}
gc_disable(sc, 2 + gc_reservations (new_slot_in_env));
x = mk_closure(sc, cons(sc, reverse_in_place(sc, sc->NIL, sc->args), cddr(sc->code)), sc->envir);
new_slot_in_env(sc, car(sc->code), x);
gc_enable(sc);
sc->code = cddr(sc->code);
sc->args = sc->NIL;
} else {
sc->code = cdr(sc->code);
sc->args = sc->NIL;
}
s_thread_to(sc,OP_BEGIN);
CASE(OP_LET0AST): /* let* */
if (car(sc->code) == sc->NIL) {
new_frame_in_env(sc, sc->envir);
sc->code = cdr(sc->code);
s_thread_to(sc,OP_BEGIN);
}
if(!is_pair(car(sc->code)) || !is_pair(caar(sc->code)) || !is_pair(cdaar(sc->code))) {
Error_1(sc, "Bad syntax of binding spec in let*", car(sc->code));
}
s_save(sc,OP_LET1AST, cdr(sc->code), car(sc->code));
sc->code = cadaar(sc->code);
s_clear_flag(sc, TAIL_CONTEXT);
s_thread_to(sc,OP_EVAL);
CASE(OP_LET1AST): /* let* (make new frame) */
new_frame_in_env(sc, sc->envir);
s_thread_to(sc,OP_LET2AST);
CASE(OP_LET2AST): /* let* (calculate parameters) */
new_slot_in_env(sc, caar(sc->code), sc->value);
sc->code = cdr(sc->code);
if (is_pair(sc->code)) { /* continue */
s_save(sc,OP_LET2AST, sc->args, sc->code);
sc->code = cadar(sc->code);
sc->args = sc->NIL;
s_clear_flag(sc, TAIL_CONTEXT);
s_thread_to(sc,OP_EVAL);
} else { /* end */
sc->code = sc->args;
sc->args = sc->NIL;
s_thread_to(sc,OP_BEGIN);
}
CASE(OP_LET0REC): /* letrec */
new_frame_in_env(sc, sc->envir);
sc->args = sc->NIL;
sc->value = sc->code;
sc->code = car(sc->code);
s_thread_to(sc,OP_LET1REC);
CASE(OP_LET1REC): /* letrec (calculate parameters) */
gc_disable(sc, 1);
sc->args = cons(sc, sc->value, sc->args);
gc_enable(sc);
if (is_pair(sc->code)) { /* continue */
if (!is_pair(car(sc->code)) || !is_pair(cdar(sc->code))) {
Error_1(sc, "Bad syntax of binding spec in letrec",
car(sc->code));
}
s_save(sc,OP_LET1REC, sc->args, cdr(sc->code));
sc->code = cadar(sc->code);
sc->args = sc->NIL;
s_clear_flag(sc, TAIL_CONTEXT);
s_thread_to(sc,OP_EVAL);
} else { /* end */
sc->args = reverse_in_place(sc, sc->NIL, sc->args);
sc->code = car(sc->args);
sc->args = cdr(sc->args);
s_thread_to(sc,OP_LET2REC);
}
CASE(OP_LET2REC): /* letrec */
for (x = car(sc->code), y = sc->args; y != sc->NIL; x = cdr(x), y = cdr(y)) {
new_slot_in_env(sc, caar(x), car(y));
}
sc->code = cdr(sc->code);
sc->args = sc->NIL;
s_thread_to(sc,OP_BEGIN);
CASE(OP_COND0): /* cond */
if (!is_pair(sc->code)) {
Error_0(sc,"syntax error in cond");
}
s_save(sc,OP_COND1, sc->NIL, sc->code);
sc->code = caar(sc->code);
s_clear_flag(sc, TAIL_CONTEXT);
s_thread_to(sc,OP_EVAL);
CASE(OP_COND1): /* cond */
if (is_true(sc->value)) {
if ((sc->code = cdar(sc->code)) == sc->NIL) {
s_return(sc,sc->value);
}
if(!sc->code || car(sc->code)==sc->FEED_TO) {
if(!is_pair(cdr(sc->code))) {
Error_0(sc,"syntax error in cond");
}
gc_disable(sc, 4);
x=cons(sc, sc->QUOTE, cons(sc, sc->value, sc->NIL));
sc->code=cons(sc,cadr(sc->code),cons(sc,x,sc->NIL));
gc_enable(sc);
s_thread_to(sc,OP_EVAL);
}
s_thread_to(sc,OP_BEGIN);
} else {
if ((sc->code = cdr(sc->code)) == sc->NIL) {
s_return(sc,sc->NIL);
} else {
s_save(sc,OP_COND1, sc->NIL, sc->code);
sc->code = caar(sc->code);
s_clear_flag(sc, TAIL_CONTEXT);
s_thread_to(sc,OP_EVAL);
}
}
CASE(OP_DELAY): /* delay */
gc_disable(sc, 2);
x = mk_closure(sc, cons(sc, sc->NIL, sc->code), sc->envir);
typeflag(x)=T_PROMISE;
s_return_enable_gc(sc,x);
CASE(OP_AND0): /* and */
if (sc->code == sc->NIL) {
s_return(sc,sc->T);
}
s_save(sc,OP_AND1, sc->NIL, cdr(sc->code));
if (cdr(sc->code) != sc->NIL)
s_clear_flag(sc, TAIL_CONTEXT);
sc->code = car(sc->code);
s_thread_to(sc,OP_EVAL);
CASE(OP_AND1): /* and */
if (is_false(sc->value)) {
s_return(sc,sc->value);
} else if (sc->code == sc->NIL) {
s_return(sc,sc->value);
} else {
s_save(sc,OP_AND1, sc->NIL, cdr(sc->code));
if (cdr(sc->code) != sc->NIL)
s_clear_flag(sc, TAIL_CONTEXT);
sc->code = car(sc->code);
s_thread_to(sc,OP_EVAL);
}
CASE(OP_OR0): /* or */
if (sc->code == sc->NIL) {
s_return(sc,sc->F);
}
s_save(sc,OP_OR1, sc->NIL, cdr(sc->code));
if (cdr(sc->code) != sc->NIL)
s_clear_flag(sc, TAIL_CONTEXT);
sc->code = car(sc->code);
s_thread_to(sc,OP_EVAL);
CASE(OP_OR1): /* or */
if (is_true(sc->value)) {
s_return(sc,sc->value);
} else if (sc->code == sc->NIL) {
s_return(sc,sc->value);
} else {
s_save(sc,OP_OR1, sc->NIL, cdr(sc->code));
if (cdr(sc->code) != sc->NIL)
s_clear_flag(sc, TAIL_CONTEXT);
sc->code = car(sc->code);
s_thread_to(sc,OP_EVAL);
}
CASE(OP_C0STREAM): /* cons-stream */
s_save(sc,OP_C1STREAM, sc->NIL, cdr(sc->code));
sc->code = car(sc->code);
s_thread_to(sc,OP_EVAL);
CASE(OP_C1STREAM): /* cons-stream */
sc->args = sc->value; /* save sc->value to register sc->args for gc */
gc_disable(sc, 3);
x = mk_closure(sc, cons(sc, sc->NIL, sc->code), sc->envir);
typeflag(x)=T_PROMISE;
s_return_enable_gc(sc, cons(sc, sc->args, x));
CASE(OP_MACRO0): /* macro */
if (is_pair(car(sc->code))) {
x = caar(sc->code);
gc_disable(sc, 2);
sc->code = cons(sc, sc->LAMBDA, cons(sc, cdar(sc->code), cdr(sc->code)));
gc_enable(sc);
} else {
x = car(sc->code);
sc->code = cadr(sc->code);
}
if (!is_symbol(x)) {
Error_0(sc,"variable is not a symbol");
}
s_save(sc,OP_MACRO1, sc->NIL, x);
s_thread_to(sc,OP_EVAL);
CASE(OP_MACRO1): { /* macro */
pointer *sslot;
typeflag(sc->value) = T_MACRO;
x = find_slot_spec_in_env(sc, sc->envir, sc->code, 0, &sslot);
if (x != sc->NIL) {
set_slot_in_env(sc, x, sc->value);
} else {
new_slot_spec_in_env(sc, sc->code, sc->value, sslot);
}
s_return(sc,sc->code);
}
CASE(OP_CASE0): /* case */
s_save(sc,OP_CASE1, sc->NIL, cdr(sc->code));
sc->code = car(sc->code);
s_clear_flag(sc, TAIL_CONTEXT);
s_thread_to(sc,OP_EVAL);
CASE(OP_CASE1): /* case */
for (x = sc->code; x != sc->NIL; x = cdr(x)) {
if (!is_pair(y = caar(x))) {
break;
}
for ( ; y != sc->NIL; y = cdr(y)) {
if (eqv(car(y), sc->value)) {
break;
}
}
if (y != sc->NIL) {
break;
}
}
if (x != sc->NIL) {
if (is_pair(caar(x))) {
sc->code = cdar(x);
s_thread_to(sc,OP_BEGIN);
} else {/* else */
s_save(sc,OP_CASE2, sc->NIL, cdar(x));
sc->code = caar(x);
s_thread_to(sc,OP_EVAL);
}
} else {
s_return(sc,sc->NIL);
}
CASE(OP_CASE2): /* case */
if (is_true(sc->value)) {
s_thread_to(sc,OP_BEGIN);
} else {
s_return(sc,sc->NIL);
}
CASE(OP_PAPPLY): /* apply */
sc->code = car(sc->args);
sc->args = list_star(sc,cdr(sc->args));
/*sc->args = cadr(sc->args);*/
s_thread_to(sc,OP_APPLY);
CASE(OP_PEVAL): /* eval */
if(cdr(sc->args)!=sc->NIL) {
sc->envir=cadr(sc->args);
}
sc->code = car(sc->args);
s_thread_to(sc,OP_EVAL);
CASE(OP_CONTINUATION): /* call-with-current-continuation */
sc->code = car(sc->args);
gc_disable(sc, 2);
sc->args = cons(sc, mk_continuation(sc, sc->dump), sc->NIL);
gc_enable(sc);
s_thread_to(sc,OP_APPLY);
#if USE_MATH
CASE(OP_INEX2EX): /* inexact->exact */
x=car(sc->args);
if(num_is_integer(x)) {
s_return(sc,x);
} else if(modf(rvalue_unchecked(x),&dd)==0.0) {
s_return(sc,mk_integer(sc,ivalue(x)));
} else {
Error_1(sc, "inexact->exact: not integral", x);
}
CASE(OP_EXP):
x=car(sc->args);
s_return(sc, mk_real(sc, exp(rvalue(x))));
CASE(OP_LOG):
x=car(sc->args);
s_return(sc, mk_real(sc, log(rvalue(x))));
CASE(OP_SIN):
x=car(sc->args);
s_return(sc, mk_real(sc, sin(rvalue(x))));
CASE(OP_COS):
x=car(sc->args);
s_return(sc, mk_real(sc, cos(rvalue(x))));
CASE(OP_TAN):
x=car(sc->args);
s_return(sc, mk_real(sc, tan(rvalue(x))));
CASE(OP_ASIN):
x=car(sc->args);
s_return(sc, mk_real(sc, asin(rvalue(x))));
CASE(OP_ACOS):
x=car(sc->args);
s_return(sc, mk_real(sc, acos(rvalue(x))));
CASE(OP_ATAN):
x=car(sc->args);
if(cdr(sc->args)==sc->NIL) {
s_return(sc, mk_real(sc, atan(rvalue(x))));
} else {
pointer y=cadr(sc->args);
s_return(sc, mk_real(sc, atan2(rvalue(x),rvalue(y))));
}
CASE(OP_SQRT):
x=car(sc->args);
s_return(sc, mk_real(sc, sqrt(rvalue(x))));
CASE(OP_EXPT): {
double result;
int real_result=1;
pointer y=cadr(sc->args);
x=car(sc->args);
if (num_is_integer(x) && num_is_integer(y))
real_result=0;
/* This 'if' is an R5RS compatibility fix. */
/* NOTE: Remove this 'if' fix for R6RS. */
if (rvalue(x) == 0 && rvalue(y) < 0) {
result = 0.0;
} else {
result = pow(rvalue(x),rvalue(y));
}
/* Before returning integer result make sure we can. */
/* If the test fails, result is too big for integer. */
if (!real_result)
{
long result_as_long = (long)result;
if (result != (double)result_as_long)
real_result = 1;
}
if (real_result) {
s_return(sc, mk_real(sc, result));
} else {
s_return(sc, mk_integer(sc, result));
}
}
CASE(OP_FLOOR):
x=car(sc->args);
s_return(sc, mk_real(sc, floor(rvalue(x))));
CASE(OP_CEILING):
x=car(sc->args);
s_return(sc, mk_real(sc, ceil(rvalue(x))));
CASE(OP_TRUNCATE ): {
double rvalue_of_x ;
x=car(sc->args);
rvalue_of_x = rvalue(x) ;
if (rvalue_of_x > 0) {
s_return(sc, mk_real(sc, floor(rvalue_of_x)));
} else {
s_return(sc, mk_real(sc, ceil(rvalue_of_x)));
}
}
CASE(OP_ROUND):
x=car(sc->args);
if (num_is_integer(x))
s_return(sc, x);
s_return(sc, mk_real(sc, round_per_R5RS(rvalue(x))));
#endif
CASE(OP_ADD): /* + */
v=num_zero;
for (x = sc->args; x != sc->NIL; x = cdr(x)) {
v=num_add(v,nvalue(car(x)));
}
gc_disable(sc, 1);
s_return_enable_gc(sc, mk_number(sc, v));
CASE(OP_MUL): /* * */
v=num_one;
for (x = sc->args; x != sc->NIL; x = cdr(x)) {
v=num_mul(v,nvalue(car(x)));
}
gc_disable(sc, 1);
s_return_enable_gc(sc, mk_number(sc, v));
CASE(OP_SUB): /* - */
if(cdr(sc->args)==sc->NIL) {
x=sc->args;
v=num_zero;
} else {
x = cdr(sc->args);
v = nvalue(car(sc->args));
}
for (; x != sc->NIL; x = cdr(x)) {
v=num_sub(v,nvalue(car(x)));
}
gc_disable(sc, 1);
s_return_enable_gc(sc, mk_number(sc, v));
CASE(OP_DIV): /* / */
if(cdr(sc->args)==sc->NIL) {
x=sc->args;
v=num_one;
} else {
x = cdr(sc->args);
v = nvalue(car(sc->args));
}
for (; x != sc->NIL; x = cdr(x)) {
if (!is_zero_double(rvalue(car(x))))
v=num_div(v,nvalue(car(x)));
else {
Error_0(sc,"/: division by zero");
}
}
gc_disable(sc, 1);
s_return_enable_gc(sc, mk_number(sc, v));
CASE(OP_INTDIV): /* quotient */
if(cdr(sc->args)==sc->NIL) {
x=sc->args;
v=num_one;
} else {
x = cdr(sc->args);
v = nvalue(car(sc->args));
}
for (; x != sc->NIL; x = cdr(x)) {
if (ivalue(car(x)) != 0)
v=num_intdiv(v,nvalue(car(x)));
else {
Error_0(sc,"quotient: division by zero");
}
}
gc_disable(sc, 1);
s_return_enable_gc(sc, mk_number(sc, v));
CASE(OP_REM): /* remainder */
v = nvalue(car(sc->args));
if (ivalue(cadr(sc->args)) != 0)
v=num_rem(v,nvalue(cadr(sc->args)));
else {
Error_0(sc,"remainder: division by zero");
}
gc_disable(sc, 1);
s_return_enable_gc(sc, mk_number(sc, v));
CASE(OP_MOD): /* modulo */
v = nvalue(car(sc->args));
if (ivalue(cadr(sc->args)) != 0)
v=num_mod(v,nvalue(cadr(sc->args)));
else {
Error_0(sc,"modulo: division by zero");
}
gc_disable(sc, 1);
s_return_enable_gc(sc, mk_number(sc, v));
CASE(OP_CAR): /* car */
s_return(sc,caar(sc->args));
CASE(OP_CDR): /* cdr */
s_return(sc,cdar(sc->args));
CASE(OP_CONS): /* cons */
cdr(sc->args) = cadr(sc->args);
s_return(sc,sc->args);
CASE(OP_SETCAR): /* set-car! */
if(!is_immutable(car(sc->args))) {
caar(sc->args) = cadr(sc->args);
s_return(sc,car(sc->args));
} else {
Error_0(sc,"set-car!: unable to alter immutable pair");
}
CASE(OP_SETCDR): /* set-cdr! */
if(!is_immutable(car(sc->args))) {
cdar(sc->args) = cadr(sc->args);
s_return(sc,car(sc->args));
} else {
Error_0(sc,"set-cdr!: unable to alter immutable pair");
}
CASE(OP_CHAR2INT): { /* char->integer */
char c;
c=(char)ivalue(car(sc->args));
gc_disable(sc, 1);
s_return_enable_gc(sc, mk_integer(sc, (unsigned char) c));
}
CASE(OP_INT2CHAR): { /* integer->char */
unsigned char c;
c=(unsigned char)ivalue(car(sc->args));
gc_disable(sc, 1);
s_return_enable_gc(sc, mk_character(sc, (char) c));
}
CASE(OP_CHARUPCASE): {
unsigned char c;
c=(unsigned char)ivalue(car(sc->args));
c=toupper(c);
gc_disable(sc, 1);
s_return_enable_gc(sc, mk_character(sc, (char) c));
}
CASE(OP_CHARDNCASE): {
unsigned char c;
c=(unsigned char)ivalue(car(sc->args));
c=tolower(c);
gc_disable(sc, 1);
s_return_enable_gc(sc, mk_character(sc, (char) c));
}
CASE(OP_STR2SYM): /* string->symbol */
gc_disable(sc, gc_reservations (mk_symbol));
s_return_enable_gc(sc, mk_symbol(sc, strvalue(car(sc->args))));
CASE(OP_STR2ATOM): /* string->atom */ {
char *s=strvalue(car(sc->args));
long pf = 0;
if(cdr(sc->args)!=sc->NIL) {
/* we know cadr(sc->args) is a natural number */
/* see if it is 2, 8, 10, or 16, or error */
pf = ivalue_unchecked(cadr(sc->args));
if(pf == 16 || pf == 10 || pf == 8 || pf == 2) {
/* base is OK */
}
else {
pf = -1;
}
}
if (pf < 0) {
Error_1(sc, "string->atom: bad base", cadr(sc->args));
} else if(*s=='#') /* no use of base! */ {
s_return(sc, mk_sharp_const(sc, s+1));
} else {
if (pf == 0 || pf == 10) {
s_return(sc, mk_atom(sc, s));
}
else {
char *ep;
long iv = strtol(s,&ep,(int )pf);
if (*ep == 0) {
s_return(sc, mk_integer(sc, iv));
}
else {
s_return(sc, sc->F);
}
}
}
}
CASE(OP_SYM2STR): /* symbol->string */
gc_disable(sc, 1);
x=mk_string(sc,symname(car(sc->args)));
setimmutable(x);
s_return_enable_gc(sc, x);
CASE(OP_ATOM2STR): /* atom->string */ {
long pf = 0;
x=car(sc->args);
if(cdr(sc->args)!=sc->NIL) {
/* we know cadr(sc->args) is a natural number */
/* see if it is 2, 8, 10, or 16, or error */
pf = ivalue_unchecked(cadr(sc->args));
if(is_number(x) && (pf == 16 || pf == 10 || pf == 8 || pf == 2)) {
/* base is OK */
}
else {
pf = -1;
}
}
if (pf < 0) {
Error_1(sc, "atom->string: bad base", cadr(sc->args));
} else if(is_number(x) || is_character(x) || is_string(x) || is_symbol(x)) {
char *p;
int len;
atom2str(sc,x,(int )pf,&p,&len);
gc_disable(sc, 1);
s_return_enable_gc(sc, mk_counted_string(sc, p, len));
} else {
Error_1(sc, "atom->string: not an atom", x);
}
}
CASE(OP_MKSTRING): { /* make-string */
int fill=' ';
int len;
len=ivalue(car(sc->args));
if(cdr(sc->args)!=sc->NIL) {
fill=charvalue(cadr(sc->args));
}
gc_disable(sc, 1);
s_return_enable_gc(sc, mk_empty_string(sc, len, (char) fill));
}
CASE(OP_STRLEN): /* string-length */
gc_disable(sc, 1);
s_return_enable_gc(sc, mk_integer(sc, strlength(car(sc->args))));
CASE(OP_STRREF): { /* string-ref */
char *str;
int index;
str=strvalue(car(sc->args));
index=ivalue(cadr(sc->args));
if(index>=strlength(car(sc->args))) {
Error_1(sc, "string-ref: out of bounds", cadr(sc->args));
}
gc_disable(sc, 1);
s_return_enable_gc(sc,
mk_character(sc, ((unsigned char*) str)[index]));
}
CASE(OP_STRSET): { /* string-set! */
char *str;
int index;
int c;
if(is_immutable(car(sc->args))) {
Error_1(sc, "string-set!: unable to alter immutable string",
car(sc->args));
}
str=strvalue(car(sc->args));
index=ivalue(cadr(sc->args));
if(index>=strlength(car(sc->args))) {
Error_1(sc, "string-set!: out of bounds", cadr(sc->args));
}
c=charvalue(caddr(sc->args));
str[index]=(char)c;
s_return(sc,car(sc->args));
}
CASE(OP_STRAPPEND): { /* string-append */
/* in 1.29 string-append was in Scheme in init.scm but was too slow */
int len = 0;
pointer newstr;
char *pos;
/* compute needed length for new string */
for (x = sc->args; x != sc->NIL; x = cdr(x)) {
len += strlength(car(x));
}
gc_disable(sc, 1);
newstr = mk_empty_string(sc, len, ' ');
/* store the contents of the argument strings into the new string */
for (pos = strvalue(newstr), x = sc->args; x != sc->NIL;
pos += strlength(car(x)), x = cdr(x)) {
memcpy(pos, strvalue(car(x)), strlength(car(x)));
}
s_return_enable_gc(sc, newstr);
}
CASE(OP_SUBSTR): { /* substring */
char *str;
int index0;
int index1;
str=strvalue(car(sc->args));
index0=ivalue(cadr(sc->args));
if(index0>strlength(car(sc->args))) {
Error_1(sc, "substring: start out of bounds", cadr(sc->args));
}
if(cddr(sc->args)!=sc->NIL) {
index1=ivalue(caddr(sc->args));
if(index1>strlength(car(sc->args)) || index1args));
}
} else {
index1=strlength(car(sc->args));
}
gc_disable(sc, 1);
s_return_enable_gc(sc, mk_counted_string(sc, str + index0, index1 - index0));
}
CASE(OP_VECTOR): { /* vector */
int i;
pointer vec;
int len=list_length(sc,sc->args);
if(len<0) {
Error_1(sc, "vector: not a proper list", sc->args);
}
vec=mk_vector(sc,len);
if(sc->no_memory) { s_return(sc, sc->sink); }
for (x = sc->args, i = 0; is_pair(x); x = cdr(x), i++) {
set_vector_elem(vec,i,car(x));
}
s_return(sc,vec);
}
CASE(OP_MKVECTOR): { /* make-vector */
pointer fill=sc->NIL;
int len;
pointer vec;
len=ivalue(car(sc->args));
if(cdr(sc->args)!=sc->NIL) {
fill=cadr(sc->args);
}
vec=mk_vector(sc,len);
if(sc->no_memory) { s_return(sc, sc->sink); }
if(fill!=sc->NIL) {
fill_vector(vec,fill);
}
s_return(sc,vec);
}
CASE(OP_VECLEN): /* vector-length */
gc_disable(sc, 1);
s_return_enable_gc(sc, mk_integer(sc, vector_length(car(sc->args))));
CASE(OP_VECREF): { /* vector-ref */
int index;
index=ivalue(cadr(sc->args));
if(index >= vector_length(car(sc->args))) {
Error_1(sc, "vector-ref: out of bounds", cadr(sc->args));
}
s_return(sc,vector_elem(car(sc->args),index));
}
CASE(OP_VECSET): { /* vector-set! */
int index;
if(is_immutable(car(sc->args))) {
Error_1(sc, "vector-set!: unable to alter immutable vector",
car(sc->args));
}
index=ivalue(cadr(sc->args));
if(index >= vector_length(car(sc->args))) {
Error_1(sc, "vector-set!: out of bounds", cadr(sc->args));
}
set_vector_elem(car(sc->args),index,caddr(sc->args));
s_return(sc,car(sc->args));
}
CASE(OP_NOT): /* not */
s_retbool(is_false(car(sc->args)));
CASE(OP_BOOLP): /* boolean? */
s_retbool(car(sc->args) == sc->F || car(sc->args) == sc->T);
CASE(OP_EOFOBJP): /* boolean? */
s_retbool(car(sc->args) == sc->EOF_OBJ);
CASE(OP_NULLP): /* null? */
s_retbool(car(sc->args) == sc->NIL);
CASE(OP_NUMEQ): /* = */
CASE(OP_LESS): /* < */
CASE(OP_GRE): /* > */
CASE(OP_LEQ): /* <= */
CASE(OP_GEQ): /* >= */
switch(op) {
case OP_NUMEQ: comp_func=num_eq; break;
case OP_LESS: comp_func=num_lt; break;
case OP_GRE: comp_func=num_gt; break;
case OP_LEQ: comp_func=num_le; break;
case OP_GEQ: comp_func=num_ge; break;
default: assert (! "reached");
}
x=sc->args;
v=nvalue(car(x));
x=cdr(x);
for (; x != sc->NIL; x = cdr(x)) {
if(!comp_func(v,nvalue(car(x)))) {
s_retbool(0);
}
v=nvalue(car(x));
}
s_retbool(1);
CASE(OP_SYMBOLP): /* symbol? */
s_retbool(is_symbol(car(sc->args)));
CASE(OP_NUMBERP): /* number? */
s_retbool(is_number(car(sc->args)));
CASE(OP_STRINGP): /* string? */
s_retbool(is_string(car(sc->args)));
CASE(OP_INTEGERP): /* integer? */
s_retbool(is_integer(car(sc->args)));
CASE(OP_REALP): /* real? */
s_retbool(is_number(car(sc->args))); /* All numbers are real */
CASE(OP_CHARP): /* char? */
s_retbool(is_character(car(sc->args)));
#if USE_CHAR_CLASSIFIERS
CASE(OP_CHARAP): /* char-alphabetic? */
s_retbool(Cisalpha(ivalue(car(sc->args))));
CASE(OP_CHARNP): /* char-numeric? */
s_retbool(Cisdigit(ivalue(car(sc->args))));
CASE(OP_CHARWP): /* char-whitespace? */
s_retbool(Cisspace(ivalue(car(sc->args))));
CASE(OP_CHARUP): /* char-upper-case? */
s_retbool(Cisupper(ivalue(car(sc->args))));
CASE(OP_CHARLP): /* char-lower-case? */
s_retbool(Cislower(ivalue(car(sc->args))));
#endif
CASE(OP_PORTP): /* port? */
s_retbool(is_port(car(sc->args)));
CASE(OP_INPORTP): /* input-port? */
s_retbool(is_inport(car(sc->args)));
CASE(OP_OUTPORTP): /* output-port? */
s_retbool(is_outport(car(sc->args)));
CASE(OP_PROCP): /* procedure? */
/*--
* continuation should be procedure by the example
* (call-with-current-continuation procedure?) ==> #t
* in R^3 report sec. 6.9
*/
s_retbool(is_proc(car(sc->args)) || is_closure(car(sc->args))
|| is_continuation(car(sc->args)) || is_foreign(car(sc->args)));
CASE(OP_PAIRP): /* pair? */
s_retbool(is_pair(car(sc->args)));
CASE(OP_LISTP): /* list? */
s_retbool(list_length(sc,car(sc->args)) >= 0);
CASE(OP_ENVP): /* environment? */
s_retbool(is_environment(car(sc->args)));
CASE(OP_VECTORP): /* vector? */
s_retbool(is_vector(car(sc->args)));
CASE(OP_EQ): /* eq? */
s_retbool(car(sc->args) == cadr(sc->args));
CASE(OP_EQV): /* eqv? */
s_retbool(eqv(car(sc->args), cadr(sc->args)));
CASE(OP_FORCE): /* force */
sc->code = car(sc->args);
if (is_promise(sc->code)) {
/* Should change type to closure here */
s_save(sc, OP_SAVE_FORCED, sc->NIL, sc->code);
sc->args = sc->NIL;
s_thread_to(sc,OP_APPLY);
} else {
s_return(sc,sc->code);
}
CASE(OP_SAVE_FORCED): /* Save forced value replacing promise */
copy_value(sc, sc->code, sc->value);
s_return(sc,sc->value);
CASE(OP_WRITE): /* write */
CASE(OP_DISPLAY): /* display */
CASE(OP_WRITE_CHAR): /* write-char */
if(is_pair(cdr(sc->args))) {
if(cadr(sc->args)!=sc->outport) {
x=cons(sc,sc->outport,sc->NIL);
s_save(sc,OP_SET_OUTPORT, x, sc->NIL);
sc->outport=cadr(sc->args);
}
}
sc->args = car(sc->args);
if(op==OP_WRITE) {
sc->print_flag = 1;
} else {
sc->print_flag = 0;
}
s_thread_to(sc,OP_P0LIST);
CASE(OP_NEWLINE): /* newline */
if(is_pair(sc->args)) {
if(car(sc->args)!=sc->outport) {
x=cons(sc,sc->outport,sc->NIL);
s_save(sc,OP_SET_OUTPORT, x, sc->NIL);
sc->outport=car(sc->args);
}
}
putstr(sc, "\n");
s_return(sc,sc->T);
CASE(OP_ERR0): /* error */
sc->retcode=-1;
if (!is_string(car(sc->args))) {
sc->args=cons(sc,mk_string(sc," -- "),sc->args);
setimmutable(car(sc->args));
}
putstr(sc, "Error: ");
putstr(sc, strvalue(car(sc->args)));
sc->args = cdr(sc->args);
s_thread_to(sc,OP_ERR1);
CASE(OP_ERR1): /* error */
putstr(sc, " ");
if (sc->args != sc->NIL) {
s_save(sc,OP_ERR1, cdr(sc->args), sc->NIL);
sc->args = car(sc->args);
sc->print_flag = 1;
s_thread_to(sc,OP_P0LIST);
} else {
putstr(sc, "\n");
if(sc->interactive_repl) {
s_thread_to(sc,OP_T0LVL);
} else {
return;
}
}
CASE(OP_REVERSE): /* reverse */
s_return(sc,reverse(sc, sc->NIL, car(sc->args)));
CASE(OP_REVERSE_IN_PLACE): /* reverse! */
s_return(sc, reverse_in_place(sc, sc->NIL, car(sc->args)));
CASE(OP_LIST_STAR): /* list* */
s_return(sc,list_star(sc,sc->args));
CASE(OP_APPEND): /* append */
x = sc->NIL;
y = sc->args;
if (y == x) {
s_return(sc, x);
}
/* cdr() in the while condition is not a typo. If car() */
/* is used (append '() 'a) will return the wrong result.*/
while (cdr(y) != sc->NIL) {
x = revappend(sc, x, car(y));
y = cdr(y);
if (x == sc->F) {
Error_0(sc, "non-list argument to append");
}
}
s_return(sc, reverse_in_place(sc, car(y), x));
#if USE_PLIST
CASE(OP_SET_SYMBOL_PROPERTY): /* set-symbol-property! */
gc_disable(sc, gc_reservations(set_property));
s_return_enable_gc(sc,
set_property(sc, car(sc->args),
cadr(sc->args), caddr(sc->args)));
CASE(OP_SYMBOL_PROPERTY): /* symbol-property */
s_return(sc, get_property(sc, car(sc->args), cadr(sc->args)));
#endif /* USE_PLIST */
CASE(OP_TAG_VALUE): { /* not exposed */
/* This tags sc->value with car(sc->args). Useful to tag
* results of opcode evaluations. */
pointer a, b, c;
free_cons(sc, sc->args, &a, &b);
free_cons(sc, b, &b, &c);
assert(c == sc->NIL);
s_return(sc, mk_tagged_value(sc, sc->value, a, b));
}
CASE(OP_MK_TAGGED): /* make-tagged-value */
if (is_vector(car(sc->args)))
Error_0(sc, "cannot tag vector");
s_return(sc, mk_tagged_value(sc, car(sc->args),
car(cadr(sc->args)),
cdr(cadr(sc->args))));
CASE(OP_GET_TAG): /* get-tag */
s_return(sc, get_tag(sc, car(sc->args)));
CASE(OP_QUIT): /* quit */
if(is_pair(sc->args)) {
sc->retcode=ivalue(car(sc->args));
}
return;
CASE(OP_GC): /* gc */
gc(sc, sc->NIL, sc->NIL);
s_return(sc,sc->T);
CASE(OP_GCVERB): /* gc-verbose */
{ int was = sc->gc_verbose;
sc->gc_verbose = (car(sc->args) != sc->F);
s_retbool(was);
}
CASE(OP_NEWSEGMENT): /* new-segment */
if (!is_pair(sc->args) || !is_number(car(sc->args))) {
Error_0(sc,"new-segment: argument must be a number");
}
alloc_cellseg(sc, (int) ivalue(car(sc->args)));
s_return(sc,sc->T);
CASE(OP_OBLIST): /* oblist */
s_return(sc, oblist_all_symbols(sc));
CASE(OP_CURR_INPORT): /* current-input-port */
s_return(sc,sc->inport);
CASE(OP_CURR_OUTPORT): /* current-output-port */
s_return(sc,sc->outport);
CASE(OP_OPEN_INFILE): /* open-input-file */
CASE(OP_OPEN_OUTFILE): /* open-output-file */
CASE(OP_OPEN_INOUTFILE): /* open-input-output-file */ {
int prop=0;
pointer p;
switch(op) {
case OP_OPEN_INFILE: prop=port_input; break;
case OP_OPEN_OUTFILE: prop=port_output; break;
case OP_OPEN_INOUTFILE: prop=port_input|port_output; break;
default: assert (! "reached");
}
p=port_from_filename(sc,strvalue(car(sc->args)),prop);
if(p==sc->NIL) {
s_return(sc,sc->F);
}
s_return(sc,p);
break;
}
#if USE_STRING_PORTS
CASE(OP_OPEN_INSTRING): /* open-input-string */
CASE(OP_OPEN_INOUTSTRING): /* open-input-output-string */ {
int prop=0;
pointer p;
switch(op) {
case OP_OPEN_INSTRING: prop=port_input; break;
case OP_OPEN_INOUTSTRING: prop=port_input|port_output; break;
default: assert (! "reached");
}
p=port_from_string(sc, strvalue(car(sc->args)),
strvalue(car(sc->args))+strlength(car(sc->args)), prop);
if(p==sc->NIL) {
s_return(sc,sc->F);
}
s_return(sc,p);
}
CASE(OP_OPEN_OUTSTRING): /* open-output-string */ {
pointer p;
if(car(sc->args)==sc->NIL) {
p=port_from_scratch(sc);
if(p==sc->NIL) {
s_return(sc,sc->F);
}
} else {
p=port_from_string(sc, strvalue(car(sc->args)),
strvalue(car(sc->args))+strlength(car(sc->args)),
port_output);
if(p==sc->NIL) {
s_return(sc,sc->F);
}
}
s_return(sc,p);
}
CASE(OP_GET_OUTSTRING): /* get-output-string */ {
port *p;
if ((p=car(sc->args)->_object._port)->kind&port_string) {
gc_disable(sc, 1);
s_return_enable_gc(
sc,
mk_counted_string(sc,
p->rep.string.start,
p->rep.string.curr - p->rep.string.start));
}
s_return(sc,sc->F);
}
#endif
CASE(OP_CLOSE_INPORT): /* close-input-port */
port_close(sc,car(sc->args),port_input);
s_return(sc,sc->T);
CASE(OP_CLOSE_OUTPORT): /* close-output-port */
port_close(sc,car(sc->args),port_output);
s_return(sc,sc->T);
CASE(OP_INT_ENV): /* interaction-environment */
s_return(sc,sc->global_env);
CASE(OP_CURR_ENV): /* current-environment */
s_return(sc,sc->envir);
/* ========== reading part ========== */
CASE(OP_READ):
if(!is_pair(sc->args)) {
s_thread_to(sc,OP_READ_INTERNAL);
}
if(!is_inport(car(sc->args))) {
Error_1(sc, "read: not an input port", car(sc->args));
}
if(car(sc->args)==sc->inport) {
s_thread_to(sc,OP_READ_INTERNAL);
}
x=sc->inport;
sc->inport=car(sc->args);
x=cons(sc,x,sc->NIL);
s_save(sc,OP_SET_INPORT, x, sc->NIL);
s_thread_to(sc,OP_READ_INTERNAL);
CASE(OP_READ_CHAR): /* read-char */
CASE(OP_PEEK_CHAR): /* peek-char */ {
int c;
if(is_pair(sc->args)) {
if(car(sc->args)!=sc->inport) {
x=sc->inport;
x=cons(sc,x,sc->NIL);
s_save(sc,OP_SET_INPORT, x, sc->NIL);
sc->inport=car(sc->args);
}
}
c=inchar(sc);
if(c==EOF) {
s_return(sc,sc->EOF_OBJ);
}
if(op==OP_PEEK_CHAR) {
backchar(sc,c);
}
s_return(sc,mk_character(sc,c));
}
CASE(OP_CHAR_READY): /* char-ready? */ {
pointer p=sc->inport;
int res;
if(is_pair(sc->args)) {
p=car(sc->args);
}
res=p->_object._port->kind&port_string;
s_retbool(res);
}
CASE(OP_SET_INPORT): /* set-input-port */
sc->inport=car(sc->args);
s_return(sc,sc->value);
CASE(OP_SET_OUTPORT): /* set-output-port */
sc->outport=car(sc->args);
s_return(sc,sc->value);
CASE(OP_RDSEXPR):
switch (sc->tok) {
case TOK_EOF:
s_return(sc,sc->EOF_OBJ);
/* NOTREACHED */
case TOK_VEC:
s_save(sc,OP_RDVEC,sc->NIL,sc->NIL);
/* fall through */
case TOK_LPAREN:
sc->tok = token(sc);
if (sc->tok == TOK_RPAREN) {
s_return(sc,sc->NIL);
} else if (sc->tok == TOK_DOT) {
Error_0(sc,"syntax error: illegal dot expression");
} else {
#if SHOW_ERROR_LINE
pointer filename;
pointer lineno;
#endif
sc->nesting_stack[sc->file_i]++;
#if SHOW_ERROR_LINE
filename = sc->load_stack[sc->file_i].filename;
lineno = sc->load_stack[sc->file_i].curr_line;
s_save(sc, OP_TAG_VALUE,
cons(sc, filename, cons(sc, lineno, sc->NIL)),
sc->NIL);
#endif
s_save(sc,OP_RDLIST, sc->NIL, sc->NIL);
s_thread_to(sc,OP_RDSEXPR);
}
case TOK_QUOTE:
s_save(sc,OP_RDQUOTE, sc->NIL, sc->NIL);
sc->tok = token(sc);
s_thread_to(sc,OP_RDSEXPR);
case TOK_BQUOTE:
sc->tok = token(sc);
if(sc->tok==TOK_VEC) {
s_save(sc,OP_RDQQUOTEVEC, sc->NIL, sc->NIL);
sc->tok=TOK_LPAREN;
s_thread_to(sc,OP_RDSEXPR);
} else {
s_save(sc,OP_RDQQUOTE, sc->NIL, sc->NIL);
}
s_thread_to(sc,OP_RDSEXPR);
case TOK_COMMA:
s_save(sc,OP_RDUNQUOTE, sc->NIL, sc->NIL);
sc->tok = token(sc);
s_thread_to(sc,OP_RDSEXPR);
case TOK_ATMARK:
s_save(sc,OP_RDUQTSP, sc->NIL, sc->NIL);
sc->tok = token(sc);
s_thread_to(sc,OP_RDSEXPR);
case TOK_ATOM:
s_return(sc,mk_atom(sc, readstr_upto(sc, DELIMITERS)));
case TOK_DQUOTE:
x=readstrexp(sc);
if(x==sc->F) {
Error_0(sc,"Error reading string");
}
setimmutable(x);
s_return(sc,x);
case TOK_SHARP: {
pointer f=find_slot_in_env(sc,sc->envir,sc->SHARP_HOOK,1);
if(f==sc->NIL) {
Error_0(sc,"undefined sharp expression");
} else {
sc->code=cons(sc,slot_value_in_env(f),sc->NIL);
s_thread_to(sc,OP_EVAL);
}
}
case TOK_SHARP_CONST:
if ((x = mk_sharp_const(sc, readstr_upto(sc, DELIMITERS))) == sc->NIL) {
Error_0(sc,"undefined sharp expression");
} else {
s_return(sc,x);
}
default:
Error_0(sc,"syntax error: illegal token");
}
break;
CASE(OP_RDLIST): {
gc_disable(sc, 1);
sc->args = cons(sc, sc->value, sc->args);
gc_enable(sc);
sc->tok = token(sc);
if (sc->tok == TOK_EOF)
{ s_return(sc,sc->EOF_OBJ); }
else if (sc->tok == TOK_RPAREN) {
int c = inchar(sc);
if (c != '\n')
backchar(sc,c);
else
port_increment_current_line(sc, &sc->load_stack[sc->file_i], 1);
sc->nesting_stack[sc->file_i]--;
s_return(sc,reverse_in_place(sc, sc->NIL, sc->args));
} else if (sc->tok == TOK_DOT) {
s_save(sc,OP_RDDOT, sc->args, sc->NIL);
sc->tok = token(sc);
s_thread_to(sc,OP_RDSEXPR);
} else {
s_save(sc,OP_RDLIST, sc->args, sc->NIL);;
s_thread_to(sc,OP_RDSEXPR);
}
}
CASE(OP_RDDOT):
if (token(sc) != TOK_RPAREN) {
Error_0(sc,"syntax error: illegal dot expression");
} else {
sc->nesting_stack[sc->file_i]--;
s_return(sc,reverse_in_place(sc, sc->value, sc->args));
}
CASE(OP_RDQUOTE):
gc_disable(sc, 2);
s_return_enable_gc(sc, cons(sc, sc->QUOTE,
cons(sc, sc->value, sc->NIL)));
CASE(OP_RDQQUOTE):
gc_disable(sc, 2);
s_return_enable_gc(sc, cons(sc, sc->QQUOTE,
cons(sc, sc->value, sc->NIL)));
CASE(OP_RDQQUOTEVEC):
gc_disable(sc, 5 + 2 * gc_reservations (mk_symbol));
s_return_enable_gc(sc,cons(sc, mk_symbol(sc,"apply"),
cons(sc, mk_symbol(sc,"vector"),
cons(sc,cons(sc, sc->QQUOTE,
cons(sc,sc->value,sc->NIL)),
sc->NIL))));
CASE(OP_RDUNQUOTE):
gc_disable(sc, 2);
s_return_enable_gc(sc, cons(sc, sc->UNQUOTE,
cons(sc, sc->value, sc->NIL)));
CASE(OP_RDUQTSP):
gc_disable(sc, 2);
s_return_enable_gc(sc, cons(sc, sc->UNQUOTESP,
cons(sc, sc->value, sc->NIL)));
CASE(OP_RDVEC):
/*sc->code=cons(sc,mk_proc(sc,OP_VECTOR),sc->value);
s_thread_to(sc,OP_EVAL); Cannot be quoted*/
/*x=cons(sc,mk_proc(sc,OP_VECTOR),sc->value);
s_return(sc,x); Cannot be part of pairs*/
/*sc->code=mk_proc(sc,OP_VECTOR);
sc->args=sc->value;
s_thread_to(sc,OP_APPLY);*/
sc->args=sc->value;
s_thread_to(sc,OP_VECTOR);
/* ========== printing part ========== */
CASE(OP_P0LIST):
if(is_vector(sc->args)) {
putstr(sc,"#(");
sc->args=cons(sc,sc->args,mk_integer(sc,0));
s_thread_to(sc,OP_PVECFROM);
} else if(is_environment(sc->args)) {
putstr(sc,"#");
s_return(sc,sc->T);
} else if (!is_pair(sc->args)) {
printatom(sc, sc->args, sc->print_flag);
s_return(sc,sc->T);
} else if (car(sc->args) == sc->QUOTE && ok_abbrev(cdr(sc->args))) {
putstr(sc, "'");
sc->args = cadr(sc->args);
s_thread_to(sc,OP_P0LIST);
} else if (car(sc->args) == sc->QQUOTE && ok_abbrev(cdr(sc->args))) {
putstr(sc, "`");
sc->args = cadr(sc->args);
s_thread_to(sc,OP_P0LIST);
} else if (car(sc->args) == sc->UNQUOTE && ok_abbrev(cdr(sc->args))) {
putstr(sc, ",");
sc->args = cadr(sc->args);
s_thread_to(sc,OP_P0LIST);
} else if (car(sc->args) == sc->UNQUOTESP && ok_abbrev(cdr(sc->args))) {
putstr(sc, ",@");
sc->args = cadr(sc->args);
s_thread_to(sc,OP_P0LIST);
} else {
putstr(sc, "(");
s_save(sc,OP_P1LIST, cdr(sc->args), sc->NIL);
sc->args = car(sc->args);
s_thread_to(sc,OP_P0LIST);
}
CASE(OP_P1LIST):
if (is_pair(sc->args)) {
s_save(sc,OP_P1LIST, cdr(sc->args), sc->NIL);
putstr(sc, " ");
sc->args = car(sc->args);
s_thread_to(sc,OP_P0LIST);
} else if(is_vector(sc->args)) {
s_save(sc,OP_P1LIST,sc->NIL,sc->NIL);
putstr(sc, " . ");
s_thread_to(sc,OP_P0LIST);
} else {
if (sc->args != sc->NIL) {
putstr(sc, " . ");
printatom(sc, sc->args, sc->print_flag);
}
putstr(sc, ")");
s_return(sc,sc->T);
}
CASE(OP_PVECFROM): {
int i=ivalue_unchecked(cdr(sc->args));
pointer vec=car(sc->args);
int len = vector_length(vec);
if(i==len) {
putstr(sc,")");
s_return(sc,sc->T);
} else {
pointer elem=vector_elem(vec,i);
cdr(sc->args) = mk_integer(sc, i + 1);
s_save(sc,OP_PVECFROM, sc->args, sc->NIL);
sc->args=elem;
if (i > 0)
putstr(sc," ");
s_thread_to(sc,OP_P0LIST);
}
}
CASE(OP_LIST_LENGTH): { /* length */ /* a.k */
long l = list_length(sc, car(sc->args));
if(l<0) {
Error_1(sc, "length: not a list", car(sc->args));
}
gc_disable(sc, 1);
s_return_enable_gc(sc, mk_integer(sc, l));
}
CASE(OP_ASSQ): /* assq */ /* a.k */
x = car(sc->args);
for (y = cadr(sc->args); is_pair(y); y = cdr(y)) {
if (!is_pair(car(y))) {
Error_0(sc,"unable to handle non pair element");
}
if (x == caar(y))
break;
}
if (is_pair(y)) {
s_return(sc,car(y));
} else {
s_return(sc,sc->F);
}
CASE(OP_GET_CLOSURE): /* get-closure-code */ /* a.k */
sc->args = car(sc->args);
if (sc->args == sc->NIL) {
s_return(sc,sc->F);
} else if (is_closure(sc->args)) {
gc_disable(sc, 1);
s_return_enable_gc(sc, cons(sc, sc->LAMBDA,
closure_code(sc->value)));
} else if (is_macro(sc->args)) {
gc_disable(sc, 1);
s_return_enable_gc(sc, cons(sc, sc->LAMBDA,
closure_code(sc->value)));
} else {
s_return(sc,sc->F);
}
CASE(OP_CLOSUREP): /* closure? */
/*
* Note, macro object is also a closure.
* Therefore, (closure? <#MACRO>) ==> #t
*/
s_retbool(is_closure(car(sc->args)));
CASE(OP_MACROP): /* macro? */
s_retbool(is_macro(car(sc->args)));
CASE(OP_VM_HISTORY): /* *vm-history* */
s_return(sc, history_flatten(sc));
default:
snprintf(sc->strbuff,STRBUFFSIZE,"%d: illegal operator", op);
Error_0(sc,sc->strbuff);
}
}
}
typedef int (*test_predicate)(pointer);
static int is_any(pointer p) {
(void)p;
return 1;
}
static int is_nonneg(pointer p) {
return ivalue(p)>=0 && is_integer(p);
}
/* Correspond carefully with following defines! */
static const struct {
test_predicate fct;
const char *kind;
} tests[]={
{0,0}, /* unused */
{is_any, 0},
{is_string, "string"},
{is_symbol, "symbol"},
{is_port, "port"},
{is_inport,"input port"},
{is_outport,"output port"},
{is_environment, "environment"},
{is_pair, "pair"},
{0, "pair or '()"},
{is_character, "character"},
{is_vector, "vector"},
{is_number, "number"},
{is_integer, "integer"},
{is_nonneg, "non-negative integer"}
};
#define TST_NONE 0
#define TST_ANY "\001"
#define TST_STRING "\002"
#define TST_SYMBOL "\003"
#define TST_PORT "\004"
#define TST_INPORT "\005"
#define TST_OUTPORT "\006"
#define TST_ENVIRONMENT "\007"
#define TST_PAIR "\010"
#define TST_LIST "\011"
#define TST_CHAR "\012"
#define TST_VECTOR "\013"
#define TST_NUMBER "\014"
#define TST_INTEGER "\015"
#define TST_NATURAL "\016"
#define INF_ARG 0xff
static const struct op_code_info dispatch_table[]= {
#define _OP_DEF(A,B,C,D,OP) {{A},B,C,{D}},
#include "opdefines.h"
#undef _OP_DEF
{{0},0,0,{0}},
};
static const char *procname(pointer x) {
int n=procnum(x);
const char *name=dispatch_table[n].name;
if (name[0] == 0) {
name="ILLEGAL!";
}
return name;
}
static int
check_arguments (scheme *sc, const struct op_code_info *pcd, char *msg, size_t msg_size)
{
int ok = 1;
int n = list_length(sc, sc->args);
/* Check number of arguments */
if (n < pcd->min_arity) {
ok = 0;
snprintf(msg, msg_size, "%s: needs%s %d argument(s)",
pcd->name,
pcd->min_arity == pcd->max_arity ? "" : " at least",
pcd->min_arity);
}
if (ok && n>pcd->max_arity) {
ok = 0;
snprintf(msg, msg_size, "%s: needs%s %d argument(s)",
pcd->name,
pcd->min_arity == pcd->max_arity ? "" : " at most",
pcd->max_arity);
}
if (ok) {
if (pcd->arg_tests_encoding[0] != 0) {
int i = 0;
int j;
const char *t = pcd->arg_tests_encoding;
pointer arglist = sc->args;
do {
pointer arg = car(arglist);
j = (int)t[0];
if (j == TST_LIST[0]) {
if (arg != sc->NIL && !is_pair(arg)) break;
} else {
if (!tests[j].fct(arg)) break;
}
if (t[1] != 0 && i < sizeof pcd->arg_tests_encoding) {
/* last test is replicated as necessary */
t++;
}
arglist = cdr(arglist);
i++;
} while (i < n);
if (i < n) {
ok = 0;
snprintf(msg, msg_size, "%s: argument %d must be: %s, got: %s",
pcd->name,
i + 1,
tests[j].kind,
type_to_string(type(car(arglist))));
}
}
}
return ok;
}
/* ========== Initialization of internal keywords ========== */
/* Symbols representing syntax are tagged with (OP . '()). */
static void assign_syntax(scheme *sc, enum scheme_opcodes op, char *name) {
pointer x, y;
pointer *slot;
x = oblist_find_by_name(sc, name, &slot);
assert (x == sc->NIL);
x = immutable_cons(sc, mk_string(sc, name), sc->NIL);
typeflag(x) = T_SYMBOL | T_SYNTAX;
setimmutable(car(x));
y = mk_tagged_value(sc, x, mk_integer(sc, op), sc->NIL);
free_cell(sc, x);
setimmutable(get_tag(sc, y));
*slot = immutable_cons(sc, y, *slot);
}
/* Returns the opcode for the syntax represented by P. */
static int syntaxnum(scheme *sc, pointer p) {
int op = ivalue_unchecked(car(get_tag(sc, p)));
assert (op < OP_MAXDEFINED);
return op;
}
static void assign_proc(scheme *sc, enum scheme_opcodes op, const char *name) {
pointer x, y;
x = mk_symbol(sc, name);
y = mk_proc(sc,op);
new_slot_in_env(sc, x, y);
}
static pointer mk_proc(scheme *sc, enum scheme_opcodes op) {
pointer y;
y = get_cell(sc, sc->NIL, sc->NIL);
typeflag(y) = (T_PROC | T_ATOM);
ivalue_unchecked(y) = (long) op;
set_num_integer(y);
return y;
}
/* initialization of TinyScheme */
#if USE_INTERFACE
INTERFACE static pointer s_cons(scheme *sc, pointer a, pointer b) {
return cons(sc,a,b);
}
INTERFACE static pointer s_immutable_cons(scheme *sc, pointer a, pointer b) {
return immutable_cons(sc,a,b);
}
static const struct scheme_interface vtbl = {
scheme_define,
s_cons,
s_immutable_cons,
reserve_cells,
mk_integer,
mk_real,
mk_symbol,
gensym,
mk_string,
mk_counted_string,
mk_character,
mk_vector,
mk_foreign_func,
mk_foreign_object,
get_foreign_object_vtable,
get_foreign_object_data,
putstr,
putcharacter,
is_string,
string_value,
is_number,
nvalue,
ivalue,
rvalue,
is_integer,
is_real,
is_character,
charvalue,
is_list,
is_vector,
list_length,
ivalue,
fill_vector,
vector_elem,
set_vector_elem,
is_port,
is_pair,
pair_car,
pair_cdr,
set_car,
set_cdr,
is_symbol,
symname,
is_syntax,
is_proc,
is_foreign,
syntaxname,
is_closure,
is_macro,
closure_code,
closure_env,
is_continuation,
is_promise,
is_environment,
is_immutable,
setimmutable,
scheme_load_file,
scheme_load_string,
port_from_file
};
#endif
scheme *scheme_init_new() {
scheme *sc=(scheme*)malloc(sizeof(scheme));
if(!scheme_init(sc)) {
free(sc);
return 0;
} else {
return sc;
}
}
scheme *scheme_init_new_custom_alloc(func_alloc malloc, func_dealloc free) {
scheme *sc=(scheme*)malloc(sizeof(scheme));
if(!scheme_init_custom_alloc(sc,malloc,free)) {
free(sc);
return 0;
} else {
return sc;
}
}
int scheme_init(scheme *sc) {
return scheme_init_custom_alloc(sc,malloc,free);
}
int scheme_init_custom_alloc(scheme *sc, func_alloc malloc, func_dealloc free) {
int i, n=sizeof(dispatch_table)/sizeof(dispatch_table[0]);
pointer x;
#if USE_INTERFACE
sc->vptr=&vtbl;
#endif
sc->gensym_cnt=0;
sc->malloc=malloc;
sc->free=free;
sc->sink = &sc->_sink;
sc->NIL = &sc->_NIL;
sc->T = &sc->_HASHT;
sc->F = &sc->_HASHF;
sc->EOF_OBJ=&sc->_EOF_OBJ;
sc->free_cell = &sc->_NIL;
sc->fcells = 0;
sc->inhibit_gc = GC_ENABLED;
sc->reserved_cells = 0;
sc->reserved_lineno = 0;
sc->no_memory=0;
sc->inport=sc->NIL;
sc->outport=sc->NIL;
sc->save_inport=sc->NIL;
sc->loadport=sc->NIL;
sc->nesting=0;
memset (sc->nesting_stack, 0, sizeof sc->nesting_stack);
sc->interactive_repl=0;
sc->strbuff = sc->malloc(STRBUFFSIZE);
if (sc->strbuff == 0) {
sc->no_memory=1;
return 0;
}
sc->strbuff_size = STRBUFFSIZE;
sc->cell_segments = NULL;
if (alloc_cellseg(sc,FIRST_CELLSEGS) != FIRST_CELLSEGS) {
sc->no_memory=1;
return 0;
}
sc->gc_verbose = 0;
dump_stack_initialize(sc);
sc->code = sc->NIL;
sc->tracing=0;
sc->flags = 0;
/* init sc->NIL */
typeflag(sc->NIL) = (T_NIL | T_ATOM | MARK);
car(sc->NIL) = cdr(sc->NIL) = sc->NIL;
/* init T */
typeflag(sc->T) = (T_BOOLEAN | T_ATOM | MARK);
car(sc->T) = cdr(sc->T) = sc->T;
/* init F */
typeflag(sc->F) = (T_BOOLEAN | T_ATOM | MARK);
car(sc->F) = cdr(sc->F) = sc->F;
/* init EOF_OBJ */
typeflag(sc->EOF_OBJ) = (T_EOF_OBJ | T_ATOM | MARK);
car(sc->EOF_OBJ) = cdr(sc->EOF_OBJ) = sc->EOF_OBJ;
/* init sink */
typeflag(sc->sink) = (T_SINK | T_PAIR | MARK);
car(sc->sink) = cdr(sc->sink) = sc->NIL;
/* init c_nest */
sc->c_nest = sc->NIL;
sc->oblist = oblist_initial_value(sc);
/* init global_env */
new_frame_in_env(sc, sc->NIL);
sc->global_env = sc->envir;
/* init else */
x = mk_symbol(sc,"else");
new_slot_in_env(sc, x, sc->T);
assign_syntax(sc, OP_LAMBDA, "lambda");
assign_syntax(sc, OP_QUOTE, "quote");
assign_syntax(sc, OP_DEF0, "define");
assign_syntax(sc, OP_IF0, "if");
assign_syntax(sc, OP_BEGIN, "begin");
assign_syntax(sc, OP_SET0, "set!");
assign_syntax(sc, OP_LET0, "let");
assign_syntax(sc, OP_LET0AST, "let*");
assign_syntax(sc, OP_LET0REC, "letrec");
assign_syntax(sc, OP_COND0, "cond");
assign_syntax(sc, OP_DELAY, "delay");
assign_syntax(sc, OP_AND0, "and");
assign_syntax(sc, OP_OR0, "or");
assign_syntax(sc, OP_C0STREAM, "cons-stream");
assign_syntax(sc, OP_MACRO0, "macro");
assign_syntax(sc, OP_CASE0, "case");
for(i=0; iLAMBDA = mk_symbol(sc, "lambda");
sc->QUOTE = mk_symbol(sc, "quote");
sc->QQUOTE = mk_symbol(sc, "quasiquote");
sc->UNQUOTE = mk_symbol(sc, "unquote");
sc->UNQUOTESP = mk_symbol(sc, "unquote-splicing");
sc->FEED_TO = mk_symbol(sc, "=>");
sc->COLON_HOOK = mk_symbol(sc,"*colon-hook*");
sc->ERROR_HOOK = mk_symbol(sc, "*error-hook*");
sc->SHARP_HOOK = mk_symbol(sc, "*sharp-hook*");
#if USE_COMPILE_HOOK
sc->COMPILE_HOOK = mk_symbol(sc, "*compile-hook*");
#endif
return !sc->no_memory;
}
void scheme_set_input_port_file(scheme *sc, FILE *fin) {
sc->inport=port_from_file(sc,fin,port_input);
}
void scheme_set_input_port_string(scheme *sc, char *start, char *past_the_end) {
sc->inport=port_from_string(sc,start,past_the_end,port_input);
}
void scheme_set_output_port_file(scheme *sc, FILE *fout) {
sc->outport=port_from_file(sc,fout,port_output);
}
void scheme_set_output_port_string(scheme *sc, char *start, char *past_the_end) {
sc->outport=port_from_string(sc,start,past_the_end,port_output);
}
void scheme_set_external_data(scheme *sc, void *p) {
sc->ext_data=p;
}
void scheme_deinit(scheme *sc) {
struct cell_segment *s;
int i;
sc->oblist=sc->NIL;
sc->global_env=sc->NIL;
dump_stack_free(sc);
sc->envir=sc->NIL;
sc->code=sc->NIL;
history_free(sc);
sc->args=sc->NIL;
sc->value=sc->NIL;
if(is_port(sc->inport)) {
typeflag(sc->inport) = T_ATOM;
}
sc->inport=sc->NIL;
sc->outport=sc->NIL;
if(is_port(sc->save_inport)) {
typeflag(sc->save_inport) = T_ATOM;
}
sc->save_inport=sc->NIL;
if(is_port(sc->loadport)) {
typeflag(sc->loadport) = T_ATOM;
}
sc->loadport=sc->NIL;
for(i=0; i<=sc->file_i; i++) {
port_clear_location(sc, &sc->load_stack[i]);
}
sc->gc_verbose=0;
gc(sc,sc->NIL,sc->NIL);
for (s = sc->cell_segments; s; s = _dealloc_cellseg(sc, s)) {
/* nop */
}
sc->free(sc->strbuff);
}
void scheme_load_file(scheme *sc, FILE *fin)
{ scheme_load_named_file(sc,fin,0); }
void scheme_load_named_file(scheme *sc, FILE *fin, const char *filename) {
dump_stack_reset(sc);
sc->envir = sc->global_env;
sc->file_i=0;
sc->load_stack[0].kind=port_input|port_file;
sc->load_stack[0].rep.stdio.file=fin;
sc->loadport=mk_port(sc,sc->load_stack);
sc->retcode=0;
if(fin==stdin) {
sc->interactive_repl=1;
}
port_init_location(sc, &sc->load_stack[0],
(fin != stdin && filename)
? mk_string(sc, filename)
: NULL);
sc->inport=sc->loadport;
sc->args = mk_integer(sc,sc->file_i);
Eval_Cycle(sc, OP_T0LVL);
typeflag(sc->loadport)=T_ATOM;
if(sc->retcode==0) {
sc->retcode=sc->nesting!=0;
}
port_clear_location(sc, &sc->load_stack[0]);
}
void scheme_load_string(scheme *sc, const char *cmd) {
scheme_load_memory(sc, cmd, strlen(cmd), NULL);
}
void scheme_load_memory(scheme *sc, const char *buf, size_t len, const char *filename) {
dump_stack_reset(sc);
sc->envir = sc->global_env;
sc->file_i=0;
sc->load_stack[0].kind=port_input|port_string;
sc->load_stack[0].rep.string.start = (char *) buf; /* This func respects const */
sc->load_stack[0].rep.string.past_the_end = (char *) buf + len;
sc->load_stack[0].rep.string.curr = (char *) buf;
port_init_location(sc, &sc->load_stack[0], filename ? mk_string(sc, filename) : NULL);
sc->loadport=mk_port(sc,sc->load_stack);
sc->retcode=0;
sc->interactive_repl=0;
sc->inport=sc->loadport;
sc->args = mk_integer(sc,sc->file_i);
Eval_Cycle(sc, OP_T0LVL);
typeflag(sc->loadport)=T_ATOM;
if(sc->retcode==0) {
sc->retcode=sc->nesting!=0;
}
port_clear_location(sc, &sc->load_stack[0]);
}
void scheme_define(scheme *sc, pointer envir, pointer symbol, pointer value) {
pointer x;
pointer *sslot;
x = find_slot_spec_in_env(sc, envir, symbol, 0, &sslot);
if (x != sc->NIL) {
set_slot_in_env(sc, x, value);
} else {
new_slot_spec_in_env(sc, symbol, value, sslot);
}
}
#if !STANDALONE
void scheme_register_foreign_func(scheme * sc, scheme_registerable * sr)
{
scheme_define(sc,
sc->global_env,
mk_symbol(sc,sr->name),
mk_foreign_func(sc, sr->f));
}
void scheme_register_foreign_func_list(scheme * sc,
scheme_registerable * list,
int count)
{
int i;
for(i = 0; i < count; i++)
{
scheme_register_foreign_func(sc, list + i);
}
}
pointer scheme_apply0(scheme *sc, const char *procname)
{ return scheme_eval(sc, cons(sc,mk_symbol(sc,procname),sc->NIL)); }
void save_from_C_call(scheme *sc)
{
pointer saved_data =
cons(sc,
car(sc->sink),
cons(sc,
sc->envir,
sc->dump));
/* Push */
sc->c_nest = cons(sc, saved_data, sc->c_nest);
/* Truncate the dump stack so TS will return here when done, not
directly resume pre-C-call operations. */
dump_stack_reset(sc);
}
void restore_from_C_call(scheme *sc)
{
car(sc->sink) = caar(sc->c_nest);
sc->envir = cadar(sc->c_nest);
sc->dump = cdr(cdar(sc->c_nest));
/* Pop */
sc->c_nest = cdr(sc->c_nest);
}
/* "func" and "args" are assumed to be already eval'ed. */
pointer scheme_call(scheme *sc, pointer func, pointer args)
{
int old_repl = sc->interactive_repl;
sc->interactive_repl = 0;
save_from_C_call(sc);
sc->envir = sc->global_env;
sc->args = args;
sc->code = func;
sc->retcode = 0;
Eval_Cycle(sc, OP_APPLY);
sc->interactive_repl = old_repl;
restore_from_C_call(sc);
return sc->value;
}
pointer scheme_eval(scheme *sc, pointer obj)
{
int old_repl = sc->interactive_repl;
sc->interactive_repl = 0;
save_from_C_call(sc);
sc->args = sc->NIL;
sc->code = obj;
sc->retcode = 0;
Eval_Cycle(sc, OP_EVAL);
sc->interactive_repl = old_repl;
restore_from_C_call(sc);
return sc->value;
}
#endif
/* ========== Main ========== */
#if STANDALONE
#if defined(__APPLE__) && !defined (OSX)
int main()
{
extern MacTS_main(int argc, char **argv);
char** argv;
int argc = ccommand(&argv);
MacTS_main(argc,argv);
return 0;
}
int MacTS_main(int argc, char **argv) {
#else
int main(int argc, char **argv) {
#endif
scheme sc;
FILE *fin;
char *file_name=InitFile;
int retcode;
int isfile=1;
if(argc==1) {
printf(banner);
}
if(argc==2 && strcmp(argv[1],"-?")==0) {
printf("Usage: tinyscheme -?\n");
printf("or: tinyscheme [ ...]\n");
printf("followed by\n");
printf(" -1 [ ...]\n");
printf(" -c [ ...]\n");
printf("assuming that the executable is named tinyscheme.\n");
printf("Use - as filename for stdin.\n");
return 1;
}
if(!scheme_init(&sc)) {
fprintf(stderr,"Could not initialize!\n");
return 2;
}
scheme_set_input_port_file(&sc, stdin);
scheme_set_output_port_file(&sc, stdout);
#if USE_DL
scheme_define(&sc,sc.global_env,mk_symbol(&sc,"load-extension"),mk_foreign_func(&sc, scm_load_ext));
#endif
argv++;
if(access(file_name,0)!=0) {
char *p=getenv("TINYSCHEMEINIT");
if(p!=0) {
file_name=p;
}
}
do {
if(strcmp(file_name,"-")==0) {
fin=stdin;
} else if(strcmp(file_name,"-1")==0 || strcmp(file_name,"-c")==0) {
pointer args=sc.NIL;
isfile=file_name[1]=='1';
file_name=*argv++;
if(strcmp(file_name,"-")==0) {
fin=stdin;
} else if(isfile) {
fin=fopen(file_name,"r");
}
for(;*argv;argv++) {
pointer value=mk_string(&sc,*argv);
args=cons(&sc,value,args);
}
args=reverse_in_place(&sc,sc.NIL,args);
scheme_define(&sc,sc.global_env,mk_symbol(&sc,"*args*"),args);
} else {
fin=fopen(file_name,"r");
}
if(isfile && fin==0) {
fprintf(stderr,"Could not open file %s\n",file_name);
} else {
if(isfile) {
scheme_load_named_file(&sc,fin,file_name);
} else {
scheme_load_string(&sc,file_name);
}
if(!isfile || fin!=stdin) {
if(sc.retcode!=0) {
fprintf(stderr,"Errors encountered reading %s\n",file_name);
}
if(isfile) {
fclose(fin);
}
}
}
file_name=*argv++;
} while(file_name!=0);
if(argc==1) {
scheme_load_named_file(&sc,stdin,0);
}
retcode=sc.retcode;
scheme_deinit(&sc);
return retcode;
}
#endif
/*
Local variables:
c-file-style: "k&r"
End:
*/
diff --git a/src/estream-printf.c b/src/estream-printf.c
index 2171409..bce6147 100644
--- a/src/estream-printf.c
+++ b/src/estream-printf.c
@@ -1,1906 +1,1905 @@
/* estream-printf.c - Versatile mostly C-99 compliant printf formatting
* Copyright (C) 2007, 2008, 2009, 2010, 2012, 2014 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 Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser 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.
*/
/* Required autoconf tests:
AC_TYPE_LONG_LONG_INT defines HAVE_LONG_LONG_INT
AC_TYPE_LONG_DOUBLE defines HAVE_LONG_DOUBLE
AC_TYPE_INTMAX_T defines HAVE_INTMAX_T
AC_TYPE_UINTMAX_T defines HAVE_UINTMAX_T
AC_CHECK_TYPES([ptrdiff_t]) defines HAVE_PTRDIFF_T
AC_CHECK_SIZEOF([unsigned long]) defines SIZEOF_UNSIGNED_LONG
AC_CHECK_SIZEOF([void *]) defines SIZEOF_VOID_P
HAVE_LANGINFO_THOUSEP
Note that the file estream.m4 provides the autoconf macro
ESTREAM_PRINTF_INIT which runs all required checks.
See estream-printf.h for ways to tune this code.
Missing stuff: wchar and wint_t
thousep in pr_float.
*/
#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
#if defined(HAVE_INTMAX_T) || defined(HAVE_UINTMAX_T)
# ifdef HAVE_STDINT_H
# include
# endif
#endif
#ifdef HAVE_LANGINFO_THOUSEP
#include
#endif
#ifdef HAVE_W32CE_SYSTEM
#include /* ERRNO replacement. */
#endif
#ifdef _ESTREAM_PRINTF_EXTRA_INCLUDE
# include _ESTREAM_PRINTF_EXTRA_INCLUDE
#endif
#include "estream-printf.h"
/* #define DEBUG 1 */
/* Allow redefinition of asprintf used realloc function. */
#if defined(_ESTREAM_PRINTF_REALLOC)
#define my_printf_realloc(a,b) _ESTREAM_PRINTF_REALLOC((a),(b))
#else
#define my_printf_realloc(a,b) fixed_realloc((a),(b))
#endif
/* A wrapper to set ERRNO. */
#ifdef HAVE_W32CE_SYSTEM
# define _set_errno(a) gpg_err_set_errno ((a))
#else
# define _set_errno(a) do { errno = (a); } while (0)
#endif
/* Calculate array dimension. */
#ifndef DIM
#define DIM(array) (sizeof (array) / sizeof (*array))
#endif
/* We allow for that many args without requiring malloced memory. */
#define DEFAULT_MAX_ARGSPECS 5
/* We allow for that many values without requiring malloced memory. */
#define DEFAULT_MAX_VALUES 8
/* We allocate this many new array argspec elements each time. */
#define ARGSPECS_BUMP_VALUE 10
/* Special values for the field width and the precision. */
#define NO_FIELD_VALUE (-1)
#define STAR_FIELD_VALUE (-2)
/* Bit valuues used for the conversion flags. */
#define FLAG_GROUPING 1
#define FLAG_LEFT_JUST 2
#define FLAG_PLUS_SIGN 4
#define FLAG_SPACE_PLUS 8
#define FLAG_ALT_CONV 16
#define FLAG_ZERO_PAD 32
/* Constants used the length modifiers. */
typedef enum
{
LENMOD_NONE = 0,
LENMOD_CHAR, /* "hh" */
LENMOD_SHORT, /* "h" */
LENMOD_LONG, /* "l" */
LENMOD_LONGLONG, /* "ll" */
LENMOD_INTMAX, /* "j" */
LENMOD_SIZET, /* "z" */
LENMOD_PTRDIFF, /* "t" */
LENMOD_LONGDBL /* "L" */
} lenmod_t;
/* All the conversion specifiers. */
typedef enum
{
CONSPEC_UNKNOWN = 0,
CONSPEC_DECIMAL,
CONSPEC_OCTAL,
CONSPEC_UNSIGNED,
CONSPEC_HEX,
CONSPEC_HEX_UP,
CONSPEC_FLOAT,
CONSPEC_FLOAT_UP,
CONSPEC_EXP,
CONSPEC_EXP_UP,
CONSPEC_F_OR_G,
CONSPEC_F_OR_G_UP,
CONSPEC_HEX_EXP,
CONSPEC_HEX_EXP_UP,
CONSPEC_CHAR,
CONSPEC_STRING,
CONSPEC_POINTER,
CONSPEC_STRERROR,
CONSPEC_BYTES_SO_FAR
} conspec_t;
/* Constants describing all the suppoorted types. Note that we list
all the types we know about even if certain types are not available
on this system. */
typedef enum
{
VALTYPE_UNSUPPORTED = 0, /* Artificial type for error detection. */
VALTYPE_CHAR,
VALTYPE_SCHAR,
VALTYPE_UCHAR,
VALTYPE_SHORT,
VALTYPE_USHORT,
VALTYPE_INT,
VALTYPE_UINT,
VALTYPE_LONG,
VALTYPE_ULONG,
VALTYPE_LONGLONG,
VALTYPE_ULONGLONG,
VALTYPE_DOUBLE,
VALTYPE_LONGDOUBLE,
VALTYPE_STRING,
VALTYPE_INTMAX,
VALTYPE_UINTMAX,
VALTYPE_SIZE,
VALTYPE_PTRDIFF,
VALTYPE_POINTER,
VALTYPE_CHAR_PTR,
VALTYPE_SCHAR_PTR,
VALTYPE_SHORT_PTR,
VALTYPE_INT_PTR,
VALTYPE_LONG_PTR,
VALTYPE_LONGLONG_PTR,
VALTYPE_INTMAX_PTR,
VALTYPE_SIZE_PTR,
VALTYPE_PTRDIFF_PTR
} valtype_t;
/* A union used to store the actual values. */
typedef union
{
char a_char;
signed char a_schar;
unsigned char a_uchar;
short a_short;
unsigned short a_ushort;
int a_int;
unsigned int a_uint;
long int a_long;
unsigned long int a_ulong;
#ifdef HAVE_LONG_LONG_INT
long long int a_longlong;
unsigned long long int a_ulonglong;
#endif
double a_double;
#ifdef HAVE_LONG_DOUBLE
long double a_longdouble;
#endif
const char *a_string;
#ifdef HAVE_INTMAX_T
intmax_t a_intmax;
#endif
#ifdef HAVE_UINTMAX_T
intmax_t a_uintmax;
#endif
size_t a_size;
#ifdef HAVE_PTRDIFF_T
ptrdiff_t a_ptrdiff;
#endif
void *a_void_ptr;
char *a_char_ptr;
signed char *a_schar_ptr;
short *a_short_ptr;
int *a_int_ptr;
long *a_long_ptr;
#ifdef HAVE_LONG_LONG_INT
long long int *a_longlong_ptr;
#endif
#ifdef HAVE_INTMAX_T
intmax_t *a_intmax_ptr;
#endif
size_t *a_size_ptr;
#ifdef HAVE_PTRDIFF_T
ptrdiff_t *a_ptrdiff_ptr;
#endif
} value_t;
/* An object used to keep track of a format option and arguments. */
struct argspec_s
{
size_t length; /* The length of these args including the percent. */
unsigned int flags; /* The conversion flags (bits defined by FLAG_foo). */
int width; /* The field width. */
int precision; /* The precision. */
lenmod_t lenmod; /* The length modifier. */
conspec_t conspec; /* The conversion specifier. */
int arg_pos; /* The position of the argument. This one may
be -1 to indicate that no value is expected
(e.g. for "%m"). */
int width_pos; /* The position of the argument for a field
width star's value. 0 for not used. */
int precision_pos; /* The position of the argument for the a
precision star's value. 0 for not used. */
valtype_t vt; /* The type of the corresponding argument. */
};
typedef struct argspec_s *argspec_t;
/* An object to build up a table of values and their types. */
struct valueitem_s
{
valtype_t vt; /* The type of the value. */
value_t value; /* The value. */
};
typedef struct valueitem_s *valueitem_t;
/* Not all systems have a C-90 compliant realloc. To cope with this
we use this simple wrapper. */
#ifndef _ESTREAM_PRINTF_REALLOC
static void *
fixed_realloc (void *a, size_t n)
{
if (!a)
return malloc (n);
if (!n)
{
free (a);
return NULL;
}
return realloc (a, n);
}
#endif /*!_ESTREAM_PRINTF_REALLOC*/
#ifdef DEBUG
static void
dump_argspecs (argspec_t arg, size_t argcount)
{
int idx;
for (idx=0; argcount; argcount--, arg++, idx++)
fprintf (stderr,
"%2d: len=%u flags=%u width=%d prec=%d mod=%d "
"con=%d vt=%d pos=%d-%d-%d\n",
idx,
(unsigned int)arg->length,
arg->flags,
arg->width,
arg->precision,
arg->lenmod,
arg->conspec,
arg->vt,
arg->arg_pos,
arg->width_pos,
arg->precision_pos);
}
#endif /*DEBUG*/
/* Set the vt field for ARG. */
static void
compute_type (argspec_t arg)
{
switch (arg->conspec)
{
case CONSPEC_UNKNOWN:
arg->vt = VALTYPE_UNSUPPORTED;
break;
case CONSPEC_DECIMAL:
switch (arg->lenmod)
{
case LENMOD_CHAR: arg->vt = VALTYPE_SCHAR; break;
case LENMOD_SHORT: arg->vt = VALTYPE_SHORT; break;
case LENMOD_LONG: arg->vt = VALTYPE_LONG; break;
case LENMOD_LONGLONG: arg->vt = VALTYPE_LONGLONG; break;
case LENMOD_INTMAX: arg->vt = VALTYPE_INTMAX; break;
case LENMOD_SIZET: arg->vt = VALTYPE_SIZE; break;
case LENMOD_PTRDIFF: arg->vt = VALTYPE_PTRDIFF; break;
default: arg->vt = VALTYPE_INT; break;
}
break;
case CONSPEC_OCTAL:
case CONSPEC_UNSIGNED:
case CONSPEC_HEX:
case CONSPEC_HEX_UP:
switch (arg->lenmod)
{
case LENMOD_CHAR: arg->vt = VALTYPE_UCHAR; break;
case LENMOD_SHORT: arg->vt = VALTYPE_USHORT; break;
case LENMOD_LONG: arg->vt = VALTYPE_ULONG; break;
case LENMOD_LONGLONG: arg->vt = VALTYPE_ULONGLONG; break;
case LENMOD_INTMAX: arg->vt = VALTYPE_UINTMAX; break;
case LENMOD_SIZET: arg->vt = VALTYPE_SIZE; break;
case LENMOD_PTRDIFF: arg->vt = VALTYPE_PTRDIFF; break;
default: arg->vt = VALTYPE_UINT; break;
}
break;
case CONSPEC_FLOAT:
case CONSPEC_FLOAT_UP:
case CONSPEC_EXP:
case CONSPEC_EXP_UP:
case CONSPEC_F_OR_G:
case CONSPEC_F_OR_G_UP:
case CONSPEC_HEX_EXP:
case CONSPEC_HEX_EXP_UP:
switch (arg->lenmod)
{
case LENMOD_LONGDBL: arg->vt = VALTYPE_LONGDOUBLE; break;
case LENMOD_LONG: arg->vt = VALTYPE_DOUBLE; break;
default: arg->vt = VALTYPE_DOUBLE; break;
}
break;
case CONSPEC_CHAR:
arg->vt = VALTYPE_INT;
break;
case CONSPEC_STRING:
arg->vt = VALTYPE_STRING;
break;
case CONSPEC_POINTER:
arg->vt = VALTYPE_POINTER;
break;
case CONSPEC_STRERROR:
arg->vt = VALTYPE_STRING;
break;
case CONSPEC_BYTES_SO_FAR:
switch (arg->lenmod)
{
case LENMOD_CHAR: arg->vt = VALTYPE_SCHAR_PTR; break;
case LENMOD_SHORT: arg->vt = VALTYPE_SHORT_PTR; break;
case LENMOD_LONG: arg->vt = VALTYPE_LONG_PTR; break;
case LENMOD_LONGLONG: arg->vt = VALTYPE_LONGLONG_PTR; break;
case LENMOD_INTMAX: arg->vt = VALTYPE_INTMAX_PTR; break;
case LENMOD_SIZET: arg->vt = VALTYPE_SIZE_PTR; break;
case LENMOD_PTRDIFF: arg->vt = VALTYPE_PTRDIFF_PTR; break;
default: arg->vt = VALTYPE_INT_PTR; break;
}
break;
}
}
/* Parse the FORMAT string and populate the specification array stored
at the address ARGSPECS_ADDR. The caller has provided enough space
to store up to MAX_ARGSPECS in that buffer. The function may
however ignore the provided buffer and malloc a larger one. On
success the address of that larger buffer will be stored at
ARGSPECS_ADDR. The actual number of specifications will be
returned at R_ARGSPECS_COUNT. */
static int
parse_format (const char *format,
argspec_t *argspecs_addr, size_t max_argspecs,
size_t *r_argspecs_count)
{
const char *s;
argspec_t argspecs = *argspecs_addr;
argspec_t arg;
size_t argcount = 0;
if (!format)
goto leave_einval;
for (; *format; format++)
{
unsigned int flags;
int width, precision;
lenmod_t lenmod;
conspec_t conspec;
int arg_pos, width_pos, precision_pos;
if (*format != '%')
continue;
s = ++format;
if (!*s)
goto leave_einval;
if (*s == '%')
continue; /* Just a quoted percent. */
/* First check whether there is a positional argument. */
arg_pos = 0; /* No positional argument given. */
if (*s >= '1' && *s <= '9')
{
const char *save_s = s;
arg_pos = (*s++ - '0');
for (; *s >= '0' && *s <= '9'; s++)
arg_pos = 10*arg_pos + (*s - '0');
if (arg_pos < 0)
goto leave_einval; /* Overflow during conversion. */
if (*s == '$')
s++;
else
{
arg_pos = 0;
s = save_s;
}
}
/* Parse the flags. */
flags = 0;
for ( ; *s; s++)
{
switch (*s)
{
case '\'': flags |= FLAG_GROUPING; break;
case '-': flags |= FLAG_LEFT_JUST; break;
case '+': flags |= FLAG_PLUS_SIGN; break;
case ' ': flags |= FLAG_SPACE_PLUS; break;
case '#': flags |= FLAG_ALT_CONV; break;
case '0': flags |= FLAG_ZERO_PAD; break;
default:
goto flags_parsed;
}
}
flags_parsed:
/* Parse the field width. */
width_pos = 0;
if (*s == '*')
{
width = STAR_FIELD_VALUE;
s++;
/* If we have a positional argument, another one might also
be used to give the position of the star's value. */
if (arg_pos && *s >= '1' && *s <= '9')
{
width_pos = (*s++ - '0');
for (; *s >= '0' && *s <= '9'; s++)
width_pos = 10*width_pos + (*s - '0');
if (width_pos < 1)
goto leave_einval; /* Overflow during conversion. */
if (*s != '$')
goto leave_einval; /* Not followed by $. */
s++;
}
}
else if ( *s >= '0' && *s <= '9')
{
width = (*s++ - '0');
for (; *s >= '0' && *s <= '9'; s++)
{
if (!width && *s == '0')
goto leave_einval; /* Leading zeroes are not allowed.
Fixme: check what other
implementations do. */
width = 10*width + (*s - '0');
}
if (width < 0)
goto leave_einval; /* Overflow during conversion. */
}
else
width = NO_FIELD_VALUE;
/* Parse the precision. */
precision_pos = 0;
precision = NO_FIELD_VALUE;
if (*s == '.')
{
int ignore_value = (s[1] == '-');
s++;
if (*s == '*')
{
precision = STAR_FIELD_VALUE;
s++;
/* If we have a positional argument, another one might also
be used to give the position of the star's value. */
if (arg_pos && *s >= '1' && *s <= '9')
{
precision_pos = (*s++ - '0');
for (; *s >= '0' && *s <= '9'; s++)
precision_pos = 10*precision_pos + (*s - '0');
if (precision_pos < 1)
goto leave_einval; /* Overflow during conversion. */
if (*s != '$')
goto leave_einval; /* Not followed by $. */
s++;
}
}
else if ( *s >= '0' && *s <= '9')
{
precision = (*s++ - '0');
for (; *s >= '0' && *s <= '9'; s++)
{
if (!precision && *s == '0')
goto leave_einval; /* Leading zeroes are not allowed.
Fixme: check what other
implementations do. */
precision = 10*precision + (*s - '0');
}
if (precision < 0)
goto leave_einval; /* Overflow during conversion. */
}
else
precision = 0;
if (ignore_value)
precision = NO_FIELD_VALUE;
}
/* Parse the length modifiers. */
switch (*s)
{
case 'h':
if (s[1] == 'h')
{
lenmod = LENMOD_CHAR;
s++;
}
else
lenmod = LENMOD_SHORT;
s++;
break;
case 'l':
if (s[1] == 'l')
{
lenmod = LENMOD_LONGLONG;
s++;
}
else
lenmod = LENMOD_LONG;
s++;
break;
case 'j': lenmod = LENMOD_INTMAX; s++; break;
case 'z': lenmod = LENMOD_SIZET; s++; break;
case 't': lenmod = LENMOD_PTRDIFF; s++; break;
case 'L': lenmod = LENMOD_LONGDBL; s++; break;
default: lenmod = LENMOD_NONE; break;
}
/* Parse the conversion specifier. */
switch (*s)
{
case 'd':
case 'i': conspec = CONSPEC_DECIMAL; break;
case 'o': conspec = CONSPEC_OCTAL; break;
case 'u': conspec = CONSPEC_UNSIGNED; break;
case 'x': conspec = CONSPEC_HEX; break;
case 'X': conspec = CONSPEC_HEX_UP; break;
case 'f': conspec = CONSPEC_FLOAT; break;
case 'F': conspec = CONSPEC_FLOAT_UP; break;
case 'e': conspec = CONSPEC_EXP; break;
case 'E': conspec = CONSPEC_EXP_UP; break;
case 'g': conspec = CONSPEC_F_OR_G; break;
case 'G': conspec = CONSPEC_F_OR_G_UP; break;
case 'a': conspec = CONSPEC_HEX_EXP; break;
case 'A': conspec = CONSPEC_HEX_EXP_UP; break;
case 'c': conspec = CONSPEC_CHAR; break;
case 's': conspec = CONSPEC_STRING; break;
case 'p': conspec = CONSPEC_POINTER; break;
case 'n': conspec = CONSPEC_BYTES_SO_FAR; break;
case 'C': conspec = CONSPEC_CHAR; lenmod = LENMOD_LONG; break;
case 'S': conspec = CONSPEC_STRING; lenmod = LENMOD_LONG; break;
case 'm': conspec = CONSPEC_STRERROR; arg_pos = -1; break;
default: conspec = CONSPEC_UNKNOWN;
}
/* Save the args. */
if (argcount >= max_argspecs)
{
/* We either need to allocate a new array instead of the
caller provided one or realloc the array. Instead of
using realloc we allocate a new one and release the
original one then. */
size_t n, newmax;
argspec_t newarg;
newmax = max_argspecs + ARGSPECS_BUMP_VALUE;
if (newmax <= max_argspecs)
goto leave_einval; /* Too many arguments. */
newarg = calloc (newmax, sizeof *newarg);
if (!newarg)
goto leave;
for (n=0; n < argcount; n++)
newarg[n] = argspecs[n];
if (argspecs != *argspecs_addr)
free (argspecs);
argspecs = newarg;
max_argspecs = newmax;
}
arg = argspecs + argcount;
arg->length = s - format + 2;
arg->flags = flags;
arg->width = width;
arg->precision = precision;
arg->lenmod = lenmod;
arg->conspec = conspec;
arg->arg_pos = arg_pos;
arg->width_pos = width_pos;
arg->precision_pos = precision_pos;
compute_type (arg);
argcount++;
format = s;
}
*argspecs_addr = argspecs;
*r_argspecs_count = argcount;
return 0; /* Success. */
leave_einval:
_set_errno (EINVAL);
leave:
if (argspecs != *argspecs_addr)
free (argspecs);
*argspecs_addr = NULL;
return -1;
}
/* This function reads all the values as specified by VALUETABLE into
VALUETABLE. The values are expected in VAARGS. The function
returns -1 if a specified type is not supported. */
static int
read_values (valueitem_t valuetable, size_t valuetable_len, va_list vaargs)
{
int validx;
for (validx=0; validx < valuetable_len; validx++)
{
value_t *value = &valuetable[validx].value;
valtype_t vt = valuetable[validx].vt;
switch (vt)
{
case VALTYPE_CHAR: value->a_char = va_arg (vaargs, int); break;
case VALTYPE_CHAR_PTR:
value->a_char_ptr = va_arg (vaargs, char *);
break;
case VALTYPE_SCHAR: value->a_schar = va_arg (vaargs, int); break;
case VALTYPE_SCHAR_PTR:
value->a_schar_ptr = va_arg (vaargs, signed char *);
break;
case VALTYPE_UCHAR: value->a_uchar = va_arg (vaargs, int); break;
case VALTYPE_SHORT: value->a_short = va_arg (vaargs, int); break;
case VALTYPE_USHORT: value->a_ushort = va_arg (vaargs, int); break;
case VALTYPE_SHORT_PTR:
value->a_short_ptr = va_arg (vaargs, short *);
break;
case VALTYPE_INT:
value->a_int = va_arg (vaargs, int);
break;
case VALTYPE_INT_PTR:
value->a_int_ptr = va_arg (vaargs, int *);
break;
case VALTYPE_UINT:
value->a_uint = va_arg (vaargs, unsigned int);
break;
case VALTYPE_LONG:
value->a_long = va_arg (vaargs, long);
break;
case VALTYPE_ULONG:
value->a_ulong = va_arg (vaargs, unsigned long);
break;
case VALTYPE_LONG_PTR:
value->a_long_ptr = va_arg (vaargs, long *);
break;
#ifdef HAVE_LONG_LONG_INT
case VALTYPE_LONGLONG:
value->a_longlong = va_arg (vaargs, long long int);
break;
case VALTYPE_ULONGLONG:
value->a_ulonglong = va_arg (vaargs, unsigned long long int);
break;
case VALTYPE_LONGLONG_PTR:
value->a_longlong_ptr = va_arg (vaargs, long long *);
break;
#endif
case VALTYPE_DOUBLE:
value->a_double = va_arg (vaargs, double);
break;
#ifdef HAVE_LONG_DOUBLE
case VALTYPE_LONGDOUBLE:
value->a_longdouble = va_arg (vaargs, long double);
break;
#endif
case VALTYPE_STRING:
value->a_string = va_arg (vaargs, const char *);
break;
case VALTYPE_POINTER:
value->a_void_ptr = va_arg (vaargs, void *);
break;
#ifdef HAVE_INTMAX_T
case VALTYPE_INTMAX:
value->a_intmax = va_arg (vaargs, intmax_t);
break;
case VALTYPE_INTMAX_PTR:
value->a_intmax_ptr = va_arg (vaargs, intmax_t *);
break;
#endif
#ifdef HAVE_UINTMAX_T
case VALTYPE_UINTMAX:
value->a_uintmax = va_arg (vaargs, uintmax_t);
break;
#endif
case VALTYPE_SIZE:
value->a_size = va_arg (vaargs, size_t);
break;
case VALTYPE_SIZE_PTR:
value->a_size_ptr = va_arg (vaargs, size_t *);
break;
#ifdef HAVE_PTRDIFF_T
case VALTYPE_PTRDIFF:
value->a_ptrdiff = va_arg (vaargs, ptrdiff_t);
break;
case VALTYPE_PTRDIFF_PTR:
value->a_ptrdiff_ptr = va_arg (vaargs, ptrdiff_t *);
break;
#endif
default: /* Unsupported type. */
return -1;
}
}
return 0;
}
/* Output COUNT padding characters PADCHAR and update NBYTES by the
number of bytes actually written. */
static int
pad_out (estream_printf_out_t outfnc, void *outfncarg,
int padchar, int count, size_t *nbytes)
{
char buf[32];
size_t n;
int rc;
while (count > 0)
{
n = (count <= sizeof buf)? count : sizeof buf;
memset (buf, padchar, n);
rc = outfnc (outfncarg, buf, n);
if (rc)
return rc;
*nbytes += n;
count -= n;
}
return 0;
}
/* "d,i,o,u,x,X" formatting. OUTFNC and OUTFNCARG describes the
output routine, ARG gives the argument description and VALUE the
actual value (its type is available through arg->vt). */
static int
pr_integer (estream_printf_out_t outfnc, void *outfncarg,
argspec_t arg, value_t value, size_t *nbytes)
{
int rc;
#ifdef HAVE_LONG_LONG_INT
unsigned long long aulong;
#else
unsigned long aulong;
#endif
char numbuf[100];
char *p, *pend;
size_t n;
char signchar = 0;
int n_prec; /* Number of extra precision digits required. */
int n_extra; /* Extra number of prefix or sign characters. */
if (arg->conspec == CONSPEC_DECIMAL)
{
#ifdef HAVE_LONG_LONG_INT
long long along;
#else
long along;
#endif
switch (arg->vt)
{
case VALTYPE_SHORT: along = value.a_short; break;
case VALTYPE_INT: along = value.a_int; break;
case VALTYPE_LONG: along = value.a_long; break;
#ifdef HAVE_LONG_LONG_INT
case VALTYPE_LONGLONG: along = value.a_longlong; break;
case VALTYPE_SIZE: along = value.a_size; break;
# ifdef HAVE_INTMAX_T
case VALTYPE_INTMAX: along = value.a_intmax; break;
# endif
# ifdef HAVE_PTRDIFF_T
case VALTYPE_PTRDIFF: along = value.a_ptrdiff; break;
# endif
#endif /*HAVE_LONG_LONG_INT*/
default:
return -1;
}
if (along < 0)
{
aulong = -along;
signchar = '-';
}
else
aulong = along;
}
else
{
switch (arg->vt)
{
case VALTYPE_USHORT: aulong = value.a_ushort; break;
case VALTYPE_UINT: aulong = value.a_uint; break;
case VALTYPE_ULONG: aulong = value.a_ulong; break;
#ifdef HAVE_LONG_LONG_INT
case VALTYPE_ULONGLONG: aulong = value.a_ulonglong; break;
case VALTYPE_SIZE: aulong = value.a_size; break;
# ifdef HAVE_UINTMAX_T
case VALTYPE_UINTMAX: aulong = value.a_uintmax; break;
# endif
# ifdef HAVE_PTRDIFF_T
case VALTYPE_PTRDIFF: aulong = value.a_ptrdiff; break;
# endif
#endif /*HAVE_LONG_LONG_INT*/
default:
return -1;
}
}
if (signchar == '-')
;
else if ((arg->flags & FLAG_PLUS_SIGN))
signchar = '+';
else if ((arg->flags & FLAG_SPACE_PLUS))
signchar = ' ';
n_extra = !!signchar;
/* We build the string up backwards. */
p = pend = numbuf + DIM(numbuf);
if ((!aulong && !arg->precision))
;
else if (arg->conspec == CONSPEC_DECIMAL
|| arg->conspec == CONSPEC_UNSIGNED)
{
int grouping = -1;
const char * grouping_string =
#ifdef HAVE_LANGINFO_THOUSEP
nl_langinfo(THOUSEP);
#else
"'";
#endif
do
{
if ((arg->flags & FLAG_GROUPING)
&& (++grouping == 3) && *grouping_string)
{
*--p = *grouping_string;
grouping = 0;
}
*--p = '0' + (aulong % 10);
aulong /= 10;
}
while (aulong);
}
else if (arg->conspec == CONSPEC_OCTAL)
{
do
{
*--p = '0' + (aulong % 8);
aulong /= 8;
}
while (aulong);
if ((arg->flags & FLAG_ALT_CONV) && *p != '0')
*--p = '0';
}
else /* HEX or HEXUP */
{
const char *digits = ((arg->conspec == CONSPEC_HEX)
? "0123456789abcdef" : "0123456789ABCDEF");
do
{
*--p = digits[(aulong % 16)];
aulong /= 16;
}
while (aulong);
if ((arg->flags & FLAG_ALT_CONV))
n_extra += 2;
}
n = pend - p;
if ((arg->flags & FLAG_ZERO_PAD)
&& arg->precision == NO_FIELD_VALUE && !(arg->flags & FLAG_LEFT_JUST)
&& n && arg->width - n_extra > n )
n_prec = arg->width - n_extra - n;
else if (arg->precision > 0 && arg->precision > n)
n_prec = arg->precision - n;
else
n_prec = 0;
if (!(arg->flags & FLAG_LEFT_JUST)
&& arg->width >= 0 && arg->width - n_extra > n
&& arg->width - n_extra - n >= n_prec )
{
rc = pad_out (outfnc, outfncarg, ' ',
arg->width - n_extra - n - n_prec, nbytes);
if (rc)
return rc;
}
if (signchar)
{
rc = outfnc (outfncarg, &signchar, 1);
if (rc)
return rc;
*nbytes += 1;
}
if ((arg->flags & FLAG_ALT_CONV)
&& (arg->conspec == CONSPEC_HEX || arg->conspec == CONSPEC_HEX_UP))
{
rc = outfnc (outfncarg, arg->conspec == CONSPEC_HEX? "0x": "0X", 2);
if (rc)
return rc;
*nbytes += 2;
}
if (n_prec)
{
rc = pad_out (outfnc, outfncarg, '0', n_prec, nbytes);
if (rc)
return rc;
}
rc = outfnc (outfncarg, p, pend - p);
if (rc)
return rc;
*nbytes += pend - p;
if ((arg->flags & FLAG_LEFT_JUST)
&& arg->width >= 0 && arg->width - n_extra - n_prec > n)
{
rc = pad_out (outfnc, outfncarg, ' ',
arg->width - n_extra - n_prec - n, nbytes);
if (rc)
return rc;
}
return 0;
}
/* "e,E,f,F,g,G,a,A" formatting. OUTFNC and OUTFNCARG describes the
output routine, ARG gives the argument description and VALUE the
actual value (its type is available through arg->vt). For
portability reasons sprintf is used for the actual formatting.
This is useful because sprint is the only standard function to
convert a floating number into its ascii representation. To avoid
using malloc we just pass the precision to sprintf and do the final
formatting with our own code. */
static int
pr_float (estream_printf_out_t outfnc, void *outfncarg,
argspec_t arg, value_t value, size_t *nbytes)
{
int rc;
#ifdef HAVE_LONG_DOUBLE
long double adblfloat = 0; /* Just to please gcc. */
int use_dbl = 0;
#endif
double afloat;
char numbuf[350];
char formatstr[20];
char *p, *pend;
size_t n;
char signchar = 0;
int n_extra; /* Extra number of prefix or sign characters. */
switch (arg->vt)
{
case VALTYPE_DOUBLE: afloat = value.a_double; break;
#ifdef HAVE_LONG_DOUBLE
case VALTYPE_LONGDOUBLE:
afloat = 0; /* Just to please gcc. */
adblfloat = value.a_longdouble;
use_dbl=1; break;
#endif
default:
return -1;
}
/* We build the string using sprint. */
p = formatstr + sizeof formatstr;
*--p = 0;
switch (arg->conspec)
{
case CONSPEC_FLOAT: *--p = 'f'; break;
case CONSPEC_FLOAT_UP: *--p = 'F'; break;
case CONSPEC_EXP: *--p = 'e'; break;
case CONSPEC_EXP_UP: *--p = 'E'; break;
case CONSPEC_F_OR_G: *--p = 'g'; break;
case CONSPEC_F_OR_G_UP: *--p = 'G'; break;
case CONSPEC_HEX_EXP: *--p = 'a'; break;
case CONSPEC_HEX_EXP_UP: *--p = 'A'; break;
default:
return -1; /* Actually a bug. */
}
#ifdef HAVE_LONG_DOUBLE
if (use_dbl)
*--p = 'L';
#endif
if (arg->precision != NO_FIELD_VALUE)
{
/* Limit it to a meaningful value so that even a stupid sprintf
won't overflow our buffer. */
n = arg->precision <= 100? arg->precision : 100;
do
{
*--p = '0' + (n % 10);
n /= 10;
}
while (n);
*--p = '.';
}
if ((arg->flags & FLAG_ALT_CONV))
*--p = '#';
*--p = '%';
#ifdef HAVE_LONG_DOUBLE
if (use_dbl)
sprintf (numbuf, p, adblfloat);
else
#endif /*HAVE_LONG_DOUBLE*/
sprintf (numbuf, p, afloat);
p = numbuf;
n = strlen (numbuf);
pend = p + n;
if (*p =='-')
{
signchar = '-';
p++;
n--;
}
else if ((arg->flags & FLAG_PLUS_SIGN))
signchar = '+';
else if ((arg->flags & FLAG_SPACE_PLUS))
signchar = ' ';
n_extra = !!signchar;
if (!(arg->flags & FLAG_LEFT_JUST)
&& arg->width >= 0 && arg->width - n_extra > n)
{
rc = pad_out (outfnc, outfncarg, ' ', arg->width - n_extra - n, nbytes);
if (rc)
return rc;
}
if (signchar)
{
rc = outfnc (outfncarg, &signchar, 1);
if (rc)
return rc;
*nbytes += 1;
}
rc = outfnc (outfncarg, p, pend - p);
if (rc)
return rc;
*nbytes += pend - p;
if ((arg->flags & FLAG_LEFT_JUST)
&& arg->width >= 0 && arg->width - n_extra > n)
{
rc = pad_out (outfnc, outfncarg, ' ', arg->width - n_extra - n, nbytes);
if (rc)
return rc;
}
return 0;
}
/* "c" formatting. */
static int
pr_char (estream_printf_out_t outfnc, void *outfncarg,
argspec_t arg, value_t value, size_t *nbytes)
{
int rc;
char buf[1];
if (arg->vt != VALTYPE_INT)
return -1;
buf[0] = (unsigned int)value.a_int;
rc = outfnc (outfncarg, buf, 1);
if(rc)
return rc;
*nbytes += 1;
return 0;
}
/* "s" formatting. */
static int
pr_string (estream_printf_out_t outfnc, void *outfncarg,
argspec_t arg, value_t value, size_t *nbytes,
gpgrt_string_filter_t sf, void *sfvalue, int string_no)
{
int rc;
size_t n;
const char *string, *s;
if (arg->vt != VALTYPE_STRING)
return -1;
string = value.a_string;
if (sf)
string = sf (value.a_string, string_no, sfvalue);
else
string = value.a_string;
if (!string)
string = "(null)";
if (arg->precision >= 0)
{
/* Test for nul after N so that we can pass a non-nul terminated
string. */
for (n=0,s=string; n < arg->precision && *s; s++)
n++;
}
else
n = strlen (string);
if (!(arg->flags & FLAG_LEFT_JUST)
&& arg->width >= 0 && arg->width > n )
{
rc = pad_out (outfnc, outfncarg, ' ', arg->width - n, nbytes);
if (rc)
goto leave;
}
rc = outfnc (outfncarg, string, n);
if (rc)
goto leave;
*nbytes += n;
if ((arg->flags & FLAG_LEFT_JUST)
&& arg->width >= 0 && arg->width > n)
{
rc = pad_out (outfnc, outfncarg, ' ', arg->width - n, nbytes);
if (rc)
goto leave;
}
rc = 0;
leave:
if (sf) /* Tell the filter to release resources. */
sf (value.a_string, -1, sfvalue);
return rc;
}
/* "p" formatting. */
static int
pr_pointer (estream_printf_out_t outfnc, void *outfncarg,
argspec_t arg, value_t value, size_t *nbytes)
{
int rc;
#if defined(HAVE_LONG_LONG_INT) && (SIZEOF_UNSIGNED_LONG < SIZEOF_VOID_P)
unsigned long long aulong;
#else
unsigned long aulong;
#endif
char numbuf[100];
char *p, *pend;
if (arg->vt != VALTYPE_POINTER)
return -1;
/* We assume that a pointer can be converted to an unsigned long.
That is not correct for a 64 bit Windows, but then we assume that
long long is supported and usable for storing a pointer. */
#if defined(HAVE_LONG_LONG_INT) && (SIZEOF_UNSIGNED_LONG < SIZEOF_VOID_P)
aulong = (unsigned long long)value.a_void_ptr;
#else
aulong = (unsigned long)value.a_void_ptr;
#endif
p = pend = numbuf + DIM(numbuf);
do
{
*--p = "0123456789abcdefx"[(aulong % 16)];
aulong /= 16;
}
while (aulong);
while ((pend-p) < 2*sizeof (aulong))
*--p = '0';
*--p = 'x';
*--p = '0';
rc = outfnc (outfncarg, p, pend - p);
if (rc)
return rc;
*nbytes += pend - p;
return 0;
}
/* "n" pesudo format operation. */
static int
pr_bytes_so_far (estream_printf_out_t outfnc, void *outfncarg,
argspec_t arg, value_t value, size_t *nbytes)
{
(void)outfnc;
(void)outfncarg;
switch (arg->vt)
{
case VALTYPE_SCHAR_PTR:
*value.a_schar_ptr = (signed char)(unsigned int)(*nbytes);
break;
case VALTYPE_SHORT_PTR:
*value.a_short_ptr = (short)(unsigned int)(*nbytes);
break;
case VALTYPE_LONG_PTR:
*value.a_long_ptr = (long)(*nbytes);
break;
#ifdef HAVE_LONG_LONG_INT
case VALTYPE_LONGLONG_PTR:
*value.a_longlong_ptr = (long long)(*nbytes);
break;
#endif
#ifdef HAVE_INTMAX_T
case VALTYPE_INTMAX_PTR:
*value.a_intmax_ptr = (intmax_t)(*nbytes);
break;
#endif
case VALTYPE_SIZE_PTR:
*value.a_size_ptr = (*nbytes);
break;
#ifdef HAVE_PTRDIFF_T
case VALTYPE_PTRDIFF_PTR:
*value.a_ptrdiff_ptr = (ptrdiff_t)(*nbytes);
break;
#endif
case VALTYPE_INT_PTR:
*value.a_int_ptr = (int)(*nbytes);
break;
default:
return -1; /* An unsupported type has been used. */
}
return 0;
}
/* Run the actual formatting. OUTFNC and OUTFNCARG are the output
* functions. FORMAT is format string ARGSPECS is the parsed format
* string, ARGSPECS_LEN the number of items in ARGSPECS.
* STRING_FILTER is an optional function to filter string (%s) args;
* it is called with the original string and the count of already
* processed %s arguments. Its return value will be used instead of
* the original string. VALUETABLE holds the values and may be
* directly addressed using the position arguments given by ARGSPECS.
* MYERRNO is used for the "%m" conversion. NBYTES well be updated to
* reflect the number of bytes send to the output function. */
static int
do_format (estream_printf_out_t outfnc, void *outfncarg,
gpgrt_string_filter_t sf, void *sfvalue,
const char *format, argspec_t argspecs, size_t argspecs_len,
valueitem_t valuetable, int myerrno, size_t *nbytes)
{
int rc = 0;
const char *s;
argspec_t arg = argspecs;
int argidx = 0; /* Only used for assertion. */
size_t n;
value_t value;
int string_no = 0; /* Number of processed "%s" args. */
s = format;
while ( *s )
{
if (*s != '%')
{
s++;
continue;
}
if (s != format)
{
rc = outfnc (outfncarg, format, (n=s-format));
if (rc)
return rc;
*nbytes += n;
}
if (s[1] == '%')
{
/* Note that this code ignores one trailing percent escape -
this is however okay as the args parser must have
detected this already. */
rc = outfnc (outfncarg, s, 1);
if (rc)
return rc;
*nbytes += 1;
s += 2;
format = s;
continue;
}
/* Save the next start. */
s += arg->length;
format = s;
- assert (argidx < argspecs_len);
+ gpgrt_assert (argidx < argspecs_len);
argidx++;
/* Apply indirect field width and precision values. */
if (arg->width == STAR_FIELD_VALUE)
{
- assert (valuetable[arg->width_pos-1].vt == VALTYPE_INT);
+ gpgrt_assert (valuetable[arg->width_pos-1].vt == VALTYPE_INT);
arg->width = valuetable[arg->width_pos-1].value.a_int;
if (arg->width < 0)
{
arg->width = -arg->width;
arg->flags |= FLAG_LEFT_JUST;
}
}
if (arg->precision == STAR_FIELD_VALUE)
{
- assert (valuetable[arg->precision_pos-1].vt == VALTYPE_INT);
+ gpgrt_assert (valuetable[arg->precision_pos-1].vt == VALTYPE_INT);
arg->precision = valuetable[arg->precision_pos-1].value.a_int;
if (arg->precision < 0)
arg->precision = NO_FIELD_VALUE;
}
if (arg->arg_pos == -1 && arg->conspec == CONSPEC_STRERROR)
value.a_string = strerror (myerrno);
else
{
- assert (arg->vt == valuetable[arg->arg_pos-1].vt);
+ gpgrt_assert (arg->vt == valuetable[arg->arg_pos-1].vt);
value = valuetable[arg->arg_pos-1].value;
}
switch (arg->conspec)
{
- case CONSPEC_UNKNOWN: assert (!"bug"); break;
+ case CONSPEC_UNKNOWN: gpgrt_assert (!"bug"); break;
case CONSPEC_DECIMAL:
case CONSPEC_UNSIGNED:
case CONSPEC_OCTAL:
case CONSPEC_HEX:
case CONSPEC_HEX_UP:
rc = pr_integer (outfnc, outfncarg, arg, value, nbytes);
break;
case CONSPEC_FLOAT:
case CONSPEC_FLOAT_UP:
case CONSPEC_EXP:
case CONSPEC_EXP_UP:
case CONSPEC_F_OR_G:
case CONSPEC_F_OR_G_UP:
case CONSPEC_HEX_EXP:
case CONSPEC_HEX_EXP_UP:
rc = pr_float (outfnc, outfncarg, arg, value, nbytes);
break;
case CONSPEC_CHAR:
rc = pr_char (outfnc, outfncarg, arg, value, nbytes);
break;
case CONSPEC_STRING:
rc = pr_string (outfnc, outfncarg, arg, value, nbytes,
sf, sfvalue, string_no++);
break;
case CONSPEC_STRERROR:
rc = pr_string (outfnc, outfncarg, arg, value, nbytes,
NULL, NULL, 0);
break;
case CONSPEC_POINTER:
rc = pr_pointer (outfnc, outfncarg, arg, value, nbytes);
break;
case CONSPEC_BYTES_SO_FAR:
rc = pr_bytes_so_far (outfnc, outfncarg, arg, value, nbytes);
break;
}
if (rc)
return rc;
arg++;
}
/* Print out any trailing stuff. */
n = s - format;
rc = n? outfnc (outfncarg, format, n) : 0;
if (!rc)
*nbytes += n;
return rc;
}
/* The versatile printf formatting routine. It expects a callback
function OUTFNC and an opaque argument OUTFNCARG used for actual
output of the formatted stuff. FORMAT is the format specification
and VAARGS a variable argumemt list matching the arguments of
FORMAT. */
int
_gpgrt_estream_format (estream_printf_out_t outfnc,
void *outfncarg,
gpgrt_string_filter_t sf, void *sfvalue,
const char *format, va_list vaargs)
{
/* Buffer to hold the argspecs and a pointer to it.*/
struct argspec_s argspecs_buffer[DEFAULT_MAX_ARGSPECS];
argspec_t argspecs = argspecs_buffer;
size_t argspecs_len; /* Number of specifications in ARGSPECS. */
/* Buffer to hold the description for the values. */
struct valueitem_s valuetable_buffer[DEFAULT_MAX_VALUES];
valueitem_t valuetable = valuetable_buffer;
int rc; /* Return code. */
size_t argidx; /* Used to index the argspecs array. */
size_t validx; /* Used to index the valuetable. */
int max_pos; /* Highest argument position. */
size_t nbytes = 0; /* Keep track of the number of bytes passed to
the output function. */
int myerrno = errno; /* Save the errno for use with "%m". */
/* Parse the arguments to come up with descriptive list. We can't
do this on the fly because we need to support positional
arguments. */
rc = parse_format (format, &argspecs, DIM(argspecs_buffer), &argspecs_len);
if (rc)
goto leave;
/* Check that all ARG_POS fields are set. */
for (argidx=0,max_pos=0; argidx < argspecs_len; argidx++)
{
if (argspecs[argidx].arg_pos != -1
&& argspecs[argidx].arg_pos > max_pos)
max_pos = argspecs[argidx].arg_pos;
if (argspecs[argidx].width_pos > max_pos)
max_pos = argspecs[argidx].width_pos;
if (argspecs[argidx].precision_pos > max_pos)
max_pos = argspecs[argidx].precision_pos;
}
if (!max_pos)
{
/* Fill in all the positions. */
for (argidx=0; argidx < argspecs_len; argidx++)
{
if (argspecs[argidx].width == STAR_FIELD_VALUE)
argspecs[argidx].width_pos = ++max_pos;
if (argspecs[argidx].precision == STAR_FIELD_VALUE)
argspecs[argidx].precision_pos = ++max_pos;
if (argspecs[argidx].arg_pos != -1 )
argspecs[argidx].arg_pos = ++max_pos;
}
}
else
{
/* Check that they are all filled. More test are done later. */
for (argidx=0; argidx < argspecs_len; argidx++)
{
if (!argspecs[argidx].arg_pos
|| (argspecs[argidx].width == STAR_FIELD_VALUE
&& !argspecs[argidx].width_pos)
|| (argspecs[argidx].precision == STAR_FIELD_VALUE
&& !argspecs[argidx].precision_pos))
goto leave_einval;
}
}
/* Check that there is no overflow in max_pos and that it has a
reasonable length. There may never be more elements than the
number of characters in FORMAT. */
if (max_pos < 0 || max_pos >= strlen (format))
goto leave_einval;
#ifdef DEBUG
dump_argspecs (argspecs, argspecs_len);
#endif
/* Allocate a table to hold the values. If it is small enough we
use a stack allocated buffer. */
if (max_pos > DIM(valuetable_buffer))
{
valuetable = calloc (max_pos, sizeof *valuetable);
if (!valuetable)
goto leave_error;
}
else
{
for (validx=0; validx < DIM(valuetable_buffer); validx++)
{
valuetable[validx].vt = VALTYPE_UNSUPPORTED;
memset (&valuetable[validx].value, 0,
sizeof valuetable[validx].value);
}
}
for (argidx=0; argidx < argspecs_len; argidx++)
{
if (argspecs[argidx].arg_pos != - 1)
{
validx = argspecs[argidx].arg_pos - 1;
if (valuetable[validx].vt)
goto leave_einval; /* Already defined. */
valuetable[validx].vt = argspecs[argidx].vt;
}
if (argspecs[argidx].width == STAR_FIELD_VALUE)
{
validx = argspecs[argidx].width_pos - 1;
if (valuetable[validx].vt)
goto leave_einval; /* Already defined. */
valuetable[validx].vt = VALTYPE_INT;
}
if (argspecs[argidx].precision == STAR_FIELD_VALUE)
{
validx = argspecs[argidx].precision_pos - 1;
if (valuetable[validx].vt)
goto leave_einval; /* Already defined. */
valuetable[validx].vt = VALTYPE_INT;
}
}
/* Read all the arguments. This will error out for unsupported
types and for not given positional arguments. */
rc = read_values (valuetable, max_pos, vaargs);
if (rc)
goto leave_einval;
/* for (validx=0; validx < max_pos; validx++) */
/* fprintf (stderr, "%2d: vt=%d\n", validx, valuetable[validx].vt); */
/* Everything has been collected, go ahead with the formatting. */
rc = do_format (outfnc, outfncarg, sf, sfvalue, format,
argspecs, argspecs_len, valuetable, myerrno, &nbytes);
goto leave;
leave_einval:
_set_errno (EINVAL);
leave_error:
rc = -1;
leave:
if (valuetable != valuetable_buffer)
free (valuetable);
if (argspecs != argspecs_buffer)
free (argspecs);
return rc;
}
/* A simple output handler utilizing stdio. */
static int
plain_stdio_out (void *outfncarg, const char *buf, size_t buflen)
{
FILE *fp = (FILE*)outfncarg;
if ( fwrite (buf, buflen, 1, fp) != 1 )
return -1;
return 0;
}
/* A replacement for printf. */
int
_gpgrt_estream_printf (const char *format, ...)
{
int rc;
va_list arg_ptr;
va_start (arg_ptr, format);
rc = _gpgrt_estream_format (plain_stdio_out, stderr, NULL, NULL,
format, arg_ptr);
va_end (arg_ptr);
return rc;
}
/* A replacement for fprintf. */
int
_gpgrt_estream_fprintf (FILE *fp, const char *format, ...)
{
int rc;
va_list arg_ptr;
va_start (arg_ptr, format);
rc = _gpgrt_estream_format (plain_stdio_out, fp, NULL, NULL,
format, arg_ptr);
va_end (arg_ptr);
return rc;
}
/* A replacement for vfprintf. */
int
_gpgrt_estream_vfprintf (FILE *fp, const char *format, va_list arg_ptr)
{
return _gpgrt_estream_format (plain_stdio_out, fp, NULL, NULL,
format, arg_ptr);
}
/* Communication object used between estream_snprintf and
fixed_buffer_out. */
struct fixed_buffer_parm_s
{
size_t size; /* Size of the buffer. */
size_t count; /* Number of bytes requested for output. */
size_t used; /* Used size of the buffer. */
char *buffer; /* Provided buffer. */
};
/* A simple malloced buffer output handler. */
static int
fixed_buffer_out (void *outfncarg, const char *buf, size_t buflen)
{
struct fixed_buffer_parm_s *parm = outfncarg;
parm->count += buflen;
if (!parm->buffer)
;
else if (parm->used + buflen < parm->size)
{
/* Handle the common case that everything fits into the buffer
separately. */
memcpy (parm->buffer + parm->used, buf, buflen);
parm->used += buflen;
}
else
{
/* The slow version of above. */
for ( ;buflen && parm->used < parm->size; buflen--)
parm->buffer[parm->used++] = *buf++;
}
return 0;
}
/* A replacement for vsnprintf. */
int
_gpgrt_estream_vsnprintf (char *buf, size_t bufsize,
const char *format, va_list arg_ptr)
{
struct fixed_buffer_parm_s parm;
int rc;
parm.size = bufsize;
parm.count = 0;
parm.used = 0;
parm.buffer = bufsize?buf:NULL;
rc = _gpgrt_estream_format (fixed_buffer_out, &parm, NULL, NULL,
format, arg_ptr);
if (!rc)
rc = fixed_buffer_out (&parm, "", 1); /* Print terminating Nul. */
if (rc == -1)
return -1;
if (bufsize && buf && parm.size && parm.count >= parm.size)
buf[parm.size-1] = 0;
parm.count--; /* Do not count the trailing nul. */
return (int)parm.count; /* Return number of bytes which would have
been written. */
}
/* A replacement for snprintf. */
int
_gpgrt_estream_snprintf (char *buf, size_t bufsize, const char *format, ...)
{
int rc;
va_list arg_ptr;
va_start (arg_ptr, format);
rc = _gpgrt_estream_vsnprintf (buf, bufsize, format, arg_ptr);
va_end (arg_ptr);
return rc;
}
/* Communication object used between estream_asprintf and
dynamic_buffer_out. */
struct dynamic_buffer_parm_s
{
int error_flag; /* Internal helper. */
size_t alloced; /* Allocated size of the buffer. */
size_t used; /* Used size of the buffer. */
char *buffer; /* Malloced buffer. */
};
/* A simple malloced buffer output handler. */
static int
dynamic_buffer_out (void *outfncarg, const char *buf, size_t buflen)
{
struct dynamic_buffer_parm_s *parm = outfncarg;
if (parm->error_flag)
{
/* Just in case some formatting routine did not checked for an
error. */
_set_errno (parm->error_flag);
return -1;
}
if (parm->used + buflen >= parm->alloced)
{
char *p;
parm->alloced += buflen + 512;
p = my_printf_realloc (parm->buffer, parm->alloced);
if (!p)
{
parm->error_flag = errno ? errno : ENOMEM;
/* Wipe out what we already accumulated. This is useful in
case sensitive data is formatted. */
memset (parm->buffer, 0, parm->used);
return -1;
}
parm->buffer = p;
}
memcpy (parm->buffer + parm->used, buf, buflen);
parm->used += buflen;
return 0;
}
/* A replacement for vasprintf. As with the BSD version of vasprintf
-1 will be returned on error and NULL stored at BUFP. On success
the number of bytes printed will be returned. */
int
_gpgrt_estream_vasprintf (char **bufp, const char *format, va_list arg_ptr)
{
struct dynamic_buffer_parm_s parm;
int rc;
parm.error_flag = 0;
parm.alloced = 512;
parm.used = 0;
parm.buffer = my_printf_realloc (NULL, parm.alloced);
if (!parm.buffer)
{
*bufp = NULL;
return -1;
}
rc = _gpgrt_estream_format (dynamic_buffer_out, &parm, NULL, NULL,
format, arg_ptr);
if (!rc)
rc = dynamic_buffer_out (&parm, "", 1); /* Print terminating Nul. */
/* Fixme: Should we shrink the resulting buffer? */
if (rc != -1 && parm.error_flag)
{
rc = -1;
_set_errno (parm.error_flag);
}
if (rc == -1)
{
memset (parm.buffer, 0, parm.used);
if (parm.buffer)
my_printf_realloc (parm.buffer, 0);
*bufp = NULL;
return -1;
}
- assert (parm.used); /* We have at least the terminating Nul. */
+ gpgrt_assert (parm.used); /* We have at least the terminating Nul. */
*bufp = parm.buffer;
return parm.used - 1; /* Do not include that Nul. */
}
/* A replacement for asprintf. As with the BSD of asprintf version -1
will be returned on error and NULL stored at BUFP. On success the
number of bytes printed will be returned. */
int
_gpgrt_estream_asprintf (char **bufp, const char *format, ...)
{
int rc;
va_list arg_ptr;
va_start (arg_ptr, format);
rc = _gpgrt_estream_vasprintf (bufp, format, arg_ptr);
va_end (arg_ptr);
return rc;
}
/* 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 *
_gpgrt_estream_bsprintf (const char *format, ...)
{
int rc;
va_list ap;
char *buf;
va_start (ap, format);
rc = _gpgrt_estream_vasprintf (&buf, format, ap);
va_end (ap);
if (rc < 0)
return NULL;
return buf;
}
diff --git a/src/estream.c b/src/estream.c
index 3645dfe..8b7ccc5 100644
--- a/src/estream.c
+++ b/src/estream.c
@@ -1,5185 +1,5184 @@
/* estream.c - Extended Stream I/O Library
* Copyright (C) 2004, 2005, 2006, 2007, 2009, 2010, 2011,
* 2014, 2015, 2016, 2017 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 Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser 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
#ifdef HAVE_SYS_SELECT_H
# include
#endif
#ifdef HAVE_SYS_TIME_H
# include
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
-#include
#ifdef HAVE_W32_SYSTEM
# ifdef HAVE_WINSOCK2_H
# include
# endif
# include
#endif
/* Enable tracing. The value is the module name to be printed. */
/*#define ENABLE_TRACING "estream"*/
#include "gpgrt-int.h"
#include "estream-printf.h"
#include "thread.h"
#include "lock.h"
#ifndef O_BINARY
# define O_BINARY 0
#endif
#ifndef HAVE_DOSISH_SYSTEM
# ifdef HAVE_W32_SYSTEM
# define HAVE_DOSISH_SYSTEM 1
# endif
#endif
#ifdef HAVE_W32_SYSTEM
# ifndef S_IRGRP
# define S_IRGRP S_IRUSR
# endif
# ifndef S_IROTH
# define S_IROTH S_IRUSR
# endif
# ifndef S_IWGRP
# define S_IWGRP S_IWUSR
# endif
# ifndef S_IWOTH
# define S_IWOTH S_IWUSR
# endif
# ifndef S_IXGRP
# define S_IXGRP S_IXUSR
# endif
# ifndef S_IXOTH
# define S_IXOTH S_IXUSR
# endif
#endif
#if !defined (EWOULDBLOCK) && defined (HAVE_W32_SYSTEM)
/* Compatibility with errno.h from mingw-2.0 */
# define EWOULDBLOCK 140
#endif
#ifndef EAGAIN
# define EAGAIN EWOULDBLOCK
#endif
#ifdef HAVE_W32CE_SYSTEM
# define _set_errno(a) gpg_err_set_errno ((a))
/* Setmode is missing in cegcc but available since CE 5.0. */
int _setmode (int handle, int mode);
# define setmode(a,b) _setmode ((a),(b))
#else
# define _set_errno(a) do { errno = (a); } while (0)
#endif
#ifdef HAVE_W32_SYSTEM
# define IS_INVALID_FD(a) ((void*)(a) == (void*)(-1)) /* ?? FIXME. */
#else
# define IS_INVALID_FD(a) ((a) == -1)
#endif
/* Calculate array dimension. */
#ifndef DIM
#define DIM(array) (sizeof (array) / sizeof (*array))
#endif
/* A helper macro used to convert to a hex string. */
#define tohex(n) ((n) < 10 ? ((n) + '0') : (((n) - 10) + 'A'))
/* Generally used types. */
typedef void *(*func_realloc_t) (void *mem, size_t size);
typedef void (*func_free_t) (void *mem);
/*
* A linked list to hold active stream objects.
* Protected by ESTREAM_LIST_LOCK.
*/
struct estream_list_s
{
struct estream_list_s *next;
estream_t stream; /* Entry is not used if NULL. */
};
typedef struct estream_list_s *estream_list_t;
static estream_list_t estream_list;
/*
* File descriptors registered for use as the standard file handles.
* Protected by ESTREAM_LIST_LOCK.
*/
static int custom_std_fds[3];
static unsigned char custom_std_fds_valid[3];
/*
* A lock object to protect ESTREAM LIST, CUSTOM_STD_FDS and
* CUSTOM_STD_FDS_VALID. Used by lock_list() and unlock_list().
*/
GPGRT_LOCK_DEFINE (estream_list_lock);
/*
* Error code replacements.
*/
#ifndef EOPNOTSUPP
# define EOPNOTSUPP ENOSYS
#endif
/* Local prototypes. */
static void fname_set_internal (estream_t stream, const char *fname, int quote);
/*
* Memory allocation wrappers used in this file.
*/
static void *
mem_alloc (size_t n)
{
return _gpgrt_malloc (n);
}
static void *
mem_realloc (void *p, size_t n)
{
return _gpgrt_realloc (p, n);
}
static void
mem_free (void *p)
{
if (p)
_gpgrt_free (p);
}
/*
* A Windows helper function to map a W32 API error code to a standard
* system error code.
*/
#ifdef HAVE_W32_SYSTEM
static int
map_w32_to_errno (DWORD w32_err)
{
switch (w32_err)
{
case 0:
return 0;
case ERROR_FILE_NOT_FOUND:
return ENOENT;
case ERROR_PATH_NOT_FOUND:
return ENOENT;
case ERROR_ACCESS_DENIED:
return EPERM;
case ERROR_INVALID_HANDLE:
case ERROR_INVALID_BLOCK:
return EINVAL;
case ERROR_NOT_ENOUGH_MEMORY:
return ENOMEM;
case ERROR_NO_DATA:
return EPIPE;
default:
return EIO;
}
}
#endif /*HAVE_W32_SYSTEM*/
/*
* Replacement for a missing memrchr.
*/
#ifndef HAVE_MEMRCHR
static void *
memrchr (const void *buffer, int c, size_t n)
{
const unsigned char *p = buffer;
for (p += n; n ; n--)
if (*--p == c)
return (void *)p;
return NULL;
}
#endif /*HAVE_MEMRCHR*/
/*
* Wrappers to lock a stream or the list of streams.
*/
#if 0
# define dbg_lock_0(f) fprintf (stderr, "estream: " f);
# define dbg_lock_1(f, a) fprintf (stderr, "estream: " f, (a));
# define dbg_lock_2(f, a, b) fprintf (stderr, "estream: " f, (a), (b));
#else
# define dbg_lock_0(f)
# define dbg_lock_1(f, a)
# define dbg_lock_2(f, a, b)
#endif
static int
init_stream_lock (estream_t _GPGRT__RESTRICT stream)
{
int rc;
if (!stream->intern->samethread)
{
dbg_lock_1 ("enter init_stream_lock for %p\n", stream);
memset (&stream->intern->lock, 0 , sizeof stream->intern->lock);
rc = _gpgrt_lock_init (&stream->intern->lock);
dbg_lock_2 ("leave init_stream_lock for %p: rc=%d\n", stream, rc);
}
else
rc = 0;
return rc;
}
static void
destroy_stream_lock (estream_t _GPGRT__RESTRICT stream)
{
if (!stream->intern->samethread)
{
dbg_lock_1 ("enter destroy_stream_lock for %p\n", stream);
_gpgrt_lock_destroy (&stream->intern->lock);
dbg_lock_1 ("leave destroy_stream_lock for %p\n", stream);
}
}
static void
lock_stream (estream_t _GPGRT__RESTRICT stream)
{
if (!stream->intern->samethread)
{
dbg_lock_1 ("enter lock_stream for %p\n", stream);
_gpgrt_lock_lock (&stream->intern->lock);
dbg_lock_1 ("leave lock_stream for %p\n", stream);
}
}
static int
trylock_stream (estream_t _GPGRT__RESTRICT stream)
{
int rc;
if (!stream->intern->samethread)
{
dbg_lock_1 ("enter trylock_stream for %p\n", stream);
rc = _gpgrt_lock_trylock (&stream->intern->lock)? 0 : -1;
dbg_lock_2 ("leave trylock_stream for %p: rc=%d\n", stream, rc);
}
else
rc = 0;
return rc;
}
static void
unlock_stream (estream_t _GPGRT__RESTRICT stream)
{
if (!stream->intern->samethread)
{
dbg_lock_1 ("enter unlock_stream for %p\n", stream);
_gpgrt_lock_unlock (&stream->intern->lock);
dbg_lock_1 ("leave unlock_stream for %p\n", stream);
}
}
static void
lock_list (void)
{
dbg_lock_0 ("enter lock_list\n");
_gpgrt_lock_lock (&estream_list_lock);
dbg_lock_0 ("leave lock_list\n");
}
static void
unlock_list (void)
{
dbg_lock_0 ("enter unlock_list\n");
_gpgrt_lock_unlock (&estream_list_lock);
dbg_lock_0 ("leave unlock_list\n");
}
#undef dbg_lock_0
#undef dbg_lock_1
#undef dbg_lock_2
/*
* Manipulation of the list of stream.
*/
/*
* Add STREAM to the list of registered stream objects. If
* WITH_LOCKED_LIST is true it is assumed that the list of streams is
* already locked. The implementation is straightforward: We first
* look for an unused entry in the list and use that; if none is
* available we put a new item at the head. We drawback of the
* strategy never to shorten the list is that a one time allocation of
* many streams will lead to scanning unused entries later. If that
* turns out to be a problem, we may either free some items from the
* list or append new entries at the end; or use a table. Returns 0
* on success; on error or non-zero is returned and ERRNO set.
*/
static int
do_list_add (estream_t stream, int with_locked_list)
{
estream_list_t item;
if (!with_locked_list)
lock_list ();
for (item = estream_list; item && item->stream; item = item->next)
;
if (!item)
{
item = mem_alloc (sizeof *item);
if (item)
{
item->next = estream_list;
estream_list = item;
}
}
if (item)
item->stream = stream;
if (!with_locked_list)
unlock_list ();
return item? 0 : -1;
}
/*
* Remove STREAM from the list of registered stream objects.
*/
static void
do_list_remove (estream_t stream, int with_locked_list)
{
estream_list_t item, item_prev = NULL;
if (!with_locked_list)
lock_list ();
for (item = estream_list; item; item = item->next)
if (item->stream == stream)
break;
else
item_prev = item;
if (item_prev)
{
item_prev->next = item->next;
mem_free (item);
}
else
{
if (item)
{
estream_list = item->next;
mem_free (item);
}
}
if (!with_locked_list)
unlock_list ();
}
/*
* The atexit handler for the entire gpgrt.
*/
static void
do_deinit (void)
{
/* Flush all streams. */
_gpgrt_fflush (NULL);
/* We should release the estream_list. However there is one
problem: That list is also used to search for the standard
estream file descriptors. If we would remove the entire list,
any use of es_foo in another atexit function may re-create the
list and the streams with possible undesirable effects. Given
that we don't close the stream either, it should not matter that
we keep the list and let the OS clean it up at process end. */
/* Reset the syscall clamp. */
_gpgrt_set_syscall_clamp (NULL, NULL);
}
/*
* Initialization of the estream module.
*/
int
_gpgrt_estream_init (void)
{
static int initialized;
if (!initialized)
{
initialized = 1;
atexit (do_deinit);
}
return 0;
}
/*
* Implementation of memory based 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. If this function succeeds DATA is now owned
* by this function. If GROW is false FUNC_REALLOC is not
* required.
*/
static int
func_mem_create (void *_GPGRT__RESTRICT *_GPGRT__RESTRICT cookie,
unsigned char *_GPGRT__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;
}
if (grow && func_free && !func_realloc)
{
_set_errno (EINVAL);
return -1;
}
/* Round a memory limit up to the next block length. */
if (memory_limit && block_size)
{
memory_limit += block_size - 1;
memory_limit /= block_size;
memory_limit *= block_size;
}
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
= grow? (func_realloc ? func_realloc : mem_realloc) : NULL;
mem_cookie->func_free = func_free ? func_free : mem_free;
*cookie = mem_cookie;
err = 0;
}
return err;
}
/*
* Read function for memory objects.
*/
static gpgrt_ssize_t
func_mem_read (void *cookie, void *buffer, size_t size)
{
estream_cookie_mem_t mem_cookie = cookie;
gpgrt_ssize_t ret;
if (!size) /* Just the pending data check. */
return (mem_cookie->data_len - mem_cookie->offset)? 0 : -1;
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 gpgrt_ssize_t
func_mem_write (void *cookie, const void *buffer, size_t size)
{
estream_cookie_mem_t mem_cookie = cookie;
gpgrt_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);
+ gpgrt_assert (mem_cookie->memory_size >= mem_cookie->offset);
nleft = mem_cookie->memory_size - mem_cookie->offset;
/* If we are not allowed to grow the buffer, 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 + (size - nleft);
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;
}
- assert (mem_cookie->func_realloc);
+ gpgrt_assert (mem_cookie->func_realloc);
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);
+ gpgrt_assert (mem_cookie->memory_size >= mem_cookie->offset);
nleft = mem_cookie->memory_size - mem_cookie->offset;
- assert (size <= nleft);
+ gpgrt_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
func_mem_seek (void *cookie, gpgrt_off_t *offset, int whence)
{
estream_cookie_mem_t mem_cookie = cookie;
gpgrt_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;
}
- assert (mem_cookie->func_realloc);
+ gpgrt_assert (mem_cookie->func_realloc);
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;
}
/*
* The IOCTL function for memory objects.
*/
static int
func_mem_ioctl (void *cookie, int cmd, void *ptr, size_t *len)
{
estream_cookie_mem_t mem_cookie = cookie;
int ret;
if (cmd == COOKIE_IOCTL_SNATCH_BUFFER)
{
/* Return the internal buffer of the stream to the caller and
invalidate it for the stream. */
*(void**)ptr = mem_cookie->memory;
*len = mem_cookie->data_len;
mem_cookie->memory = NULL;
mem_cookie->memory_size = 0;
mem_cookie->offset = 0;
ret = 0;
}
else if (cmd == COOKIE_IOCTL_TRUNCATE)
{
gpgrt_off_t length = *(gpgrt_off_t *)ptr;
ret = func_mem_seek (cookie, &length, SEEK_SET);
if (ret != -1)
mem_cookie->data_len = mem_cookie->offset;
}
else
{
_set_errno (EINVAL);
ret = -1;
}
return ret;
}
/*
* The destroy function for memory objects.
*/
static int
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;
}
/*
* Access object for the memory functions.
*/
static struct cookie_io_functions_s estream_functions_mem =
{
{
func_mem_read,
func_mem_write,
func_mem_seek,
func_mem_destroy,
},
func_mem_ioctl,
};
/*
* Implementation of file descriptor based 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. */
int nonblock; /* Non-blocking mode is enabled. */
} *estream_cookie_fd_t;
/*
* Create function for objects indentified by a libc file descriptor.
*/
static int
func_fd_create (void **cookie, int fd, unsigned int modeflags, int no_close)
{
estream_cookie_fd_t fd_cookie;
int err;
trace (("enter: fd=%d mf=%x nc=%d", fd, modeflags, no_close));
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);
#endif
fd_cookie->fd = fd;
fd_cookie->no_close = no_close;
fd_cookie->nonblock = !!(modeflags & O_NONBLOCK);
*cookie = fd_cookie;
err = 0;
}
trace_errno (err, ("leave: cookie=%p err=%d", *cookie, err));
return err;
}
/*
* Read function for fd objects.
*/
static gpgrt_ssize_t
func_fd_read (void *cookie, void *buffer, size_t size)
{
estream_cookie_fd_t file_cookie = cookie;
gpgrt_ssize_t bytes_read;
trace (("enter: cookie=%p buffer=%p size=%d", cookie, buffer, (int)size));
if (!size)
bytes_read = -1; /* We don't know whether anything is pending. */
else if (IS_INVALID_FD (file_cookie->fd))
{
_gpgrt_yield ();
bytes_read = 0;
}
else
{
_gpgrt_pre_syscall ();
do
{
bytes_read = read (file_cookie->fd, buffer, size);
}
while (bytes_read == -1 && errno == EINTR);
_gpgrt_post_syscall ();
}
trace_errno (bytes_read == -1, ("leave: bytes_read=%d", (int)bytes_read));
return bytes_read;
}
/*
* Write function for fd objects.
*/
static gpgrt_ssize_t
func_fd_write (void *cookie, const void *buffer, size_t size)
{
estream_cookie_fd_t file_cookie = cookie;
gpgrt_ssize_t bytes_written;
trace (("enter: cookie=%p buffer=%p size=%d", cookie, buffer, (int)size));
if (IS_INVALID_FD (file_cookie->fd))
{
_gpgrt_yield ();
bytes_written = size; /* Yeah: Success writing to the bit bucket. */
}
else if (buffer)
{
_gpgrt_pre_syscall ();
do
{
bytes_written = write (file_cookie->fd, buffer, size);
}
while (bytes_written == -1 && errno == EINTR);
_gpgrt_post_syscall ();
}
else
bytes_written = size; /* Note that for a flush SIZE should be 0. */
trace_errno (bytes_written == -1,
("leave: bytes_written=%d", (int)bytes_written));
return bytes_written;
}
/*
* Seek function for fd objects.
*/
static int
func_fd_seek (void *cookie, gpgrt_off_t *offset, int whence)
{
estream_cookie_fd_t file_cookie = cookie;
gpgrt_off_t offset_new;
int err;
if (IS_INVALID_FD (file_cookie->fd))
{
_set_errno (ESPIPE);
err = -1;
}
else
{
_gpgrt_pre_syscall ();
offset_new = lseek (file_cookie->fd, *offset, whence);
_gpgrt_post_syscall ();
if (offset_new == -1)
err = -1;
else
{
*offset = offset_new;
err = 0;
}
}
return err;
}
/*
* The IOCTL function for fd objects.
*/
static int
func_fd_ioctl (void *cookie, int cmd, void *ptr, size_t *len)
{
estream_cookie_fd_t fd_cookie = cookie;
int ret;
if (cmd == COOKIE_IOCTL_NONBLOCK && !len)
{
fd_cookie->nonblock = !!ptr;
if (IS_INVALID_FD (fd_cookie->fd))
{
_set_errno (EINVAL);
ret = -1;
}
else
{
#ifdef _WIN32
_set_errno (EOPNOTSUPP); /* FIXME: Implement for Windows. */
ret = -1;
#else
_set_errno (0);
ret = fcntl (fd_cookie->fd, F_GETFL, 0);
if (ret == -1 && errno)
;
else if (fd_cookie->nonblock)
ret = fcntl (fd_cookie->fd, F_SETFL, (ret | O_NONBLOCK));
else
ret = fcntl (fd_cookie->fd, F_SETFL, (ret & ~O_NONBLOCK));
#endif
}
}
else
{
_set_errno (EINVAL);
ret = -1;
}
return ret;
}
/*
* The destroy function for fd objects.
*/
static int
func_fd_destroy (void *cookie)
{
estream_cookie_fd_t fd_cookie = cookie;
int err;
trace (("enter: cookie=%p", cookie));
if (fd_cookie)
{
if (IS_INVALID_FD (fd_cookie->fd))
err = 0;
else
err = fd_cookie->no_close? 0 : close (fd_cookie->fd);
mem_free (fd_cookie);
}
else
err = 0;
trace_errno (err,("leave: err=%d", err));
return err;
}
/*
* Access object for the fd functions.
*/
static struct cookie_io_functions_s estream_functions_fd =
{
{
func_fd_read,
func_fd_write,
func_fd_seek,
func_fd_destroy,
},
func_fd_ioctl,
};
/*
* Implementation of W32 handle based I/O.
*/
#ifdef HAVE_W32_SYSTEM
/* Cookie for fd objects. */
typedef struct estream_cookie_w32
{
HANDLE hd; /* The handle we are using for actual output. */
int no_close; /* If set we won't close the handle. */
int no_syscall_clamp; /* Do not use the syscall clamp. */
} *estream_cookie_w32_t;
/*
* Create function for w32 handle objects.
*/
static int
func_w32_create (void **cookie, HANDLE hd,
unsigned int modeflags, int no_close, int no_syscall_clamp)
{
estream_cookie_w32_t w32_cookie;
int err;
trace (("enter: hd=%p mf=%x nc=%d nsc=%d",
hd, modeflags, no_close, no_syscall_clamp));
w32_cookie = mem_alloc (sizeof (*w32_cookie));
if (!w32_cookie)
err = -1;
else
{
/* CR/LF translations are not supported when using the bare W32
API. If that is really required we need to implemented that
in the upper layer. */
(void)modeflags;
w32_cookie->hd = hd;
w32_cookie->no_close = no_close;
w32_cookie->no_syscall_clamp = no_syscall_clamp;
*cookie = w32_cookie;
err = 0;
}
trace_errno (err, ("leave: cookie=%p err=%d", *cookie, err));
return err;
}
/*
* Read function for W32 handle objects.
*
* Note that this function may also be used by the reader thread of
* w32-stream. In that case the NO_SYSCALL_CLAMP is set.
*/
static gpgrt_ssize_t
func_w32_read (void *cookie, void *buffer, size_t size)
{
estream_cookie_w32_t w32_cookie = cookie;
gpgrt_ssize_t bytes_read;
trace (("enter: cookie=%p buffer=%p size=%d", cookie, buffer, (int)size));
if (!size)
bytes_read = -1; /* We don't know whether anything is pending. */
else if (w32_cookie->hd == INVALID_HANDLE_VALUE)
{
_gpgrt_yield ();
bytes_read = 0;
}
else
{
if (!w32_cookie->no_syscall_clamp)
_gpgrt_pre_syscall ();
do
{
DWORD nread, ec;
trace (("cookie=%p calling ReadFile", cookie));
if (!ReadFile (w32_cookie->hd, buffer, size, &nread, NULL))
{
ec = GetLastError ();
trace (("cookie=%p ReadFile failed: ec=%ld", cookie,ec));
if (ec == ERROR_BROKEN_PIPE)
bytes_read = 0; /* Like our pth_read we handle this as EOF. */
else
{
_set_errno (map_w32_to_errno (ec));
bytes_read = -1;
}
}
else
bytes_read = (int)nread;
}
while (bytes_read == -1 && errno == EINTR);
if (!w32_cookie->no_syscall_clamp)
_gpgrt_post_syscall ();
}
trace_errno (bytes_read==-1,("leave: bytes_read=%d", (int)bytes_read));
return bytes_read;
}
/*
* Write function for W32 handle objects.
*
* Note that this function may also be used by the writer thread of
* w32-stream. In that case the NO_SYSCALL_CLAMP is set.
*/
static gpgrt_ssize_t
func_w32_write (void *cookie, const void *buffer, size_t size)
{
estream_cookie_w32_t w32_cookie = cookie;
gpgrt_ssize_t bytes_written;
trace (("enter: cookie=%p buffer=%p size=%d", cookie, buffer, (int)size));
if (w32_cookie->hd == INVALID_HANDLE_VALUE)
{
_gpgrt_yield ();
bytes_written = size; /* Yeah: Success writing to the bit bucket. */
}
else if (buffer)
{
if (!w32_cookie->no_syscall_clamp)
_gpgrt_pre_syscall ();
do
{
DWORD nwritten;
trace (("cookie=%p calling WriteFile", cookie));
if (!WriteFile (w32_cookie->hd, buffer, size, &nwritten, NULL))
{
DWORD ec = GetLastError ();
trace (("cookie=%p WriteFile failed: ec=%ld", cookie, ec));
_set_errno (map_w32_to_errno (ec));
bytes_written = -1;
}
else
bytes_written = (int)nwritten;
}
while (bytes_written == -1 && errno == EINTR);
if (!w32_cookie->no_syscall_clamp)
_gpgrt_post_syscall ();
}
else
bytes_written = size; /* Note that for a flush SIZE should be 0. */
trace_errno (bytes_written==-1,
("leave: bytes_written=%d", (int)bytes_written));
return bytes_written;
}
/*
* Seek function for W32 handle objects.
*/
static int
func_w32_seek (void *cookie, gpgrt_off_t *offset, int whence)
{
estream_cookie_w32_t w32_cookie = cookie;
DWORD method;
LARGE_INTEGER distance, newoff;
if (w32_cookie->hd == INVALID_HANDLE_VALUE)
{
_set_errno (ESPIPE);
return -1;
}
if (whence == SEEK_SET)
{
method = FILE_BEGIN;
distance.QuadPart = (unsigned long long)(*offset);
}
else if (whence == SEEK_CUR)
{
method = FILE_CURRENT;
distance.QuadPart = (long long)(*offset);
}
else if (whence == SEEK_END)
{
method = FILE_END;
distance.QuadPart = (long long)(*offset);
}
else
{
_set_errno (EINVAL);
return -1;
}
#ifdef HAVE_W32CE_SYSTEM
# warning need to use SetFilePointer
#else
if (!w32_cookie->no_syscall_clamp)
_gpgrt_pre_syscall ();
if (!SetFilePointerEx (w32_cookie->hd, distance, &newoff, method))
{
_set_errno (map_w32_to_errno (GetLastError ()));
_gpgrt_post_syscall ();
return -1;
}
if (!w32_cookie->no_syscall_clamp)
_gpgrt_post_syscall ();
#endif
/* Note that gpgrt_off_t is always 64 bit. */
*offset = (gpgrt_off_t)newoff.QuadPart;
return 0;
}
/*
* Destroy function for W32 handle objects.
*/
static int
func_w32_destroy (void *cookie)
{
estream_cookie_w32_t w32_cookie = cookie;
int err;
trace (("enter: cookie=%p", cookie));
if (w32_cookie)
{
if (w32_cookie->hd == INVALID_HANDLE_VALUE)
err = 0;
else if (w32_cookie->no_close)
err = 0;
else
{
trace (("cookie=%p closing handle %p", cookie, w32_cookie->hd));
if (!CloseHandle (w32_cookie->hd))
{
DWORD ec = GetLastError ();
trace (("cookie=%p CloseHandle failed: ec=%ld", cookie,ec));
_set_errno (map_w32_to_errno (ec));
err = -1;
}
else
err = 0;
}
mem_free (w32_cookie);
}
else
err = 0;
trace_errno (err, ("leave: err=%d", err));
return err;
}
/*
* Access object for the W32 handle based objects.
*/
static struct cookie_io_functions_s estream_functions_w32 =
{
{
func_w32_read,
func_w32_write,
func_w32_seek,
func_w32_destroy,
},
NULL,
};
#endif /*HAVE_W32_SYSTEM*/
/*
* Implementation of stdio based 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 stdio based objects.
*/
static int
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 stdio based objects.
*/
static gpgrt_ssize_t
func_fp_read (void *cookie, void *buffer, size_t size)
{
estream_cookie_fp_t file_cookie = cookie;
gpgrt_ssize_t bytes_read;
if (!size)
return -1; /* We don't know whether anything is pending. */
if (file_cookie->fp)
{
_gpgrt_pre_syscall ();
bytes_read = fread (buffer, 1, size, file_cookie->fp);
_gpgrt_post_syscall ();
}
else
bytes_read = 0;
if (!bytes_read && ferror (file_cookie->fp))
return -1;
return bytes_read;
}
/*
* Write function for stdio bases objects.
*/
static gpgrt_ssize_t
func_fp_write (void *cookie, const void *buffer, size_t size)
{
estream_cookie_fp_t file_cookie = cookie;
size_t bytes_written;
if (file_cookie->fp)
{
_gpgrt_pre_syscall ();
if (buffer)
{
#ifdef HAVE_W32_SYSTEM
/* Using an fwrite to stdout connected to the console fails
with the error "Not enough space" for an fwrite size of
>= 52KB (tested on Windows XP SP2). To solve this we
always chunk the writes up into smaller blocks. */
bytes_written = 0;
while (bytes_written < size)
{
size_t cnt = size - bytes_written;
if (cnt > 32*1024)
cnt = 32*1024;
if (fwrite ((const char*)buffer + bytes_written,
cnt, 1, file_cookie->fp) != 1)
break; /* Write error. */
bytes_written += cnt;
}
#else
bytes_written = fwrite (buffer, 1, size, file_cookie->fp);
#endif
}
else /* Only flush requested. */
bytes_written = size;
fflush (file_cookie->fp);
_gpgrt_post_syscall ();
}
else
bytes_written = size; /* Successfully written to the bit bucket. */
if (bytes_written != size)
return -1;
return bytes_written;
}
/*
* Seek function for stdio based objects.
*/
static int
func_fp_seek (void *cookie, gpgrt_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;
}
_gpgrt_pre_syscall ();
if ( fseek (file_cookie->fp, (long int)*offset, whence) )
{
/* fprintf (stderr, "\nfseek failed: errno=%d (%s)\n", */
/* errno,strerror (errno)); */
_gpgrt_post_syscall ();
return -1;
}
offset_new = ftell (file_cookie->fp);
_gpgrt_post_syscall ();
if (offset_new == -1)
{
/* fprintf (stderr, "\nftell failed: errno=%d (%s)\n", */
/* errno,strerror (errno)); */
return -1;
}
*offset = offset_new;
return 0;
}
/*
* Destroy function for stdio based objects.
*/
static int
func_fp_destroy (void *cookie)
{
estream_cookie_fp_t fp_cookie = cookie;
int err;
if (fp_cookie)
{
if (fp_cookie->fp)
{
_gpgrt_pre_syscall ();
fflush (fp_cookie->fp);
_gpgrt_post_syscall ();
err = fp_cookie->no_close? 0 : fclose (fp_cookie->fp);
}
else
err = 0;
mem_free (fp_cookie);
}
else
err = 0;
return err;
}
/*
* Access object for stdio based objects.
*/
static struct cookie_io_functions_s estream_functions_fp =
{
{
func_fp_read,
func_fp_write,
func_fp_seek,
func_fp_destroy,
},
NULL,
};
/*
* Implementation of file name based I/O.
*
* Note that only a create function is required because the other
* operations ares handled by file descriptor based I/O.
*/
/* Create function for objects identified by a file name. */
static int
func_file_create (void **cookie, int *filedes,
const char *path, unsigned int modeflags, unsigned int cmode)
{
estream_cookie_fd_t file_cookie;
int err;
int fd;
err = 0;
file_cookie = mem_alloc (sizeof (*file_cookie));
if (! file_cookie)
{
err = -1;
goto out;
}
fd = open (path, modeflags, cmode);
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;
}
/* Flags used by parse_mode and friends. */
#define X_SAMETHREAD (1 << 0)
#define X_SYSOPEN (1 << 1)
#define X_POLLABLE (1 << 2)
/* Parse the mode flags of fopen et al. In addition to the POSIX
* defined mode flags keyword parameters are supported. These are
* key/value pairs delimited by comma and optional white spaces.
* Keywords and values may not contain a comma or white space; unknown
* keywords are skipped. Supported keywords are:
*
* mode=
*
* Creates a file and gives the new file read and write permissions
* for the user and read permission for the group. The format of
* the string is the same as shown by the -l option of the ls(1)
* command. However the first letter must be a dash and it is
* allowed to leave out trailing dashes. If this keyword parameter
* is not given the default mode for creating files is "-rw-rw-r--"
* (664). Note that the system still applies the current umask to
* the mode when creating a file. Example:
*
* "wb,mode=-rw-r--"
*
* samethread
*
* Assumes that the object is only used by the creating thread and
* disables any internal locking. This keyword is also found on
* IBM systems.
*
* nonblock
*
* The object is opened in non-blocking mode. This is the same as
* calling gpgrt_set_nonblock on the file.
*
* sysopen
*
* The object is opened in sysmode. On POSIX this is a NOP but
* under Windows the direct W32 API functions (HANDLE) are used
* instead of their libc counterparts (fd).
* FIXME: The functionality is not yet implemented.
*
* pollable
*
* The object is opened in a way suitable for use with es_poll. On
* POSIX this is a NOP but under Windows we create up to two
* threads, one for reading and one for writing, do any I/O there,
* and synchronize with them in order to support es_poll.
*
* Note: R_CMODE is optional because is only required by functions
* which are able to creat a file.
*/
static int
parse_mode (const char *modestr,
unsigned int *modeflags,
unsigned int *r_xmode,
unsigned int *r_cmode)
{
unsigned int omode, oflags, cmode;
int got_cmode = 0;
*r_xmode = 0;
switch (*modestr)
{
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 (modestr++; *modestr; modestr++)
{
switch (*modestr)
{
case '+':
omode = O_RDWR;
break;
case 'b':
oflags |= O_BINARY;
break;
case 'x':
oflags |= O_EXCL;
break;
case ',':
goto keyvalue;
default: /* Ignore unknown flags. */
break;
}
}
keyvalue:
/* Parse key/value pairs (similar to fopen on mainframes). */
for (cmode=0; *modestr == ','; modestr += strcspn (modestr, ","))
{
modestr++;
modestr += strspn (modestr, " \t");
if (!strncmp (modestr, "mode=", 5))
{
static struct {
char letter;
unsigned int value;
} table[] = { { '-', 0 },
{ 'r', S_IRUSR }, { 'w', S_IWUSR }, { 'x', S_IXUSR },
{ 'r', S_IRGRP }, { 'w', S_IWGRP }, { 'x', S_IXGRP },
{ 'r', S_IROTH }, { 'w', S_IWOTH }, { 'x', S_IXOTH }};
int idx;
got_cmode = 1;
modestr += 5;
/* For now we only support a string as used by ls(1) and no
octal numbers. The first character must be a dash. */
for (idx=0; idx < 10 && *modestr; idx++, modestr++)
{
if (*modestr == table[idx].letter)
cmode |= table[idx].value;
else if (*modestr != '-')
break;
}
if (*modestr && !strchr (" \t,", *modestr))
{
_set_errno (EINVAL);
return -1;
}
}
else if (!strncmp (modestr, "samethread", 10))
{
modestr += 10;
if (*modestr && !strchr (" \t,", *modestr))
{
_set_errno (EINVAL);
return -1;
}
*r_xmode |= X_SAMETHREAD;
}
else if (!strncmp (modestr, "nonblock", 8))
{
modestr += 8;
if (*modestr && !strchr (" \t,", *modestr))
{
_set_errno (EINVAL);
return -1;
}
oflags |= O_NONBLOCK;
#if HAVE_W32_SYSTEM
/* Currently, nonblock implies pollable on Windows. */
*r_xmode |= X_POLLABLE;
#endif
}
else if (!strncmp (modestr, "sysopen", 7))
{
modestr += 7;
if (*modestr && !strchr (" \t,", *modestr))
{
_set_errno (EINVAL);
return -1;
}
*r_xmode |= X_SYSOPEN;
}
else if (!strncmp (modestr, "pollable", 8))
{
modestr += 8;
if (*modestr && !strchr (" \t,", *modestr))
{
_set_errno (EINVAL);
return -1;
}
*r_xmode |= X_POLLABLE;
}
}
if (!got_cmode)
cmode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
*modeflags = (omode | oflags);
if (r_cmode)
*r_cmode = cmode;
return 0;
}
/*
* Low level stream functionality.
*/
static int
fill_stream (estream_t stream)
{
size_t bytes_read = 0;
int err;
if (!stream->intern->func_read)
{
_set_errno (EOPNOTSUPP);
err = -1;
}
else if (!stream->buffer_size)
err = 0;
else
{
gpgrt_cookie_read_function_t func_read = stream->intern->func_read;
gpgrt_ssize_t ret;
ret = (*func_read) (stream->intern->cookie,
stream->buffer, stream->buffer_size);
if (ret == -1)
{
bytes_read = 0;
err = -1;
#if EWOULDBLOCK != EAGAIN
if (errno == EWOULDBLOCK)
_set_errno (EAGAIN);
#endif
}
else
{
bytes_read = ret;
err = 0;
}
}
if (err)
{
if (errno != EAGAIN)
{
if (errno == EPIPE)
stream->intern->indicators.hup = 1;
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
flush_stream (estream_t stream)
{
gpgrt_cookie_write_function_t func_write = stream->intern->func_write;
int err;
- assert (stream->flags.writing);
+ gpgrt_assert (stream->flags.writing);
if (stream->data_offset)
{
size_t bytes_written;
size_t data_flushed;
gpgrt_ssize_t ret;
if (! func_write)
{
_set_errno (EOPNOTSUPP);
err = -1;
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 ((((gpgrt_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;
#if EWOULDBLOCK != EAGAIN
if (errno == EWOULDBLOCK)
_set_errno (EAGAIN);
#endif
}
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;
}
}
else
err = 0;
/* Always propagate flush event in case gpgrt_fflush was called
* explictly to do flush buffers in caller's cookie functions. */
(*func_write) (stream->intern->cookie, NULL, 0);
out:
if (err && errno != EAGAIN)
{
if (errno == EPIPE)
stream->intern->indicators.hup = 1;
stream->intern->indicators.err = 1;
}
return err;
}
/*
* Discard buffered data for STREAM.
*/
static void
es_empty (estream_t stream)
{
- assert (!stream->flags.writing);
+ gpgrt_assert (!stream->flags.writing);
stream->data_len = 0;
stream->data_offset = 0;
stream->unread_data_len = 0;
}
/*
* Initialize STREAM.
*/
static void
init_stream_obj (estream_t stream,
void *cookie, es_syshd_t *syshd,
gpgrt_stream_backend_kind_t kind,
struct cookie_io_functions_s functions,
unsigned int modeflags, unsigned int xmode)
{
stream->intern->kind = kind;
stream->intern->cookie = cookie;
stream->intern->opaque = NULL;
stream->intern->offset = 0;
stream->intern->func_read = functions.public.func_read;
stream->intern->func_write = functions.public.func_write;
stream->intern->func_seek = functions.public.func_seek;
stream->intern->func_ioctl = functions.func_ioctl;
stream->intern->func_close = functions.public.func_close;
stream->intern->strategy = _IOFBF;
stream->intern->syshd = *syshd;
stream->intern->print_ntotal = 0;
stream->intern->indicators.err = 0;
stream->intern->indicators.eof = 0;
stream->intern->indicators.hup = 0;
stream->intern->is_stdstream = 0;
stream->intern->stdstream_fd = 0;
stream->intern->deallocate_buffer = 0;
stream->intern->printable_fname = NULL;
stream->intern->printable_fname_inuse = 0;
stream->intern->samethread = !! (xmode & X_SAMETHREAD);
stream->intern->onclose = NULL;
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
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 output will be delivered. */
if ((modeflags & O_WRONLY) || (modeflags & O_RDWR) )
stream->flags.writing = 1;
else
stream->flags.writing = 0;
}
/*
* Deinitialize the STREAM object. This does _not_ free the memory,
* destroys the lock, or closes the underlying descriptor.
*/
static int
deinit_stream_obj (estream_t stream)
{
gpgrt_cookie_close_function_t func_close;
int err, tmp_err;
trace (("enter: stream %p", stream));
func_close = stream->intern->func_close;
err = 0;
if (stream->flags.writing)
{
tmp_err = flush_stream (stream);
if (!err)
err = tmp_err;
}
if (func_close)
{
trace (("stream %p calling func_close", stream));
tmp_err = func_close (stream->intern->cookie);
if (!err)
err = tmp_err;
}
mem_free (stream->intern->printable_fname);
stream->intern->printable_fname = NULL;
stream->intern->printable_fname_inuse = 0;
while (stream->intern->onclose)
{
notify_list_t tmp = stream->intern->onclose->next;
mem_free (stream->intern->onclose);
stream->intern->onclose = tmp;
}
trace_errno (err, ("leave: stream %p err=%d", stream, err));
return err;
}
/*
* Create a new stream and initialize it. On success the new stream
* handle is tsored at R_STREAM. On failure NULL is stored at
* R_STREAM.
*/
static int
create_stream (estream_t *r_stream, void *cookie, es_syshd_t *syshd,
gpgrt_stream_backend_kind_t kind,
struct cookie_io_functions_s functions, unsigned int modeflags,
unsigned int xmode, int with_locked_list)
{
estream_internal_t stream_internal_new;
estream_t stream_new;
int err;
#if HAVE_W32_SYSTEM
void *old_cookie = NULL;
#endif
stream_new = NULL;
stream_internal_new = NULL;
#if HAVE_W32_SYSTEM
if ((xmode & X_POLLABLE) && kind != BACKEND_W32)
{
/* We require the W32 backend, because only that allows us to
* write directly using the native W32 API and to disable the
* system clamp. Note that func_w32_create has already been
* called with the flag to disable the system call clamp. */
_set_errno (EINVAL);
err = -1;
goto out;
}
#endif /*HAVE_W32_SYSTEM*/
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;
#if HAVE_W32_SYSTEM
if ((xmode & X_POLLABLE))
{
void *new_cookie;
err = _gpgrt_w32_pollable_create (&new_cookie, modeflags,
functions, cookie);
if (err)
goto out;
modeflags &= ~O_NONBLOCK;
old_cookie = cookie;
cookie = new_cookie;
kind = BACKEND_W32_POLLABLE;
functions = _gpgrt_functions_w32_pollable;
}
#endif /*HAVE_W32_SYSTEM*/
init_stream_obj (stream_new, cookie, syshd, kind, functions, modeflags,
xmode);
init_stream_lock (stream_new);
err = do_list_add (stream_new, with_locked_list);
if (err)
goto out;
*r_stream = stream_new;
out:
if (err)
{
trace_errno (err, ("leave: err=%d", err));
if (stream_new)
{
deinit_stream_obj (stream_new);
destroy_stream_lock (stream_new);
mem_free (stream_new->intern);
mem_free (stream_new);
}
}
#if HAVE_W32_SYSTEM
else if (old_cookie)
trace (("leave: success stream=%p cookie=%p,%p",
*r_stream, old_cookie, cookie));
#endif
else
trace (("leave: success stream=%p cookie=%p", *r_stream, cookie));
return err;
}
/*
* Deinitialize a stream object and destroy it.
*/
static int
do_close (estream_t stream, int with_locked_list)
{
int err;
trace (("stream %p %s", stream, with_locked_list? "(with locked list)":""));
if (stream)
{
do_list_remove (stream, with_locked_list);
while (stream->intern->onclose)
{
notify_list_t tmp = stream->intern->onclose->next;
if (stream->intern->onclose->fnc)
stream->intern->onclose->fnc (stream,
stream->intern->onclose->fnc_value);
mem_free (stream->intern->onclose);
stream->intern->onclose = tmp;
}
err = deinit_stream_obj (stream);
destroy_stream_lock (stream);
if (stream->intern->deallocate_buffer)
mem_free (stream->buffer);
mem_free (stream->intern);
mem_free (stream);
}
else
err = 0;
trace_errno (err, ("stream %p err=%d", stream, err));
return err;
}
/*
* The onclose worker function which is called with a locked
* stream.
*/
static int
do_onclose (estream_t stream, int mode,
void (*fnc) (estream_t, void*), void *fnc_value)
{
notify_list_t item;
if (!mode)
{
for (item = stream->intern->onclose; item; item = item->next)
if (item->fnc && item->fnc == fnc && item->fnc_value == fnc_value)
item->fnc = NULL; /* Disable this notification. */
}
else
{
item = mem_alloc (sizeof *item);
if (!item)
return -1;
item->fnc = fnc;
item->fnc_value = fnc_value;
item->next = stream->intern->onclose;
stream->intern->onclose = item;
}
return 0;
}
/*
* Try to read BYTES_TO_READ bytes from STREAM into BUFFER in
* unbuffered-mode, storing the amount of bytes read at BYTES_READ.
*/
static int
do_read_nbf (estream_t _GPGRT__RESTRICT stream,
unsigned char *_GPGRT__RESTRICT buffer,
size_t bytes_to_read, size_t *_GPGRT__RESTRICT bytes_read)
{
gpgrt_cookie_read_function_t func_read = stream->intern->func_read;
size_t data_read;
gpgrt_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;
#if EWOULDBLOCK != EAGAIN
if (errno == EWOULDBLOCK)
_set_errno (EAGAIN);
#endif
break;
}
else if (ret)
data_read += ret;
else
break;
}
stream->intern->offset += data_read;
*bytes_read = data_read;
return err;
}
/*
* Helper for check_pending.
*/
static int
check_pending_nbf (estream_t _GPGRT__RESTRICT stream)
{
gpgrt_cookie_read_function_t func_read = stream->intern->func_read;
char buffer[1];
if (!(*func_read) (stream->intern->cookie, buffer, 0))
return 1; /* Pending bytes. */
return 0; /* No pending bytes or error. */
}
/*
* Try to read BYTES_TO_READ bytes from STREAM into BUFFER in
* fully-buffered-mode, storing the amount of bytes read at
* BYTES_READ.
*/
static int
do_read_fbf (estream_t _GPGRT__RESTRICT stream,
unsigned char *_GPGRT__RESTRICT buffer,
size_t bytes_to_read, size_t *_GPGRT__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 = fill_stream (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;
}
/*
* Helper for check_pending.
*/
static int
check_pending_fbf (estream_t _GPGRT__RESTRICT stream)
{
gpgrt_cookie_read_function_t func_read = stream->intern->func_read;
char buffer[1];
if (stream->data_offset == stream->data_len)
{
/* Nothing more to read in current container, check whether it
would be possible to fill the container with new data. */
if (!(*func_read) (stream->intern->cookie, buffer, 0))
return 1; /* Pending bytes. */
}
else
return 1;
return 0;
}
/*
* Try to read BYTES_TO_READ bytes from STREAM into BUFFER in
* line-buffered-mode, storing the amount of bytes read at BYTES_READ.
*/
static int
do_read_lbf (estream_t _GPGRT__RESTRICT stream,
unsigned char *_GPGRT__RESTRICT buffer,
size_t bytes_to_read, size_t *_GPGRT__RESTRICT bytes_read)
{
int err;
err = do_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 at BYTES_READ.
*/
static int
es_readn (estream_t _GPGRT__RESTRICT stream,
void *_GPGRT__RESTRICT buffer_arg,
size_t bytes_to_read, size_t *_GPGRT__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 = flush_stream (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 = do_read_nbf (stream,
buffer + data_read_unread,
bytes_to_read - data_read_unread, &data_read);
break;
case _IOLBF:
err = do_read_lbf (stream,
buffer + data_read_unread,
bytes_to_read - data_read_unread, &data_read);
break;
case _IOFBF:
err = do_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;
}
/*
* Return true if at least one byte is pending for read. This is a
* best effort check and it it possible that bytes are still pending
* even if false is returned. If the stream is in writing mode it is
* switched to read mode.
*/
static int
check_pending (estream_t _GPGRT__RESTRICT stream)
{
if (stream->flags.writing)
{
/* Switching to reading mode -> flush output. */
if (flush_stream (stream))
return 0; /* Better return 0 on error. */
stream->flags.writing = 0;
}
/* Check unread data first. */
if (stream->unread_data_len)
return 1;
switch (stream->intern->strategy)
{
case _IONBF:
return check_pending_nbf (stream);
case _IOLBF:
case _IOFBF:
return check_pending_fbf (stream);
}
return 0;
}
/*
* Try to unread DATA_N bytes from DATA into STREAM, storing the
* amount of bytes successfully unread at BYTES_UNREAD.
*/
static void
es_unreadn (estream_t _GPGRT__RESTRICT stream,
const unsigned char *_GPGRT__RESTRICT data, size_t data_n,
size_t *_GPGRT__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 _GPGRT__RESTRICT stream, gpgrt_off_t offset, int whence,
gpgrt_off_t *_GPGRT__RESTRICT offset_new)
{
gpgrt_cookie_seek_function_t func_seek = stream->intern->func_seek;
int err, ret;
gpgrt_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 = flush_stream (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;
#if EWOULDBLOCK != EAGAIN
if (errno == EWOULDBLOCK)
_set_errno (EAGAIN);
#endif
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)
{
if (errno == EPIPE)
stream->intern->indicators.hup = 1;
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 at
* BYTES_WRITTEN.
*/
static int
es_write_nbf (estream_t _GPGRT__RESTRICT stream,
const unsigned char *_GPGRT__RESTRICT buffer,
size_t bytes_to_write, size_t *_GPGRT__RESTRICT bytes_written)
{
gpgrt_cookie_write_function_t func_write = stream->intern->func_write;
size_t data_written;
gpgrt_ssize_t ret;
int err;
if (bytes_to_write && (! func_write))
{
_set_errno (EOPNOTSUPP);
err = -1;
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;
#if EWOULDBLOCK != EAGAIN
if (errno == EWOULDBLOCK)
_set_errno (EAGAIN);
#endif
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 at
* BYTES_WRITTEN.
*/
static int
es_write_fbf (estream_t _GPGRT__RESTRICT stream,
const unsigned char *_GPGRT__RESTRICT buffer,
size_t bytes_to_write, size_t *_GPGRT__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 = flush_stream (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 _GPGRT__RESTRICT stream,
const unsigned char *_GPGRT__RESTRICT buffer,
size_t bytes_to_write, size_t *_GPGRT__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 = flush_stream (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 _GPGRT__RESTRICT stream,
const void *_GPGRT__RESTRICT buffer,
size_t bytes_to_write, size_t *_GPGRT__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;
}
stream->flags.writing = 1;
}
}
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;
return err;
}
static int
peek_stream (estream_t _GPGRT__RESTRICT stream,
unsigned char **_GPGRT__RESTRICT data,
size_t *_GPGRT__RESTRICT data_len)
{
int err;
if (stream->flags.writing)
{
/* Switching to reading mode -> flush output. */
err = flush_stream (stream);
if (err)
goto out;
stream->flags.writing = 0;
}
if (stream->data_offset == stream->data_len)
{
/* Refill container. */
err = fill_stream (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
skip_stream (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 _GPGRT__RESTRICT stream, size_t max_length,
char *_GPGRT__RESTRICT *_GPGRT__RESTRICT line,
size_t *_GPGRT__RESTRICT line_length)
{
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;
es_syshd_t syshd;
line_new = NULL;
line_stream = NULL;
line_stream_cookie = NULL;
err = func_mem_create (&line_stream_cookie, NULL, 0, 0,
BUFFER_BLOCK_SIZE, 1,
mem_realloc, mem_free,
O_RDWR,
0);
if (err)
goto out;
memset (&syshd, 0, sizeof syshd);
err = create_stream (&line_stream, line_stream_cookie,
&syshd, BACKEND_MEM,
estream_functions_mem, O_RDWR, 1, 0);
if (err)
goto out;
{
size_t space_left = max_length;
line_size = 0;
for (;;)
{
if (max_length && (space_left == 1))
break;
err = peek_stream (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 = _gpgrt_write (line_stream, data, data_len, NULL);
if (! err)
{
/* Not needed: space_left -= data_len */
line_size += data_len;
skip_stream (stream, data_len);
break; /* endless loop */
}
}
else
{
err = _gpgrt_write (line_stream, data, data_len, NULL);
if (! err)
{
space_left -= data_len;
line_size += data_len;
skip_stream (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 = _gpgrt_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)
do_close (line_stream, 0);
else if (line_stream_cookie)
func_mem_destroy (line_stream_cookie);
if (err)
{
if (! *line)
mem_free (line_new);
stream->intern->indicators.err = 1;
}
return err;
}
/* Output function used by 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
do_print_stream (estream_t _GPGRT__RESTRICT stream,
gpgrt_string_filter_t sf, void *sfvalue,
const char *_GPGRT__RESTRICT format, va_list ap)
{
int rc;
stream->intern->print_ntotal = 0;
rc = _gpgrt_estream_format (print_writer, stream, sf, sfvalue, format, ap);
if (rc)
return -1;
return (int)stream->intern->print_ntotal;
}
static int
es_set_buffering (estream_t _GPGRT__RESTRICT stream,
char *_GPGRT__RESTRICT buffer, int mode, size_t size)
{
int err;
/* Flush or empty buffer depending on mode. */
if (stream->flags.writing)
{
err = flush_stream (stream);
if (err)
goto out;
}
else
es_empty (stream);
stream->intern->indicators.eof = 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
{
if (!size)
size = BUFSIZ;
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 gpgrt_off_t
es_offset_calculate (estream_t stream)
{
gpgrt_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 _GPGRT__RESTRICT stream,
void *_GPGRT__RESTRICT opaque_new,
void **_GPGRT__RESTRICT opaque_old)
{
if (opaque_old)
*opaque_old = stream->intern->opaque;
if (opaque_new)
stream->intern->opaque = opaque_new;
}
/* API. */
estream_t
_gpgrt_fopen (const char *_GPGRT__RESTRICT path,
const char *_GPGRT__RESTRICT mode)
{
unsigned int modeflags, cmode, xmode;
int create_called;
estream_t stream;
void *cookie;
int err;
int fd;
es_syshd_t syshd;
stream = NULL;
cookie = NULL;
create_called = 0;
err = parse_mode (mode, &modeflags, &xmode, &cmode);
if (err)
goto out;
err = func_file_create (&cookie, &fd, path, modeflags, cmode);
if (err)
goto out;
syshd.type = ES_SYSHD_FD;
syshd.u.fd = fd;
create_called = 1;
err = create_stream (&stream, cookie, &syshd, BACKEND_FD,
estream_functions_fd, modeflags, xmode, 0);
if (err)
goto out;
if (stream && path)
fname_set_internal (stream, path, 1);
out:
if (err && create_called)
(*estream_functions_fd.public.func_close) (cookie);
return stream;
}
/* Create a new estream object in memory. If DATA is not NULL this
buffer will be used as the memory buffer; thus after this functions
returns with the success the the memory at DATA belongs to the new
estream. The allocated length of DATA is given by DATA_LEN and its
used length by DATA_N. Usually this is malloced buffer; if a
static buffer is provided, the caller must pass false for GROW and
provide a dummy function for FUNC_FREE. FUNC_FREE and FUNC_REALLOC
allow the caller to provide custom functions for realloc and free
to be used by the new estream object. Note that the realloc
function is also used for initial allocation. If DATA is NULL a
buffer is internally allocated; either using internal function or
those provide by the caller. It is an error to provide a realloc
function but no free function. Providing only a free function is
allowed as long as GROW is false. */
estream_t
_gpgrt_mopen (void *_GPGRT__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 *_GPGRT__RESTRICT mode)
{
int create_called = 0;
estream_t stream = NULL;
void *cookie = NULL;
unsigned int modeflags, xmode;
int err;
es_syshd_t syshd;
err = parse_mode (mode, &modeflags, &xmode, NULL);
if (err)
goto out;
err = func_mem_create (&cookie, data, data_n, data_len,
BUFFER_BLOCK_SIZE, grow,
func_realloc, func_free, modeflags, 0);
if (err)
goto out;
memset (&syshd, 0, sizeof syshd);
create_called = 1;
err = create_stream (&stream, cookie, &syshd, BACKEND_MEM,
estream_functions_mem, modeflags, xmode, 0);
out:
if (err && create_called)
(*estream_functions_mem.public.func_close) (cookie);
return stream;
}
estream_t
_gpgrt_fopenmem (size_t memlimit, const char *_GPGRT__RESTRICT mode)
{
unsigned int modeflags, xmode;
estream_t stream = NULL;
void *cookie = NULL;
es_syshd_t syshd;
/* Memory streams are always read/write. We use MODE only to get
the append flag. */
if (parse_mode (mode, &modeflags, &xmode, NULL))
return NULL;
modeflags |= O_RDWR;
if (func_mem_create (&cookie, NULL, 0, 0,
BUFFER_BLOCK_SIZE, 1,
mem_realloc, mem_free, modeflags,
memlimit))
return NULL;
memset (&syshd, 0, sizeof syshd);
if (create_stream (&stream, cookie, &syshd, BACKEND_MEM,
estream_functions_mem, modeflags, xmode, 0))
(*estream_functions_mem.public.func_close) (cookie);
return stream;
}
/* This is the same as es_fopenmem but intializes the memory with a
copy of (DATA,DATALEN). The stream is initially set to the
beginning. If MEMLIMIT is not 0 but shorter than DATALEN it
DATALEN will be used as the value for MEMLIMIT. */
estream_t
_gpgrt_fopenmem_init (size_t memlimit, const char *_GPGRT__RESTRICT mode,
const void *data, size_t datalen)
{
estream_t stream;
if (memlimit && memlimit < datalen)
memlimit = datalen;
stream = _gpgrt_fopenmem (memlimit, mode);
if (stream && data && datalen)
{
if (es_writen (stream, data, datalen, NULL))
{
int saveerrno = errno;
_gpgrt_fclose (stream);
stream = NULL;
_set_errno (saveerrno);
}
else
{
es_seek (stream, 0L, SEEK_SET, NULL);
stream->intern->indicators.eof = 0;
stream->intern->indicators.err = 0;
}
}
return stream;
}
estream_t
_gpgrt_fopencookie (void *_GPGRT__RESTRICT cookie,
const char *_GPGRT__RESTRICT mode,
gpgrt_cookie_io_functions_t functions)
{
unsigned int modeflags, xmode;
estream_t stream;
int err;
es_syshd_t syshd;
struct cookie_io_functions_s io_functions = { functions, NULL, };
stream = NULL;
modeflags = 0;
err = parse_mode (mode, &modeflags, &xmode, NULL);
if (err)
goto out;
memset (&syshd, 0, sizeof syshd);
err = create_stream (&stream, cookie, &syshd, BACKEND_USER, io_functions,
modeflags, xmode, 0);
if (err)
goto out;
out:
return stream;
}
static estream_t
do_fdopen (int filedes, const char *mode, int no_close, int with_locked_list)
{
int create_called = 0;
estream_t stream = NULL;
void *cookie = NULL;
unsigned int modeflags, xmode;
int err;
es_syshd_t syshd;
err = parse_mode (mode, &modeflags, &xmode, NULL);
if (err)
goto out;
if ((xmode & X_SYSOPEN))
{
/* Not allowed for fdopen. */
_set_errno (EINVAL);
err = -1;
goto out;
}
err = func_fd_create (&cookie, filedes, modeflags, no_close);
if (err)
goto out;
syshd.type = ES_SYSHD_FD;
syshd.u.fd = filedes;
create_called = 1;
err = create_stream (&stream, cookie, &syshd,
BACKEND_FD, estream_functions_fd,
modeflags, xmode, with_locked_list);
if (!err && stream)
{
if ((modeflags & O_NONBLOCK))
err = stream->intern->func_ioctl (cookie, COOKIE_IOCTL_NONBLOCK,
"", NULL);
}
out:
if (err && create_called)
(*estream_functions_fd.public.func_close) (cookie);
return stream;
}
estream_t
_gpgrt_fdopen (int filedes, const char *mode)
{
return do_fdopen (filedes, mode, 0, 0);
}
/* A variant of es_fdopen which does not close FILEDES at the end. */
estream_t
_gpgrt_fdopen_nc (int filedes, const char *mode)
{
return do_fdopen (filedes, mode, 1, 0);
}
static estream_t
do_fpopen (FILE *fp, const char *mode, int no_close, int with_locked_list)
{
unsigned int modeflags, cmode, xmode;
int create_called = 0;
estream_t stream = NULL;
void *cookie = NULL;
int err;
es_syshd_t syshd;
err = parse_mode (mode, &modeflags, &xmode, &cmode);
if (err)
goto out;
if ((xmode & X_SYSOPEN))
{
/* Not allowed for fpopen. */
_set_errno (EINVAL);
err = -1;
goto out;
}
if (fp)
fflush (fp);
err = func_fp_create (&cookie, fp, modeflags, no_close);
if (err)
goto out;
syshd.type = ES_SYSHD_FD;
syshd.u.fd = fp? fileno (fp): -1;
create_called = 1;
err = create_stream (&stream, cookie, &syshd,
BACKEND_FP, estream_functions_fp,
modeflags, xmode, with_locked_list);
out:
if (err && create_called)
(*estream_functions_fp.public.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
_gpgrt_fpopen (FILE *fp, const char *mode)
{
return do_fpopen (fp, mode, 0, 0);
}
/* Same as es_fpopen but does not close FP at the end. */
estream_t
_gpgrt_fpopen_nc (FILE *fp, const char *mode)
{
return do_fpopen (fp, mode, 1, 0);
}
#ifdef HAVE_W32_SYSTEM
estream_t
do_w32open (HANDLE hd, const char *mode,
int no_close, int with_locked_list)
{
unsigned int modeflags, cmode, xmode;
int create_called = 0;
estream_t stream = NULL;
void *cookie = NULL;
int err;
es_syshd_t syshd;
/* For obvious reasons we ignore sysmode here. */
err = parse_mode (mode, &modeflags, &xmode, &cmode);
if (err)
goto leave;
/* If we are pollable we create the function cookie with syscall
* clamp disabled. This is because functions are called from
* separatre reader and writer threads in w32-stream. */
err = func_w32_create (&cookie, hd, modeflags,
no_close, !!(xmode & X_POLLABLE));
if (err)
goto leave;
syshd.type = ES_SYSHD_HANDLE;
syshd.u.handle = hd;
create_called = 1;
err = create_stream (&stream, cookie, &syshd,
BACKEND_W32, estream_functions_w32,
modeflags, xmode, with_locked_list);
leave:
if (err && create_called)
(*estream_functions_w32.public.func_close) (cookie);
return stream;
}
#endif /*HAVE_W32_SYSTEM*/
static estream_t
do_sysopen (es_syshd_t *syshd, const char *mode, int no_close)
{
estream_t stream;
switch (syshd->type)
{
case ES_SYSHD_FD:
case ES_SYSHD_SOCK:
stream = do_fdopen (syshd->u.fd, mode, no_close, 0);
break;
#ifdef HAVE_W32_SYSTEM
case ES_SYSHD_HANDLE:
stream = do_w32open (syshd->u.handle, mode, no_close, 0);
break;
#endif
/* FIXME: Support RVIDs under Wince? */
default:
_set_errno (EINVAL);
stream = NULL;
}
return stream;
}
/* On POSIX systems this function is an alias for es_fdopen. Under
Windows it uses the bare W32 API and thus a HANDLE instead of a
file descriptor. */
estream_t
_gpgrt_sysopen (es_syshd_t *syshd, const char *mode)
{
return do_sysopen (syshd, mode, 0);
}
/* Same as es_sysopen but the handle/fd will not be closed by
es_fclose. */
estream_t
_gpgrt_sysopen_nc (es_syshd_t *syshd, const char *mode)
{
return do_sysopen (syshd, mode, 1);
}
/* Set custom standard descriptors to be used for stdin, stdout and
stderr. This function needs to be called before any of the
standard streams are accessed. This internal version uses a double
dash inside its name. */
void
_gpgrt__set_std_fd (int no, int fd)
{
/* fprintf (stderr, "es_set_std_fd(%d, %d)\n", no, fd); */
lock_list ();
if (no >= 0 && no < 3 && !custom_std_fds_valid[no])
{
custom_std_fds[no] = fd;
custom_std_fds_valid[no] = 1;
}
unlock_list ();
}
/* Return the stream used for stdin, stdout or stderr.
This internal version uses a double dash inside its name. */
estream_t
_gpgrt__get_std_stream (int fd)
{
estream_list_t list_obj;
estream_t stream = NULL;
fd %= 3; /* We only allow 0, 1 or 2 but we don't want to return an error. */
lock_list ();
for (list_obj = estream_list; list_obj; list_obj = list_obj->next)
if (list_obj->stream && list_obj->stream->intern->is_stdstream
&& list_obj->stream->intern->stdstream_fd == fd)
{
stream = list_obj->stream;
break;
}
if (!stream)
{
/* Standard stream not yet created. We first try to create them
from registered file descriptors. */
if (!fd && custom_std_fds_valid[0])
stream = do_fdopen (custom_std_fds[0], "r", 1, 1);
else if (fd == 1 && custom_std_fds_valid[1])
stream = do_fdopen (custom_std_fds[1], "a", 1, 1);
else if (custom_std_fds_valid[2])
stream = do_fdopen (custom_std_fds[2], "a", 1, 1);
if (!stream)
{
/* Second try is to use the standard C streams. */
if (!fd)
stream = do_fpopen (stdin, "r", 1, 1);
else if (fd == 1)
stream = do_fpopen (stdout, "a", 1, 1);
else
stream = do_fpopen (stderr, "a", 1, 1);
}
if (!stream)
{
/* Last try: Create a bit bucket. */
stream = do_fpopen (NULL, fd? "a":"r", 0, 1);
if (!stream)
{
fprintf (stderr, "fatal: error creating a dummy estream"
" for %d: %s\n", fd, strerror (errno));
- abort();
+ _gpgrt_abort();
}
}
stream->intern->is_stdstream = 1;
stream->intern->stdstream_fd = fd;
if (fd == 2)
es_set_buffering (stream, NULL, _IOLBF, 0);
fname_set_internal (stream,
fd == 0? "[stdin]" :
fd == 1? "[stdout]" : "[stderr]", 0);
}
unlock_list ();
return stream;
}
/* Note: A "samethread" keyword given in "mode" is ignored and the
* value used by STREAM is used instead. Note that this function is
* the reasons why some of the init and deinit code is split up into
* several functions. */
estream_t
_gpgrt_freopen (const char *_GPGRT__RESTRICT path,
const char *_GPGRT__RESTRICT mode,
estream_t _GPGRT__RESTRICT stream)
{
int err;
if (path)
{
unsigned int modeflags, cmode, xmode, dummy;
int create_called;
void *cookie;
int fd;
es_syshd_t syshd;
cookie = NULL;
create_called = 0;
xmode = stream->intern->samethread ? X_SAMETHREAD : 0;
lock_stream (stream);
deinit_stream_obj (stream);
err = parse_mode (mode, &modeflags, &dummy, &cmode);
if (err)
goto leave;
(void)dummy;
err = func_file_create (&cookie, &fd, path, modeflags, cmode);
if (err)
goto leave;
syshd.type = ES_SYSHD_FD;
syshd.u.fd = fd;
create_called = 1;
init_stream_obj (stream, cookie, &syshd, BACKEND_FD,
estream_functions_fd, modeflags, xmode);
leave:
if (err)
{
if (create_called)
func_fd_destroy (cookie);
do_close (stream, 0);
stream = NULL;
}
else
{
if (path)
fname_set_internal (stream, path, 1);
unlock_stream (stream);
}
}
else
{
/* FIXME? We don't support re-opening at the moment. */
_set_errno (EINVAL);
deinit_stream_obj (stream);
do_close (stream, 0);
stream = NULL;
}
return stream;
}
int
_gpgrt_fclose (estream_t stream)
{
int err;
err = do_close (stream, 0);
return err;
}
/* This is a special version of es_fclose which can be used with
es_fopenmem to return the memory buffer. This is feature is useful
to write to a memory buffer using estream. Note that the function
does not close the stream if the stream does not support snatching
the buffer. On error NULL is stored at R_BUFFER. Note that if no
write operation has happened, NULL may also be stored at BUFFER on
success. The caller needs to release the returned memory using
gpgrt_free. */
int
_gpgrt_fclose_snatch (estream_t stream, void **r_buffer, size_t *r_buflen)
{
int err;
/* Note: There is no need to lock the stream in a close call. The
object will be destroyed after the close and thus any other
contender for the lock would work on a closed stream. */
if (r_buffer)
{
cookie_ioctl_function_t func_ioctl = stream->intern->func_ioctl;
size_t buflen;
*r_buffer = NULL;
if (!func_ioctl)
{
_set_errno (EOPNOTSUPP);
err = -1;
goto leave;
}
if (stream->flags.writing)
{
err = flush_stream (stream);
if (err)
goto leave;
stream->flags.writing = 0;
}
err = func_ioctl (stream->intern->cookie, COOKIE_IOCTL_SNATCH_BUFFER,
r_buffer, &buflen);
if (err)
goto leave;
if (r_buflen)
*r_buflen = buflen;
}
err = do_close (stream, 0);
leave:
if (err && r_buffer)
{
mem_free (*r_buffer);
*r_buffer = NULL;
}
return err;
}
/* Register or unregister a close notification function for STREAM.
FNC is the function to call and FNC_VALUE the value passed as
second argument. To register the notification the value for MODE
must be 1. If mode is 0 the function tries to remove or disable an
already registered notification; for this to work the value of FNC
and FNC_VALUE must be the same as with the registration and
FNC_VALUE must be a unique value. No error will be returned if
MODE is 0.
FIXME: I think the next comment is not anymore correct:
Unregister should only be used in the error case because it may not
be able to remove memory internally allocated for the onclose
handler.
FIXME: Unregister is not thread safe.
The notification will be called right before the stream is closed.
It may not call any estream function for STREAM, neither direct nor
indirectly. */
int
_gpgrt_onclose (estream_t stream, int mode,
void (*fnc) (estream_t, void*), void *fnc_value)
{
int err;
lock_stream (stream);
err = do_onclose (stream, mode, fnc, fnc_value);
unlock_stream (stream);
return err;
}
int
_gpgrt_fileno_unlocked (estream_t stream)
{
es_syshd_t syshd;
if (_gpgrt_syshd_unlocked (stream, &syshd))
return -1;
switch (syshd.type)
{
case ES_SYSHD_FD: return syshd.u.fd;
case ES_SYSHD_SOCK: return syshd.u.sock;
default:
_set_errno (EINVAL);
return -1;
}
}
/* Return the handle of a stream which has been opened by es_sysopen.
The caller needs to pass a structure which will be filled with the
sys handle. Return 0 on success or true on error and sets errno.
This is the unlocked version. */
int
_gpgrt_syshd_unlocked (estream_t stream, es_syshd_t *syshd)
{
if (!stream || !syshd || stream->intern->syshd.type == ES_SYSHD_NONE)
{
if (syshd)
syshd->type = ES_SYSHD_NONE;
_set_errno (EINVAL);
return -1;
}
*syshd = stream->intern->syshd;
return 0;
}
void
_gpgrt_flockfile (estream_t stream)
{
lock_stream (stream);
}
int
_gpgrt_ftrylockfile (estream_t stream)
{
return trylock_stream (stream);
}
void
_gpgrt_funlockfile (estream_t stream)
{
unlock_stream (stream);
}
int
_gpgrt_fileno (estream_t stream)
{
int ret;
lock_stream (stream);
ret = _gpgrt_fileno_unlocked (stream);
unlock_stream (stream);
return ret;
}
/* Return the handle of a stream which has been opened by es_sysopen.
The caller needs to pass a structure which will be filled with the
sys handle. Return 0 on success or true on error and sets errno.
This is the unlocked version. */
int
_gpgrt_syshd (estream_t stream, es_syshd_t *syshd)
{
int ret;
lock_stream (stream);
ret = _gpgrt_syshd_unlocked (stream, syshd);
unlock_stream (stream);
return ret;
}
int
_gpgrt__pending_unlocked (estream_t stream)
{
return check_pending (stream);
}
/* Return true if there is at least one byte pending for read on
STREAM. This does only work if the backend supports checking for
pending bytes and is thus mostly useful with cookie based backends.
Note that if this function is used with cookie based functions, the
read cookie may be called with 0 for the SIZE argument. If bytes
are pending the function is expected to return -1 in this case and
thus deviates from the standard behavior of read(2). */
int
_gpgrt__pending (estream_t stream)
{
int ret;
lock_stream (stream);
ret = _gpgrt__pending_unlocked (stream);
unlock_stream (stream);
return ret;
}
int
_gpgrt_feof_unlocked (estream_t stream)
{
return stream->intern->indicators.eof;
}
int
_gpgrt_feof (estream_t stream)
{
int ret;
lock_stream (stream);
ret = _gpgrt_feof_unlocked (stream);
unlock_stream (stream);
return ret;
}
int
_gpgrt_ferror_unlocked (estream_t stream)
{
return stream->intern->indicators.err;
}
int
_gpgrt_ferror (estream_t stream)
{
int ret;
lock_stream (stream);
ret = _gpgrt_ferror_unlocked (stream);
unlock_stream (stream);
return ret;
}
void
_gpgrt_clearerr_unlocked (estream_t stream)
{
stream->intern->indicators.eof = 0;
stream->intern->indicators.err = 0;
/* We do not reset the HUP indicator because there is no way to
get out of this state. */
}
void
_gpgrt_clearerr (estream_t stream)
{
lock_stream (stream);
_gpgrt_clearerr_unlocked (stream);
unlock_stream (stream);
}
static int
do_fflush (estream_t stream)
{
int err;
if (stream->flags.writing)
err = flush_stream (stream);
else
{
es_empty (stream);
err = 0;
}
return err;
}
int
_gpgrt_fflush (estream_t stream)
{
int err;
if (stream)
{
lock_stream (stream);
err = do_fflush (stream);
unlock_stream (stream);
}
else
{
estream_list_t item;
err = 0;
lock_list ();
for (item = estream_list; item; item = item->next)
if (item->stream)
{
lock_stream (item->stream);
err |= do_fflush (item->stream);
unlock_stream (item->stream);
}
unlock_list ();
}
return err ? EOF : 0;
}
int
_gpgrt_fseek (estream_t stream, long int offset, int whence)
{
int err;
lock_stream (stream);
err = es_seek (stream, offset, whence, NULL);
unlock_stream (stream);
return err;
}
int
_gpgrt_fseeko (estream_t stream, gpgrt_off_t offset, int whence)
{
int err;
lock_stream (stream);
err = es_seek (stream, offset, whence, NULL);
unlock_stream (stream);
return err;
}
long int
_gpgrt_ftell (estream_t stream)
{
long int ret;
lock_stream (stream);
ret = es_offset_calculate (stream);
unlock_stream (stream);
return ret;
}
gpgrt_off_t
_gpgrt_ftello (estream_t stream)
{
gpgrt_off_t ret = -1;
lock_stream (stream);
ret = es_offset_calculate (stream);
unlock_stream (stream);
return ret;
}
void
_gpgrt_rewind (estream_t stream)
{
lock_stream (stream);
es_seek (stream, 0L, SEEK_SET, NULL);
/* Note that es_seek already cleared the EOF flag. */
stream->intern->indicators.err = 0;
unlock_stream (stream);
}
int
_gpgrt_ftruncate (estream_t stream, gpgrt_off_t length)
{
cookie_ioctl_function_t func_ioctl;
int ret;
lock_stream (stream);
func_ioctl = stream->intern->func_ioctl;
if (!func_ioctl)
{
_set_errno (EOPNOTSUPP);
ret = -1;
}
else
{
ret = func_ioctl (stream->intern->cookie, COOKIE_IOCTL_TRUNCATE,
&length, NULL);
}
unlock_stream (stream);
return ret;
}
int
_gpgrt__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
_gpgrt__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
_gpgrt_fgetc (estream_t stream)
{
int ret;
lock_stream (stream);
ret = _gpgrt_getc_unlocked (stream);
unlock_stream (stream);
return ret;
}
int
_gpgrt_fputc (int c, estream_t stream)
{
int ret;
lock_stream (stream);
ret = _gpgrt_putc_unlocked (c, stream);
unlock_stream (stream);
return ret;
}
int
_gpgrt_ungetc (int c, estream_t stream)
{
unsigned char data = (unsigned char) c;
size_t data_unread;
lock_stream (stream);
es_unreadn (stream, &data, 1, &data_unread);
unlock_stream (stream);
return data_unread ? c : EOF;
}
int
_gpgrt_read (estream_t _GPGRT__RESTRICT stream,
void *_GPGRT__RESTRICT buffer, size_t bytes_to_read,
size_t *_GPGRT__RESTRICT bytes_read)
{
int err;
if (bytes_to_read)
{
lock_stream (stream);
err = es_readn (stream, buffer, bytes_to_read, bytes_read);
unlock_stream (stream);
}
else
err = 0;
return err;
}
int
_gpgrt_write (estream_t _GPGRT__RESTRICT stream,
const void *_GPGRT__RESTRICT buffer, size_t bytes_to_write,
size_t *_GPGRT__RESTRICT bytes_written)
{
int err;
if (bytes_to_write)
{
lock_stream (stream);
err = es_writen (stream, buffer, bytes_to_write, bytes_written);
unlock_stream (stream);
}
else
err = 0;
return err;
}
size_t
_gpgrt_fread (void *_GPGRT__RESTRICT ptr, size_t size, size_t nitems,
estream_t _GPGRT__RESTRICT stream)
{
size_t ret, bytes;
if (size && nitems)
{
lock_stream (stream);
es_readn (stream, ptr, size * nitems, &bytes);
unlock_stream (stream);
ret = bytes / size;
}
else
ret = 0;
return ret;
}
size_t
_gpgrt_fwrite (const void *_GPGRT__RESTRICT ptr, size_t size, size_t nitems,
estream_t _GPGRT__RESTRICT stream)
{
size_t ret, bytes;
if (size && nitems)
{
lock_stream (stream);
es_writen (stream, ptr, size * nitems, &bytes);
unlock_stream (stream);
ret = bytes / size;
}
else
ret = 0;
return ret;
}
char *
_gpgrt_fgets (char *_GPGRT__RESTRICT buffer, int length,
estream_t _GPGRT__RESTRICT stream)
{
unsigned char *s = (unsigned char*)buffer;
int c;
if (!length)
return NULL;
c = EOF;
lock_stream (stream);
while (length > 1 && (c = _gpgrt_getc_unlocked (stream)) != EOF && c != '\n')
{
*s++ = c;
length--;
}
unlock_stream (stream);
if (c == EOF && s == (unsigned char*)buffer)
return NULL; /* Nothing read. */
if (c != EOF && length > 1)
*s++ = c;
*s = 0;
return buffer;
}
int
_gpgrt_fputs_unlocked (const char *_GPGRT__RESTRICT s,
estream_t _GPGRT__RESTRICT stream)
{
size_t length;
int err;
length = strlen (s);
err = es_writen (stream, s, length, NULL);
return err ? EOF : 0;
}
int
_gpgrt_fputs (const char *_GPGRT__RESTRICT s, estream_t _GPGRT__RESTRICT stream)
{
size_t length;
int err;
length = strlen (s);
lock_stream (stream);
err = es_writen (stream, s, length, NULL);
unlock_stream (stream);
return err ? EOF : 0;
}
gpgrt_ssize_t
_gpgrt_getline (char *_GPGRT__RESTRICT *_GPGRT__RESTRICT lineptr,
size_t *_GPGRT__RESTRICT n, estream_t _GPGRT__RESTRICT stream)
{
char *line = NULL;
size_t line_n = 0;
int err;
lock_stream (stream);
err = doreadline (stream, 0, &line, &line_n);
unlock_stream (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 : (gpgrt_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 available 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 gpgrt_free.
*/
gpgrt_ssize_t
_gpgrt_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. */
lock_stream (stream);
p = buffer;
while ((c = _gpgrt_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=_gpgrt_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;
unlock_stream (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. */
unlock_stream (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. If a custom allocation handler has been set with
gpgrt_set_alloc_func that register function may be used
instead. This function has been moved to init.c. */
/* void */
/* _gpgrt_free (void *a) */
/* { */
/* mem_free (a); */
/* } */
int
_gpgrt_vfprintf_unlocked (estream_t _GPGRT__RESTRICT stream,
gpgrt_string_filter_t sf, void *sfvalue,
const char *_GPGRT__RESTRICT format,
va_list ap)
{
return do_print_stream (stream, sf, sfvalue, format, ap);
}
int
_gpgrt_vfprintf (estream_t _GPGRT__RESTRICT stream,
gpgrt_string_filter_t sf, void *sfvalue,
const char *_GPGRT__RESTRICT format,
va_list ap)
{
int ret;
lock_stream (stream);
ret = do_print_stream (stream, sf, sfvalue, format, ap);
unlock_stream (stream);
return ret;
}
int
_gpgrt_fprintf_unlocked (estream_t _GPGRT__RESTRICT stream,
const char *_GPGRT__RESTRICT format, ...)
{
int ret;
va_list ap;
va_start (ap, format);
ret = do_print_stream (stream, NULL, NULL, format, ap);
va_end (ap);
return ret;
}
int
_gpgrt_fprintf (estream_t _GPGRT__RESTRICT stream,
const char *_GPGRT__RESTRICT format, ...)
{
int ret;
va_list ap;
va_start (ap, format);
lock_stream (stream);
ret = do_print_stream (stream, NULL, NULL, format, ap);
unlock_stream (stream);
va_end (ap);
return ret;
}
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
_gpgrt_tmpfile (void)
{
unsigned int modeflags;
int create_called = 0;
estream_t stream = NULL;
void *cookie = NULL;
int err;
int fd;
es_syshd_t syshd;
modeflags = O_RDWR | O_TRUNC | O_CREAT;
fd = tmpfd ();
if (fd == -1)
{
err = -1;
goto out;
}
err = func_fd_create (&cookie, fd, modeflags, 0);
if (err)
goto out;
syshd.type = ES_SYSHD_FD;
syshd.u.fd = fd;
create_called = 1;
err = create_stream (&stream, cookie, &syshd,
BACKEND_FD, estream_functions_fd,
modeflags, 0, 0);
out:
if (err)
{
if (create_called)
func_fd_destroy (cookie);
else if (fd != -1)
close (fd);
stream = NULL;
}
return stream;
}
int
_gpgrt_setvbuf (estream_t _GPGRT__RESTRICT stream,
char *_GPGRT__RESTRICT buf, int type, size_t size)
{
int err;
if ((type == _IOFBF || type == _IOLBF || type == _IONBF)
&& (!buf || size || type == _IONBF))
{
lock_stream (stream);
err = es_set_buffering (stream, buf, type, size);
unlock_stream (stream);
}
else
{
_set_errno (EINVAL);
err = -1;
}
return err;
}
/* Put a stream into binary mode. This is only needed for the
standard streams if they are to be used in a binary way. On Unix
systems it is never needed but MSDOS based systems require such a
call. It needs to be called before any I/O is done on STREAM. */
void
_gpgrt_set_binary (estream_t stream)
{
lock_stream (stream);
if (!(stream->intern->modeflags & O_BINARY))
{
stream->intern->modeflags |= O_BINARY;
#ifdef HAVE_DOSISH_SYSTEM
if (stream->intern->func_read == func_fd_read)
{
estream_cookie_fd_t fd_cookie = stream->intern->cookie;
if (!IS_INVALID_FD (fd_cookie->fd))
setmode (fd_cookie->fd, O_BINARY);
}
else if (stream->intern->func_read == func_fp_read)
{
estream_cookie_fp_t fp_cookie = stream->intern->cookie;
if (fp_cookie->fp)
setmode (fileno (fp_cookie->fp), O_BINARY);
}
#endif
}
unlock_stream (stream);
}
/* Set non-blocking mode for STREAM. Use true for ONOFF to enable and
false to disable non-blocking mode. Returns 0 on success or -1 on
error and sets ERRNO. Note that not all backends support
non-blocking mode.
In non-blocking mode a system call will not block but return an
error and set errno to EAGAIN. The estream API always uses EAGAIN
and not EWOULDBLOCK. If a buffered function like es_fgetc() or
es_fgets() returns an error and both, feof() and ferror() return
false the caller may assume that the error condition was EAGAIN.
Switching back from non-blocking to blocking may raise problems
with buffering, thus care should be taken. Although read+write
sockets are supported in theory, switching from write to read may
result into problems because estream may first flush the write
buffers and there is no way to handle that non-blocking (EAGAIN)
case. Explicit flushing should thus be done before before
switching to read. */
int
_gpgrt_set_nonblock (estream_t stream, int onoff)
{
cookie_ioctl_function_t func_ioctl;
int ret;
lock_stream (stream);
func_ioctl = stream->intern->func_ioctl;
if (!func_ioctl)
{
_set_errno (EOPNOTSUPP);
ret = -1;
}
else
{
unsigned int save_flags = stream->intern->modeflags;
if (onoff)
stream->intern->modeflags |= O_NONBLOCK;
else
stream->intern->modeflags &= ~O_NONBLOCK;
ret = func_ioctl (stream->intern->cookie, COOKIE_IOCTL_NONBLOCK,
onoff?"":NULL, NULL);
if (ret)
stream->intern->modeflags = save_flags;
}
unlock_stream (stream);
return ret;
}
/* Return true if STREAM is in non-blocking mode. */
int
_gpgrt_get_nonblock (estream_t stream)
{
int ret;
lock_stream (stream);
ret = !!(stream->intern->modeflags & O_NONBLOCK);
unlock_stream (stream);
return ret;
}
/* A version of poll(2) working on estream handles. Note that not all
estream types work with this function. In contrast to the standard
poll function the gpgrt_poll_t object uses a set of bit flags
instead of the EVENTS and REVENTS members. An item with the IGNORE
flag set is entirely ignored. The TIMEOUT values is given in
milliseconds, a value of -1 waits indefinitely, and a value of 0
returns immediately.
A positive return value gives the number of fds with new
information. A return value of 0 indicates a timeout and -1
indicates an error in which case ERRNO is set. */
int
_gpgrt_poll (gpgrt_poll_t *fds, unsigned int nfds, int timeout)
{
gpgrt_poll_t *item;
int count = 0;
int idx;
#ifndef HAVE_W32_SYSTEM
fd_set readfds, writefds, exceptfds;
int any_readfd, any_writefd, any_exceptfd;
int max_fd;
int fd, ret, any;
#endif /*HAVE_W32_SYSTEM*/
trace (("enter: nfds=%u timeout=%d", nfds, timeout));
if (!fds)
{
_set_errno (EINVAL);
count = -1;
goto leave;
}
/* Clear all response fields (even for ignored items). */
for (item = fds, idx = 0; idx < nfds; item++, idx++)
{
item->got_read = 0;
item->got_write = 0;
item->got_oob = 0;
item->got_rdhup = 0;
item->got_err = 0;
item->got_hup = 0;
item->got_nval = 0;
}
/* Check for pending reads. */
for (item = fds, idx = 0; idx < nfds; item++, idx++)
{
if (item->ignore)
continue;
if (!item->want_read)
continue;
if (_gpgrt__pending (item->stream))
{
item->got_read = 1;
count++;
}
}
/* Check for space in the write buffers. */
for (item = fds, idx = 0; idx < nfds; item++, idx++)
{
if (item->ignore)
continue;
if (!item->want_write)
continue;
/* FIXME */
}
/* Now do the real select. */
#ifdef HAVE_W32_SYSTEM
_gpgrt_pre_syscall ();
count = _gpgrt_w32_poll (fds, nfds, timeout);
_gpgrt_post_syscall ();
#else /*!HAVE_W32_SYSTEM*/
any_readfd = any_writefd = any_exceptfd = 0;
max_fd = 0;
for (item = fds, idx = 0; idx < nfds; item++, idx++)
{
if (item->ignore)
continue;
fd = _gpgrt_fileno (item->stream);
if (fd == -1)
continue; /* Stream does not support polling. */
if (item->want_read)
{
if (!any_readfd)
{
FD_ZERO (&readfds);
any_readfd = 1;
}
FD_SET (fd, &readfds);
if (fd > max_fd)
max_fd = fd;
}
if (item->want_write)
{
if (!any_writefd)
{
FD_ZERO (&writefds);
any_writefd = 1;
}
FD_SET (fd, &writefds);
if (fd > max_fd)
max_fd = fd;
}
if (item->want_oob)
{
if (!any_exceptfd)
{
FD_ZERO (&exceptfds);
any_exceptfd = 1;
}
FD_SET (fd, &exceptfds);
if (fd > max_fd)
max_fd = fd;
}
}
_gpgrt_pre_syscall ();
do
{
struct timeval timeout_val;
timeout_val.tv_sec = timeout / 1000;
timeout_val.tv_usec = (timeout % 1000) * 1000;
ret = select (max_fd+1,
any_readfd? &readfds : NULL,
any_writefd? &writefds : NULL,
any_exceptfd? &exceptfds : NULL,
timeout == -1 ? NULL : &timeout_val);
}
while (ret == -1 && errno == EINTR);
_gpgrt_post_syscall ();
if (ret == -1)
{
trace_errno (1, ("select failed: "));
count = -1;
goto leave;
}
if (!ret)
{
/* Timeout. Note that in this case we can't return got_err for
* an invalid stream. */
count = 0;
goto leave;
}
for (item = fds, idx = 0; idx < nfds; item++, idx++)
{
if (item->ignore)
continue;
fd = _gpgrt_fileno (item->stream);
if (fd == -1)
{
item->got_err = 1; /* Stream does not support polling. */
count++;
continue;
}
any = 0;
if (item->stream->intern->indicators.hup)
{
item->got_hup = 1;
any = 1;
}
if (item->want_read && FD_ISSET (fd, &readfds))
{
item->got_read = 1;
any = 1;
}
if (item->want_write && FD_ISSET (fd, &writefds))
{
item->got_write = 1;
any = 1;
}
if (item->want_oob && FD_ISSET (fd, &exceptfds))
{
item->got_oob = 1;
any = 1;
}
if (any)
count++;
}
#endif /*!HAVE_W32_SYSTEM*/
leave:
#ifdef ENABLE_TRACING
trace (("leave: count=%d", count));
if (count > 0)
{
for (item = fds, idx = 0; idx < nfds; item++, idx++)
{
trace ((" %3d %c%c%c%c%c %c%c%c%c%c%c%c",
idx,
fds[idx].want_read? 'r':'-',
fds[idx].want_write? 'w':'-',
fds[idx].want_oob? 'o':'-',
fds[idx].want_rdhup? 'h':'-',
fds[idx].ignore? 'i':'-',
fds[idx].got_read? 'r':'-',
fds[idx].got_write? 'w':'-',
fds[idx].got_oob? 'o':'-',
fds[idx].got_rdhup? 'h':'-',
fds[idx].got_hup? 'H':'-',
fds[idx].got_err? 'e':'-',
fds[idx].got_nval? 'n':'-'
));
}
}
#endif /*ENABLE_TRACING*/
return count;
}
void
_gpgrt_opaque_set (estream_t stream, void *opaque)
{
lock_stream (stream);
es_opaque_ctrl (stream, opaque, NULL);
unlock_stream (stream);
}
void *
_gpgrt_opaque_get (estream_t stream)
{
void *opaque;
lock_stream (stream);
es_opaque_ctrl (stream, NULL, &opaque);
unlock_stream (stream);
return opaque;
}
static void
fname_set_internal (estream_t stream, const char *fname, int quote)
{
if (stream->intern->printable_fname
&& !stream->intern->printable_fname_inuse)
{
mem_free (stream->intern->printable_fname);
stream->intern->printable_fname = NULL;
}
if (stream->intern->printable_fname)
return; /* Can't change because it is in use. */
if (*fname != '[')
quote = 0;
else
quote = !!quote;
stream->intern->printable_fname = mem_alloc (strlen (fname) + quote + 1);
if (quote)
stream->intern->printable_fname[0] = '\\';
strcpy (stream->intern->printable_fname+quote, fname);
}
/* Set the filename attribute of STREAM. There is no error return.
as long as STREAM is valid. This function is called internally by
functions which open a filename. */
void
_gpgrt_fname_set (estream_t stream, const char *fname)
{
if (fname)
{
lock_stream (stream);
fname_set_internal (stream, fname, 1);
unlock_stream (stream);
}
}
/* Return the filename attribute of STREAM. In case no filename has
been set, "[?]" will be returned. The returned file name is valid
as long as STREAM is valid. */
const char *
_gpgrt_fname_get (estream_t stream)
{
const char *fname;
lock_stream (stream);
fname = stream->intern->printable_fname;
if (fname)
stream->intern->printable_fname_inuse = 1;
unlock_stream (stream);
if (!fname)
fname = "[?]";
return fname;
}
/* 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
_gpgrt_write_sanitized (estream_t _GPGRT__RESTRICT stream,
const void * _GPGRT__RESTRICT buffer, size_t length,
const char * delimiters,
size_t * _GPGRT__RESTRICT bytes_written)
{
const unsigned char *p = buffer;
size_t count = 0;
int ret;
lock_stream (stream);
for (; length; length--, p++, count++)
{
if (*p < 0x20
|| *p == 0x7f
|| (delimiters
&& (strchr (delimiters, *p) || *p == '\\')))
{
_gpgrt_putc_unlocked ('\\', stream);
count++;
if (*p == '\n')
{
_gpgrt_putc_unlocked ('n', stream);
count++;
}
else if (*p == '\r')
{
_gpgrt_putc_unlocked ('r', stream);
count++;
}
else if (*p == '\f')
{
_gpgrt_putc_unlocked ('f', stream);
count++;
}
else if (*p == '\v')
{
_gpgrt_putc_unlocked ('v', stream);
count++;
}
else if (*p == '\b')
{
_gpgrt_putc_unlocked ('b', stream);
count++;
}
else if (!*p)
{
_gpgrt_putc_unlocked('0', stream);
count++;
}
else
{
_gpgrt_fprintf_unlocked (stream, "x%02x", *p);
count += 3;
}
}
else
{
_gpgrt_putc_unlocked (*p, stream);
count++;
}
}
if (bytes_written)
*bytes_written = count;
ret = _gpgrt_ferror_unlocked (stream)? -1 : 0;
unlock_stream (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
_gpgrt_write_hexstring (estream_t _GPGRT__RESTRICT stream,
const void *_GPGRT__RESTRICT buffer, size_t length,
int reserved, size_t *_GPGRT__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;
lock_stream (stream);
for (s = buffer; length; s++, length--)
{
_gpgrt_putc_unlocked ( tohex ((*s>>4)&15), stream);
_gpgrt_putc_unlocked ( tohex (*s&15), stream);
count += 2;
}
if (bytes_written)
*bytes_written = count;
ret = _gpgrt_ferror_unlocked (stream)? -1 : 0;
unlock_stream (stream);
return ret;
#undef tohex
}
diff --git a/src/gpg-error.def.in b/src/gpg-error.def.in
index 0eca3c7..a55b31a 100644
--- a/src/gpg-error.def.in
+++ b/src/gpg-error.def.in
@@ -1,229 +1,232 @@
/* libgpg-error.def - Exported symbols for W32
* Copyright (C) 2014 g10 Code GmbH
*
* This file is part of libgpg-error.
*
* libgpg-error is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* libgpg-error 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 .
* SPDX-License-Identifier: LGPL-2.1+
*
* Note: This file should be updated manually and the ordinals shall
* never be changed. Also check gpg-error.vers and visibility.h.
*
* This file needs to be pre-processed.
*/
#include
EXPORTS
gpg_strerror @1
gpg_strerror_r @2
gpg_strsource @3
gpg_err_code_from_errno @4
gpg_err_code_to_errno @5
/* @6 - Not anymore used. */
gpg_err_code_from_syserror @7
gpg_err_set_errno @8
#ifdef HAVE_W32CE_SYSTEM
_gpg_w32ce_get_errno @9
_gpg_w32ce_strerror @10
#endif
#ifdef HAVE_W32_SYSTEM
_gpg_w32_bindtextdomain @11
_gpg_w32_textdomain @12
_gpg_w32_gettext @13
_gpg_w32_dgettext @14
_gpg_w32_dngettext @15
_gpg_w32_gettext_localename @16
_gpg_w32_gettext_use_utf8 @17
#endif
/* @18 - Not anymore used. */
gpg_error_check_version @19
gpgrt_lock_init @20
gpgrt_lock_lock @21
gpgrt_lock_unlock @22
gpgrt_lock_destroy @23
gpgrt_yield @24
gpgrt_lock_trylock @25
gpgrt_set_syscall_clamp @26
gpgrt_fopen @27
gpgrt_mopen @28
gpgrt_fopenmem @29
gpgrt_fopenmem_init @30
gpgrt_fdopen @31
gpgrt_fdopen_nc @32
gpgrt_sysopen @33
gpgrt_sysopen_nc @34
gpgrt_fpopen @35
gpgrt_fpopen_nc @36
gpgrt_freopen @37
gpgrt_fopencookie @38
gpgrt_fclose @39
gpgrt_fclose_snatch @40
gpgrt_onclose @41
gpgrt_fileno @42
gpgrt_fileno_unlocked @43
gpgrt_syshd @44
gpgrt_syshd_unlocked @45
_gpgrt_set_std_fd @46
_gpgrt_get_std_stream @47
gpgrt_flockfile @48
gpgrt_ftrylockfile @49
gpgrt_funlockfile @50
gpgrt_feof @51
gpgrt_feof_unlocked @52
gpgrt_ferror @53
gpgrt_ferror_unlocked @54
gpgrt_clearerr @55
gpgrt_clearerr_unlocked @56
gpgrt_fflush @57
gpgrt_fseek @58
gpgrt_fseeko @59
gpgrt_ftell @60
gpgrt_ftello @61
gpgrt_rewind @62
gpgrt_fgetc @63
_gpgrt_getc_underflow @64
gpgrt_fputc @65
_gpgrt_putc_overflow @66
gpgrt_ungetc @67
gpgrt_read @68
gpgrt_write @69
gpgrt_write_sanitized @70
gpgrt_write_hexstring @71
gpgrt_fread @72
gpgrt_fwrite @73
gpgrt_fgets @74
gpgrt_fputs @75
gpgrt_fputs_unlocked @76
gpgrt_getline @77
gpgrt_read_line @78
gpgrt_free @79
gpgrt_fprintf @80
gpgrt_fprintf_unlocked @81
gpgrt_printf @82
gpgrt_printf_unlocked @83
gpgrt_vfprintf @84
gpgrt_vfprintf_unlocked @85
gpgrt_setvbuf @86
gpgrt_setbuf @87
gpgrt_set_binary @88
gpgrt_tmpfile @89
gpgrt_opaque_set @90
gpgrt_opaque_get @91
gpgrt_fname_set @92
gpgrt_fname_get @93
gpgrt_asprintf @94
gpgrt_vasprintf @95
gpgrt_bsprintf @96
gpgrt_vbsprintf @97
gpgrt_snprintf @98
gpgrt_vsnprintf @99
gpgrt_check_version @100
gpg_err_init @101
gpg_err_deinit @102
gpgrt_set_alloc_func @103
_gpgrt_pending @104
_gpgrt_pending_unlocked @105
gpgrt_set_nonblock @106
gpgrt_get_nonblock @107
gpgrt_poll @108
#ifdef HAVE_W32_SYSTEM
gpgrt_w32_iconv_open @109
gpgrt_w32_iconv_close @110
gpgrt_w32_iconv @111
#endif
gpgrt_get_syscall_clamp @112
gpgrt_b64dec_start @113
gpgrt_b64dec_proc @114
gpgrt_b64dec_finish @115
gpgrt_get_errorcount @116
gpgrt_inc_errorcount @117
gpgrt_log_set_sink @118
gpgrt_log_set_socket_dir_cb @119
gpgrt_log_set_pid_suffix_cb @120
gpgrt_log_set_prefix @121
gpgrt_log_get_prefix @122
gpgrt_log_test_fd @123
gpgrt_log_get_fd @124
gpgrt_log_get_stream @125
gpgrt_log @126
gpgrt_logv @127
gpgrt_logv_prefix @128
gpgrt_log_string @129
gpgrt_log_bug @130
gpgrt_log_fatal @131
gpgrt_log_error @132
gpgrt_log_info @133
gpgrt_log_debug @134
gpgrt_log_debug_string @135
gpgrt_log_printf @136
gpgrt_log_printhex @137
gpgrt_log_clock @138
gpgrt_log_flush @139
_gpgrt_log_assert @140
gpgrt_realloc @141
gpgrt_malloc @142
gpgrt_calloc @143
gpgrt_strdup @144
gpgrt_strconcat @145
gpgrt_w32_reg_query_string @146
gpgrt_getenv @147
gpgrt_setenv @148
gpgrt_mkdir @149
gpgrt_chdir @150
gpgrt_getcwd @151
;; API not yet finished for:
;; gpgrt_make_pipe @152
;; gpgrt_spawn_process @153
;; gpgrt_spawn_process_fd @154
;; gpgrt_spawn_process_detached @155
;; gpgrt_wait_process @156
;; gpgrt_wait_processes @157
;; gpgrt_kill_process @158
;; gpgrt_release_process @159
gpgrt_argparse @160
gpgrt_usage @161
gpgrt_strusage @162
gpgrt_set_strusage @163
gpgrt_set_usage_outfnc @164
gpgrt_set_fixed_string_mapper @165
gpgrt_b64enc_start @166
gpgrt_b64enc_write @167
gpgrt_b64enc_finish @168
gpgrt_cmp_version @169
gpgrt_ftruncate @170
gpgrt_fprintf_sf @171
gpgrt_fprintf_sf_unlocked @172
gpgrt_w32_override_locale @173
+ gpgrt_add_emergency_cleanup @174
+ gogrt_abort @175
+
;; end of file with public symbols for Windows.
diff --git a/src/gpg-error.h.in b/src/gpg-error.h.in
index b7aa5f6..a31fb84 100644
--- a/src/gpg-error.h.in
+++ b/src/gpg-error.h.in
@@ -1,1305 +1,1311 @@
/* gpg-error.h or gpgrt.h - Common code for GnuPG and others. -*- c -*-
* Copyright (C) 2001-2018 g10 Code GmbH
*
* This file is part of libgpg-error (aka libgpgrt).
*
* libgpg-error is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* libgpg-error 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 .
* SPDX-License-Identifier: LGPL-2.1+
*
* @configure_input@
*/
/* The GnuPG project consists of many components. Error codes are
* exchanged between all components. The common error codes and their
* user-presentable descriptions are kept into a shared library to
* allow adding new error codes and components without recompiling any
* of the other components. In addition to error codes this library
* also features several other groups of functions which are common to
* all GnuPG components. They may be used by independet project as
* well. The interfaces will not change in a backward incompatible way.
*
* An error code together with an error source build up an error
* value. As the error value is been passed from one component to
* another, it preserves the information about the source and nature
* of the error.
*
* A component of the GnuPG project can define the following macros to
* tune the behaviour of the library:
*
* GPG_ERR_SOURCE_DEFAULT: Define to an error source of type
* gpg_err_source_t to make that source the default for gpg_error().
* Otherwise GPG_ERR_SOURCE_UNKNOWN is used as default.
*
* GPG_ERR_ENABLE_GETTEXT_MACROS: Define to provide macros to map the
* internal gettext API to standard names. This has only an effect on
* Windows platforms.
*
* GPGRT_ENABLE_ES_MACROS: Define to provide "es_" macros for the
* estream functions.
*
* GPGRT_ENABLE_LOG_MACROS: Define to provide short versions of the
* log functions.
*
* GPGRT_ENABLE_ARGPARSE_MACROS: Needs to be defined to provide the
* mandatory macros of the argparse interface.
*/
#ifndef GPG_ERROR_H
#define GPG_ERROR_H 1
#ifndef GPGRT_H
#define GPGRT_H 1
#include
#include
#include
/* The version string of this header. */
#define GPG_ERROR_VERSION @version@
#define GPGRT_VERSION @version@
/* The version number of this header. */
#define GPG_ERROR_VERSION_NUMBER @version-number@
#define GPGRT_VERSION_NUMBER @version-number@
#ifdef __GNUC__
# define GPG_ERR_INLINE __inline__
#elif defined(_MSC_VER) && _MSC_VER >= 1300
# define GPG_ERR_INLINE __inline
#elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
# define GPG_ERR_INLINE inline
#else
# ifndef GPG_ERR_INLINE
# define GPG_ERR_INLINE
# endif
#endif
#ifdef __cplusplus
extern "C" {
#if 0 /* just to make Emacs auto-indent happy */
}
#endif
#endif /* __cplusplus */
/* The error source type gpg_err_source_t.
*
* Where as the Poo out of a welle small
* Taketh his firste springing and his sours.
* --Chaucer.
*/
/* Only use free slots, never change or reorder the existing
* entries. */
typedef enum
{
@include:err-sources@
/* This is one more than the largest allowed entry. */
GPG_ERR_SOURCE_DIM = 128
} gpg_err_source_t;
/* The error code type gpg_err_code_t. */
/* Only use free slots, never change or reorder the existing
* entries. */
typedef enum
{
@include:err-codes@
/* The following error codes are used to map system errors. */
#define GPG_ERR_SYSTEM_ERROR (1 << 15)
@include:errnos@
/* This is one more than the largest allowed entry. */
GPG_ERR_CODE_DIM = 65536
} gpg_err_code_t;
/* The error value type gpg_error_t. */
/* We would really like to use bit-fields in a struct, but using
* structs as return values can cause binary compatibility issues, in
* particular if you want to do it efficiently (also see
* -freg-struct-return option to GCC). */
typedef unsigned int gpg_error_t;
/* We use the lowest 16 bits of gpg_error_t for error codes. The 16th
* bit indicates system errors. */
#define GPG_ERR_CODE_MASK (GPG_ERR_CODE_DIM - 1)
/* Bits 17 to 24 are reserved. */
/* We use the upper 7 bits of gpg_error_t for error sources. */
#define GPG_ERR_SOURCE_MASK (GPG_ERR_SOURCE_DIM - 1)
#define GPG_ERR_SOURCE_SHIFT 24
/* The highest bit is reserved. It shouldn't be used to prevent
* potential negative numbers when transmitting error values as
* text. */
/*
* GCC feature test.
*/
#if __GNUC__
# define _GPG_ERR_GCC_VERSION (__GNUC__ * 10000 \
+ __GNUC_MINOR__ * 100 \
+ __GNUC_PATCHLEVEL__)
#else
# define _GPG_ERR_GCC_VERSION 0
#endif
#undef _GPG_ERR_HAVE_CONSTRUCTOR
#if _GPG_ERR_GCC_VERSION > 30100
# define _GPG_ERR_CONSTRUCTOR __attribute__ ((__constructor__))
# define _GPG_ERR_HAVE_CONSTRUCTOR
#else
# define _GPG_ERR_CONSTRUCTOR
#endif
#define GPGRT_GCC_VERSION _GPG_ERR_GCC_VERSION
#if _GPG_ERR_GCC_VERSION >= 29200
# define _GPGRT__RESTRICT __restrict__
#else
# define _GPGRT__RESTRICT
#endif
/* The noreturn attribute. */
#if _GPG_ERR_GCC_VERSION >= 20500
# define GPGRT_ATTR_NORETURN __attribute__ ((__noreturn__))
#else
# define GPGRT_ATTR_NORETURN
#endif
/* The printf attributes. */
#if _GPG_ERR_GCC_VERSION >= 40400
# define GPGRT_ATTR_PRINTF(f, a) \
__attribute__ ((format(__gnu_printf__,f,a)))
# define GPGRT_ATTR_NR_PRINTF(f, a) \
__attribute__ ((__noreturn__, format(__gnu_printf__,f,a)))
#elif _GPG_ERR_GCC_VERSION >= 20500
# define GPGRT_ATTR_PRINTF(f, a) \
__attribute__ ((format(printf,f,a)))
# define GPGRT_ATTR_NR_PRINTF(f, a) \
__attribute__ ((__noreturn__, format(printf,f,a)))
#else
# define GPGRT_ATTR_PRINTF(f, a)
# define GPGRT_ATTR_NR_PRINTF(f, a)
#endif
#if _GPG_ERR_GCC_VERSION >= 20800
# define GPGRT_ATTR_FORMAT_ARG(a) __attribute__ ((__format_arg__ (a)))
#else
# define GPGRT_ATTR_FORMAT_ARG(a)
#endif
/* The sentinel attribute. */
#if _GPG_ERR_GCC_VERSION >= 40000
# define GPGRT_ATTR_SENTINEL(a) __attribute__ ((sentinel(a)))
#else
# define GPGRT_ATTR_SENTINEL(a)
#endif
/* The used and unused attributes.
* I am not sure since when the unused attribute is really supported.
* In any case it it only needed for gcc versions which print a
* warning. Thus let us require gcc >= 3.5. */
#if _GPG_ERR_GCC_VERSION >= 40000
# define GPGRT_ATTR_USED __attribute__ ((used))
#else
# define GPGRT_ATTR_USED
#endif
#if _GPG_ERR_GCC_VERSION >= 30500
# define GPGRT_ATTR_UNUSED __attribute__ ((unused))
#else
# define GPGRT_ATTR_UNUSED
#endif
/* The deprecated attribute. */
#if _GPG_ERR_GCC_VERSION >= 30100
# define GPGRT_ATTR_DEPRECATED __attribute__ ((__deprecated__))
#else
# define GPGRT_ATTR_DEPRECATED
#endif
/* The pure attribute. */
#if _GPG_ERR_GCC_VERSION >= 29600
# define GPGRT_ATTR_PURE __attribute__ ((__pure__))
#else
# define GPGRT_ATTR_PURE
#endif
/* The malloc attribute. */
#if _GPG_ERR_GCC_VERSION >= 30200
# define GPGRT_ATTR_MALLOC __attribute__ ((__malloc__))
#else
# define GPGRT_ATTR_MALLOC
#endif
/* A macro defined if a GCC style __FUNCTION__ macro is available. */
#undef GPGRT_HAVE_MACRO_FUNCTION
#if _GPG_ERR_GCC_VERSION >= 20500
# define GPGRT_HAVE_MACRO_FUNCTION 1
#endif
/* A macro defined if the pragma GCC push_options is available. */
#undef GPGRT_HAVE_PRAGMA_GCC_PUSH
#if _GPG_ERR_GCC_VERSION >= 40400
# define GPGRT_HAVE_PRAGMA_GCC_PUSH 1
#endif
/* Detect LeakSanitizer (LSan) support for GCC and Clang based on
* whether AddressSanitizer (ASAN) is enabled via -fsanitize=address).
* Note that -fsanitize=leak just affect the linker options which
* cannot be detected here. In that case you have to define the
* GPGRT_HAVE_LEAK_SANITIZER macro manually. */
#ifdef __GNUC__
# ifdef __SANITIZE_ADDRESS__
# define GPGRT_HAVE_LEAK_SANITIZER
# elif defined(__has_feature)
# if __has_feature(address_sanitizer)
# define GPGRT_HAVE_LEAK_SANITIZER
# endif
# endif
#endif
/* The new name for the inline macro. */
#define GPGRT_INLINE GPG_ERR_INLINE
#ifdef GPGRT_HAVE_LEAK_SANITIZER
# include
#endif
/* Mark heap objects as non-leaked memory. */
static GPGRT_INLINE void
gpgrt_annotate_leaked_object (const void *p)
{
#ifdef GPGRT_HAVE_LEAK_SANITIZER
__lsan_ignore_object(p);
#else
(void)p;
#endif
}
/*
* Initialization function.
*/
/* Initialize the library. This function should be run early. */
gpg_error_t gpg_err_init (void) _GPG_ERR_CONSTRUCTOR;
/* If this is defined, the library is already initialized by the
constructor and does not need to be initialized explicitely. */
#undef GPG_ERR_INITIALIZED
#ifdef _GPG_ERR_HAVE_CONSTRUCTOR
# define GPG_ERR_INITIALIZED 1
# define gpgrt_init() do { gpg_err_init (); } while (0)
#else
# define gpgrt_init() do { ; } while (0)
#endif
/* See the source on how to use the deinit function; it is usually not
required. */
void gpg_err_deinit (int mode);
/* Register blocking system I/O clamping functions. */
void gpgrt_set_syscall_clamp (void (*pre)(void), void (*post)(void));
/* Get current I/O clamping functions. */
void gpgrt_get_syscall_clamp (void (**r_pre)(void), void (**r_post)(void));
/* Register a custom malloc/realloc/free function. */
void gpgrt_set_alloc_func (void *(*f)(void *a, size_t n));
+/* Register an emergency cleanup handler. */
+void gpgrt_add_emergency_cleanup (void (*f)(void));
+
+/* Wrapper around abort to make sure emergency cleanups are run. */
+void gpgrt_abort (void) GPGRT_ATTR_NORETURN;
+
/*
* Constructor and accessor functions.
*/
/* Construct an error value from an error code and source. Within a
* subsystem, use gpg_error. */
static GPG_ERR_INLINE gpg_error_t
gpg_err_make (gpg_err_source_t source, gpg_err_code_t code)
{
return code == GPG_ERR_NO_ERROR ? GPG_ERR_NO_ERROR
: (((source & GPG_ERR_SOURCE_MASK) << GPG_ERR_SOURCE_SHIFT)
| (code & GPG_ERR_CODE_MASK));
}
/* The user should define GPG_ERR_SOURCE_DEFAULT before including this
* file to specify a default source for gpg_error. */
#ifndef GPG_ERR_SOURCE_DEFAULT
#define GPG_ERR_SOURCE_DEFAULT GPG_ERR_SOURCE_UNKNOWN
#endif
static GPG_ERR_INLINE gpg_error_t
gpg_error (gpg_err_code_t code)
{
return gpg_err_make (GPG_ERR_SOURCE_DEFAULT, code);
}
/* Retrieve the error code from an error value. */
static GPG_ERR_INLINE gpg_err_code_t
gpg_err_code (gpg_error_t err)
{
return (gpg_err_code_t) (err & GPG_ERR_CODE_MASK);
}
/* Retrieve the error source from an error value. */
static GPG_ERR_INLINE gpg_err_source_t
gpg_err_source (gpg_error_t err)
{
return (gpg_err_source_t) ((err >> GPG_ERR_SOURCE_SHIFT)
& GPG_ERR_SOURCE_MASK);
}
/* String functions. */
/* Return a pointer to a string containing a description of the error
* code in the error value ERR. This function is not thread-safe. */
const char *gpg_strerror (gpg_error_t err);
/* Return the error string for ERR in the user-supplied buffer BUF of
* size BUFLEN. This function is, in contrast to gpg_strerror,
* thread-safe if a thread-safe strerror_r() function is provided by
* the system. If the function succeeds, 0 is returned and BUF
* contains the string describing the error. If the buffer was not
* large enough, ERANGE is returned and BUF contains as much of the
* beginning of the error string as fits into the buffer. */
int gpg_strerror_r (gpg_error_t err, char *buf, size_t buflen);
/* Return a pointer to a string containing a description of the error
* source in the error value ERR. */
const char *gpg_strsource (gpg_error_t err);
/*
* Mapping of system errors (errno).
*/
/* Retrieve the error code for the system error ERR. This returns
* GPG_ERR_UNKNOWN_ERRNO if the system error is not mapped (report
* this). */
gpg_err_code_t gpg_err_code_from_errno (int err);
/* Retrieve the system error for the error code CODE. This returns 0
* if CODE is not a system error code. */
int gpg_err_code_to_errno (gpg_err_code_t code);
/* Retrieve the error code directly from the ERRNO variable. This
* returns GPG_ERR_UNKNOWN_ERRNO if the system error is not mapped
* (report this) and GPG_ERR_MISSING_ERRNO if ERRNO has the value 0. */
gpg_err_code_t gpg_err_code_from_syserror (void);
/* Set the ERRNO variable. This function is the preferred way to set
* ERRNO due to peculiarities on WindowsCE. */
void gpg_err_set_errno (int err);
/* Return or check the version. Both functions are identical. */
const char *gpgrt_check_version (const char *req_version);
const char *gpg_error_check_version (const char *req_version);
/* System specific type definitions. */
@define:pid_t@
@define:gpgrt_ssize_t@
@define:gpgrt_off_t@
@include:os-add@
/* Self-documenting convenience functions. */
static GPG_ERR_INLINE gpg_error_t
gpg_err_make_from_errno (gpg_err_source_t source, int err)
{
return gpg_err_make (source, gpg_err_code_from_errno (err));
}
static GPG_ERR_INLINE gpg_error_t
gpg_error_from_errno (int err)
{
return gpg_error (gpg_err_code_from_errno (err));
}
static GPG_ERR_INLINE gpg_error_t
gpg_error_from_syserror (void)
{
return gpg_error (gpg_err_code_from_syserror ());
}
/*
* Malloc and friends
*/
void *gpgrt_realloc (void *a, size_t n);
void *gpgrt_malloc (size_t n);
void *gpgrt_calloc (size_t n, size_t m);
char *gpgrt_strdup (const char *string);
char *gpgrt_strconcat (const char *s1, ...) GPGRT_ATTR_SENTINEL(0);
void gpgrt_free (void *a);
/*
* System specific function wrappers.
*/
/* A getenv replacement which mallocs the returned string. */
char *gpgrt_getenv (const char *name);
/* A setenv and a unsetenv replacement.*/
gpg_err_code_t gpgrt_setenv (const char *name,
const char *value, int overwrite);
#define gpgrt_unsetenv(n) gpgrt_setenv ((n), NULL, 1)
/* A wrapper around mkdir using a string for the mode. */
gpg_err_code_t gpgrt_mkdir (const char *name, const char *modestr);
/* A simple wrapper around chdir. */
gpg_err_code_t gpgrt_chdir (const char *name);
/* Return the current WD as a malloced string. */
char *gpgrt_getcwd (void);
/*
* Lock functions.
*/
@include:lock-obj@
#define GPGRT_LOCK_DEFINE(name) \
static gpgrt_lock_t name = GPGRT_LOCK_INITIALIZER
/* NB: If GPGRT_LOCK_DEFINE is not used, zero out the lock variable
before passing it to gpgrt_lock_init. */
gpg_err_code_t gpgrt_lock_init (gpgrt_lock_t *lockhd);
gpg_err_code_t gpgrt_lock_lock (gpgrt_lock_t *lockhd);
gpg_err_code_t gpgrt_lock_trylock (gpgrt_lock_t *lockhd);
gpg_err_code_t gpgrt_lock_unlock (gpgrt_lock_t *lockhd);
gpg_err_code_t gpgrt_lock_destroy (gpgrt_lock_t *lockhd);
/*
* Thread functions.
*/
gpg_err_code_t gpgrt_yield (void);
/*
* Estream
*/
/* 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 _gpgrt_stream_internal;
struct _gpgrt__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. */
/* Various flags. */
struct {
unsigned int magic: 16;
unsigned int writing: 1;
unsigned int reserved: 15;
} flags;
/* 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;
/* A pointer to our internal data for this stream. */
struct _gpgrt_stream_internal *intern;
};
/* The opaque type for an estream. */
typedef struct _gpgrt__stream *gpgrt_stream_t;
#ifdef GPGRT_ENABLE_ES_MACROS
typedef struct _gpgrt__stream *estream_t;
#endif
typedef @api_ssize_t@ (*gpgrt_cookie_read_function_t) (void *cookie,
void *buffer, size_t size);
typedef @api_ssize_t@ (*gpgrt_cookie_write_function_t) (void *cookie,
const void *buffer,
size_t size);
typedef int (*gpgrt_cookie_seek_function_t) (void *cookie,
gpgrt_off_t *pos, int whence);
typedef int (*gpgrt_cookie_close_function_t) (void *cookie);
struct _gpgrt_cookie_io_functions
{
gpgrt_cookie_read_function_t func_read;
gpgrt_cookie_write_function_t func_write;
gpgrt_cookie_seek_function_t func_seek;
gpgrt_cookie_close_function_t func_close;
};
typedef struct _gpgrt_cookie_io_functions gpgrt_cookie_io_functions_t;
#ifdef GPGRT_ENABLE_ES_MACROS
typedef struct _gpgrt_cookie_io_functions es_cookie_io_functions_t;
#define es_cookie_read_function_t gpgrt_cookie_read_function_t
#define es_cookie_write_function_t gpgrt_cookie_read_function_t
#define es_cookie_seek_function_t gpgrt_cookie_read_function_t
#define es_cookie_close_function_t gpgrt_cookie_read_function_t
#endif
enum gpgrt_syshd_types
{
GPGRT_SYSHD_NONE = 0, /* No system handle available. */
GPGRT_SYSHD_FD = 1, /* A file descriptor as returned by open(). */
GPGRT_SYSHD_SOCK = 2, /* A socket as returned by socket(). */
GPGRT_SYSHD_RVID = 3, /* A rendezvous id (see libassuan's gpgcedev.c). */
GPGRT_SYSHD_HANDLE = 4 /* A HANDLE object (Windows). */
};
struct _gpgrt_syshd
{
enum gpgrt_syshd_types type;
union {
int fd;
int sock;
int rvid;
void *handle;
} u;
};
typedef struct _gpgrt_syshd gpgrt_syshd_t;
#ifdef GPGRT_ENABLE_ES_MACROS
typedef struct _gpgrt_syshd es_syshd_t;
#define ES_SYSHD_NONE GPGRT_SYSHD_NONE
#define ES_SYSHD_FD GPGRT_SYSHD_FD
#define ES_SYSHD_SOCK GPGRT_SYSHD_SOCK
#define ES_SYSHD_RVID GPGRT_SYSHD_RVID
#define ES_SYSHD_HANDLE GPGRT_SYSHD_HANDLE
#endif
/* The object used with gpgrt_poll. */
struct _gpgrt_poll_s
{
gpgrt_stream_t stream;
unsigned int want_read:1;
unsigned int want_write:1;
unsigned int want_oob:1;
unsigned int want_rdhup:1;
unsigned int _reserv1:4;
unsigned int got_read:1;
unsigned int got_write:1;
unsigned int got_oob:1;
unsigned int got_rdhup:1;
unsigned int _reserv2:4;
unsigned int got_err:1;
unsigned int got_hup:1;
unsigned int got_nval:1;
unsigned int _reserv3:4;
unsigned int ignore:1;
unsigned int user:8; /* For application use. */
};
typedef struct _gpgrt_poll_s gpgrt_poll_t;
#ifdef GPGRT_ENABLE_ES_MACROS
typedef struct _gpgrt_poll_s es_poll_t;
#endif
/* The type of the string filter function as used by fprintf_sf et al. */
typedef char *(*gpgrt_string_filter_t) (const char *s, int n, void *opaque);
gpgrt_stream_t gpgrt_fopen (const char *_GPGRT__RESTRICT path,
const char *_GPGRT__RESTRICT mode);
gpgrt_stream_t gpgrt_mopen (void *_GPGRT__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 *_GPGRT__RESTRICT mode);
gpgrt_stream_t gpgrt_fopenmem (size_t memlimit,
const char *_GPGRT__RESTRICT mode);
gpgrt_stream_t gpgrt_fopenmem_init (size_t memlimit,
const char *_GPGRT__RESTRICT mode,
const void *data, size_t datalen);
gpgrt_stream_t gpgrt_fdopen (int filedes, const char *mode);
gpgrt_stream_t gpgrt_fdopen_nc (int filedes, const char *mode);
gpgrt_stream_t gpgrt_sysopen (gpgrt_syshd_t *syshd, const char *mode);
gpgrt_stream_t gpgrt_sysopen_nc (gpgrt_syshd_t *syshd, const char *mode);
gpgrt_stream_t gpgrt_fpopen (FILE *fp, const char *mode);
gpgrt_stream_t gpgrt_fpopen_nc (FILE *fp, const char *mode);
gpgrt_stream_t gpgrt_freopen (const char *_GPGRT__RESTRICT path,
const char *_GPGRT__RESTRICT mode,
gpgrt_stream_t _GPGRT__RESTRICT stream);
gpgrt_stream_t gpgrt_fopencookie (void *_GPGRT__RESTRICT cookie,
const char *_GPGRT__RESTRICT mode,
gpgrt_cookie_io_functions_t functions);
int gpgrt_fclose (gpgrt_stream_t stream);
int gpgrt_fclose_snatch (gpgrt_stream_t stream,
void **r_buffer, size_t *r_buflen);
int gpgrt_onclose (gpgrt_stream_t stream, int mode,
void (*fnc) (gpgrt_stream_t, void*), void *fnc_value);
int gpgrt_fileno (gpgrt_stream_t stream);
int gpgrt_fileno_unlocked (gpgrt_stream_t stream);
int gpgrt_syshd (gpgrt_stream_t stream, gpgrt_syshd_t *syshd);
int gpgrt_syshd_unlocked (gpgrt_stream_t stream, gpgrt_syshd_t *syshd);
void _gpgrt_set_std_fd (int no, int fd);
gpgrt_stream_t _gpgrt_get_std_stream (int fd);
#define gpgrt_stdin _gpgrt_get_std_stream (0)
#define gpgrt_stdout _gpgrt_get_std_stream (1)
#define gpgrt_stderr _gpgrt_get_std_stream (2)
void gpgrt_flockfile (gpgrt_stream_t stream);
int gpgrt_ftrylockfile (gpgrt_stream_t stream);
void gpgrt_funlockfile (gpgrt_stream_t stream);
int gpgrt_feof (gpgrt_stream_t stream);
int gpgrt_feof_unlocked (gpgrt_stream_t stream);
int gpgrt_ferror (gpgrt_stream_t stream);
int gpgrt_ferror_unlocked (gpgrt_stream_t stream);
void gpgrt_clearerr (gpgrt_stream_t stream);
void gpgrt_clearerr_unlocked (gpgrt_stream_t stream);
int _gpgrt_pending (gpgrt_stream_t stream); /* (private) */
int _gpgrt_pending_unlocked (gpgrt_stream_t stream); /* (private) */
#define gpgrt_pending(stream) _gpgrt_pending (stream)
#define gpgrt_pending_unlocked(stream) \
(((!(stream)->flags.writing) \
&& (((stream)->data_offset < (stream)->data_len) \
|| ((stream)->unread_data_len))) \
? 1 : _gpgrt_pending_unlocked ((stream)))
int gpgrt_fflush (gpgrt_stream_t stream);
int gpgrt_fseek (gpgrt_stream_t stream, long int offset, int whence);
int gpgrt_fseeko (gpgrt_stream_t stream, gpgrt_off_t offset, int whence);
int gpgrt_ftruncate (gpgrt_stream_t stream, gpgrt_off_t length);
long int gpgrt_ftell (gpgrt_stream_t stream);
gpgrt_off_t gpgrt_ftello (gpgrt_stream_t stream);
void gpgrt_rewind (gpgrt_stream_t stream);
int gpgrt_fgetc (gpgrt_stream_t stream);
int gpgrt_fputc (int c, gpgrt_stream_t stream);
int _gpgrt_getc_underflow (gpgrt_stream_t stream); /* (private) */
int _gpgrt_putc_overflow (int c, gpgrt_stream_t stream); /* (private) */
#define gpgrt_getc_unlocked(stream) \
(((!(stream)->flags.writing) \
&& ((stream)->data_offset < (stream)->data_len) \
&& (! (stream)->unread_data_len)) \
? ((int) (stream)->buffer[((stream)->data_offset)++]) \
: _gpgrt_getc_underflow ((stream)))
#define gpgrt_putc_unlocked(c, stream) \
(((stream)->flags.writing \
&& ((stream)->data_offset < (stream)->buffer_size) \
&& (c != '\n')) \
? ((int) ((stream)->buffer[((stream)->data_offset)++] = (c))) \
: _gpgrt_putc_overflow ((c), (stream)))
#define gpgrt_getc(stream) gpgrt_fgetc (stream)
#define gpgrt_putc(c, stream) gpgrt_fputc (c, stream)
int gpgrt_ungetc (int c, gpgrt_stream_t stream);
int gpgrt_read (gpgrt_stream_t _GPGRT__RESTRICT stream,
void *_GPGRT__RESTRICT buffer, size_t bytes_to_read,
size_t *_GPGRT__RESTRICT bytes_read);
int gpgrt_write (gpgrt_stream_t _GPGRT__RESTRICT stream,
const void *_GPGRT__RESTRICT buffer, size_t bytes_to_write,
size_t *_GPGRT__RESTRICT bytes_written);
int gpgrt_write_sanitized (gpgrt_stream_t _GPGRT__RESTRICT stream,
const void *_GPGRT__RESTRICT buffer, size_t length,
const char *delimiters,
size_t *_GPGRT__RESTRICT bytes_written);
int gpgrt_write_hexstring (gpgrt_stream_t _GPGRT__RESTRICT stream,
const void *_GPGRT__RESTRICT buffer, size_t length,
int reserved,
size_t *_GPGRT__RESTRICT bytes_written);
size_t gpgrt_fread (void *_GPGRT__RESTRICT ptr, size_t size, size_t nitems,
gpgrt_stream_t _GPGRT__RESTRICT stream);
size_t gpgrt_fwrite (const void *_GPGRT__RESTRICT ptr, size_t size,
size_t nitems, gpgrt_stream_t _GPGRT__RESTRICT stream);
char *gpgrt_fgets (char *_GPGRT__RESTRICT s, int n,
gpgrt_stream_t _GPGRT__RESTRICT stream);
int gpgrt_fputs (const char *_GPGRT__RESTRICT s,
gpgrt_stream_t _GPGRT__RESTRICT stream);
int gpgrt_fputs_unlocked (const char *_GPGRT__RESTRICT s,
gpgrt_stream_t _GPGRT__RESTRICT stream);
@api_ssize_t@ gpgrt_getline (char *_GPGRT__RESTRICT *_GPGRT__RESTRICT lineptr,
size_t *_GPGRT__RESTRICT n,
gpgrt_stream_t stream);
@api_ssize_t@ gpgrt_read_line (gpgrt_stream_t stream,
char **addr_of_buffer, size_t *length_of_buffer,
size_t *max_length);
int gpgrt_fprintf (gpgrt_stream_t _GPGRT__RESTRICT stream,
const char *_GPGRT__RESTRICT format, ...)
GPGRT_ATTR_PRINTF(2,3);
int gpgrt_fprintf_unlocked (gpgrt_stream_t _GPGRT__RESTRICT stream,
const char *_GPGRT__RESTRICT format, ...)
GPGRT_ATTR_PRINTF(2,3);
int gpgrt_fprintf_sf (gpgrt_stream_t _GPGRT__RESTRICT stream,
gpgrt_string_filter_t sf, void *sfvalue,
const char *_GPGRT__RESTRICT format,
...) GPGRT_ATTR_PRINTF(4,5);
int gpgrt_fprintf_sf_unlocked (gpgrt_stream_t _GPGRT__RESTRICT stream,
gpgrt_string_filter_t sf, void *sfvalue,
const char *_GPGRT__RESTRICT format,
...) GPGRT_ATTR_PRINTF(4,5);
int gpgrt_printf (const char *_GPGRT__RESTRICT format, ...)
GPGRT_ATTR_PRINTF(1,2);
int gpgrt_printf_unlocked (const char *_GPGRT__RESTRICT format, ...)
GPGRT_ATTR_PRINTF(1,2);
int gpgrt_vfprintf (gpgrt_stream_t _GPGRT__RESTRICT stream,
const char *_GPGRT__RESTRICT format, va_list ap)
GPGRT_ATTR_PRINTF(2,0);
int gpgrt_vfprintf_unlocked (gpgrt_stream_t _GPGRT__RESTRICT stream,
const char *_GPGRT__RESTRICT format, va_list ap)
GPGRT_ATTR_PRINTF(2,0);
int gpgrt_setvbuf (gpgrt_stream_t _GPGRT__RESTRICT stream,
char *_GPGRT__RESTRICT buf, int mode, size_t size);
void gpgrt_setbuf (gpgrt_stream_t _GPGRT__RESTRICT stream,
char *_GPGRT__RESTRICT buf);
void gpgrt_set_binary (gpgrt_stream_t stream);
int gpgrt_set_nonblock (gpgrt_stream_t stream, int onoff);
int gpgrt_get_nonblock (gpgrt_stream_t stream);
int gpgrt_poll (gpgrt_poll_t *fdlist, unsigned int nfds, int timeout);
gpgrt_stream_t gpgrt_tmpfile (void);
void gpgrt_opaque_set (gpgrt_stream_t _GPGRT__RESTRICT stream,
void *_GPGRT__RESTRICT opaque);
void *gpgrt_opaque_get (gpgrt_stream_t stream);
void gpgrt_fname_set (gpgrt_stream_t stream, const char *fname);
const char *gpgrt_fname_get (gpgrt_stream_t stream);
int gpgrt_asprintf (char **r_buf, const char * _GPGRT__RESTRICT format, ...)
GPGRT_ATTR_PRINTF(2,3);
int gpgrt_vasprintf (char **r_buf, const char * _GPGRT__RESTRICT format,
va_list ap)
GPGRT_ATTR_PRINTF(2,0);
char *gpgrt_bsprintf (const char * _GPGRT__RESTRICT format, ...)
GPGRT_ATTR_PRINTF(1,2);
char *gpgrt_vbsprintf (const char * _GPGRT__RESTRICT format, va_list ap)
GPGRT_ATTR_PRINTF(1,0);
int gpgrt_snprintf (char *buf, size_t bufsize,
const char * _GPGRT__RESTRICT format, ...)
GPGRT_ATTR_PRINTF(3,4);
int gpgrt_vsnprintf (char *buf,size_t bufsize,
const char * _GPGRT__RESTRICT format, va_list arg_ptr)
GPGRT_ATTR_PRINTF(3,0);
#ifdef GPGRT_ENABLE_ES_MACROS
# define es_fopen gpgrt_fopen
# define es_mopen gpgrt_mopen
# define es_fopenmem gpgrt_fopenmem
# define es_fopenmem_init gpgrt_fopenmem_init
# define es_fdopen gpgrt_fdopen
# define es_fdopen_nc gpgrt_fdopen_nc
# define es_sysopen gpgrt_sysopen
# define es_sysopen_nc gpgrt_sysopen_nc
# define es_fpopen gpgrt_fpopen
# define es_fpopen_nc gpgrt_fpopen_nc
# define es_freopen gpgrt_freopen
# define es_fopencookie gpgrt_fopencookie
# define es_fclose gpgrt_fclose
# define es_fclose_snatch gpgrt_fclose_snatch
# define es_onclose gpgrt_onclose
# define es_fileno gpgrt_fileno
# define es_fileno_unlocked gpgrt_fileno_unlocked
# define es_syshd gpgrt_syshd
# define es_syshd_unlocked gpgrt_syshd_unlocked
# define es_stdin _gpgrt_get_std_stream (0)
# define es_stdout _gpgrt_get_std_stream (1)
# define es_stderr _gpgrt_get_std_stream (2)
# define es_flockfile gpgrt_flockfile
# define es_ftrylockfile gpgrt_ftrylockfile
# define es_funlockfile gpgrt_funlockfile
# define es_feof gpgrt_feof
# define es_feof_unlocked gpgrt_feof_unlocked
# define es_ferror gpgrt_ferror
# define es_ferror_unlocked gpgrt_ferror_unlocked
# define es_clearerr gpgrt_clearerr
# define es_clearerr_unlocked gpgrt_clearerr_unlocked
# define es_pending gpgrt_pending
# define es_pending_unlocked gpgrt_pending_unlocked
# define es_fflush gpgrt_fflush
# define es_fseek gpgrt_fseek
# define es_fseeko gpgrt_fseeko
# define es_ftruncate gpgrt_ftruncate
# define es_ftell gpgrt_ftell
# define es_ftello gpgrt_ftello
# define es_rewind gpgrt_rewind
# define es_fgetc gpgrt_fgetc
# define es_fputc gpgrt_fputc
# define es_getc_unlocked gpgrt_getc_unlocked
# define es_putc_unlocked gpgrt_putc_unlocked
# define es_getc gpgrt_getc
# define es_putc gpgrt_putc
# define es_ungetc gpgrt_ungetc
# define es_read gpgrt_read
# define es_write gpgrt_write
# define es_write_sanitized gpgrt_write_sanitized
# define es_write_hexstring gpgrt_write_hexstring
# define es_fread gpgrt_fread
# define es_fwrite gpgrt_fwrite
# define es_fgets gpgrt_fgets
# define es_fputs gpgrt_fputs
# define es_fputs_unlocked gpgrt_fputs_unlocked
# define es_getline gpgrt_getline
# define es_read_line gpgrt_read_line
# define es_free gpgrt_free
# define es_fprintf gpgrt_fprintf
# define es_fprintf_unlocked gpgrt_fprintf_unlocked
# define es_printf gpgrt_printf
# define es_printf_unlocked gpgrt_printf_unlocked
# define es_vfprintf gpgrt_vfprintf
# define es_vfprintf_unlocked gpgrt_vfprintf_unlocked
# define es_setvbuf gpgrt_setvbuf
# define es_setbuf gpgrt_setbuf
# define es_set_binary gpgrt_set_binary
# define es_set_nonblock gpgrt_set_nonblock
# define es_get_nonblock gpgrt_get_nonblock
# define es_poll gpgrt_poll
# define es_tmpfile gpgrt_tmpfile
# define es_opaque_set gpgrt_opaque_set
# define es_opaque_get gpgrt_opaque_get
# define es_fname_set gpgrt_fname_set
# define es_fname_get gpgrt_fname_get
# define es_asprintf gpgrt_asprintf
# define es_vasprintf gpgrt_vasprintf
# define es_bsprintf gpgrt_bsprintf
# define es_vbsprintf gpgrt_vbsprintf
#endif /*GPGRT_ENABLE_ES_MACROS*/
/*
* Base64 encode and decode functions.
*/
struct _gpgrt_b64state;
typedef struct _gpgrt_b64state *gpgrt_b64state_t;
gpgrt_b64state_t gpgrt_b64enc_start (gpgrt_stream_t stream, const char *title);
gpg_err_code_t gpgrt_b64enc_write (gpgrt_b64state_t state,
const void *buffer, size_t nbytes);
gpg_err_code_t gpgrt_b64enc_finish (gpgrt_b64state_t state);
gpgrt_b64state_t gpgrt_b64dec_start (const char *title);
gpg_error_t gpgrt_b64dec_proc (gpgrt_b64state_t state,
void *buffer, size_t length,
size_t *r_nbytes);
gpg_error_t gpgrt_b64dec_finish (gpgrt_b64state_t state);
/*
* Logging functions
*/
/* Flag values for gpgrt_log_set_prefix. */
#define GPGRT_LOG_WITH_PREFIX 1
#define GPGRT_LOG_WITH_TIME 2
#define GPGRT_LOG_WITH_PID 4
#define GPGRT_LOG_RUN_DETACHED 256
#define GPGRT_LOG_NO_REGISTRY 512
/* Log levels as used by gpgrt_log. */
enum gpgrt_log_levels
{
GPGRT_LOGLVL_BEGIN,
GPGRT_LOGLVL_CONT,
GPGRT_LOGLVL_INFO,
GPGRT_LOGLVL_WARN,
GPGRT_LOGLVL_ERROR,
GPGRT_LOGLVL_FATAL,
GPGRT_LOGLVL_BUG,
GPGRT_LOGLVL_DEBUG
};
/* The next 4 functions are not thread-safe - call them early. */
void gpgrt_log_set_sink (const char *name, gpgrt_stream_t stream, int fd);
void gpgrt_log_set_socket_dir_cb (const char *(*fnc)(void));
void gpgrt_log_set_pid_suffix_cb (int (*cb)(unsigned long *r_value));
void gpgrt_log_set_prefix (const char *text, unsigned int flags);
int gpgrt_get_errorcount (int clear);
void gpgrt_inc_errorcount (void);
const char *gpgrt_log_get_prefix (unsigned int *flags);
int gpgrt_log_test_fd (int fd);
int gpgrt_log_get_fd (void);
gpgrt_stream_t gpgrt_log_get_stream (void);
void gpgrt_log (int level, const char *fmt, ...) GPGRT_ATTR_PRINTF(2,3);
void gpgrt_logv (int level, const char *fmt, va_list arg_ptr);
void gpgrt_logv_prefix (int level, const char *prefix,
const char *fmt, va_list arg_ptr);
void gpgrt_log_string (int level, const char *string);
void gpgrt_log_bug (const char *fmt, ...) GPGRT_ATTR_NR_PRINTF(1,2);
void gpgrt_log_fatal (const char *fmt, ...) GPGRT_ATTR_NR_PRINTF(1,2);
void gpgrt_log_error (const char *fmt, ...) GPGRT_ATTR_PRINTF(1,2);
void gpgrt_log_info (const char *fmt, ...) GPGRT_ATTR_PRINTF(1,2);
void gpgrt_log_debug (const char *fmt, ...) GPGRT_ATTR_PRINTF(1,2);
void gpgrt_log_debug_string (const char *string,
const char *fmt, ...) GPGRT_ATTR_PRINTF(2,3);
void gpgrt_log_printf (const char *fmt, ...) GPGRT_ATTR_PRINTF(1,2);
void gpgrt_log_printhex (const void *buffer, size_t length,
const char *fmt, ...) GPGRT_ATTR_PRINTF(3,4);
void gpgrt_log_clock (const char *fmt, ...) GPGRT_ATTR_PRINTF(1,2);
void gpgrt_log_flush (void);
void _gpgrt_log_assert (const char *expr, const char *file, int line,
const char *func) GPGRT_ATTR_NORETURN;
#ifdef GPGRT_HAVE_MACRO_FUNCTION
# define gpgrt_assert(expr) \
((expr) \
? (void) 0 \
: _gpgrt_log_assert (#expr, __FILE__, __LINE__, __FUNCTION__))
#else /*!GPGRT_HAVE_MACRO_FUNCTION*/
# define gpgrt_assert(expr) \
((expr) \
? (void) 0 \
: _gpgrt_log_assert (#expr, __FILE__, __LINE__, NULL))
#endif /*!GPGRT_HAVE_MACRO_FUNCTION*/
#ifdef GPGRT_ENABLE_LOG_MACROS
# define log_get_errorcount gpgrt_get_errorcount
# define log_inc_errorcount gpgrt_inc_errorcount
# define log_set_file(a) gpgrt_log_set_sink ((a), NULL, -1)
# define log_set_fd(a) gpgrt_log_set_sink (NULL, NULL, (a))
# define log_set_stream(a) gpgrt_log_set_sink (NULL, (a), -1)
# define log_set_socket_dir_cb gpgrt_log_set_socket_dir_cb
# define log_set_pid_suffix_cb gpgrt_log_set_pid_suffix_cb
# define log_set_prefix gpgrt_log_set_prefix
# define log_get_prefix gpgrt_log_get_prefix
# define log_test_fd gpgrt_log_test_fd
# define log_get_fd gpgrt_log_get_fd
# define log_get_stream gpgrt_log_get_stream
# define log_log gpgrt_log
# define log_logv gpgrt_logv
# define log_logv_prefix gpgrt_logv_prefix
# define log_string gpgrt_log_string
# define log_bug gpgrt_log_bug
# define log_fatal gpgrt_log_fatal
# define log_error gpgrt_log_error
# define log_info gpgrt_log_info
# define log_debug gpgrt_log_debug
# define log_debug_string gpgrt_log_debug_string
# define log_printf gpgrt_log_printf
# define log_printhex gpgrt_log_printhex
# define log_clock gpgrt_log_clock
# define log_flush gpgrt_log_flush
# ifdef GPGRT_HAVE_MACRO_FUNCTION
# define log_assert(expr) \
((expr) \
? (void) 0 \
: _gpgrt_log_assert (#expr, __FILE__, __LINE__, __FUNCTION__))
# else /*!GPGRT_HAVE_MACRO_FUNCTION*/
# define log_assert(expr) \
((expr) \
? (void) 0 \
: _gpgrt_log_assert (#expr, __FILE__, __LINE__, NULL))
# endif /*!GPGRT_HAVE_MACRO_FUNCTION*/
#endif /*GPGRT_ENABLE_LOG_MACROS*/
/*
* Spawn functions (Not yet available)
*/
#define GPGRT_SPAWN_NONBLOCK 16 /* Set the streams to non-blocking. */
#define GPGRT_SPAWN_RUN_ASFW 64 /* Use AllowSetForegroundWindow on W32. */
#define GPGRT_SPAWN_DETACHED 128 /* Start the process in the background. */
#if 0
/* Function and convenience macros to create pipes. */
gpg_err_code_t gpgrt_make_pipe (int filedes[2], gpgrt_stream_t *r_fp,
int direction, int nonblock);
#define gpgrt_create_pipe(a) gpgrt_make_pipe ((a),NULL, 0, 0);
#define gpgrt_create_inbound_pipe(a,b,c) gpgrt_make_pipe ((a), (b), -1,(c));
#define gpgrt_create_outbound_pipe(a,b,c) gpgrt_make_pipe ((a), (b), 1,(c));
/* Fork and exec PGMNAME. */
gpg_err_code_t gpgrt_spawn_process (const char *pgmname, const char *argv[],
int *execpt, void (*preexec)(void),
unsigned int flags,
gpgrt_stream_t *r_infp,
gpgrt_stream_t *r_outfp,
gpgrt_stream_t *r_errfp,
pid_t *pid);
/* Fork and exec PGNNAME and connect the process to the given FDs. */
gpg_err_code_t gpgrt_spawn_process_fd (const char *pgmname, const char *argv[],
int infd, int outfd, int errfd,
pid_t *pid);
/* Fork and exec PGMNAME as a detached process. */
gpg_err_code_t gpgrt_spawn_process_detached (const char *pgmname,
const char *argv[],
const char *envp[] );
/* Wait for a single process. */
gpg_err_code_t gpgrt_wait_process (const char *pgmname, pid_t pid, int hang,
int *r_exitcode);
/* Wait for a multiple processes. */
gpg_err_code_t gpgrt_wait_processes (const char **pgmnames, pid_t *pids,
size_t count, int hang, int *r_exitcodes);
/* Kill the process identified by PID. */
void gpgrt_kill_process (pid_t pid);
/* Release process resources identified by PID. */
void gpgrt_release_process (pid_t pid);
#endif /*0*/
/*
* Option parsing.
*/
struct _gpgrt_argparse_internal_s;
typedef struct
{
int *argc; /* Pointer to ARGC (value subject to change). */
char ***argv; /* Pointer to ARGV (value subject to change). */
unsigned int flags; /* Global flags. May be set prior to calling the
parser. The parser may change the value. */
int err; /* Print error description for last option.
Either 0, ARGPARSE_PRINT_WARNING or
ARGPARSE_PRINT_ERROR. */
unsigned int lineno;/* The current line number. */
int r_opt; /* Returns option code. */
int r_type; /* Returns type of option value. */
union {
int ret_int;
long ret_long;
unsigned long ret_ulong;
char *ret_str;
} r; /* Return values */
struct _gpgrt_argparse_internal_s *internal;
} gpgrt_argparse_t;
typedef struct
{
int short_opt;
const char *long_opt;
unsigned int flags;
const char *description; /* Optional description. */
} gpgrt_opt_t;
#ifdef GPGRT_ENABLE_ARGPARSE_MACROS
/* Global flags for (gpgrt_argparse_t).flags. */
#define ARGPARSE_FLAG_KEEP 1 /* Do not remove options form argv. */
#define ARGPARSE_FLAG_ALL 2 /* Do not stop at last option but return
remaining args with R_OPT set to -1. */
#define ARGPARSE_FLAG_MIXED 4 /* Assume options and args are mixed. */
#define ARGPARSE_FLAG_NOSTOP 8 /* Do not stop processing at "--". */
#define ARGPARSE_FLAG_ARG0 16 /* Do not skip the first arg. */
#define ARGPARSE_FLAG_ONEDASH 32 /* Allow long options with one dash. */
#define ARGPARSE_FLAG_NOVERSION 64 /* No output for "--version". */
#define ARGPARSE_FLAG_RESET 128 /* Request to reset the internal state. */
#define ARGPARSE_FLAG_STOP_SEEN 256 /* Set to true if a "--" has been seen. */
#define ARGPARSE_FLAG_NOLINENO 512 /* Do not zero the lineno field. */
/* Constants for (gpgrt_argparse_t).err. */
#define ARGPARSE_PRINT_WARNING 1 /* Print a diagnostic. */
#define ARGPARSE_PRINT_ERROR 2 /* Print a diagnostic and call exit. */
/* Special return values of gpgrt_argparse. */
#define ARGPARSE_IS_ARG (-1)
#define ARGPARSE_INVALID_OPTION (-2)
#define ARGPARSE_MISSING_ARG (-3)
#define ARGPARSE_KEYWORD_TOO_LONG (-4)
#define ARGPARSE_READ_ERROR (-5)
#define ARGPARSE_UNEXPECTED_ARG (-6)
#define ARGPARSE_INVALID_COMMAND (-7)
#define ARGPARSE_AMBIGUOUS_OPTION (-8)
#define ARGPARSE_AMBIGUOUS_COMMAND (-9)
#define ARGPARSE_INVALID_ALIAS (-10)
#define ARGPARSE_OUT_OF_CORE (-11)
#define ARGPARSE_INVALID_ARG (-12)
/* Flags for the option descriptor (gpgrt_opt_t)->flags. Note that
* a TYPE constant may be or-ed with the OPT constants. */
#define ARGPARSE_TYPE_NONE 0 /* Does not take an argument. */
#define ARGPARSE_TYPE_INT 1 /* Takes an int argument. */
#define ARGPARSE_TYPE_STRING 2 /* Takes a string argument. */
#define ARGPARSE_TYPE_LONG 3 /* Takes a long argument. */
#define ARGPARSE_TYPE_ULONG 4 /* Takes an unsigned long argument. */
#define ARGPARSE_OPT_OPTIONAL (1<<3) /* Argument is optional. */
#define ARGPARSE_OPT_PREFIX (1<<4) /* Allow 0x etc. prefixed values. */
#define ARGPARSE_OPT_IGNORE (1<<6) /* Ignore command or option. */
#define ARGPARSE_OPT_COMMAND (1<<7) /* The argument is a command. */
/* A set of macros to make option definitions easier to read. */
#define ARGPARSE_x(s,l,t,f,d) \
{ (s), (l), ARGPARSE_TYPE_ ## t | (f), (d) }
#define ARGPARSE_s(s,l,t,d) \
{ (s), (l), ARGPARSE_TYPE_ ## t, (d) }
#define ARGPARSE_s_n(s,l,d) \
{ (s), (l), ARGPARSE_TYPE_NONE, (d) }
#define ARGPARSE_s_i(s,l,d) \
{ (s), (l), ARGPARSE_TYPE_INT, (d) }
#define ARGPARSE_s_s(s,l,d) \
{ (s), (l), ARGPARSE_TYPE_STRING, (d) }
#define ARGPARSE_s_l(s,l,d) \
{ (s), (l), ARGPARSE_TYPE_LONG, (d) }
#define ARGPARSE_s_u(s,l,d) \
{ (s), (l), ARGPARSE_TYPE_ULONG, (d) }
#define ARGPARSE_o(s,l,t,d) \
{ (s), (l), (ARGPARSE_TYPE_ ## t | ARGPARSE_OPT_OPTIONAL), (d) }
#define ARGPARSE_o_n(s,l,d) \
{ (s), (l), (ARGPARSE_TYPE_NONE | ARGPARSE_OPT_OPTIONAL), (d) }
#define ARGPARSE_o_i(s,l,d) \
{ (s), (l), (ARGPARSE_TYPE_INT | ARGPARSE_OPT_OPTIONAL), (d) }
#define ARGPARSE_o_s(s,l,d) \
{ (s), (l), (ARGPARSE_TYPE_STRING | ARGPARSE_OPT_OPTIONAL), (d) }
#define ARGPARSE_o_l(s,l,d) \
{ (s), (l), (ARGPARSE_TYPE_LONG | ARGPARSE_OPT_OPTIONAL), (d) }
#define ARGPARSE_o_u(s,l,d) \
{ (s), (l), (ARGPARSE_TYPE_ULONG | ARGPARSE_OPT_OPTIONAL), (d) }
#define ARGPARSE_p(s,l,t,d) \
{ (s), (l), (ARGPARSE_TYPE_ ## t | ARGPARSE_OPT_PREFIX), (d) }
#define ARGPARSE_p_n(s,l,d) \
{ (s), (l), (ARGPARSE_TYPE_NONE | ARGPARSE_OPT_PREFIX), (d) }
#define ARGPARSE_p_i(s,l,d) \
{ (s), (l), (ARGPARSE_TYPE_INT | ARGPARSE_OPT_PREFIX), (d) }
#define ARGPARSE_p_s(s,l,d) \
{ (s), (l), (ARGPARSE_TYPE_STRING | ARGPARSE_OPT_PREFIX), (d) }
#define ARGPARSE_p_l(s,l,d) \
{ (s), (l), (ARGPARSE_TYPE_LONG | ARGPARSE_OPT_PREFIX), (d) }
#define ARGPARSE_p_u(s,l,d) \
{ (s), (l), (ARGPARSE_TYPE_ULONG | ARGPARSE_OPT_PREFIX), (d) }
#define ARGPARSE_op(s,l,t,d) \
{ (s), (l), (ARGPARSE_TYPE_ ## t \
| ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) }
#define ARGPARSE_op_n(s,l,d) \
{ (s), (l), (ARGPARSE_TYPE_NONE \
| ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) }
#define ARGPARSE_op_i(s,l,d) \
{ (s), (l), (ARGPARSE_TYPE_INT \
| ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) }
#define ARGPARSE_op_s(s,l,d) \
{ (s), (l), (ARGPARSE_TYPE_STRING \
| ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) }
#define ARGPARSE_op_l(s,l,d) \
{ (s), (l), (ARGPARSE_TYPE_LONG \
| ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) }
#define ARGPARSE_op_u(s,l,d) \
{ (s), (l), (ARGPARSE_TYPE_ULONG \
| ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) }
#define ARGPARSE_c(s,l,d) \
{ (s), (l), (ARGPARSE_TYPE_NONE | ARGPARSE_OPT_COMMAND), (d) }
#define ARGPARSE_ignore(s,l) \
{ (s), (l), (ARGPARSE_OPT_IGNORE), "@" }
#define ARGPARSE_group(s,d) \
{ (s), NULL, 0, (d) }
/* Mark the end of the list (mandatory). */
#define ARGPARSE_end() \
{ 0, NULL, 0, NULL }
#endif /* GPGRT_ENABLE_ARGPARSE_MACROS */
/* Take care: gpgrt_argparse keeps state in ARG and requires that
* either ARGPARSE_FLAG_RESET is used after OPTS has been changed or
* gpgrt_argparse (NULL, ARG, NULL) is called first. */
int gpgrt_argparse (gpgrt_stream_t fp,
gpgrt_argparse_t *arg, gpgrt_opt_t *opts);
void gpgrt_usage (int level);
const char *gpgrt_strusage (int level);
void gpgrt_set_strusage (const char *(*f)(int));
void gpgrt_set_usage_outfnc (int (*f)(int, const char *));
void gpgrt_set_fixed_string_mapper (const char *(*f)(const char*));
/*
* Various helper functions
*/
/* Compare arbitrary version strings. For the standard m.n.o version
* numbering scheme a LEVEL of 3 is suitable; see the manual. */
int gpgrt_cmp_version (const char *a, const char *b, int level);
#ifdef __cplusplus
}
#endif
#endif /* GPGRT_H */
#endif /* GPG_ERROR_H */
diff --git a/src/gpg-error.vers b/src/gpg-error.vers
index 105e3bb..eef4cbc 100644
--- a/src/gpg-error.vers
+++ b/src/gpg-error.vers
@@ -1,201 +1,204 @@
# libgpg-error.vers - What symbols to export -*- std -*-
# Copyright (C) 2014 g10 Code GmbH
#
# This file is part of libgpg-error.
#
# libgpg-error is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as
# published by the Free Software Foundation; either version 2.1 of the
# License, or (at your option) any later version.
#
# libgpg-error 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 .
# SPDX-License-Identifier: LGPL-2.1+
#
# NOTE: When adding new functions, please make sure to add them to
# visibility.h and gpg-error.def.in as well.
GPG_ERROR_1.0 {
global:
gpg_strerror;
gpg_strerror_r;
gpg_strsource;
gpg_err_code_from_errno;
gpg_err_code_to_errno;
gpg_err_code_from_syserror;
gpg_err_set_errno;
gpg_error_check_version;
gpgrt_lock_init;
gpgrt_lock_lock;
gpgrt_lock_unlock;
gpgrt_lock_destroy;
gpgrt_yield;
gpgrt_lock_trylock;
gpgrt_set_syscall_clamp;
gpgrt_get_syscall_clamp;
gpgrt_fopen;
gpgrt_mopen;
gpgrt_fopenmem;
gpgrt_fopenmem_init;
gpgrt_fdopen;
gpgrt_fdopen_nc;
gpgrt_sysopen;
gpgrt_sysopen_nc;
gpgrt_fpopen;
gpgrt_fpopen_nc;
gpgrt_freopen;
gpgrt_fopencookie;
gpgrt_fclose;
gpgrt_fclose_snatch;
gpgrt_onclose;
gpgrt_fileno;
gpgrt_fileno_unlocked;
gpgrt_syshd;
gpgrt_syshd_unlocked;
_gpgrt_set_std_fd;
_gpgrt_get_std_stream;
gpgrt_flockfile;
gpgrt_ftrylockfile;
gpgrt_funlockfile;
_gpgrt_pending;
_gpgrt_pending_unlocked;
gpgrt_feof;
gpgrt_feof_unlocked;
gpgrt_ferror;
gpgrt_ferror_unlocked;
gpgrt_clearerr;
gpgrt_clearerr_unlocked;
gpgrt_fflush;
gpgrt_fseek;
gpgrt_fseeko;
gpgrt_ftell;
gpgrt_ftello;
gpgrt_rewind;
gpgrt_fgetc;
_gpgrt_getc_underflow;
gpgrt_fputc;
_gpgrt_putc_overflow;
gpgrt_ungetc;
gpgrt_read;
gpgrt_write;
gpgrt_write_sanitized;
gpgrt_write_hexstring;
gpgrt_fread;
gpgrt_fwrite;
gpgrt_fgets;
gpgrt_fputs;
gpgrt_fputs_unlocked;
gpgrt_getline;
gpgrt_read_line;
gpgrt_free;
gpgrt_fprintf;
gpgrt_fprintf_unlocked;
gpgrt_printf;
gpgrt_printf_unlocked;
gpgrt_vfprintf;
gpgrt_vfprintf_unlocked;
gpgrt_setvbuf;
gpgrt_setbuf;
gpgrt_set_binary;
gpgrt_set_nonblock;
gpgrt_get_nonblock;
gpgrt_poll;
gpgrt_tmpfile;
gpgrt_opaque_set;
gpgrt_opaque_get;
gpgrt_fname_set;
gpgrt_fname_get;
gpgrt_asprintf;
gpgrt_vasprintf;
gpgrt_bsprintf;
gpgrt_vbsprintf;
gpgrt_snprintf;
gpgrt_vsnprintf;
gpgrt_check_version;
gpg_err_init;
gpg_err_deinit;
gpgrt_set_alloc_func;
gpgrt_b64dec_start;
gpgrt_b64dec_proc;
gpgrt_b64dec_finish;
gpgrt_get_errorcount;
gpgrt_inc_errorcount;
gpgrt_log_set_sink;
gpgrt_log_set_socket_dir_cb;
gpgrt_log_set_pid_suffix_cb;
gpgrt_log_set_prefix;
gpgrt_log_get_prefix;
gpgrt_log_test_fd;
gpgrt_log_get_fd;
gpgrt_log_get_stream;
gpgrt_log;
gpgrt_logv;
gpgrt_logv_prefix;
gpgrt_log_string;
gpgrt_log_bug;
gpgrt_log_fatal;
gpgrt_log_error;
gpgrt_log_info;
gpgrt_log_debug;
gpgrt_log_debug_string;
gpgrt_log_printf;
gpgrt_log_printhex;
gpgrt_log_clock;
gpgrt_log_flush;
_gpgrt_log_assert;
gpgrt_realloc;
gpgrt_malloc;
gpgrt_calloc;
gpgrt_strdup;
gpgrt_strconcat;
gpgrt_getenv;
gpgrt_setenv;
gpgrt_mkdir;
gpgrt_chdir;
gpgrt_getcwd;
## API not yet finished for:
# gpgrt_make_pipe;
# gpgrt_spawn_process;
# gpgrt_spawn_process_fd;
# gpgrt_spawn_process_detached;
# gpgrt_wait_process;
# gpgrt_wait_processes;
# gpgrt_kill_process;
# gpgrt_release_process;
gpgrt_argparse;
gpgrt_usage;
gpgrt_strusage;
gpgrt_set_strusage;
gpgrt_set_usage_outfnc;
gpgrt_set_fixed_string_mapper;
gpgrt_b64enc_start;
gpgrt_b64enc_write;
gpgrt_b64enc_finish;
gpgrt_cmp_version;
gpgrt_ftruncate;
gpgrt_fprintf_sf;
gpgrt_fprintf_sf_unlocked;
+ gpgrt_add_emergency_cleanup;
+ gpgrt_abort;
+
local:
*;
};
diff --git a/src/gpgrt-int.h b/src/gpgrt-int.h
index 17244c5..08496b2 100644
--- a/src/gpgrt-int.h
+++ b/src/gpgrt-int.h
@@ -1,810 +1,814 @@
/* gpgrt-int.h - Internal definitions
* Copyright (C) 2014, 2017 g10 Code GmbH
*
* This file is part of libgpg-error.
*
* libgpg-error is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* libgpg-error 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 .
* SPDX-License-Identifier: LGPL-2.1+
*/
#ifndef _GPGRT_GPGRT_INT_H
#define _GPGRT_GPGRT_INT_H
#include "gpg-error.h"
#include "visibility.h"
/*
* Internal i18n macros.
*/
#ifdef ENABLE_NLS
# ifdef HAVE_W32_SYSTEM
# include "gettext.h"
# else
# include
# endif
# define _(a) gettext (a)
# ifdef gettext_noop
# define N_(a) gettext_noop (a)
# else
# define N_(a) (a)
# endif
#else /*!ENABLE_NLS*/
# define _(a) (a)
# define N_(a) (a)
#endif /*!ENABLE_NLS */
/*
* Hacks mainly required for Slowaris.
*/
#ifdef _GPGRT_NEED_AFLOCAL
# ifndef HAVE_W32_SYSTEM
# include
# include
# else
# ifdef HAVE_WINSOCK2_H
# include
# endif
# include
# endif
# ifndef PF_LOCAL
# ifdef PF_UNIX
# define PF_LOCAL PF_UNIX
# else
# define PF_LOCAL AF_UNIX
# endif
# endif /*PF_LOCAL*/
# ifndef AF_LOCAL
# define AF_LOCAL AF_UNIX
# endif /*AF_UNIX*/
/* We used to avoid this macro in GnuPG and inlined the AF_LOCAL name
* length computation directly with the little twist of adding 1 extra
* byte. It seems that this was needed once on an old HP/UX box and
* there are also rumours that 4.3 Reno and DEC systems need it. This
* one-off buglet did not harm any current system until it came to Mac
* OS X where the kernel (as of May 2009) exhibited a strange bug: The
* systems basically froze in the connect call if the passed name
* contained an invalid directory part. Ignore the old Unices. */
# ifndef SUN_LEN
# define SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path) \
+ strlen ((ptr)->sun_path))
# endif /*SUN_LEN*/
#endif /*_GPGRT_NEED_AFLOCAL*/
/*
* Common helper macros.
*/
#ifndef DIM
# define DIM(array) (sizeof (array) / sizeof (*array))
#endif
/*
* Local error function prototypes.
*/
const char *_gpg_strerror (gpg_error_t err);
int _gpg_strerror_r (gpg_error_t err, char *buf, size_t buflen);
const char *_gpg_strsource (gpg_error_t err);
gpg_err_code_t _gpg_err_code_from_errno (int err);
int _gpg_err_code_to_errno (gpg_err_code_t code);
gpg_err_code_t _gpg_err_code_from_syserror (void);
void _gpg_err_set_errno (int err);
gpg_error_t _gpg_err_init (void);
void _gpg_err_deinit (int mode);
+
+void _gpgrt_add_emergency_cleanup (void (*f)(void));
+void _gpgrt_abort (void) GPGRT_ATTR_NORETURN;
+
void _gpgrt_set_alloc_func (void *(*f)(void *a, size_t n));
void *_gpgrt_realloc (void *a, size_t n);
void *_gpgrt_malloc (size_t n);
void *_gpgrt_calloc (size_t n, size_t m);
char *_gpgrt_strdup (const char *string);
char *_gpgrt_strconcat (const char *s1, ...) GPGRT_ATTR_SENTINEL(0);
void _gpgrt_free (void *a);
/* The next is only to be used by visibility.c. */
char *_gpgrt_strconcat_core (const char *s1, va_list arg_ptr);
#define xfree(a) _gpgrt_free ((a))
#define xtrymalloc(a) _gpgrt_malloc ((a))
#define xtrycalloc(a,b) _gpgrt_calloc ((a),(b))
#define xtryrealloc(a,b) _gpgrt_realloc ((a),(b))
#define xtrystrdup(a) _gpgrt_strdup ((a))
void _gpgrt_pre_syscall (void);
void _gpgrt_post_syscall (void);
const char *_gpg_error_check_version (const char *req_version);
gpg_err_code_t _gpgrt_lock_init (gpgrt_lock_t *lockhd);
gpg_err_code_t _gpgrt_lock_lock (gpgrt_lock_t *lockhd);
gpg_err_code_t _gpgrt_lock_trylock (gpgrt_lock_t *lockhd);
gpg_err_code_t _gpgrt_lock_unlock (gpgrt_lock_t *lockhd);
gpg_err_code_t _gpgrt_lock_destroy (gpgrt_lock_t *lockhd);
gpg_err_code_t _gpgrt_yield (void);
/*
* Tracing
*/
/* The trace macro is used this way:
* trace (("enter - foo=%d bar=%s", foo, bar));
* Note the double parenthesis, they are important.
* To append the current errno to the output, use
* trace_errno (EXTPR,("leave - baz=%d", faz));
* If EXPR evaluates to true the output of strerror (errno)
* is appended to the output. Note that the trace function does
* not modify ERRNO. To enable tracing you need to have this
* #define ENABLE_TRACING "modulename"
* before you include gpgrt-int.h.
*/
#ifdef ENABLE_TRACING
# define trace(X) do { \
_gpgrt_internal_trace_begin \
(ENABLE_TRACING, __func__, __LINE__, 0); \
_gpgrt_internal_trace X; \
_gpgrt_internal_trace_end (); \
} while (0)
# define trace_errno(C,X) do { \
_gpgrt_internal_trace_begin \
(ENABLE_TRACING, __func__, __LINE__, (C)); \
_gpgrt_internal_trace X; \
_gpgrt_internal_trace_end (); \
} while (0)
# define trace_start(X) do { \
_gpgrt_internal_trace_begin \
(ENABLE_TRACING, __func__, __LINE__, 0); \
_gpgrt_internal_trace_printf X; \
} while (0)
# define trace_append(X) do { \
_gpgrt_internal_trace_printf X; \
} while (0)
# define trace_finish(X) do { \
_gpgrt_internal_trace_printf X; \
_gpgrt_internal_trace_end (); \
} while (0)
#else
# define trace(X) do { } while (0)
# define trace_errno(C,X) do { } while (0)
# define trace_start(X) do { } while (0)
# define trace_append(X) do { } while (0)
# define trace_finish(X) do { } while (0)
#endif /*!ENABLE_TRACING*/
void _gpgrt_internal_trace_begin (const char *mod, const char *file, int line,
int with_errno);
void _gpgrt_internal_trace (const char *format,
...) GPGRT_ATTR_PRINTF(1,2);
void _gpgrt_internal_trace_printf (const char *format,
...) GPGRT_ATTR_PRINTF(1,2);
void _gpgrt_internal_trace_end (void);
/*
* Local definitions for estream.
*/
#if HAVE_W32_SYSTEM
# ifndef O_NONBLOCK
# define O_NONBLOCK 0x40000000 /* FIXME: Is that safe? */
# endif
#endif
/*
* A private cookie function to implement an internal IOCTL service.
*/
typedef int (*cookie_ioctl_function_t) (void *cookie, int cmd,
void *ptr, size_t *len);
#define COOKIE_IOCTL_SNATCH_BUFFER 1
#define COOKIE_IOCTL_NONBLOCK 2
#define COOKIE_IOCTL_TRUNCATE 3
/* An internal variant of gpgrt_cookie_close_function_t with a slot
* for the ioctl function. */
struct cookie_io_functions_s
{
struct _gpgrt_cookie_io_functions public;
cookie_ioctl_function_t func_ioctl;
};
typedef enum
{
BACKEND_MEM,
BACKEND_FD,
BACKEND_W32,
BACKEND_FP,
BACKEND_USER,
BACKEND_W32_POLLABLE
} gpgrt_stream_backend_kind_t;
/*
* A type to hold notification functions.
*/
struct notify_list_s
{
struct notify_list_s *next;
void (*fnc) (estream_t, void*); /* The notification function. */
void *fnc_value; /* The value to be passed to FNC. */
};
typedef struct notify_list_s *notify_list_t;
/*
* Buffer management layer.
*/
#define BUFFER_BLOCK_SIZE BUFSIZ
#define BUFFER_UNREAD_SIZE 16
/*
* The private object describing a stream.
*/
struct _gpgrt_stream_internal
{
unsigned char buffer[BUFFER_BLOCK_SIZE];
unsigned char unread_buffer[BUFFER_UNREAD_SIZE];
gpgrt_lock_t lock; /* Lock. Used by *_stream_lock(). */
gpgrt_stream_backend_kind_t kind;
void *cookie; /* Cookie. */
void *opaque; /* Opaque data. */
unsigned int modeflags; /* Flags for the backend. */
char *printable_fname; /* Malloced filename for es_fname_get. */
gpgrt_off_t offset;
gpgrt_cookie_read_function_t func_read;
gpgrt_cookie_write_function_t func_write;
gpgrt_cookie_seek_function_t func_seek;
gpgrt_cookie_close_function_t func_close;
cookie_ioctl_function_t func_ioctl;
int strategy;
es_syshd_t syshd; /* A copy of the system handle. */
struct
{
unsigned int err: 1;
unsigned int eof: 1;
unsigned int hup: 1;
} indicators;
unsigned int deallocate_buffer: 1;
unsigned int is_stdstream:1; /* This is a standard stream. */
unsigned int stdstream_fd:2; /* 0, 1 or 2 for a standard stream. */
unsigned int printable_fname_inuse: 1; /* es_fname_get has been used. */
unsigned int samethread: 1; /* The "samethread" mode keyword. */
size_t print_ntotal; /* Bytes written from in print_writer. */
notify_list_t onclose; /* On close notify function list. */
};
typedef struct _gpgrt_stream_internal *estream_internal_t;
/*
* Local prototypes for estream.
*/
int _gpgrt_estream_init (void);
void _gpgrt_set_syscall_clamp (void (*pre)(void), void (*post)(void));
void _gpgrt_get_syscall_clamp (void (**r_pre)(void), void (**r_post)(void));
gpgrt_stream_t _gpgrt_fopen (const char *_GPGRT__RESTRICT path,
const char *_GPGRT__RESTRICT mode);
gpgrt_stream_t _gpgrt_mopen (void *_GPGRT__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 *_GPGRT__RESTRICT mode);
gpgrt_stream_t _gpgrt_fopenmem (size_t memlimit,
const char *_GPGRT__RESTRICT mode);
gpgrt_stream_t _gpgrt_fopenmem_init (size_t memlimit,
const char *_GPGRT__RESTRICT mode,
const void *data, size_t datalen);
gpgrt_stream_t _gpgrt_fdopen (int filedes, const char *mode);
gpgrt_stream_t _gpgrt_fdopen_nc (int filedes, const char *mode);
gpgrt_stream_t _gpgrt_sysopen (gpgrt_syshd_t *syshd, const char *mode);
gpgrt_stream_t _gpgrt_sysopen_nc (gpgrt_syshd_t *syshd, const char *mode);
gpgrt_stream_t _gpgrt_fpopen (FILE *fp, const char *mode);
gpgrt_stream_t _gpgrt_fpopen_nc (FILE *fp, const char *mode);
gpgrt_stream_t _gpgrt_freopen (const char *_GPGRT__RESTRICT path,
const char *_GPGRT__RESTRICT mode,
gpgrt_stream_t _GPGRT__RESTRICT stream);
gpgrt_stream_t _gpgrt_fopencookie (void *_GPGRT__RESTRICT cookie,
const char *_GPGRT__RESTRICT mode,
gpgrt_cookie_io_functions_t functions);
int _gpgrt_fclose (gpgrt_stream_t stream);
int _gpgrt_fclose_snatch (gpgrt_stream_t stream,
void **r_buffer, size_t *r_buflen);
int _gpgrt_onclose (gpgrt_stream_t stream, int mode,
void (*fnc) (gpgrt_stream_t, void*), void *fnc_value);
int _gpgrt_fileno (gpgrt_stream_t stream);
int _gpgrt_fileno_unlocked (gpgrt_stream_t stream);
int _gpgrt_syshd (gpgrt_stream_t stream, gpgrt_syshd_t *syshd);
int _gpgrt_syshd_unlocked (gpgrt_stream_t stream, gpgrt_syshd_t *syshd);
void _gpgrt__set_std_fd (int no, int fd);
gpgrt_stream_t _gpgrt__get_std_stream (int fd);
/* The es_stderr et al macros are pretty common so that we want to use
* them too. This requires that we redefine them. */
#undef es_stdin
#define es_stdin _gpgrt__get_std_stream (0)
#undef es_stdout
#define es_stdout _gpgrt__get_std_stream (1)
#undef es_stderr
#define es_stderr _gpgrt__get_std_stream (2)
void _gpgrt_flockfile (gpgrt_stream_t stream);
int _gpgrt_ftrylockfile (gpgrt_stream_t stream);
void _gpgrt_funlockfile (gpgrt_stream_t stream);
int _gpgrt_feof (gpgrt_stream_t stream);
int _gpgrt_feof_unlocked (gpgrt_stream_t stream);
int _gpgrt_ferror (gpgrt_stream_t stream);
int _gpgrt_ferror_unlocked (gpgrt_stream_t stream);
void _gpgrt_clearerr (gpgrt_stream_t stream);
void _gpgrt_clearerr_unlocked (gpgrt_stream_t stream);
int _gpgrt__pending (gpgrt_stream_t stream);
int _gpgrt__pending_unlocked (gpgrt_stream_t stream);
int _gpgrt_fflush (gpgrt_stream_t stream);
int _gpgrt_fseek (gpgrt_stream_t stream, long int offset, int whence);
int _gpgrt_fseeko (gpgrt_stream_t stream, gpgrt_off_t offset, int whence);
long int _gpgrt_ftell (gpgrt_stream_t stream);
gpgrt_off_t _gpgrt_ftello (gpgrt_stream_t stream);
void _gpgrt_rewind (gpgrt_stream_t stream);
int _gpgrt_ftruncate (estream_t stream, gpgrt_off_t length);
int _gpgrt_fgetc (gpgrt_stream_t stream);
int _gpgrt_fputc (int c, gpgrt_stream_t stream);
int _gpgrt__getc_underflow (gpgrt_stream_t stream);
int _gpgrt__putc_overflow (int c, gpgrt_stream_t stream);
/* Note: Keeps the next two macros in sync
with their counterparts in gpg-error.h. */
#define _gpgrt_getc_unlocked(stream) \
(((!(stream)->flags.writing) \
&& ((stream)->data_offset < (stream)->data_len) \
&& (! (stream)->unread_data_len)) \
? ((int) (stream)->buffer[((stream)->data_offset)++]) \
: _gpgrt__getc_underflow ((stream)))
#define _gpgrt_putc_unlocked(c, stream) \
(((stream)->flags.writing \
&& ((stream)->data_offset < (stream)->buffer_size) \
&& (c != '\n')) \
? ((int) ((stream)->buffer[((stream)->data_offset)++] = (c))) \
: _gpgrt__putc_overflow ((c), (stream)))
int _gpgrt_ungetc (int c, gpgrt_stream_t stream);
int _gpgrt_read (gpgrt_stream_t _GPGRT__RESTRICT stream,
void *_GPGRT__RESTRICT buffer, size_t bytes_to_read,
size_t *_GPGRT__RESTRICT bytes_read);
int _gpgrt_write (gpgrt_stream_t _GPGRT__RESTRICT stream,
const void *_GPGRT__RESTRICT buffer, size_t bytes_to_write,
size_t *_GPGRT__RESTRICT bytes_written);
int _gpgrt_write_sanitized (gpgrt_stream_t _GPGRT__RESTRICT stream,
const void *_GPGRT__RESTRICT buffer, size_t length,
const char *delimiters,
size_t *_GPGRT__RESTRICT bytes_written);
int _gpgrt_write_hexstring (gpgrt_stream_t _GPGRT__RESTRICT stream,
const void *_GPGRT__RESTRICT buffer, size_t length,
int reserved,
size_t *_GPGRT__RESTRICT bytes_written);
size_t _gpgrt_fread (void *_GPGRT__RESTRICT ptr, size_t size, size_t nitems,
gpgrt_stream_t _GPGRT__RESTRICT stream);
size_t _gpgrt_fwrite (const void *_GPGRT__RESTRICT ptr,
size_t size, size_t memb,
gpgrt_stream_t _GPGRT__RESTRICT stream);
char *_gpgrt_fgets (char *_GPGRT__RESTRICT s, int n,
gpgrt_stream_t _GPGRT__RESTRICT stream);
int _gpgrt_fputs (const char *_GPGRT__RESTRICT s,
gpgrt_stream_t _GPGRT__RESTRICT stream);
int _gpgrt_fputs_unlocked (const char *_GPGRT__RESTRICT s,
gpgrt_stream_t _GPGRT__RESTRICT stream);
gpgrt_ssize_t _gpgrt_getline (char *_GPGRT__RESTRICT *_GPGRT__RESTRICT lineptr,
size_t *_GPGRT__RESTRICT n,
gpgrt_stream_t stream);
gpgrt_ssize_t _gpgrt_read_line (gpgrt_stream_t stream,
char **addr_of_buffer, size_t *length_of_buffer,
size_t *max_length);
int _gpgrt_fprintf (gpgrt_stream_t _GPGRT__RESTRICT stream,
const char *_GPGRT__RESTRICT format, ...)
GPGRT_ATTR_PRINTF(2,3);
int _gpgrt_fprintf_unlocked (gpgrt_stream_t _GPGRT__RESTRICT stream,
const char *_GPGRT__RESTRICT format, ...)
GPGRT_ATTR_PRINTF(2,3);
int _gpgrt_vfprintf (gpgrt_stream_t _GPGRT__RESTRICT stream,
gpgrt_string_filter_t sf, void *sfvalue,
const char *_GPGRT__RESTRICT format, va_list ap)
GPGRT_ATTR_PRINTF(4,0);
int _gpgrt_vfprintf_unlocked (gpgrt_stream_t _GPGRT__RESTRICT stream,
gpgrt_string_filter_t sf, void *sfvalue,
const char *_GPGRT__RESTRICT format, va_list ap)
GPGRT_ATTR_PRINTF(4,0);
int _gpgrt_setvbuf (gpgrt_stream_t _GPGRT__RESTRICT stream,
char *_GPGRT__RESTRICT buf, int mode, size_t size);
void _gpgrt_set_binary (gpgrt_stream_t stream);
int _gpgrt_set_nonblock (gpgrt_stream_t stream, int onoff);
int _gpgrt_get_nonblock (gpgrt_stream_t stream);
int _gpgrt_poll (gpgrt_poll_t *fds, unsigned int nfds, int timeout);
gpgrt_stream_t _gpgrt_tmpfile (void);
void _gpgrt_opaque_set (gpgrt_stream_t _GPGRT__RESTRICT stream,
void *_GPGRT__RESTRICT opaque);
void *_gpgrt_opaque_get (gpgrt_stream_t stream);
void _gpgrt_fname_set (gpgrt_stream_t stream, const char *fname);
const char *_gpgrt_fname_get (gpgrt_stream_t stream);
#include "estream-printf.h"
/* Make sure we always use our snprintf */
#undef snprintf
#define snprintf _gpgrt_estream_snprintf
#if HAVE_W32_SYSTEM
/* Prototypes for w32-estream.c. */
struct cookie_io_functions_s _gpgrt_functions_w32_pollable;
int _gpgrt_w32_pollable_create (void *_GPGRT__RESTRICT *_GPGRT__RESTRICT cookie,
unsigned int modeflags,
struct cookie_io_functions_s next_functions,
void *next_cookie);
int _gpgrt_w32_poll (gpgrt_poll_t *fds, size_t nfds, int timeout);
#endif /*HAVE_W32_SYSTEM*/
/*
* Local prototypes for the encoders.
*/
struct _gpgrt_b64state
{
int idx;
int quad_count;
estream_t stream;
char *title;
unsigned char radbuf[4];
unsigned int crc;
gpg_err_code_t lasterr;
unsigned int flags;
int stop_seen:1;
int invalid_encoding:1;
int using_decoder:1;
};
gpgrt_b64state_t _gpgrt_b64enc_start (estream_t stream, const char *title);
gpg_err_code_t _gpgrt_b64enc_write (gpgrt_b64state_t state,
const void *buffer, size_t nbytes);
gpg_err_code_t _gpgrt_b64enc_finish (gpgrt_b64state_t state);
gpgrt_b64state_t _gpgrt_b64dec_start (const char *title);
gpg_err_code_t _gpgrt_b64dec_proc (gpgrt_b64state_t state, void *buffer,
size_t length, size_t *r_nbytes);
gpg_err_code_t _gpgrt_b64dec_finish (gpgrt_b64state_t state);
/*
* Local prototypes for logging
*/
int _gpgrt_get_errorcount (int clear);
void _gpgrt_inc_errorcount (void);
void _gpgrt_log_set_sink (const char *name, estream_t stream, int fd);
void _gpgrt_log_set_socket_dir_cb (const char *(*fnc)(void));
void _gpgrt_log_set_pid_suffix_cb (int (*cb)(unsigned long *r_value));
void _gpgrt_log_set_prefix (const char *text, unsigned int flags);
const char *_gpgrt_log_get_prefix (unsigned int *flags);
int _gpgrt_log_test_fd (int fd);
int _gpgrt_log_get_fd (void);
estream_t _gpgrt_log_get_stream (void);
void _gpgrt_log (int level, const char *fmt, ...) GPGRT_ATTR_PRINTF(2,3);
void _gpgrt_logv (int level, const char *fmt, va_list arg_ptr);
void _gpgrt_logv_prefix (int level, const char *prefix,
const char *fmt, va_list arg_ptr);
void _gpgrt_log_string (int level, const char *string);
void _gpgrt_log_bug (const char *fmt, ...) GPGRT_ATTR_NR_PRINTF(1,2);
void _gpgrt_log_fatal (const char *fmt, ...) GPGRT_ATTR_NR_PRINTF(1,2);
void _gpgrt_log_error (const char *fmt, ...) GPGRT_ATTR_PRINTF(1,2);
void _gpgrt_log_info (const char *fmt, ...) GPGRT_ATTR_PRINTF(1,2);
void _gpgrt_log_debug (const char *fmt, ...) GPGRT_ATTR_PRINTF(1,2);
void _gpgrt_log_debug_string (const char *string, const char *fmt,
...) GPGRT_ATTR_PRINTF(2,3);
void _gpgrt_log_printf (const char *fmt, ...) GPGRT_ATTR_PRINTF(1,2);
void _gpgrt_log_flush (void);
void _gpgrt_logv_printhex (const void *buffer, size_t length,
const char *fmt, va_list arg_ptr);
void _gpgrt_log_printhex (const void *buffer, size_t length,
const char *fmt, ...) GPGRT_ATTR_PRINTF(3,4);;
void _gpgrt_logv_clock (const char *fmt, va_list arg_ptr);
void _gpgrt_log_clock (const char *fmt, ...) GPGRT_ATTR_PRINTF(1,2);
void _gpgrt__log_assert (const char *expr, const char *file, int line,
const char *func) GPGRT_ATTR_NORETURN;
/* Redefine the assert macro to use our internal function. */
#undef gpgrt_assert
#ifdef GPGRT_HAVE_MACRO_FUNCTION
#define gpgrt_assert(expr) \
((expr) \
? (void) 0 \
: _gpgrt__log_assert (#expr, __FILE__, __LINE__, __FUNCTION__))
#else /*!GPGRT_HAVE_MACRO_FUNCTION*/
/* # define BUG() bug_at( __FILE__ , __LINE__ ) */
#define gpgrt_assert(expr) \
((expr) \
? (void) 0 \
: _gpgrt__log_assert (#expr, __FILE__, __LINE__, NULL))
#endif /*!GPGRT_HAVE_MACRO_FUNCTION*/
/* Note: The next function is only to be used by visibility.c. */
int _gpgrt_logv_internal (int level, int ignore_arg_ptr,
const char *extrastring,
const char *prefmt, const char *fmt,
va_list arg_ptr);
/*
* Local prototypes for the spawn functions.
*
* We put the docs here because we have separate implementations in
* the files spawn-posix.c and spawn-w32.c
*/
/* Return the maximum number of currently allowed file descriptors.
* Only useful on POSIX systems. */
/* int get_max_fds (void); */
/* Close all file descriptors starting with descriptor FIRST. If
* EXCEPT is not NULL, it is expected to be a list of file descriptors
* which are not to close. This list shall be sorted in ascending
* order with its end marked by -1. */
/* void close_all_fds (int first, int *except); */
/* Returns an array with all currently open file descriptors. The end
* of the array is marked by -1. The caller needs to release this
* array using the *standard free* and not with xfree. This allow the
* use of this function right at startup even before libgcrypt has
* been initialized. Returns NULL on error and sets ERRNO accordingly. */
/* int *get_all_open_fds (void); */
/* Create a pipe. The DIRECTION parameter gives the type of the created pipe:
* DIRECTION < 0 := Inbound pipe: On Windows the write end is inheritable.
* DIRECTION > 0 := Outbound pipe: On Windows the read end is inheritable.
* If R_FP is NULL a standard pipe and no stream is created, DIRECTION
* should then be 0. */
gpg_err_code_t _gpgrt_make_pipe (int filedes[2], estream_t *r_fp,
int direction, int nonblock);
/* Convenience macros to create a pipe. */
#define _gpgrt_create_pipe(a) _gpgrt_make_pipe ((a),NULL, 0, 0);
#define _gpgrt_create_inbound_pipe(a,b,c) _gpgrt_make_pipe ((a), (b), -1, (c));
#define _gpgrt_create_outbound_pipe(a,b,c) _gpgrt_make_pipe ((a), (b), 1, (c));
/* Fork and exec the program PGMNAME.
*
* If R_INFP is NULL connect stdin of the new process to /dev/null; if
* it is not NULL store the address of a pointer to a new estream
* there. If R_OUTFP is NULL connect stdout of the new process to
* /dev/null; if it is not NULL store the address of a pointer to a
* new estream there. If R_ERRFP is NULL connect stderr of the new
* process to /dev/null; if it is not NULL store the address of a
* pointer to a new estream there. On success the pid of the new
* process is stored at PID. On error -1 is stored at PID and if
* R_OUTFP or R_ERRFP are not NULL, NULL is stored there.
*
* The arguments for the process are expected in the NULL terminated
* array ARGV. The program name itself should not be included there.
* If PREEXEC is not NULL, the given function will be called right
* before the exec.
*
* IF EXCEPT is not NULL, it is expected to be an ordered list of file
* descriptors, terminated by an entry with the value (-1). These
* file descriptors won't be closed before spawning a new program.
*
* Returns 0 on success or an error code. Calling gpgrt_wait_process
* and gpgrt_release_process is required if the function succeeded.
*
* FLAGS is a bit vector:
*
* GPGRT_SPAWN_NONBLOCK
* If set the two output streams are created in non-blocking
* mode and the input stream is switched to non-blocking mode.
* This is merely a convenience feature because the caller
* could do the same with gpgrt_set_nonblock. Does not yet
* work for Windows.
*
* GPGRT_SPAWN_DETACHED
* If set the process will be started as a background process.
* This flag is only useful under W32 (but not W32CE) systems,
* so that no new console is created and pops up a console
* window when starting the server. Does not work on W32CE.
*
* GPGRT_SPAWN_RUN_ASFW
* On W32 (but not on W32CE) run AllowSetForegroundWindow for
* the child. Note that due to unknown problems this actually
* allows SetForegroundWindow for all children of this process.
*/
gpg_err_code_t
_gpgrt_spawn_process (const char *pgmname, const char *argv[],
int *execpt, void (*preexec)(void), unsigned int flags,
estream_t *r_infp,
estream_t *r_outfp,
estream_t *r_errfp,
pid_t *pid);
/* Simplified version of gpgrt_spawn_process. This function forks and
* then execs PGMNAME, while connecting INFD to stdin, OUTFD to stdout
* and ERRFD to stderr (any of them may be -1 to connect them to
* /dev/null). The arguments for the process are expected in the NULL
* terminated array ARGV. The program name itself should not be
* included there. Calling gpgrt_wait_process and
* gpgrt_release_process is required. Returns 0 on success or an
* error code. */
gpg_err_code_t _gpgrt_spawn_process_fd (const char *pgmname,
const char *argv[],
int infd, int outfd, int errfd,
pid_t *pid);
/* Spawn a new process and immediately detach from it. The name of
* the program to exec is PGMNAME and its arguments are in ARGV (the
* programname is automatically passed as first argument).
* Environment strings in ENVP are set. An error is returned if
* pgmname is not executable; to make this work it is necessary to
* provide an absolute file name. */
gpg_err_code_t _gpgrt_spawn_process_detached (const char *pgmname,
const char *argv[],
const char *envp[] );
/* If HANG is true, waits for the process identified by PID to exit;
* if HANG is false, checks whether the process has terminated.
* PGMNAME should be the same as supplied to the spawn function and is
* only used for diagnostics. Return values:
*
* 0
* The process exited successful. 0 is stored at R_EXITCODE.
*
* GPG_ERR_GENERAL
* The process exited without success. The exit code of process
* is then stored at R_EXITCODE. An exit code of -1 indicates
* that the process terminated abnormally (e.g. due to a signal).
*
* GPG_ERR_TIMEOUT
* The process is still running (returned only if HANG is false).
*
* GPG_ERR_INV_VALUE
* An invalid PID has been specified.
*
* Other error codes may be returned as well. Unless otherwise noted,
* -1 will be stored at R_EXITCODE. R_EXITCODE may be passed as NULL
* if the exit code is not required (in that case an error message will
* be printed). Note that under Windows PID is not the process id but
* the handle of the process. */
gpg_err_code_t _gpgrt_wait_process (const char *pgmname, pid_t pid, int hang,
int *r_exitcode);
/* Like _gpgrt_wait_process, but for COUNT processes. */
gpg_err_code_t _gpgrt_wait_processes (const char **pgmnames, pid_t *pids,
size_t count, int hang, int *r_exitcodes);
/* Kill a process; that is send an appropriate signal to the process.
* gpgrt_wait_process must be called to actually remove the process
* from the system. An invalid PID is ignored. */
void _gpgrt_kill_process (pid_t pid);
/* Release the process identified by PID. This function is actually
* only required for Windows but it does not harm to always call it.
* It is a nop if PID is invalid. */
void _gpgrt_release_process (pid_t pid);
/*
* Local prototypes for argparse.
*/
int _gpgrt_argparse (estream_t fp, gpgrt_argparse_t *arg, gpgrt_opt_t *opts);
void _gpgrt_usage (int level);
const char *_gpgrt_strusage (int level);
void _gpgrt_set_strusage (const char *(*f)(int));
void _gpgrt_set_usage_outfnc (int (*fnc)(int, const char *));
void _gpgrt_set_fixed_string_mapper (const char *(*f)(const char*));
/*
* Various helper functions
*/
int _gpgrt_cmp_version (const char *a, const char *b, int level);
/*
* Internal platform abstraction functions (sysutils.c)
*/
/* Return true if FD is valid. */
int _gpgrt_fd_valid_p (int fd);
/* A getenv variant which returns a malloced copy. */
char *_gpgrt_getenv (const char *name);
/* A setenv variant which can be used for unsetenv by setting VALUE to
* NULL and OVERRIDE to true. */
gpg_err_code_t _gpgrt_setenv (const char *name,
const char *value, int overwrite);
/* A wrapper around mkdir using a string for the mode (permissions). */
gpg_err_code_t _gpgrt_mkdir (const char *name, const char *modestr);
/* A simple wrapper around chdir. */
gpg_err_code_t _gpgrt_chdir (const char *name);
/* Return the current WD as a malloced string. */
char *_gpgrt_getcwd (void);
/*
* Platform specific functions (Windows)
*/
#ifdef HAVE_W32_SYSTEM
char *_gpgrt_w32_reg_query_string (const char *root,
const char *dir,
const char *name);
#endif /*HAVE_W32_SYSTEM*/
/*
* Missing functions implemented inline.
*/
#ifndef HAVE_STPCPY
static GPG_ERR_INLINE char *
_gpgrt_stpcpy (char *a, const char *b)
{
while (*b)
*a++ = *b++;
*a = 0;
return a;
}
#define stpcpy(a,b) _gpgrt_stpcpy ((a), (b))
#endif /*!HAVE_STPCPY*/
#endif /*_GPGRT_GPGRT_INT_H*/
diff --git a/src/init.c b/src/init.c
index f104ec4..8ddf0c0 100644
--- a/src/init.c
+++ b/src/init.c
@@ -1,617 +1,692 @@
/* init.c - Initialize the GnuPG error library.
Copyright (C) 2005, 2010 g10 Code GmbH
This file is part of libgpg-error.
libgpg-error is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
as published by the Free Software Foundation; either version 2.1 of
the License, or (at your option) any later version.
libgpg-error 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 .
*/
#if HAVE_CONFIG_H
#include
#endif
#include
#include
#include
#include
#include "gpgrt-int.h"
#include "gettext.h"
#include "init.h"
#ifdef HAVE_W32CE_SYSTEM
# include "mkw32errmap.map.c" /* Generated map_w32codes () */
# ifndef TLS_OUT_OF_INDEXES
# define TLS_OUT_OF_INDEXES 0xFFFFFFFF
# endif
# ifndef __MINGW32CE__
# /* Replace the Mingw32CE provided abort function. */
# define abort() do { TerminateProcess (GetCurrentProcess(), 8); } while (0)
# endif
#endif
/* Locale directory support. */
#if HAVE_W32_SYSTEM
#include
static int tls_index = TLS_OUT_OF_INDEXES; /* Index for the TLS functions. */
static char *get_locale_dir (void);
static void drop_locale_dir (char *locale_dir);
#else /*!HAVE_W32_SYSTEM*/
#define get_locale_dir() LOCALEDIR
#define drop_locale_dir(dir)
#endif /*!HAVE_W32_SYSTEM*/
+/* The list of emergency cleanup functions; see _gpgrt_abort and
+ * _gpgrt_add_emergency_cleanup. */
+struct emergency_cleanup_item_s;
+typedef struct emergency_cleanup_item_s *emergency_cleanup_item_t;
+struct emergency_cleanup_item_s
+{
+ emergency_cleanup_item_t next;
+ void (*func) (void);
+};
+static emergency_cleanup_item_t emergency_cleanup_list;
+
+
+
+
/* The realloc function as set by gpgrt_set_alloc_func. */
static void *(*custom_realloc)(void *a, size_t n);
static void
real_init (void)
{
#ifdef ENABLE_NLS
char *locale_dir;
/* We only have to bind our locale directory to our text domain. */
locale_dir = get_locale_dir ();
if (locale_dir)
{
bindtextdomain (PACKAGE, locale_dir);
drop_locale_dir (locale_dir);
}
#endif
_gpgrt_estream_init ();
}
/* Initialize the library. This function should be run early. */
gpg_error_t
_gpg_err_init (void)
{
#ifdef HAVE_W32_SYSTEM
# ifdef DLL_EXPORT
/* We always have a constructor and thus this function is called
automatically. Due to the way the C init code of mingw works,
the constructors are called before our DllMain function is
called. The problem with that is that the TLS has not been setup
and w32-gettext.c requires TLS. To solve this we do nothing here
but call the actual init code from our DllMain. */
# else /*!DLL_EXPORT*/
/* Note that if the TLS is actually used, we can't release the TLS
as there is no way to know when a thread terminates (i.e. no
thread-specific-atexit). You are really better off to use the
DLL! */
if (tls_index == TLS_OUT_OF_INDEXES)
{
tls_index = TlsAlloc ();
if (tls_index == TLS_OUT_OF_INDEXES)
{
/* No way to continue - commit suicide. */
- abort ();
+ _gpgrt_abort ();
}
_gpg_w32__init_gettext_module ();
real_init ();
}
# endif /*!DLL_EXPORT*/
#else
real_init ();
#endif
return 0;
}
/* Deinitialize libgpg-error. This function is only used in special
circumstances. No gpg-error function should be used after this
function has been called. A value of 0 passed for MODE
deinitializes the entire libgpg-error, a value of 1 releases
resources allocated for the current thread and only that thread may
not anymore access libgpg-error after such a call. Under Windows
this function may be called from the DllMain function of a DLL
which statically links to libgpg-error. */
void
_gpg_err_deinit (int mode)
{
#if defined (HAVE_W32_SYSTEM) && !defined(DLL_EXPORT)
struct tls_space_s *tls;
tls = TlsGetValue (tls_index);
if (tls)
{
TlsSetValue (tls_index, NULL);
LocalFree (tls);
}
if (mode == 0)
{
TlsFree (tls_index);
tls_index = TLS_OUT_OF_INDEXES;
}
#else
(void)mode;
#endif
}
+/* Add the emergency cleanup function F to the list of those function.
+ * If the a function with that address has already been registered, it
+ * is not added a second time. These emergency functions are called
+ * whenever gpgrt_abort is called and at no other place. Like signal
+ * handles the emergency cleanup functions shall not call any
+ * non-trivial functions and return as soon as possible. They allow
+ * to cleanup internal states which should not go into a core dumps or
+ * similar. This is independent of any atexit functions. We don't
+ * use locks here because in an emergency case we can't use them
+ * anyway. */
+void
+_gpgrt_add_emergency_cleanup (void (*f)(void))
+{
+ emergency_cleanup_item_t item;
+
+ for (item = emergency_cleanup_list; item; item = item->next)
+ if (item->func == f)
+ return; /* Function has already been registered. */
+
+ /* We use a standard malloc here. */
+ item = malloc (sizeof *item);
+ if (item)
+ {
+ item->func = f;
+ item->next = emergency_cleanup_list;
+ emergency_cleanup_list = item;
+ }
+ else
+ _gpgrt_log_fatal ("out of core in gpgrt_add_emergency_cleanup\n");
+}
+
+
+/* Run the emergency handlers. No locks are used because we are anyway
+ * in an emergency state. We also can't release any memory. */
+static void
+run_emergency_cleanup (void)
+{
+ emergency_cleanup_item_t next;
+ void (*f)(void);
+
+ while (emergency_cleanup_list)
+ {
+ next = emergency_cleanup_list->next;
+ f = emergency_cleanup_list->func;
+ emergency_cleanup_list->func = NULL;
+ emergency_cleanup_list = next;
+ if (f)
+ f ();
+ }
+}
+
+
+/* Wrapper around abort to be able to run all emergency cleanup
+ * functions. */
+void
+_gpgrt_abort (void)
+{
+ run_emergency_cleanup ();
+ abort ();
+}
+
/* Register F as allocation function. This function is used for all
APIs which return an allocated buffer. F needs to have standard
realloc semantics. It should be called as early as possible and
not changed later. */
void
_gpgrt_set_alloc_func (void *(*f)(void *a, size_t n))
{
custom_realloc = f;
}
/* The realloc to be used for data returned by the public API. */
void *
_gpgrt_realloc (void *a, size_t n)
{
if (custom_realloc)
return custom_realloc (a, n);
if (!n)
{
free (a);
return NULL;
}
if (!a)
return malloc (n);
return realloc (a, n);
}
/* The malloc to be used for data returned by the public API. */
void *
_gpgrt_malloc (size_t n)
{
if (!n)
n++;
return _gpgrt_realloc (NULL, n);
}
void *
_gpgrt_calloc (size_t n, size_t m)
{
size_t bytes;
void *p;
bytes = n * m; /* size_t is unsigned so the behavior on overflow is
defined. */
if (m && bytes / m != n)
{
_gpg_err_set_errno (ENOMEM);
return NULL;
}
p = _gpgrt_realloc (NULL, bytes);
if (p)
memset (p, 0, bytes);
return p;
}
char *
_gpgrt_strdup (const char *string)
{
size_t len = strlen (string);
char *p;
p = _gpgrt_realloc (NULL, len + 1);
if (p)
strcpy (p, string);
return p;
}
/* Helper for _gpgrt_strconcat and gpgrt_strconcat. */
char *
_gpgrt_strconcat_core (const char *s1, va_list arg_ptr)
{
const char *argv[48];
size_t argc;
size_t needed;
char *buffer, *p;
argc = 0;
argv[argc++] = s1;
needed = strlen (s1);
while (((argv[argc] = va_arg (arg_ptr, const char *))))
{
needed += strlen (argv[argc]);
if (argc >= DIM (argv)-1)
{
_gpg_err_set_errno (EINVAL);
return NULL;
}
argc++;
}
needed++;
buffer = _gpgrt_malloc (needed);
if (buffer)
{
for (p = buffer, argc=0; argv[argc]; argc++)
p = stpcpy (p, argv[argc]);
}
return buffer;
}
char *
_gpgrt_strconcat (const char *s1, ...)
{
va_list arg_ptr;
char *result;
if (!s1)
result = _gpgrt_strdup ("");
else
{
va_start (arg_ptr, s1);
result = _gpgrt_strconcat_core (s1, arg_ptr);
va_end (arg_ptr);
}
return result;
}
/* The free to be used for data returned by the public API. */
void
_gpgrt_free (void *a)
{
_gpgrt_realloc (a, 0);
}
void
_gpg_err_set_errno (int err)
{
#ifdef HAVE_W32CE_SYSTEM
SetLastError (err);
#else /*!HAVE_W32CE_SYSTEM*/
errno = err;
#endif /*!HAVE_W32CE_SYSTEM*/
}
/* Internal tracing functions. Except for TRACE_FP we use flockfile
* and funlockfile to protect their use.
*
* Warning: Take care with the trace functions - they may not use any
* of our services, in particular not the syscall clamp mechanism for
* reasons explained in w32-stream.c:create_reader. */
static FILE *trace_fp;
static int trace_save_errno;
static int trace_with_errno;
static const char *trace_arg_module;
static const char *trace_arg_file;
static int trace_arg_line;
static int trace_missing_lf;
static int trace_prefix_done;
void
_gpgrt_internal_trace_begin (const char *module, const char *file, int line,
int with_errno)
{
int save_errno = errno;
if (!trace_fp)
{
FILE *fp;
const char *s = getenv ("GPGRT_TRACE_FILE");
if (!s || !(fp = fopen (s, "wb")))
fp = stderr;
trace_fp = fp;
}
#ifdef HAVE_FLOCKFILE
flockfile (trace_fp);
#endif
trace_save_errno = save_errno;
trace_with_errno = with_errno;
trace_arg_module = module;
trace_arg_file = file;
trace_arg_line = line;
trace_missing_lf = 0;
trace_prefix_done = 0;
}
static void
print_internal_trace_prefix (void)
{
if (!trace_prefix_done)
{
trace_prefix_done = 1;
fprintf (trace_fp, "%s:%s:%d: ",
trace_arg_module,/* npth_is_protected ()?"":"^",*/
trace_arg_file, trace_arg_line);
}
}
static void
do_internal_trace (const char *format, va_list arg_ptr)
{
print_internal_trace_prefix ();
vfprintf (trace_fp, format, arg_ptr);
if (trace_with_errno)
fprintf (trace_fp, " errno=%s", strerror (trace_save_errno));
if (*format && format[strlen(format)-1] != '\n')
fputc ('\n', trace_fp);
}
void
_gpgrt_internal_trace_printf (const char *format, ...)
{
va_list arg_ptr;
print_internal_trace_prefix ();
va_start (arg_ptr, format) ;
vfprintf (trace_fp, format, arg_ptr);
va_end (arg_ptr);
trace_missing_lf = (*format && format[strlen(format)-1] != '\n');
}
void
_gpgrt_internal_trace (const char *format, ...)
{
va_list arg_ptr;
va_start (arg_ptr, format) ;
do_internal_trace (format, arg_ptr);
va_end (arg_ptr);
}
void
_gpgrt_internal_trace_end (void)
{
int save_errno = trace_save_errno;
if (trace_missing_lf)
fputc ('\n', trace_fp);
#ifdef HAVE_FLOCKFILE
funlockfile (trace_fp);
#endif
errno = save_errno;
}
#ifdef HAVE_W32_SYSTEM
/*****************************************
******** Below is only Windows code. ****
*****************************************/
static char *
get_locale_dir (void)
{
static wchar_t moddir[MAX_PATH+5];
char *result, *p;
int nbytes;
if (!GetModuleFileNameW (NULL, moddir, MAX_PATH))
*moddir = 0;
#define SLDIR "\\share\\locale"
if (*moddir)
{
nbytes = WideCharToMultiByte (CP_UTF8, 0, moddir, -1, NULL, 0, NULL, NULL);
if (nbytes < 0)
return NULL;
result = malloc (nbytes + strlen (SLDIR) + 1);
if (result)
{
nbytes = WideCharToMultiByte (CP_UTF8, 0, moddir, -1,
result, nbytes, NULL, NULL);
if (nbytes < 0)
{
free (result);
result = NULL;
}
else
{
p = strrchr (result, '\\');
if (p)
*p = 0;
/* If we are installed below "bin" strip that part and
use the top directory instead.
Background: Under Windows we don't install GnuPG
below bin/ but in the top directory with only share/,
lib/, and etc/ below it. One of the reasons is to
keep the the length of the filenames at bay so not to
increase the limited length of the PATH envvar.
Another and more important reason, however, is that
the very first GPG versions on W32 were installed
into a flat directory structure and for best
compatibility with these versions we didn't changed
that later. For WindowsCE we can right away install
it under bin, though. The hack with detection of the
bin directory part allows us to eventually migrate to
such a directory layout under plain Windows without
the need to change libgpg-error. */
p = strrchr (result, '\\');
if (p && !strcmp (p+1, "bin"))
*p = 0;
/* Append the static part. */
strcat (result, SLDIR);
}
}
}
else /* Use the old default value. */
{
result = malloc (10 + strlen (SLDIR) + 1);
if (result)
{
strcpy (result, "c:\\gnupg");
strcat (result, SLDIR);
}
}
#undef SLDIR
return result;
}
static void
drop_locale_dir (char *locale_dir)
{
free (locale_dir);
}
/* Return the tls object. This function is guaranteed to return a
valid non-NULL object. */
struct tls_space_s *
get_tls (void)
{
struct tls_space_s *tls;
tls = TlsGetValue (tls_index);
if (!tls)
{
/* Called by a thread which existed before this DLL was loaded.
Allocate the space. */
tls = LocalAlloc (LPTR, sizeof *tls);
if (!tls)
{
/* No way to continue - commit suicide. */
- abort ();
+ _gpgrt_abort ();
}
tls->gt_use_utf8 = 0;
TlsSetValue (tls_index, tls);
}
return tls;
}
/* Return the value of the ERRNO variable. This needs to be a
function so that we can have a per-thread ERRNO. This is used only
on WindowsCE because that OS misses an errno. */
#ifdef HAVE_W32CE_SYSTEM
int
_gpg_w32ce_get_errno (void)
{
return map_w32codes ( GetLastError () );
}
#endif /*HAVE_W32CE_SYSTEM*/
/* Replacement strerror function for WindowsCE. */
#ifdef HAVE_W32CE_SYSTEM
char *
_gpg_w32ce_strerror (int err)
{
struct tls_space_s *tls = get_tls ();
wchar_t tmpbuf[STRBUFFER_SIZE];
int n;
if (err == -1)
err = _gpg_w32ce_get_errno ();
/* Note: On a German HTC Touch Pro2 device I also tried
LOCALE_USER_DEFAULT and LOCALE_SYSTEM_DEFAULT - both returned
English messages. */
if (FormatMessageW (FORMAT_MESSAGE_FROM_SYSTEM, NULL, err,
MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
tmpbuf, STRBUFFER_SIZE -1,
NULL))
{
n = WideCharToMultiByte (CP_UTF8, 0, tmpbuf, -1,
tls->strerror_buffer,
sizeof tls->strerror_buffer -1,
NULL, NULL);
}
else
n = -1;
if (n < 0)
snprintf (tls->strerror_buffer, sizeof tls->strerror_buffer -1,
"[w32err=%d]", err);
return tls->strerror_buffer;
}
#endif /*HAVE_W32CE_SYSTEM*/
/* Entry point called by the DLL loader. */
#ifdef DLL_EXPORT
int WINAPI
DllMain (HINSTANCE hinst, DWORD reason, LPVOID reserved)
{
struct tls_space_s *tls;
(void)reserved;
(void)hinst;
switch (reason)
{
case DLL_PROCESS_ATTACH:
tls_index = TlsAlloc ();
if (tls_index == TLS_OUT_OF_INDEXES)
return FALSE;
#ifndef _GPG_ERR_HAVE_CONSTRUCTOR
/* If we have not constructors (e.g. MSC) we call it here. */
_gpg_w32__init_gettext_module ();
#endif
/* fallthru. */
case DLL_THREAD_ATTACH:
tls = LocalAlloc (LPTR, sizeof *tls);
if (!tls)
return FALSE;
tls->gt_use_utf8 = 0;
TlsSetValue (tls_index, tls);
if (reason == DLL_PROCESS_ATTACH)
{
real_init ();
}
break;
case DLL_THREAD_DETACH:
tls = TlsGetValue (tls_index);
if (tls)
LocalFree (tls);
break;
case DLL_PROCESS_DETACH:
tls = TlsGetValue (tls_index);
if (tls)
LocalFree (tls);
TlsFree (tls_index);
break;
default:
break;
}
return TRUE;
}
#endif /*DLL_EXPORT*/
#endif /*HAVE_W32_SYSTEM*/
diff --git a/src/logging.c b/src/logging.c
index 01732ca..86cf7c3 100644
--- a/src/logging.c
+++ b/src/logging.c
@@ -1,1335 +1,1342 @@
/* logging.c - Useful logging functions
* Copyright (C) 1998-2001, 2003-2006, 2009-2010,
* 2017 Free Software Foundation, Inc.
* Copyright (C) 1998-1999, 2001-2006, 2008-2017 Werner Koch
*
* This file is part of Libgpg-error.
*
* Libgpg-error is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* Libgpg-error 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 .
* SPDX-License-Identifier: LGPL-2.1+
*
* This file was originally a part of GnuPG.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef HAVE_W32_SYSTEM
# ifdef HAVE_WINSOCK2_H
# include
# endif
# include
#else /*!HAVE_W32_SYSTEM*/
# include
# include
# include
# include
#endif /*!HAVE_W32_SYSTEM*/
#include
#include
-#include
/* #include */
#define _GPGRT_NEED_AFLOCAL 1
#include "gpgrt-int.h"
#ifdef HAVE_W32_SYSTEM
# ifndef S_IRWXG
# define S_IRGRP S_IRUSR
# define S_IWGRP S_IWUSR
# endif
# ifndef S_IRWXO
# define S_IROTH S_IRUSR
# define S_IWOTH S_IWUSR
# endif
#endif
#ifdef HAVE_W32CE_SYSTEM
# define isatty(a) (0)
#endif
#undef WITH_IPV6
#if defined (AF_INET6) && defined(PF_INET) \
&& defined (INET6_ADDRSTRLEN) && defined(HAVE_INET_PTON)
# define WITH_IPV6 1
#endif
#ifndef EAFNOSUPPORT
# define EAFNOSUPPORT EINVAL
#endif
#ifndef INADDR_NONE /* Slowaris is missing that. */
#define INADDR_NONE ((unsigned long)(-1))
#endif /*INADDR_NONE*/
#ifdef HAVE_W32_SYSTEM
#define sock_close(a) closesocket(a)
#else
#define sock_close(a) close(a)
#endif
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;
#ifdef HAVE_W32_SYSTEM
static int no_registry;
#endif
static int (*get_pid_suffix_cb)(unsigned long *r_value);
static const char * (*socket_dir_cb)(void);
static int running_detached;
static int force_prefixes;
static int missing_lf;
static int errorcount;
/* An object to convey data to the fmt_string_filter. */
struct fmt_string_filter_s
{
char *last_result;
};
/* Get the error count as maintained by the log fucntions. With CLEAR
* set reset the counter. */
int
_gpgrt_get_errorcount (int clear)
{
int n = errorcount;
if (clear)
errorcount = 0;
return n;
}
/* Increment the error count as maintainer by the log functions. */
void
_gpgrt_inc_errorcount (void)
{
errorcount++;
}
/* The following 3 functions are used by _gpgrt_fopencookie to write logs
to a socket. */
struct fun_cookie_s
{
int fd;
int quiet;
int want_socket;
int is_socket;
#ifdef HAVE_W32CE_SYSTEM
int use_writefile;
#endif
char name[1];
};
/* Write NBYTES of BUFFER to file descriptor FD. */
static int
writen (int fd, const void *buffer, size_t nbytes, int is_socket)
{
const char *buf = buffer;
size_t nleft = nbytes;
int nwritten;
#ifndef HAVE_W32_SYSTEM
(void)is_socket; /* Not required. */
#endif
while (nleft > 0)
{
#ifdef HAVE_W32_SYSTEM
if (is_socket)
nwritten = send (fd, buf, nleft, 0);
else
#endif
nwritten = write (fd, buf, nleft);
if (nwritten < 0 && errno == EINTR)
continue;
if (nwritten < 0)
return -1;
nleft -= nwritten;
buf = buf + nwritten;
}
return 0;
}
/* Returns true if STR represents a valid port number in decimal
notation and no garbage is following. */
static int
parse_portno (const char *str, unsigned short *r_port)
{
unsigned int value;
for (value=0; *str && (*str >= '0' && *str <= '9'); str++)
{
value = value * 10 + (*str - '0');
if (value > 65535)
return 0;
}
if (*str || !value)
return 0;
*r_port = value;
return 1;
}
static gpgrt_ssize_t
fun_writer (void *cookie_arg, const void *buffer, size_t size)
{
struct fun_cookie_s *cookie = cookie_arg;
/* FIXME: Use only estream with a callback for socket writing. This
avoids the ugly mix of fd and estream code. */
/* Note that we always try to reconnect to the socket but print
error messages only the first time an error occurred. 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)
{
#ifdef WITH_IPV6
struct sockaddr_in6 srvr_addr_in6;
#endif
struct sockaddr_in srvr_addr_in;
#ifndef HAVE_W32_SYSTEM
struct sockaddr_un srvr_addr_un;
#endif
const char *name_for_err = "";
size_t addrlen;
struct sockaddr *srvr_addr = NULL;
unsigned short port = 0;
int af = AF_LOCAL;
int pf = PF_LOCAL;
const char *name = cookie->name;
/* Not yet open or meanwhile closed due to an error. */
cookie->is_socket = 0;
/* Check whether this is a TCP socket or a local socket. */
if (!strncmp (name, "tcp://", 6) && name[6])
{
name += 6;
af = AF_INET;
pf = PF_INET;
}
#ifndef HAVE_W32_SYSTEM
else if (!strncmp (name, "socket://", 9))
name += 9;
#endif
if (af == AF_LOCAL)
{
addrlen = 0;
#ifndef HAVE_W32_SYSTEM
memset (&srvr_addr, 0, sizeof srvr_addr);
srvr_addr_un.sun_family = af;
if (!*name && (name = socket_dir_cb ()) && *name)
{
if (strlen (name) + 7 < sizeof (srvr_addr_un.sun_path)-1)
{
strncpy (srvr_addr_un.sun_path,
name, sizeof (srvr_addr_un.sun_path)-1);
strcat (srvr_addr_un.sun_path, "/S.log");
srvr_addr_un.sun_path[sizeof (srvr_addr_un.sun_path)-1] = 0;
srvr_addr = (struct sockaddr *)&srvr_addr_un;
addrlen = SUN_LEN (&srvr_addr_un);
name_for_err = srvr_addr_un.sun_path;
}
}
else
{
if (*name && strlen (name) < sizeof (srvr_addr_un.sun_path)-1)
{
strncpy (srvr_addr_un.sun_path,
name, sizeof (srvr_addr_un.sun_path)-1);
srvr_addr_un.sun_path[sizeof (srvr_addr_un.sun_path)-1] = 0;
srvr_addr = (struct sockaddr *)&srvr_addr_un;
addrlen = SUN_LEN (&srvr_addr_un);
}
}
#endif /*!HAVE_W32SYSTEM*/
}
else
{
char *addrstr, *p;
#ifdef HAVE_INET_PTON
void *addrbuf = NULL;
#endif /*HAVE_INET_PTON*/
addrstr = _gpgrt_malloc (strlen (name) + 1);
if (!addrstr)
addrlen = 0; /* This indicates an error. */
else if (*name == '[')
{
/* Check for IPv6 literal address. */
strcpy (addrstr, name+1);
p = strchr (addrstr, ']');
if (!p || p[1] != ':' || !parse_portno (p+2, &port))
{
_gpg_err_set_errno (EINVAL);
addrlen = 0;
}
else
{
*p = 0;
#ifdef WITH_IPV6
af = AF_INET6;
pf = PF_INET6;
memset (&srvr_addr_in6, 0, sizeof srvr_addr_in6);
srvr_addr_in6.sin6_family = af;
srvr_addr_in6.sin6_port = htons (port);
#ifdef HAVE_INET_PTON
addrbuf = &srvr_addr_in6.sin6_addr;
#endif /*HAVE_INET_PTON*/
srvr_addr = (struct sockaddr *)&srvr_addr_in6;
addrlen = sizeof srvr_addr_in6;
#else
_gpg_err_set_errno (EAFNOSUPPORT);
addrlen = 0;
#endif
}
}
else
{
/* Check for IPv4 literal address. */
strcpy (addrstr, name);
p = strchr (addrstr, ':');
if (!p || !parse_portno (p+1, &port))
{
_gpg_err_set_errno (EINVAL);
addrlen = 0;
}
else
{
*p = 0;
memset (&srvr_addr_in, 0, sizeof srvr_addr_in);
srvr_addr_in.sin_family = af;
srvr_addr_in.sin_port = htons (port);
#ifdef HAVE_INET_PTON
addrbuf = &srvr_addr_in.sin_addr;
#endif /*HAVE_INET_PTON*/
srvr_addr = (struct sockaddr *)&srvr_addr_in;
addrlen = sizeof srvr_addr_in;
}
}
if (addrlen)
{
#ifdef HAVE_INET_PTON
if (inet_pton (af, addrstr, addrbuf) != 1)
addrlen = 0;
#else /*!HAVE_INET_PTON*/
/* We need to use the old function. If we are here v6
support isn't enabled anyway and thus we can do fine
without. Note that Windows has a compatible inet_pton
function named inetPton, but only since Vista. */
srvr_addr_in.sin_addr.s_addr = inet_addr (addrstr);
if (srvr_addr_in.sin_addr.s_addr == INADDR_NONE)
addrlen = 0;
#endif /*!HAVE_INET_PTON*/
}
_gpgrt_free (addrstr);
}
cookie->fd = addrlen? socket (pf, SOCK_STREAM, 0) : -1;
if (cookie->fd == -1)
{
if (!cookie->quiet && !running_detached
&& isatty (_gpgrt_fileno (es_stderr)))
_gpgrt_fprintf (es_stderr,
"failed to create socket for logging: %s\n",
strerror (errno));
}
else
{
if (connect (cookie->fd, srvr_addr, addrlen) == -1)
{
if (!cookie->quiet && !running_detached
&& isatty (_gpgrt_fileno (es_stderr)))
_gpgrt_fprintf (es_stderr, "can't connect to '%s%s': %s\n",
cookie->name, name_for_err, strerror(errno));
sock_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)
{
#ifdef HAVE_W32CE_SYSTEM
if (cookie->use_writefile)
{
DWORD nwritten;
WriteFile ((HANDLE)cookie->fd, buffer, size, &nwritten, NULL);
return (gpgrt_ssize_t)size; /* Okay. */
}
#endif
if (!writen (cookie->fd, buffer, size, cookie->is_socket))
return (gpgrt_ssize_t)size; /* Okay. */
}
if (!running_detached && cookie->fd != -1
&& isatty (_gpgrt_fileno (es_stderr)))
{
if (*cookie->name)
_gpgrt_fprintf (es_stderr, "error writing to '%s': %s\n",
cookie->name, strerror(errno));
else
_gpgrt_fprintf (es_stderr, "error writing to file descriptor %d: %s\n",
cookie->fd, strerror(errno));
}
if (cookie->is_socket && cookie->fd != -1)
{
sock_close (cookie->fd);
cookie->fd = -1;
log_socket = -1;
}
return (gpgrt_ssize_t)size;
}
static int
fun_closer (void *cookie_arg)
{
struct fun_cookie_s *cookie = cookie_arg;
if (cookie->fd != -1 && cookie->fd != 2)
sock_close (cookie->fd);
_gpgrt_free (cookie);
log_socket = -1;
return 0;
}
/* Common function to either set the logging to a file or a file
descriptor. */
static void
set_file_fd (const char *name, int fd, estream_t stream)
{
estream_t fp;
int want_socket = 0;
#ifdef HAVE_W32CE_SYSTEM
int use_writefile = 0;
#endif
struct fun_cookie_s *cookie;
/* Close an open log stream. */
if (logstream)
{
if (logstream != es_stderr)
_gpgrt_fclose (logstream);
logstream = NULL;
}
if (stream)
{
/* We don't use a cookie to log directly to a stream. */
fp = stream;
goto leave;
}
/* Figure out what kind of logging we want. */
if (name && !strcmp (name, "-"))
{
name = NULL;
fd = _gpgrt_fileno (es_stderr);
}
if (name && !strncmp (name, "tcp://", 6) && name[6])
want_socket = 1;
#ifndef HAVE_W32_SYSTEM
else if (name && !strncmp (name, "socket://", 9))
want_socket = 2;
#endif /*HAVE_W32_SYSTEM*/
#ifdef HAVE_W32CE_SYSTEM
else if (name && !strcmp (name, "GPG2:"))
{
HANDLE hd;
ActivateDevice (L"Drivers\\"GNUPG_NAME"_Log", 0);
/* Ignore a filename and write the debug output to the GPG2:
device. */
hd = CreateFile (L"GPG2:", GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
fd = (hd == INVALID_HANDLE_VALUE)? -1 : (int)hd;
name = NULL;
force_prefixes = 1;
use_writefile = 1;
}
#endif /*HAVE_W32CE_SYSTEM*/
/* Setup a new stream. */
cookie = _gpgrt_malloc (sizeof *cookie + (name? strlen (name):0));
if (!cookie)
return; /* oops */
strcpy (cookie->name, name? name:"");
cookie->quiet = 0;
cookie->is_socket = 0;
cookie->want_socket = want_socket;
#ifdef HAVE_W32CE_SYSTEM
cookie->use_writefile = use_writefile;
#endif
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;
{
es_cookie_io_functions_t io = { NULL };
io.func_write = fun_writer;
io.func_close = fun_closer;
fp = _gpgrt_fopencookie (cookie, "w", io);
}
/* On error default to a stderr based estream. */
if (!fp)
fp = es_stderr;
leave:
_gpgrt_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. Calling this
* function with (NULL, NULL, -1) sets the default sink.
* Warning: This function is not thread-safe.
*/
void
_gpgrt_log_set_sink (const char *name, estream_t stream, int fd)
{
if (name && !stream && fd == -1)
set_file_fd (name, -1, NULL);
else if (!name && !stream && fd != -1)
{
if (!_gpgrt_fd_valid_p (fd))
_gpgrt_log_fatal ("gpgrt_log_set_sink: fd is invalid: %s\n",
strerror (errno));
set_file_fd (NULL, fd, NULL);
}
else if (!name && stream && fd == -1)
{
set_file_fd (NULL, -1, stream);
}
else /* default */
set_file_fd ("-", -1, NULL);
}
/* Set a function to retrieve the directory name of a socket if
* only "socket://" has been given to log_set_file.
* Warning: This function is not thread-safe. */
void
_gpgrt_log_set_socket_dir_cb (const char *(*fnc)(void))
{
socket_dir_cb = fnc;
}
/* Warning: This function is not thread-safe. */
void
_gpgrt_log_set_pid_suffix_cb (int (*cb)(unsigned long *r_value))
{
get_pid_suffix_cb = cb;
}
/* Warning: Changing TEXT is not thread-safe. Changing only flags
* might be thread-safe. */
void
_gpgrt_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 & GPGRT_LOG_WITH_PREFIX);
with_time = (flags & GPGRT_LOG_WITH_TIME);
with_pid = (flags & GPGRT_LOG_WITH_PID);
running_detached = (flags & GPGRT_LOG_RUN_DETACHED);
#ifdef HAVE_W32_SYSTEM
no_registry = (flags & GPGRT_LOG_NO_REGISTRY);
#endif
}
const char *
_gpgrt_log_get_prefix (unsigned int *flags)
{
if (flags)
{
*flags = 0;
if (with_prefix)
*flags |= GPGRT_LOG_WITH_PREFIX;
if (with_time)
*flags |= GPGRT_LOG_WITH_TIME;
if (with_pid)
*flags |= GPGRT_LOG_WITH_PID;
if (running_detached)
*flags |= GPGRT_LOG_RUN_DETACHED;
#ifdef HAVE_W32_SYSTEM
if (no_registry)
*flags |= GPGRT_LOG_NO_REGISTRY;
#endif
}
return prefix_buffer;
}
/* This function returns true if the file descriptor FD is in use for
* logging. This is preferable over a test using log_get_fd in that
* it allows the logging code to use more then one file descriptor. */
int
_gpgrt_log_test_fd (int fd)
{
if (logstream)
{
int tmp = _gpgrt_fileno (logstream);
if ( tmp != -1 && tmp == fd)
return 1;
}
if (log_socket != -1 && log_socket == fd)
return 1;
return 0;
}
int
_gpgrt_log_get_fd ()
{
return logstream? _gpgrt_fileno (logstream) : -1;
}
estream_t
_gpgrt_log_get_stream ()
{
if (!logstream)
{
/* Make sure a log stream has been set. */
_gpgrt_log_set_sink (NULL, NULL, -1);
- assert (logstream);
+ if (!logstream)
+ {
+ fputs ("gpgrt fatal: failed to init log stream\n", stderr);
+ _gpgrt_abort ();
+ }
}
return logstream;
}
/* A fiter used with the fprintf_sf function to sanitize the args for
* "%s" format specifiers. */
static char *
fmt_string_filter (const char *string, int no, void *opaque)
{
struct fmt_string_filter_s *state = opaque;
const unsigned char *p;
size_t buflen;
char *d;
int any;
if (no == -1)
{
/* The printf engine asked us to release resources. */
if (state->last_result)
{
_gpgrt_free (state->last_result);
state->last_result = NULL;
}
return NULL;
}
if (!string)
return NULL; /* Nothing to filter - printf handles NULL nicely. */
/* Check whether escaping is needed and count needed length. */
any = 0;
buflen = 1;
for (p = (const unsigned char *)string; *p; p++)
{
switch (*p)
{
case '\n':
case '\r':
case '\f':
case '\v':
case '\b':
case '\t':
case '\a':
case '\\':
buflen += 2;
any = 1;
break;
default:
if (*p < 0x20 || *p == 0x7f)
{
buflen += 5;
any = 1;
}
else
buflen++;
}
}
if (!any)
return (char*)string; /* Nothing to escape. */
/* Create a buffer and escape the input. */
_gpgrt_free (state->last_result);
state->last_result = _gpgrt_malloc (buflen);
if (!state->last_result)
return "[out_of_core_in_format_string_filter]";
d = state->last_result;
for (p = (const unsigned char *)string; *p; p++)
{
switch (*p)
{
case '\n': *d++ = '\\'; *d++ = 'n'; break;
case '\r': *d++ = '\\'; *d++ = 'r'; break;
case '\f': *d++ = '\\'; *d++ = 'f'; break;
case '\v': *d++ = '\\'; *d++ = 'v'; break;
case '\b': *d++ = '\\'; *d++ = 'b'; break;
case '\t': *d++ = '\\'; *d++ = 't'; break;
case '\a': *d++ = '\\'; *d++ = 'a'; break;
case '\\': *d++ = '\\'; *d++ = '\\'; break;
default:
if (*p < 0x20 || *p == 0x7f)
{
snprintf (d, 5, "\\x%02x", *p);
d += 4;
}
else
*d++ = *p;
}
}
*d = 0;
return state->last_result;
}
/* Note: LOGSTREAM is expected to be locked. */
static int
print_prefix (int level, int leading_backspace)
{
int rc;
int length = 0;
if (level != GPGRT_LOGLVL_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);
rc = _gpgrt_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 (rc > 0)
length += rc;
}
if (with_prefix || force_prefixes)
{
_gpgrt_fputs_unlocked (prefix_buffer, logstream);
length += strlen (prefix_buffer);
}
if (with_pid || force_prefixes)
{
unsigned long pidsuf;
int pidfmt;
if (get_pid_suffix_cb && (pidfmt=get_pid_suffix_cb (&pidsuf)))
rc = _gpgrt_fprintf_unlocked (logstream,
pidfmt == 1? "[%u.%lu]":"[%u.%lx]",
(unsigned int)getpid (), pidsuf);
else
rc = _gpgrt_fprintf_unlocked (logstream, "[%u]",
(unsigned int)getpid ());
if (rc > 0)
length += rc;
}
if ((!with_time && (with_prefix || with_pid)) || force_prefixes)
{
_gpgrt_putc_unlocked (':', logstream);
length++;
}
/* A leading backspace suppresses the extra space so that we can
correctly output, programname, filename and linenumber. */
if (!leading_backspace
&& (with_time || with_prefix || with_pid || force_prefixes))
{
_gpgrt_putc_unlocked (' ', logstream);
length++;
}
}
switch (level)
{
case GPGRT_LOGLVL_BEGIN: break;
case GPGRT_LOGLVL_CONT: break;
case GPGRT_LOGLVL_INFO: break;
case GPGRT_LOGLVL_WARN: break;
case GPGRT_LOGLVL_ERROR: break;
case GPGRT_LOGLVL_FATAL:
_gpgrt_fputs_unlocked ("Fatal: ", logstream);
length += 7;
break;
case GPGRT_LOGLVL_BUG:
_gpgrt_fputs_unlocked ("Ohhhh jeeee: ", logstream);
length += 13;
break;
case GPGRT_LOGLVL_DEBUG:
_gpgrt_fputs_unlocked ("DBG: ", logstream);
length += 5;
break;
default:
rc = _gpgrt_fprintf_unlocked (logstream,
"[Unknown log level %d]: ", level);
if (rc > 0)
length += rc;
break;
}
return length;
}
/* Internal worker function. Exported so that we can use it in
* visibility.c. Returs the number of characters printed or 0 if the
* line ends in a LF. */
int
_gpgrt_logv_internal (int level, int ignore_arg_ptr, const char *extrastring,
const char *prefmt, const char *fmt, va_list arg_ptr)
{
int leading_backspace = (fmt && *fmt == '\b');
int length;
int rc;
if (!logstream)
{
#ifdef HAVE_W32_SYSTEM
char *tmp;
tmp = (no_registry
? NULL
: _gpgrt_w32_reg_query_string (NULL, "Software\\\\GNU\\\\GnuPG",
"DefaultLogFile"));
_gpgrt_log_set_sink (tmp && *tmp? tmp : NULL, NULL, -1);
_gpgrt_free (tmp);
#else
/* Make sure a log stream has been set. */
_gpgrt_log_set_sink (NULL, NULL, -1);
#endif
- assert (logstream);
+ if (!logstream)
+ {
+ fputs ("gpgrt fatal: failed to init log stream\n", stderr);
+ _gpgrt_abort ();
+ }
}
_gpgrt_flockfile (logstream);
if (missing_lf && level != GPGRT_LOGLVL_CONT)
_gpgrt_putc_unlocked ('\n', logstream );
missing_lf = 0;
length = print_prefix (level, leading_backspace);
if (leading_backspace)
fmt++;
if (fmt)
{
if (prefmt)
{
_gpgrt_fputs_unlocked (prefmt, logstream);
length += strlen (prefmt);
}
if (ignore_arg_ptr)
{ /* This is used by log_string and comes with the extra
* feature that after a LF the next line is indent at the
* length of the prefix. Note that we do not yet include
* the length of the timestamp and pid in the indent
* computation. */
const char *p, *pend;
for (p = fmt; (pend = strchr (p, '\n')); p = pend+1)
{
rc = _gpgrt_fprintf_unlocked (logstream, "%*s%.*s",
(int)((p != fmt
&& (with_prefix || force_prefixes))
?strlen (prefix_buffer)+2:0), "",
(int)(pend - p)+1, p);
if (rc > 0)
length += rc;
}
_gpgrt_fputs_unlocked (p, logstream);
length += strlen (p);
}
else
{
struct fmt_string_filter_s sf = {NULL};
rc = _gpgrt_vfprintf_unlocked (logstream, fmt_string_filter, &sf,
fmt, arg_ptr);
if (rc > 0)
length += rc;
}
if (*fmt && fmt[strlen(fmt)-1] != '\n')
missing_lf = 1;
}
/* If we have an EXTRASTRING print it now while we still hold the
* lock on the logstream. */
if (extrastring)
{
int c;
if (missing_lf)
{
_gpgrt_putc_unlocked ('\n', logstream);
missing_lf = 0;
length = 0;
}
length += print_prefix (level, leading_backspace);
_gpgrt_fputs_unlocked (">> ", logstream);
length += 3;
missing_lf = 1;
while ((c = *extrastring++))
{
missing_lf = 1;
if (c == '\\')
{
_gpgrt_fputs_unlocked ("\\\\", logstream);
length += 2;
}
else if (c == '\r')
{
_gpgrt_fputs_unlocked ("\\r", logstream);
length += 2;
}
else if (c == '\n')
{
_gpgrt_fputs_unlocked ("\\n\n", logstream);
length = 0;
if (*extrastring)
{
length += print_prefix (level, leading_backspace);
_gpgrt_fputs_unlocked (">> ", logstream);
length += 3;
}
else
missing_lf = 0;
}
else
{
_gpgrt_putc_unlocked (c, logstream);
length++;
}
}
if (missing_lf)
{
_gpgrt_putc_unlocked ('\n', logstream);
length = 0;
missing_lf = 0;
}
}
if (level == GPGRT_LOGLVL_FATAL)
{
if (missing_lf)
_gpgrt_putc_unlocked ('\n', logstream);
_gpgrt_funlockfile (logstream);
exit (2);
}
else if (level == GPGRT_LOGLVL_BUG)
{
if (missing_lf)
_gpgrt_putc_unlocked ('\n', logstream );
_gpgrt_funlockfile (logstream);
/* Using backtrace requires a configure test and to pass
* -rdynamic to gcc. Thus we do not enable it now. */
/* { */
/* void *btbuf[20]; */
/* int btidx, btlen; */
/* char **btstr; */
/* btlen = backtrace (btbuf, DIM (btbuf)); */
/* btstr = backtrace_symbols (btbuf, btlen); */
/* if (btstr) */
/* for (btidx=0; btidx < btlen; btidx++) */
/* log_debug ("[%d] %s\n", btidx, btstr[btidx]); */
/* } */
- abort ();
+ _gpgrt_abort ();
}
else
_gpgrt_funlockfile (logstream);
/* Bumb the error counter for log_error. */
if (level == GPGRT_LOGLVL_ERROR)
{
/* Protect against counter overflow. */
if (errorcount < 30000)
errorcount++;
}
return length;
}
void
_gpgrt_log (int level, const char *fmt, ...)
{
va_list arg_ptr ;
va_start (arg_ptr, fmt) ;
_gpgrt_logv_internal (level, 0, NULL, NULL, fmt, arg_ptr);
va_end (arg_ptr);
}
void
_gpgrt_logv (int level, const char *fmt, va_list arg_ptr)
{
_gpgrt_logv_internal (level, 0, NULL, NULL, fmt, arg_ptr);
}
/* Same as log_logv but PREFIX is printed immediately before FMT.
* Note that PREFIX is an additional string and independent of the
* prefix set by gpgrt_log_set_prefix. */
void
_gpgrt_logv_prefix (int level, const char *prefix,
const char *fmt, va_list arg_ptr)
{
_gpgrt_logv_internal (level, 0, NULL, prefix, fmt, arg_ptr);
}
static void
do_log_ignore_arg (int level, const char *str, ...)
{
va_list arg_ptr;
va_start (arg_ptr, str);
_gpgrt_logv_internal (level, 1, NULL, NULL, str, arg_ptr);
va_end (arg_ptr);
}
/* Log STRING at LEVEL but indent from the second line on by the
* length of the prefix. */
void
_gpgrt_log_string (int level, const char *string)
{
/* We need a dummy arg_ptr, but there is no portable way to create
* one. So we call the _gpgrt_logv_internal function through a
* variadic wrapper. */
do_log_ignore_arg (level, string);
}
void
_gpgrt_log_info (const char *fmt, ...)
{
va_list arg_ptr ;
va_start (arg_ptr, fmt);
_gpgrt_logv_internal (GPGRT_LOGLVL_INFO, 0, NULL, NULL, fmt, arg_ptr);
va_end (arg_ptr);
}
void
_gpgrt_log_error (const char *fmt, ...)
{
va_list arg_ptr ;
va_start (arg_ptr, fmt);
_gpgrt_logv_internal (GPGRT_LOGLVL_ERROR, 0, NULL, NULL, fmt, arg_ptr);
va_end (arg_ptr);
}
void
_gpgrt_log_fatal (const char *fmt, ...)
{
va_list arg_ptr ;
va_start (arg_ptr, fmt);
_gpgrt_logv_internal (GPGRT_LOGLVL_FATAL, 0, NULL, NULL, fmt, arg_ptr);
va_end (arg_ptr);
- abort (); /* Never called; just to make the compiler happy. */
+ _gpgrt_abort (); /* Never called; just to make the compiler happy. */
}
void
_gpgrt_log_bug (const char *fmt, ...)
{
va_list arg_ptr ;
va_start (arg_ptr, fmt);
_gpgrt_logv_internal (GPGRT_LOGLVL_BUG, 0, NULL, NULL, fmt, arg_ptr);
va_end (arg_ptr);
- abort (); /* Never called; just to make the compiler happy. */
+ _gpgrt_abort (); /* Never called; just to make the compiler happy. */
}
void
_gpgrt_log_debug (const char *fmt, ...)
{
va_list arg_ptr;
va_start (arg_ptr, fmt);
_gpgrt_logv_internal (GPGRT_LOGLVL_DEBUG, 0, NULL, NULL, fmt, arg_ptr);
va_end (arg_ptr);
}
/* The same as log_debug but at the end of the output STRING is
* printed with LFs expanded to include the prefix and a final --end--
* marker. */
void
_gpgrt_log_debug_string (const char *string, const char *fmt, ...)
{
va_list arg_ptr;
va_start (arg_ptr, fmt);
_gpgrt_logv_internal (GPGRT_LOGLVL_DEBUG, 0, string, NULL, fmt, arg_ptr);
va_end (arg_ptr);
}
void
_gpgrt_log_printf (const char *fmt, ...)
{
va_list arg_ptr;
va_start (arg_ptr, fmt);
_gpgrt_logv_internal (fmt ? GPGRT_LOGLVL_CONT : GPGRT_LOGLVL_BEGIN,
0, NULL, NULL, fmt, arg_ptr);
va_end (arg_ptr);
}
/* Flush the log - this is useful to make sure that the trailing
linefeed has been printed. */
void
_gpgrt_log_flush (void)
{
do_log_ignore_arg (GPGRT_LOGLVL_CONT, NULL);
}
/* Print a hexdump of (BUFFER,LENGTH). With FMT passed as NULL print
* just the raw dump (in this case ARG_PTR is not used), with FMT
* being an empty string, print a trailing linefeed, otherwise print
* an entire debug line with the expanded FMT followed by a possible
* wrapped hexdump and a final LF. */
void
_gpgrt_logv_printhex (const void *buffer, size_t length,
const char *fmt, va_list arg_ptr)
{
int wrap = 0;
int cnt = 0;
const unsigned char *p;
/* FIXME: This printing is not yet protected by _gpgrt_flockfile. */
if (fmt && *fmt)
{
_gpgrt_logv_internal (GPGRT_LOGLVL_DEBUG, 0, NULL, NULL, fmt, arg_ptr);
wrap = 1;
}
if (length)
{
if (wrap)
_gpgrt_log_printf (" ");
for (p = buffer; length--; p++)
{
_gpgrt_log_printf ("%02x", *p);
if (wrap && ++cnt == 32 && length)
{
cnt = 0;
/* (we indicate continuations with a backslash) */
_gpgrt_log_printf (" \\\n");
_gpgrt_log_debug ("%s", "");
if (fmt && *fmt)
_gpgrt_log_printf (" ");
}
}
}
if (fmt)
_gpgrt_log_printf ("\n");
}
/* Print a hexdump of (BUFFER,LENGTH). With FMT passed as NULL print
* just the raw dump, with FMT being an empty string, print a trailing
* linefeed, otherwise print an entire debug line with the expanded
* FMT followed by the hexdump and a final LF. */
void
_gpgrt_log_printhex (const void *buffer, size_t length,
const char *fmt, ...)
{
va_list arg_ptr;
if (fmt)
{
va_start (arg_ptr, fmt);
_gpgrt_logv_printhex (buffer, length, fmt, arg_ptr);
va_end (arg_ptr);
}
else
{
/* va_list is not necessary a pointer and thus we can't use NULL
* because that would conflict with platforms using a straight
* struct for it (e.g. arm64). We use a dummy variable instead;
* the static is a simple way zero it out so to not get
* complains about uninitialized use. */
static va_list dummy_argptr;
_gpgrt_logv_printhex (buffer, length, NULL, dummy_argptr);
}
}
/* Print a microsecond timestamp followed by FMT. */
void
_gpgrt_logv_clock (const char *fmt, va_list arg_ptr)
{
#if ENABLE_LOG_CLOCK
static unsigned long long initial;
struct timespec tv;
unsigned long long now;
char clockbuf[50];
if (clock_gettime (CLOCK_REALTIME, &tv))
{
_gpgrt_log_debug ("error getting the realtime clock value\n");
return;
}
now = tv.tv_sec * 1000000000ull;
now += tv.tv_nsec;
if (!initial)
initial = now;
snprintf (clockbuf, sizeof clockbuf, "[%6llu] ", (now - initial)/1000);
_gpgrt_logv_internal (GPGRT_LOGLVL_DEBUG, 0, NULL, clockbuf, fmt, arg_ptr);
#else /*!ENABLE_LOG_CLOCK*/
/* You may need to link with -ltr to use the above code. */
_gpgrt_logv_internal (GPGRT_LOGLVL_DEBUG,
0, NULL, "[no clock] ", fmt, arg_ptr);
#endif /*!ENABLE_LOG_CLOCK*/
}
/* Print a microsecond timestamp followed by FMT. */
void
_gpgrt_log_clock (const char *fmt, ...)
{
va_list arg_ptr;
va_start (arg_ptr, fmt);
_gpgrt_logv_clock (fmt, arg_ptr);
va_end (arg_ptr);
}
void
_gpgrt__log_assert (const char *expr, const char *file,
int line, const char *func)
{
#ifdef GPGRT_HAVE_MACRO_FUNCTION
_gpgrt_log (GPGRT_LOGLVL_BUG, "Assertion \"%s\" in %s failed (%s:%d)\n",
expr, func, file, line);
#else /*!GPGRT_HAVE_MACRO_FUNCTION*/
_gpgrt_log (GPGRT_LOGLVL_BUG, "Assertion \"%s\" failed (%s:%d)\n",
expr, file, line);
#endif /*!GPGRT_HAVE_MACRO_FUNCTION*/
- abort (); /* Never called; just to make the compiler happy. */
+ _gpgrt_abort (); /* Never called; just to make the compiler happy. */
}
diff --git a/src/posix-lock.c b/src/posix-lock.c
index b5e6916..be4cc27 100644
--- a/src/posix-lock.c
+++ b/src/posix-lock.c
@@ -1,263 +1,263 @@
/* posix-lock.c - GPGRT lock functions for POSIX systems
Copyright (C) 2005-2009 Free Software Foundation, Inc.
Copyright (C) 2014 g10 Code GmbH
This file is part of libgpg-error.
libgpg-error is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
as published by the Free Software Foundation; either version 2.1 of
the License, or (at your option) any later version.
libgpg-error 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 .
Parts of the code, in particular use_pthreads_p, are based on code
from gettext, written by Bruno Haible , 2005.
*/
#if HAVE_CONFIG_H
#include
#endif
#ifdef HAVE_W32_SYSTEM
# error This module may not be build for Windows.
#endif
#include
#include
#include
#include
-#include
#if USE_POSIX_THREADS
# include
#endif
#include "gpgrt-int.h"
#include "lock.h"
#include "posix-lock-obj.h"
#if USE_POSIX_THREADS
# if USE_POSIX_THREADS_WEAK
/* On ELF systems it is easy to use pthreads using weak
references. Take care not to test the address of a weak
referenced function we actually use; some GCC versions have a
bug were &foo != NULL is always evaluated to true in PIC mode. */
# pragma weak pthread_cancel
# pragma weak pthread_mutex_init
# pragma weak pthread_mutex_lock
# pragma weak pthread_mutex_trylock
# pragma weak pthread_mutex_unlock
# pragma weak pthread_mutex_destroy
# if ! PTHREAD_IN_USE_DETECTION_HARD
# define use_pthread_p() (!!pthread_cancel)
# endif
# else /*!USE_POSIX_THREADS_WEAK*/
# if ! PTHREAD_IN_USE_DETECTION_HARD
# define use_pthread_p() (1)
# endif
# endif /*!USE_POSIX_THREADS_WEAK*/
# if PTHREAD_IN_USE_DETECTION_HARD
/* The function to be executed by a dummy thread. */
static void *
dummy_thread_func (void *arg)
{
return arg;
}
static int
use_pthread_p (void)
{
static int tested;
static int result; /* 1: linked with -lpthread, 0: only with libc */
if (!tested)
{
pthread_t thread;
if (pthread_create (&thread, NULL, dummy_thread_func, NULL))
result = 0; /* Thread creation failed. */
else
{
/* Thread creation works. */
void *retval;
if (pthread_join (thread, &retval) != 0)
{
- assert (!"pthread_join");
- abort ();
+ fputs ("gpgrt fatal: pthread_join in use_pthread_p failed\n",
+ stderr);
+ _gpgrt_abort ();
}
result = 1;
}
tested = 1;
}
return result;
}
#endif /*PTHREAD_IN_USE_DETECTION_HARD*/
#endif /*USE_POSIX_THREADS*/
static _gpgrt_lock_t *
get_lock_object (gpgrt_lock_t *lockhd)
{
_gpgrt_lock_t *lock = (_gpgrt_lock_t*)lockhd;
if (lock->vers != LOCK_ABI_VERSION)
{
- assert (!"lock ABI version");
- abort ();
+ fputs ("gpgrt fatal: lock ABI version mismatch\n", stderr);
+ _gpgrt_abort ();
}
if (sizeof (gpgrt_lock_t) < sizeof (_gpgrt_lock_t))
{
- assert (!"sizeof lock obj");
- abort ();
+ fputs ("gpgrt fatal: sizeof lock obj\n", stderr);
+ _gpgrt_abort ();
}
return lock;
}
gpg_err_code_t
_gpgrt_lock_init (gpgrt_lock_t *lockhd)
{
_gpgrt_lock_t *lock = (_gpgrt_lock_t*)lockhd;
int rc;
/* If VERS is zero we assume that no static initialization has been
done, so we setup our ABI version right here. The caller might
have called us to test whether lock support is at all available. */
if (!lock->vers)
{
if (sizeof (gpgrt_lock_t) < sizeof (_gpgrt_lock_t))
{
- assert (!"sizeof lock obj");
- abort ();
+ fputs ("gpgrt fatal: sizeof lock obj\n", stderr);
+ _gpgrt_abort ();
}
lock->vers = LOCK_ABI_VERSION;
}
else /* Run the usual check. */
lock = get_lock_object (lockhd);
#if USE_POSIX_THREADS
if (use_pthread_p())
{
rc = pthread_mutex_init (&lock->u.mtx, NULL);
if (rc)
rc = _gpg_err_code_from_errno (rc);
}
else
rc = 0; /* Threads are not used. */
#else /* Unknown thread system. */
rc = lock->vers == LOCK_ABI_NOT_AVAILABLE? 0 : GPG_ERR_NOT_IMPLEMENTED;
#endif /* Unknown thread system. */
return rc;
}
gpg_err_code_t
_gpgrt_lock_lock (gpgrt_lock_t *lockhd)
{
_gpgrt_lock_t *lock = get_lock_object (lockhd);
int rc;
#if USE_POSIX_THREADS
if (use_pthread_p())
{
_gpgrt_pre_syscall ();
rc = pthread_mutex_lock (&lock->u.mtx);
if (rc)
rc = _gpg_err_code_from_errno (rc);
_gpgrt_post_syscall ();
}
else
rc = 0; /* Threads are not used. */
#else /* Unknown thread system. */
rc = lock->vers == LOCK_ABI_NOT_AVAILABLE? 0 : GPG_ERR_NOT_IMPLEMENTED;
#endif /* Unknown thread system. */
return rc;
}
gpg_err_code_t
_gpgrt_lock_trylock (gpgrt_lock_t *lockhd)
{
_gpgrt_lock_t *lock = get_lock_object (lockhd);
int rc;
#if USE_POSIX_THREADS
if (use_pthread_p())
{
rc = pthread_mutex_trylock (&lock->u.mtx);
if (rc)
rc = _gpg_err_code_from_errno (rc);
}
else
rc = 0; /* Threads are not used. */
#else /* Unknown thread system. */
rc = lock->vers == LOCK_ABI_NOT_AVAILABLE? 0 : GPG_ERR_NOT_IMPLEMENTED;
#endif /* Unknown thread system. */
return rc;
}
gpg_err_code_t
_gpgrt_lock_unlock (gpgrt_lock_t *lockhd)
{
_gpgrt_lock_t *lock = get_lock_object (lockhd);
int rc;
#if USE_POSIX_THREADS
if (use_pthread_p())
{
rc = pthread_mutex_unlock (&lock->u.mtx);
if (rc)
rc = _gpg_err_code_from_errno (rc);
}
else
rc = 0; /* Threads are not used. */
#else /* Unknown thread system. */
rc = lock->vers == LOCK_ABI_NOT_AVAILABLE? 0 : GPG_ERR_NOT_IMPLEMENTED;
#endif /* Unknown thread system. */
return rc;
}
/* Note: Use this function only if no other thread holds or waits for
this lock. */
gpg_err_code_t
_gpgrt_lock_destroy (gpgrt_lock_t *lockhd)
{
_gpgrt_lock_t *lock = get_lock_object (lockhd);
int rc;
#if USE_POSIX_THREADS
if (use_pthread_p())
{
rc = pthread_mutex_destroy (&lock->u.mtx);
if (rc)
rc = _gpg_err_code_from_errno (rc);
else
{
/* Re-init the mutex so that it can be re-used. */
gpgrt_lock_t tmp = GPGRT_LOCK_INITIALIZER;
memcpy (lockhd, &tmp, sizeof tmp);
}
}
else
rc = 0; /* Threads are not used. */
#else /* Unknown thread system. */
rc = lock->vers == LOCK_ABI_NOT_AVAILABLE? 0 : GPG_ERR_NOT_IMPLEMENTED;
#endif /* Unknown thread system. */
return rc;
}
diff --git a/src/spawn-posix.c b/src/spawn-posix.c
index 52780a8..7be15ea 100644
--- a/src/spawn-posix.c
+++ b/src/spawn-posix.c
@@ -1,887 +1,886 @@
/* exechelp.c - Fork and exec helpers for POSIX
* Copyright (C) 2004, 2007-2009, 2010 Free Software Foundation, Inc.
* Copyright (C) 2004, 2006-2012, 2014-2017 g10 Code GmbH
*
* This file is part of Libgpg-error.
*
* Libgpg-error is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* Libgpg-error 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 .
* SPDX-License-Identifier: LGPL-2.1+
*
* This file was originally a part of GnuPG.
*/
#include
#if defined(HAVE_W32_SYSTEM) || defined (HAVE_W32CE_SYSTEM)
#error This code is only used on POSIX
#endif
#include
#include
#include
#include
#include
-#include
#ifdef HAVE_SIGNAL_H
# include
#endif
#include
#include
#include
#ifdef HAVE_GETRLIMIT
#include
#include
#endif /*HAVE_GETRLIMIT*/
#ifdef HAVE_STAT
# include
#endif
#if __linux__
# include
# include
#endif /*__linux__ */
#include "gpgrt-int.h"
static void
out_of_core (int line)
{
_gpgrt_log_fatal ("malloc failed at line %d: %s\n",
line, _gpg_strerror (_gpg_err_code_from_syserror ()));
/*NOTREACHED*/
}
/* Return the maximum number of currently allowed open file
* descriptors. Only useful on POSIX systems but returns a value on
* other systems too. */
static int
get_max_fds (void)
{
int max_fds = -1;
#ifdef HAVE_GETRLIMIT
struct rlimit rl;
/* Under Linux we can figure out the highest used file descriptor by
* reading /proc/PID/fd. This is in the common cases much fast than
* for example doing 4096 close calls where almost all of them will
* fail. On a system with a limit of 4096 files and only 8 files
* open with the highest number being 10, we speedup close_all_fds
* from 125ms to 0.4ms including readdir.
*
* Another option would be to close the file descriptors as returned
* from reading that directory - however then we need to snapshot
* that list before starting to close them. */
#ifdef __linux__
{
DIR *dir = NULL;
struct dirent *dir_entry;
const char *s;
int x;
/* FIXME: Check gpgme on how to do this right on Linux. */
dir = opendir ("/proc/self/fd");
if (dir)
{
while ((dir_entry = readdir (dir)))
{
s = dir_entry->d_name;
if ( *s < '0' || *s > '9')
continue;
x = atoi (s);
if (x > max_fds)
max_fds = x;
}
closedir (dir);
}
if (max_fds != -1)
return max_fds + 1;
}
#endif /* __linux__ */
# ifdef RLIMIT_NOFILE
if (!getrlimit (RLIMIT_NOFILE, &rl))
max_fds = rl.rlim_max;
# endif
# ifdef RLIMIT_OFILE
if (max_fds == -1 && !getrlimit (RLIMIT_OFILE, &rl))
max_fds = rl.rlim_max;
# endif
#endif /*HAVE_GETRLIMIT*/
#ifdef _SC_OPEN_MAX
if (max_fds == -1)
{
long int scres = sysconf (_SC_OPEN_MAX);
if (scres >= 0)
max_fds = scres;
}
#endif
#ifdef _POSIX_OPEN_MAX
if (max_fds == -1)
max_fds = _POSIX_OPEN_MAX;
#endif
#ifdef OPEN_MAX
if (max_fds == -1)
max_fds = OPEN_MAX;
#endif
if (max_fds == -1)
max_fds = 256; /* Arbitrary limit. */
/* AIX returns INT32_MAX instead of a proper value. We assume that
this is always an error and use an arbitrary limit. */
#ifdef INT32_MAX
if (max_fds == INT32_MAX)
max_fds = 256;
#endif
return max_fds;
}
/* Close all file descriptors starting with descriptor FIRST. If
* EXCEPT is not NULL, it is expected to be a list of file descriptors
* which shall not be closed. This list shall be sorted in ascending
* order with the end marked by -1. */
static void
close_all_fds (int first, int *except)
{
int max_fd = get_max_fds ();
int fd, i, except_start;
if (except)
{
except_start = 0;
for (fd=first; fd < max_fd; fd++)
{
for (i=except_start; except[i] != -1; i++)
{
if (except[i] == fd)
{
/* If we found the descriptor in the exception list
we can start the next compare run at the next
index because the exception list is ordered. */
except_start = i + 1;
break;
}
}
if (except[i] == -1)
close (fd);
}
}
else
{
for (fd=first; fd < max_fd; fd++)
close (fd);
}
_gpg_err_set_errno (0);
}
/* Returns an array with all currently open file descriptors. The end
* of the array is marked by -1. The caller needs to release this
* array using the *standard free* and not with xfree. This allow the
* use of this function right at startup even before libgcrypt has
* been initialized. Returns NULL on error and sets ERRNO
* accordingly.
*
* FIXME: Needs to be adjusted for use here.
*/
#if 0
int *
get_all_open_fds (void)
{
int *array;
size_t narray;
int fd, max_fd, idx;
#ifndef HAVE_STAT
array = calloc (1, sizeof *array);
if (array)
array[0] = -1;
#else /*HAVE_STAT*/
struct stat statbuf;
max_fd = get_max_fds ();
narray = 32; /* If you change this change also t-exechelp.c. */
array = calloc (narray, sizeof *array);
if (!array)
return NULL;
/* Note: The list we return is ordered. */
for (idx=0, fd=0; fd < max_fd; fd++)
if (!(fstat (fd, &statbuf) == -1 && errno == EBADF))
{
if (idx+1 >= narray)
{
int *tmp;
narray += (narray < 256)? 32:256;
tmp = realloc (array, narray * sizeof *array);
if (!tmp)
{
free (array);
return NULL;
}
array = tmp;
}
array[idx++] = fd;
}
array[idx] = -1;
#endif /*HAVE_STAT*/
return array;
}
#endif /*0*/
/* The exec core used right after the fork. This will never return. */
static void
do_exec (const char *pgmname, const char *argv[],
int fd_in, int fd_out, int fd_err,
int *except, void (*preexec)(void) )
{
char **arg_list;
int i, j;
int fds[3];
fds[0] = fd_in;
fds[1] = fd_out;
fds[2] = fd_err;
/* Create the command line argument array. */
i = 0;
if (argv)
while (argv[i])
i++;
arg_list = xtrycalloc (i+2, sizeof *arg_list);
if (!arg_list)
out_of_core (__LINE__);
arg_list[0] = strrchr (pgmname, '/');
if (arg_list[0])
arg_list[0]++;
else
{
arg_list[0] = xtrystrdup (pgmname);
if (!arg_list[0])
out_of_core (__LINE__);
}
if (argv)
for (i=0,j=1; argv[i]; i++, j++)
arg_list[j] = (char*)argv[i];
/* Assign /dev/null to unused FDs. */
for (i=0; i <= 2; i++)
{
if (fds[i] == -1 )
{
fds[i] = open ("/dev/null", i? O_WRONLY : O_RDONLY);
if (fds[i] == -1)
_gpgrt_log_fatal ("failed to open '%s': %s\n",
"/dev/null", strerror (errno));
}
}
/* Connect the standard files. */
for (i=0; i <= 2; i++)
{
if (fds[i] != i && dup2 (fds[i], i) == -1)
_gpgrt_log_fatal ("dup2 std%s failed: %s\n",
i==0?"in":i==1?"out":"err", strerror (errno));
}
/* Close all other files. */
close_all_fds (3, except);
if (preexec)
preexec ();
execv (pgmname, arg_list);
/* No way to print anything, as we have may have closed all streams. */
_exit (127);
}
/* Helper for _gpgrt_make_pipe. */
static gpg_err_code_t
do_create_pipe (int filedes[2])
{
gpg_error_t err = 0;
_gpgrt_pre_syscall ();
if (pipe (filedes) == -1)
{
err = _gpg_err_code_from_syserror ();
filedes[0] = filedes[1] = -1;
}
_gpgrt_post_syscall ();
return err;
}
/* Helper for _gpgrt_make_pipe. */
static gpg_err_code_t
do_create_pipe_and_estream (int filedes[2], estream_t *r_fp,
int outbound, int nonblock)
{
gpg_err_code_t err;
_gpgrt_pre_syscall ();
if (pipe (filedes) == -1)
{
err = _gpg_err_code_from_syserror ();
_gpgrt_log_error (_("error creating a pipe: %s\n"), _gpg_strerror (err));
filedes[0] = filedes[1] = -1;
*r_fp = NULL;
return err;
}
_gpgrt_post_syscall ();
if (!outbound)
*r_fp = _gpgrt_fdopen (filedes[0], nonblock? "r,nonblock" : "r");
else
*r_fp = _gpgrt_fdopen (filedes[1], nonblock? "w,nonblock" : "w");
if (!*r_fp)
{
err = _gpg_err_code_from_syserror ();
_gpgrt_log_error (_("error creating a stream for a pipe: %s\n"),
_gpg_strerror (err));
close (filedes[0]);
close (filedes[1]);
filedes[0] = filedes[1] = -1;
return err;
}
return 0;
}
/* Create a pipe. The DIRECTION parameter gives the type of the created pipe:
* DIRECTION < 0 := Inbound pipe: On Windows the write end is inheritable.
* DIRECTION > 0 := Outbound pipe: On Windows the read end is inheritable.
* If R_FP is NULL a standard pipe and no stream is created, DIRECTION
* should then be 0. */
gpg_err_code_t
_gpgrt_make_pipe (int filedes[2], estream_t *r_fp, int direction,
int nonblock)
{
if (r_fp && direction)
return do_create_pipe_and_estream (filedes, r_fp,
(direction > 0), nonblock);
else
return do_create_pipe (filedes);
}
/* Fork and exec the PGMNAME, see gpgrt-int.h for details. */
gpg_err_code_t
_gpgrt_spawn_process (const char *pgmname, const char *argv[],
int *except, void (*preexec)(void), unsigned int flags,
estream_t *r_infp,
estream_t *r_outfp,
estream_t *r_errfp,
pid_t *pid)
{
gpg_error_t err;
int inpipe[2] = {-1, -1};
int outpipe[2] = {-1, -1};
int errpipe[2] = {-1, -1};
estream_t infp = NULL;
estream_t outfp = NULL;
estream_t errfp = NULL;
int nonblock = !!(flags & GPGRT_SPAWN_NONBLOCK);
if (r_infp)
*r_infp = NULL;
if (r_outfp)
*r_outfp = NULL;
if (r_errfp)
*r_errfp = NULL;
*pid = (pid_t)(-1); /* Always required. */
if (r_infp)
{
err = _gpgrt_create_outbound_pipe (inpipe, &infp, nonblock);
if (err)
return err;
}
if (r_outfp)
{
err = _gpgrt_create_inbound_pipe (outpipe, &outfp, nonblock);
if (err)
{
if (infp)
_gpgrt_fclose (infp);
else if (inpipe[1] != -1)
close (inpipe[1]);
if (inpipe[0] != -1)
close (inpipe[0]);
return err;
}
}
if (r_errfp)
{
err = _gpgrt_create_inbound_pipe (errpipe, &errfp, nonblock);
if (err)
{
if (infp)
_gpgrt_fclose (infp);
else if (inpipe[1] != -1)
close (inpipe[1]);
if (inpipe[0] != -1)
close (inpipe[0]);
if (outfp)
_gpgrt_fclose (outfp);
else if (outpipe[0] != -1)
close (outpipe[0]);
if (outpipe[1] != -1)
close (outpipe[1]);
return err;
}
}
_gpgrt_pre_syscall ();
*pid = fork ();
_gpgrt_post_syscall ();
if (*pid == (pid_t)(-1))
{
err = _gpg_err_code_from_syserror ();
_gpgrt_log_error (_("error forking process: %s\n"), _gpg_strerror (err));
if (infp)
_gpgrt_fclose (infp);
else if (inpipe[1] != -1)
close (inpipe[1]);
if (inpipe[0] != -1)
close (inpipe[0]);
if (outfp)
_gpgrt_fclose (outfp);
else if (outpipe[0] != -1)
close (outpipe[0]);
if (outpipe[1] != -1)
close (outpipe[1]);
if (errfp)
_gpgrt_fclose (errfp);
else if (errpipe[0] != -1)
close (errpipe[0]);
if (errpipe[1] != -1)
close (errpipe[1]);
return err;
}
if (!*pid)
{
/* This is the child. */
/* FIXME: Needs to be done by preexec:
gcry_control (GCRYCTL_TERM_SECMEM); */
_gpgrt_fclose (infp);
_gpgrt_fclose (outfp);
_gpgrt_fclose (errfp);
do_exec (pgmname, argv, inpipe[0], outpipe[1], errpipe[1],
except, preexec);
/*NOTREACHED*/
}
/* This is the parent. */
if (inpipe[0] != -1)
close (inpipe[0]);
if (outpipe[1] != -1)
close (outpipe[1]);
if (errpipe[1] != -1)
close (errpipe[1]);
if (r_infp)
*r_infp = infp;
if (r_outfp)
*r_outfp = outfp;
if (r_errfp)
*r_errfp = errfp;
return 0;
}
/* Fork and exec the PGMNAME using FDs, see gpgrt-int.h for details. */
gpg_err_code_t
_gpgrt_spawn_process_fd (const char *pgmname, const char *argv[],
int infd, int outfd, int errfd, pid_t *pid)
{
gpg_error_t err;
_gpgrt_pre_syscall ();
*pid = fork ();
_gpgrt_post_syscall ();
if (*pid == (pid_t)(-1))
{
err = _gpg_err_code_from_syserror ();
_gpgrt_log_error (_("error forking process: %s\n"), _gpg_strerror (err));
return err;
}
if (!*pid)
{
/* FIXME: We need to add a preexec so that a
gcry_control (GCRYCTL_TERM_SECMEM);
can be done. */
/* Run child. */
do_exec (pgmname, argv, infd, outfd, errfd, NULL, NULL);
/*NOTREACHED*/
}
return 0;
}
/* Waiting for child processes.
*
* waitpid(2) may return information about terminated children that we
* did not yet request, and there is no portable way to wait for a
* specific set of children.
*
* As a workaround, we store the results of children for later use.
*
* XXX: This assumes that PIDs are not reused too quickly.
* FIXME: This is not thread-safe.
*/
struct terminated_child
{
pid_t pid;
int exitcode;
struct terminated_child *next;
};
static struct terminated_child *terminated_children;
static gpg_err_code_t
store_result (pid_t pid, int exitcode)
{
struct terminated_child *c;
c = xtrymalloc (sizeof *c);
if (c == NULL)
return _gpg_err_code_from_syserror ();
c->pid = pid;
c->exitcode = exitcode;
c->next = terminated_children;
terminated_children = c;
return 0;
}
static int
get_result (pid_t pid, int *r_exitcode)
{
struct terminated_child *c, **prevp;
for (prevp = &terminated_children, c = terminated_children;
c;
prevp = &c->next, c = c->next)
if (c->pid == pid)
{
*prevp = c->next;
*r_exitcode = c->exitcode;
xfree (c);
return 1;
}
return 0;
}
/* See gpgrt-int.h for a description. */
gpg_err_code_t
_gpgrt_wait_process (const char *pgmname, pid_t pid, int hang, int *r_exitcode)
{
gpg_err_code_t ec;
int i, status;
if (r_exitcode)
*r_exitcode = -1;
if (pid == (pid_t)(-1))
return GPG_ERR_INV_VALUE;
_gpgrt_pre_syscall ();
while ((i=waitpid (pid, &status, hang? 0:WNOHANG)) == (pid_t)(-1)
&& errno == EINTR);
_gpgrt_post_syscall ();
if (i == (pid_t)(-1))
{
ec = _gpg_err_code_from_syserror ();
_gpgrt_log_error (_("waiting for process %d to terminate failed: %s\n"),
(int)pid, _gpg_strerror (ec));
}
else if (!i)
{
ec = GPG_ERR_TIMEOUT; /* Still running. */
}
else if (WIFEXITED (status) && WEXITSTATUS (status) == 127)
{
/* FIXME: This is GnuPG specific. */
_gpgrt_log_error (_("error running '%s': probably not installed\n"),
pgmname);
ec = GPG_ERR_CONFIGURATION;
}
else if (WIFEXITED (status) && WEXITSTATUS (status))
{
if (!r_exitcode)
_gpgrt_log_error (_("error running '%s': exit status %d\n"), pgmname,
WEXITSTATUS (status));
else
*r_exitcode = WEXITSTATUS (status);
ec = GPG_ERR_GENERAL;
}
else if (!WIFEXITED (status))
{
_gpgrt_log_error (_("error running '%s': terminated\n"), pgmname);
ec = GPG_ERR_GENERAL;
}
else
{
if (r_exitcode)
*r_exitcode = 0;
ec = 0;
}
return ec;
}
/* See gpgrt-int.h for a description.
*
* FIXME: What about using a poll like data structure for the pids and
* their exit codes? The whole thing is anyway problematic in a
* threaded processs because waitpid has no association between PIDS
* and threads.
*/
gpg_err_code_t
_gpgrt_wait_processes (const char **pgmnames, pid_t *pids, size_t count,
int hang, int *r_exitcodes)
{
gpg_err_code_t ec = 0;
size_t i, left;
int *dummy = NULL;
if (!r_exitcodes)
{
dummy = r_exitcodes = xtrymalloc (sizeof *r_exitcodes * count);
if (!dummy)
return _gpg_err_code_from_syserror ();
}
for (i = 0, left = count; i < count; i++)
{
int status = -1;
if (pids[i] == (pid_t)(-1))
return GPG_ERR_INV_VALUE;
/* See if there was a previously stored result for this pid. */
if (get_result (pids[i], &status))
left -= 1;
r_exitcodes[i] = status;
}
while (left > 0)
{
pid_t pid;
int status;
_gpgrt_pre_syscall ();
while ((pid = waitpid (-1, &status, hang ? 0 : WNOHANG)) == (pid_t)(-1)
&& errno == EINTR);
_gpgrt_post_syscall ();
if (pid == (pid_t)(-1))
{
ec = _gpg_err_code_from_syserror ();
_gpgrt_log_error (_("waiting for processes to terminate"
" failed: %s\n"), _gpg_strerror (ec));
break;
}
else if (!pid)
{
ec = GPG_ERR_TIMEOUT; /* Still running. */
break;
}
else
{
for (i = 0; i < count; i++)
if (pid == pids[i])
break;
if (i == count)
{
/* No match, store this result. */
ec = store_result (pid, status);
if (ec)
break;
continue;
}
/* Process PIDS[i] died. */
if (r_exitcodes[i] != (pid_t) -1)
{
_gpgrt_log_error ("PID %d was reused", pid);
ec = GPG_ERR_GENERAL;
break;
}
left -= 1;
r_exitcodes[i] = status;
}
}
for (i = 0; i < count; i++)
{
if (r_exitcodes[i] == -1)
continue;
if (WIFEXITED (r_exitcodes[i]) && WEXITSTATUS (r_exitcodes[i]) == 127)
{
_gpgrt_log_error (_("error running '%s': probably not installed\n"),
pgmnames[i]);
ec = GPG_ERR_CONFIGURATION;
}
else if (WIFEXITED (r_exitcodes[i]) && WEXITSTATUS (r_exitcodes[i]))
{
if (dummy)
_gpgrt_log_error (_("error running '%s': exit status %d\n"),
pgmnames[i], WEXITSTATUS (r_exitcodes[i]));
else
r_exitcodes[i] = WEXITSTATUS (r_exitcodes[i]);
ec = GPG_ERR_GENERAL;
}
else if (!WIFEXITED (r_exitcodes[i]))
{
_gpgrt_log_error (_("error running '%s': terminated\n"), pgmnames[i]);
ec = GPG_ERR_GENERAL;
}
}
xfree (dummy);
return ec;
}
/* See gpgrt-int.h for a description. FIXME: We should add a prexec
* callback. */
gpg_err_code_t
_gpgrt_spawn_process_detached (const char *pgmname, const char *argv[],
const char *envp[] )
{
gpg_err_code_t ec;
pid_t pid;
int i;
/* FIXME: Is this GnuPG specific or should we keep it. */
if (getuid() != geteuid())
return GPG_ERR_BUG;
if (access (pgmname, X_OK))
return _gpg_err_code_from_syserror ();
_gpgrt_pre_syscall ();
pid = fork ();
_gpgrt_post_syscall ();
if (pid == (pid_t)(-1))
{
ec = _gpg_err_code_from_syserror ();
_gpgrt_log_error (_("error forking process: %s\n"), _gpg_strerror (ec));
return ec;
}
if (!pid)
{
pid_t pid2;
/* gcry_control (GCRYCTL_TERM_SECMEM); */
if (setsid() == -1 || chdir ("/"))
_exit (1);
pid2 = fork (); /* Double fork to let init take over the new child. */
if (pid2 == (pid_t)(-1))
_exit (1);
if (pid2)
_exit (0); /* Let the parent exit immediately. */
for (i=0; envp && envp[i]; i++)
{
char *p = xtrystrdup (envp[i]);
if (!p)
out_of_core (__LINE__);
putenv (p);
}
do_exec (pgmname, argv, -1, -1, -1, NULL, NULL);
/*NOTREACHED*/
}
_gpgrt_pre_syscall ();
if (waitpid (pid, NULL, 0) == -1)
{
_gpgrt_post_syscall ();
ec = _gpg_err_code_from_syserror ();
_gpgrt_log_error ("waitpid failed in gpgrt_spawn_process_detached: %s",
_gpg_strerror (ec));
return ec;
}
else
_gpgrt_post_syscall ();
return 0;
}
/* Kill a process; that is send an appropriate signal to the process.
* gnupg_wait_process must be called to actually remove the process
from the system. An invalid PID is ignored. */
void
_gpgrt_kill_process (pid_t pid)
{
if (pid != (pid_t)(-1))
{
_gpgrt_pre_syscall ();
kill (pid, SIGTERM);
_gpgrt_post_syscall ();
}
}
void
_gpgrt_release_process (pid_t pid)
{
(void)pid;
}
diff --git a/src/spawn-w32.c b/src/spawn-w32.c
index 4c57756..91f9ac4 100644
--- a/src/spawn-w32.c
+++ b/src/spawn-w32.c
@@ -1,904 +1,903 @@
/* spawn-w32.c - Fork and exec helpers for W32.
* Copyright (C) 2004, 2007-2009, 2010 Free Software Foundation, Inc.
* Copyright (C) 2004, 2006-2012, 2014-2017 g10 Code GmbH
*
* This file is part of Libgpg-error.
*
* Libgpg-error is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* Libgpg-error 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 .
* SPDX-License-Identifier: LGPL-2.1+
*
* This file was originally a part of GnuPG.
*/
#include
#if !defined(HAVE_W32_SYSTEM) || defined (HAVE_W32CE_SYSTEM)
#error This code is only used on W32.
#endif
#include
#include
#include
#include
-#include
#ifdef HAVE_SIGNAL_H
# include
#endif
#include
#include
#ifdef HAVE_STAT
# include
#endif
#define WIN32_LEAN_AND_MEAN /* We only need the OS core stuff. */
#include
#include "gpgrt-int.h"
/* Define to 1 do enable debugging. */
#define DEBUG_W32_SPAWN 0
/* It seems Vista doesn't grok X_OK and so fails access() tests.
* Previous versions interpreted X_OK as F_OK anyway, so we'll just
* use F_OK directly. */
#undef X_OK
#define X_OK F_OK
/* We assume that a HANDLE can be represented by an int which should
* be true for all i386 systems (HANDLE is defined as void *) and
* these are the only systems for which Windows is available. Further
* we assume that -1 denotes an invalid handle.
* FIXME: With Windows 64 this is no longer true.
*/
#define fd_to_handle(a) ((HANDLE)(a))
#define handle_to_fd(a) ((int)(a))
#define pid_to_handle(a) ((HANDLE)(a))
#define handle_to_pid(a) ((int)(a))
/* Return the maximum number of currently allowed open file
* descriptors. Only useful on POSIX systems but returns a value on
* other systems too. */
int
get_max_fds (void)
{
int max_fds = -1;
#ifdef OPEN_MAX
if (max_fds == -1)
max_fds = OPEN_MAX;
#endif
if (max_fds == -1)
max_fds = 256; /* Arbitrary limit. */
return max_fds;
}
/* Under Windows this is a dummy function. */
/* static void */
/* close_all_fds (int first, int *except) */
/* { */
/* (void)first; */
/* (void)except; */
/* } */
/* Returns an array with all currently open file descriptors. The end
* of the array is marked by -1. The caller needs to release this
* array using the *standard free* and not with xfree. This allow the
* use of this function right at startup even before libgcrypt has
* been initialized. Returns NULL on error and sets ERRNO
* accordingly. Note that fstat prints a warning to DebugView for all
* invalid fds which is a bit annoying. We actually do not need this
* function in real code (close_all_fds is a dummy anyway) but we keep
* it for use by t-exechelp.c. */
#if 0
int *
get_all_open_fds (void)
{
int *array;
size_t narray;
int fd, max_fd, idx;
#ifndef HAVE_STAT
array = calloc (1, sizeof *array);
if (array)
array[0] = -1;
#else /*HAVE_STAT*/
struct stat statbuf;
max_fd = get_max_fds ();
narray = 32; /* If you change this change also t-exechelp.c. */
array = calloc (narray, sizeof *array);
if (!array)
return NULL;
/* Note: The list we return is ordered. */
for (idx=0, fd=0; fd < max_fd; fd++)
if (!(fstat (fd, &statbuf) == -1 && errno == EBADF))
{
if (idx+1 >= narray)
{
int *tmp;
narray += (narray < 256)? 32:256;
tmp = realloc (array, narray * sizeof *array);
if (!tmp)
{
free (array);
return NULL;
}
array = tmp;
}
array[idx++] = fd;
}
array[idx] = -1;
#endif /*HAVE_STAT*/
return array;
}
#endif
/* Helper function to build_w32_commandline. */
static char *
build_w32_commandline_copy (char *buffer, const char *string)
{
char *p = buffer;
const char *s;
if (!*string) /* Empty string. */
p = stpcpy (p, "\"\"");
else if (strpbrk (string, " \t\n\v\f\""))
{
/* Need to do some kind of quoting. */
p = stpcpy (p, "\"");
for (s=string; *s; s++)
{
*p++ = *s;
if (*s == '\"')
*p++ = *s;
}
*p++ = '\"';
*p = 0;
}
else
p = stpcpy (p, string);
return p;
}
/* Build a command line for use with W32's CreateProcess. On success
* CMDLINE gets the address of a newly allocated string. */
static gpg_err_code_t
build_w32_commandline (const char *pgmname, const char * const *argv,
char **cmdline)
{
int i, n;
const char *s;
char *buf, *p;
*cmdline = NULL;
n = 0;
s = pgmname;
n += strlen (s) + 1 + 2; /* (1 space, 2 quoting */
for (; *s; s++)
if (*s == '\"')
n++; /* Need to double inner quotes. */
for (i=0; (s=argv[i]); i++)
{
n += strlen (s) + 1 + 2; /* (1 space, 2 quoting */
for (; *s; s++)
if (*s == '\"')
n++; /* Need to double inner quotes. */
}
n++;
buf = p = xtrymalloc (n);
if (!buf)
return _gpg_err_code_from_syserror ();
p = build_w32_commandline_copy (p, pgmname);
for (i=0; argv[i]; i++)
{
*p++ = ' ';
p = build_w32_commandline_copy (p, argv[i]);
}
*cmdline= buf;
return 0;
}
#define INHERIT_READ 1
#define INHERIT_WRITE 2
#define INHERIT_BOTH (INHERIT_READ|INHERIT_WRITE)
/* Create pipe. FLAGS indicates which ends are inheritable. */
static int
create_inheritable_pipe (HANDLE filedes[2], int flags)
{
HANDLE r, w;
SECURITY_ATTRIBUTES sec_attr;
memset (&sec_attr, 0, sizeof sec_attr );
sec_attr.nLength = sizeof sec_attr;
sec_attr.bInheritHandle = TRUE;
_gpgrt_pre_syscall ();
if (!CreatePipe (&r, &w, &sec_attr, 0))
{
_gpgrt_post_syscall ();
return -1;
}
_gpgrt_post_syscall ();
if ((flags & INHERIT_READ) == 0)
if (! SetHandleInformation (r, HANDLE_FLAG_INHERIT, 0))
goto fail;
if ((flags & INHERIT_WRITE) == 0)
if (! SetHandleInformation (w, HANDLE_FLAG_INHERIT, 0))
goto fail;
filedes[0] = r;
filedes[1] = w;
return 0;
fail:
_gpgrt_log_error ("SetHandleInformation failed: ec=%d\n",
(int)GetLastError ());
CloseHandle (r);
CloseHandle (w);
return -1;
}
static HANDLE
w32_open_null (int for_write)
{
HANDLE hfile;
hfile = CreateFileW (L"nul",
for_write? GENERIC_WRITE : GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL);
if (hfile == INVALID_HANDLE_VALUE)
_gpgrt_log_debug ("can't open 'nul': ec=%d\n", (int)GetLastError ());
return hfile;
}
static gpg_err_code_t
do_create_pipe_and_estream (int filedes[2],
estream_t *r_fp, int direction, int nonblock)
{
gpg_err_code_t err = 0;
int flags;
HANDLE fds[2];
gpgrt_syshd_t syshd;
if (direction < 0)
flags = INHERIT_WRITE;
else if (direction > 0)
flags = INHERIT_READ;
else
flags = INHERIT_BOTH;
filedes[0] = filedes[1] = -1;
err = GPG_ERR_GENERAL;
if (!create_inheritable_pipe (fds, flags))
{
filedes[0] = _open_osfhandle (handle_to_fd (fds[0]), O_RDONLY);
if (filedes[0] == -1)
{
_gpgrt_log_error ("failed to translate osfhandle %p\n", fds[0]);
CloseHandle (fds[1]);
}
else
{
filedes[1] = _open_osfhandle (handle_to_fd (fds[1]), O_APPEND);
if (filedes[1] == -1)
{
_gpgrt_log_error ("failed to translate osfhandle %p\n", fds[1]);
close (filedes[0]);
filedes[0] = -1;
CloseHandle (fds[1]);
}
else
err = 0;
}
}
if (! err && r_fp)
{
syshd.type = ES_SYSHD_HANDLE;
if (direction < 0)
{
syshd.u.handle = fds[0];
*r_fp = _gpgrt_sysopen (&syshd, nonblock? "r,nonblock" : "r");
}
else
{
syshd.u.handle = fds[1];
*r_fp = _gpgrt_sysopen (&syshd, nonblock? "w,nonblock" : "w");
}
if (!*r_fp)
{
err = _gpg_err_code_from_syserror ();
_gpgrt_log_error (_("error creating a stream for a pipe: %s\n"),
_gpg_strerror (err));
close (filedes[0]);
close (filedes[1]);
filedes[0] = filedes[1] = -1;
return err;
}
}
return err;
}
/* Create a pipe. The DIRECTION parameter gives the type of the created pipe:
* DIRECTION < 0 := Inbound pipe: On Windows the write end is inheritable.
* DIRECTION > 0 := Outbound pipe: On Windows the read end is inheritable.
* If R_FP is NULL a standard pipe and no stream is created, DIRECTION
* should then be 0. */
gpg_err_code_t
_gpgrt_make_pipe (int filedes[2], estream_t *r_fp, int direction, int nonblock)
{
if (r_fp && direction)
return do_create_pipe_and_estream (filedes, r_fp, direction, nonblock);
else
return do_create_pipe_and_estream (filedes, NULL, 0, 0);
}
/* Fork and exec the PGMNAME, see gpgrt-int.h for details. */
gpg_err_code_t
_gpgrt_spawn_process (const char *pgmname, const char *argv[],
int *except, void (*preexec)(void), unsigned int flags,
estream_t *r_infp, estream_t *r_outfp, estream_t *r_errfp,
pid_t *pid)
{
gpg_err_code_t err;
SECURITY_ATTRIBUTES sec_attr;
PROCESS_INFORMATION pi =
{
NULL, /* Returns process handle. */
0, /* Returns primary thread handle. */
0, /* Returns pid. */
0 /* Returns tid. */
};
STARTUPINFO si;
int cr_flags;
char *cmdline;
HANDLE inpipe[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE};
HANDLE outpipe[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE};
HANDLE errpipe[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE};
estream_t infp = NULL;
estream_t outfp = NULL;
estream_t errfp = NULL;
HANDLE nullhd[3] = {INVALID_HANDLE_VALUE,
INVALID_HANDLE_VALUE,
INVALID_HANDLE_VALUE};
int i;
es_syshd_t syshd;
int nonblock = !!(flags & GPGRT_SPAWN_NONBLOCK);
int ret;
(void)except; /* Not yet used. */
if (r_infp)
*r_infp = NULL;
if (r_outfp)
*r_outfp = NULL;
if (r_errfp)
*r_errfp = NULL;
*pid = (pid_t)(-1); /* Always required. */
if (r_infp)
{
if (create_inheritable_pipe (inpipe, INHERIT_READ))
{
err = GPG_ERR_GENERAL;
_gpgrt_log_error (_("error creating a pipe: %s\n"),
_gpg_strerror (err));
return err;
}
syshd.type = ES_SYSHD_HANDLE;
syshd.u.handle = inpipe[1];
infp = _gpgrt_sysopen (&syshd, nonblock? "w,nonblock" : "w");
if (!infp)
{
err = _gpg_err_code_from_syserror ();
_gpgrt_log_error (_("error creating a stream for a pipe: %s\n"),
_gpg_strerror (err));
CloseHandle (inpipe[0]);
CloseHandle (inpipe[1]);
inpipe[0] = inpipe[1] = INVALID_HANDLE_VALUE;
return err;
}
}
if (r_outfp)
{
if (create_inheritable_pipe (outpipe, INHERIT_WRITE))
{
err = GPG_ERR_GENERAL;
_gpgrt_log_error (_("error creating a pipe: %s\n"),
_gpg_strerror (err));
return err;
}
syshd.type = ES_SYSHD_HANDLE;
syshd.u.handle = outpipe[0];
outfp = _gpgrt_sysopen (&syshd, nonblock? "r,nonblock" : "r");
if (!outfp)
{
err = _gpg_err_code_from_syserror ();
_gpgrt_log_error (_("error creating a stream for a pipe: %s\n"),
_gpg_strerror (err));
CloseHandle (outpipe[0]);
CloseHandle (outpipe[1]);
outpipe[0] = outpipe[1] = INVALID_HANDLE_VALUE;
if (infp)
_gpgrt_fclose (infp);
else if (inpipe[1] != INVALID_HANDLE_VALUE)
CloseHandle (inpipe[1]);
if (inpipe[0] != INVALID_HANDLE_VALUE)
CloseHandle (inpipe[0]);
return err;
}
}
if (r_errfp)
{
if (create_inheritable_pipe (errpipe, INHERIT_WRITE))
{
err = GPG_ERR_GENERAL;
_gpgrt_log_error (_("error creating a pipe: %s\n"),
_gpg_strerror (err));
return err;
}
syshd.type = ES_SYSHD_HANDLE;
syshd.u.handle = errpipe[0];
errfp = _gpgrt_sysopen (&syshd, nonblock? "r,nonblock" : "r");
if (!errfp)
{
err = _gpg_err_code_from_syserror ();
_gpgrt_log_error (_("error creating a stream for a pipe: %s\n"),
_gpg_strerror (err));
CloseHandle (errpipe[0]);
CloseHandle (errpipe[1]);
errpipe[0] = errpipe[1] = INVALID_HANDLE_VALUE;
if (outfp)
_gpgrt_fclose (outfp);
else if (outpipe[0] != INVALID_HANDLE_VALUE)
CloseHandle (outpipe[0]);
if (outpipe[1] != INVALID_HANDLE_VALUE)
CloseHandle (outpipe[1]);
if (infp)
_gpgrt_fclose (infp);
else if (inpipe[1] != INVALID_HANDLE_VALUE)
CloseHandle (inpipe[1]);
if (inpipe[0] != INVALID_HANDLE_VALUE)
CloseHandle (inpipe[0]);
return err;
}
}
/* Prepare security attributes. */
memset (&sec_attr, 0, sizeof sec_attr );
sec_attr.nLength = sizeof sec_attr;
sec_attr.bInheritHandle = FALSE;
/* Build the command line. */
err = build_w32_commandline (pgmname, argv, &cmdline);
if (err)
return err;
if (inpipe[0] == INVALID_HANDLE_VALUE)
nullhd[0] = w32_open_null (0);
if (outpipe[1] == INVALID_HANDLE_VALUE)
nullhd[1] = w32_open_null (1);
if (errpipe[1] == INVALID_HANDLE_VALUE)
nullhd[2] = w32_open_null (1);
/* Start the process. Note that we can't run the PREEXEC function
because this might change our own environment. */
(void)preexec;
memset (&si, 0, sizeof si);
si.cb = sizeof (si);
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE;
si.hStdInput = inpipe[0] == INVALID_HANDLE_VALUE? nullhd[0] : inpipe[0];
si.hStdOutput = outpipe[1] == INVALID_HANDLE_VALUE? nullhd[1] : outpipe[1];
si.hStdError = errpipe[1] == INVALID_HANDLE_VALUE? nullhd[2] : errpipe[1];
cr_flags = (CREATE_DEFAULT_ERROR_MODE
| ((flags & GPGRT_SPAWN_DETACHED)? DETACHED_PROCESS : 0)
| GetPriorityClass (GetCurrentProcess ())
| CREATE_SUSPENDED);
_gpgrt_log_debug ("CreateProcess, path='%s' cmdline='%s'\n",
pgmname, cmdline);
ret = CreateProcess (pgmname, /* Program to start. */
cmdline, /* Command line arguments. */
&sec_attr, /* Process security attributes. */
&sec_attr, /* Thread security attributes. */
TRUE, /* Inherit handles. */
cr_flags, /* Creation flags. */
NULL, /* Environment. */
NULL, /* Use current drive/directory. */
&si, /* Startup information. */
&pi /* Returns process information. */
);
if (!ret)
{
_gpgrt_log_error ("CreateProcess failed: ec=%d\n", (int)GetLastError ());
xfree (cmdline);
if (infp)
_gpgrt_fclose (infp);
else if (inpipe[1] != INVALID_HANDLE_VALUE)
CloseHandle (outpipe[1]);
if (inpipe[0] != INVALID_HANDLE_VALUE)
CloseHandle (inpipe[0]);
if (outfp)
_gpgrt_fclose (outfp);
else if (outpipe[0] != INVALID_HANDLE_VALUE)
CloseHandle (outpipe[0]);
if (outpipe[1] != INVALID_HANDLE_VALUE)
CloseHandle (outpipe[1]);
if (errfp)
_gpgrt_fclose (errfp);
else if (errpipe[0] != INVALID_HANDLE_VALUE)
CloseHandle (errpipe[0]);
if (errpipe[1] != INVALID_HANDLE_VALUE)
CloseHandle (errpipe[1]);
return GPG_ERR_GENERAL;
}
xfree (cmdline);
cmdline = NULL;
/* Close the inherited handles to /dev/null. */
for (i=0; i < DIM (nullhd); i++)
if (nullhd[i] != INVALID_HANDLE_VALUE)
CloseHandle (nullhd[i]);
/* Close the inherited ends of the pipes. */
if (inpipe[0] != INVALID_HANDLE_VALUE)
CloseHandle (inpipe[0]);
if (outpipe[1] != INVALID_HANDLE_VALUE)
CloseHandle (outpipe[1]);
if (errpipe[1] != INVALID_HANDLE_VALUE)
CloseHandle (errpipe[1]);
_gpgrt_log_debug ("CreateProcess ready: hProcess=%p hThread=%p"
" dwProcessID=%d dwThreadId=%d\n",
pi.hProcess, pi.hThread,
(int) pi.dwProcessId, (int) pi.dwThreadId);
_gpgrt_log_debug (" outfp=%p errfp=%p\n", outfp, errfp);
if ((flags & GPGRT_SPAWN_RUN_ASFW))
{
/* Fixme: For unknown reasons AllowSetForegroundWindow returns
* an invalid argument error if we pass it the correct
* processID. As a workaround we use -1 (ASFW_ANY). */
if (!AllowSetForegroundWindow (ASFW_ANY /*pi.dwProcessId*/))
_gpgrt_log_info ("AllowSetForegroundWindow() failed: ec=%d\n",
(int)GetLastError ());
}
/* Process has been created suspended; resume it now. */
_gpgrt_pre_syscall ();
ResumeThread (pi.hThread);
CloseHandle (pi.hThread);
_gpgrt_post_syscall ();
if (r_infp)
*r_infp = infp;
if (r_outfp)
*r_outfp = outfp;
if (r_errfp)
*r_errfp = errfp;
*pid = handle_to_pid (pi.hProcess);
return 0;
}
/* Fork and exec the PGMNAME using FDs, see gpgrt-int.h for details. */
gpg_err_code_t
_gpgrt_spawn_process_fd (const char *pgmname, const char *argv[],
int infd, int outfd, int errfd, pid_t *pid)
{
gpg_err_code_t err;
SECURITY_ATTRIBUTES sec_attr;
PROCESS_INFORMATION pi = { NULL, 0, 0, 0 };
STARTUPINFO si;
char *cmdline;
int ret, i;
HANDLE stdhd[3];
/* Setup return values. */
*pid = (pid_t)(-1);
/* Prepare security attributes. */
memset (&sec_attr, 0, sizeof sec_attr );
sec_attr.nLength = sizeof sec_attr;
sec_attr.bInheritHandle = FALSE;
/* Build the command line. */
err = build_w32_commandline (pgmname, argv, &cmdline);
if (err)
return err;
memset (&si, 0, sizeof si);
si.cb = sizeof (si);
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE;
stdhd[0] = infd == -1? w32_open_null (0) : INVALID_HANDLE_VALUE;
stdhd[1] = outfd == -1? w32_open_null (1) : INVALID_HANDLE_VALUE;
stdhd[2] = errfd == -1? w32_open_null (1) : INVALID_HANDLE_VALUE;
si.hStdInput = infd == -1? stdhd[0] : (void*)_get_osfhandle (infd);
si.hStdOutput = outfd == -1? stdhd[1] : (void*)_get_osfhandle (outfd);
si.hStdError = errfd == -1? stdhd[2] : (void*)_get_osfhandle (errfd);
_gpgrt_log_debug ("CreateProcess, path='%s' cmdline='%s'\n",
pgmname, cmdline);
ret = CreateProcess (pgmname, /* Program to start. */
cmdline, /* Command line arguments. */
&sec_attr, /* Process security attributes. */
&sec_attr, /* Thread security attributes. */
TRUE, /* Inherit handles. */
(CREATE_DEFAULT_ERROR_MODE
| GetPriorityClass (GetCurrentProcess ())
| CREATE_SUSPENDED | DETACHED_PROCESS),
NULL, /* Environment. */
NULL, /* Use current drive/directory. */
&si, /* Startup information. */
&pi /* Returns process information. */
);
if (!ret)
{
_gpgrt_log_error ("CreateProcess failed: ec=%d\n", (int)GetLastError ());
err = GPG_ERR_GENERAL;
}
else
err = 0;
xfree (cmdline);
for (i=0; i < 3; i++)
if (stdhd[i] != INVALID_HANDLE_VALUE)
CloseHandle (stdhd[i]);
if (err)
return err;
_gpgrt_log_debug ("CreateProcess ready: hProcess=%p hThread=%p"
" dwProcessID=%d dwThreadId=%d\n",
pi.hProcess, pi.hThread,
(int) pi.dwProcessId, (int) pi.dwThreadId);
/* Process has been created suspended; resume it now. */
ResumeThread (pi.hThread);
CloseHandle (pi.hThread);
*pid = handle_to_pid (pi.hProcess);
return 0;
}
/* See gpgrt-int.h for a description. */
gpg_err_code_t
_gpgrt_wait_process (const char *pgmname, pid_t pid, int hang, int *r_exitcode)
{
return _gpgrt_wait_processes (&pgmname, &pid, 1, hang, r_exitcode);
}
/* See gpgrt-int.h for a description. */
gpg_err_code_t
_gpgrt_wait_processes (const char **pgmnames, pid_t *pids, size_t count,
int hang, int *r_exitcodes)
{
gpg_err_code_t ec = 0;
size_t i;
HANDLE *procs;
int code;
procs = xtrycalloc (count, sizeof *procs);
if (procs == NULL)
return _gpg_err_code_from_syserror ();
for (i = 0; i < count; i++)
{
if (r_exitcodes)
r_exitcodes[i] = -1;
if (pids[i] == (pid_t)(-1))
return GPG_ERR_INV_VALUE;
procs[i] = pid_to_handle (pids[i]);
}
_gpgrt_pre_syscall ();
code = WaitForMultipleObjects (count, procs, TRUE, hang? INFINITE : 0);
_gpgrt_post_syscall ();
switch (code)
{
case WAIT_TIMEOUT:
ec = GPG_ERR_TIMEOUT;
goto leave;
case WAIT_FAILED:
_gpgrt_log_error (_("waiting for processes to terminate failed: ec=%d\n"),
(int)GetLastError ());
ec = GPG_ERR_GENERAL;
goto leave;
case WAIT_OBJECT_0:
for (i = 0; i < count; i++)
{
DWORD exc;
if (! GetExitCodeProcess (procs[i], &exc))
{
_gpgrt_log_error (_("error getting exit code of process %d:"
" ec=%d\n"),
(int) pids[i], (int)GetLastError ());
ec = GPG_ERR_GENERAL;
}
else if (exc)
{
if (!r_exitcodes)
_gpgrt_log_error (_("error running '%s': exit status %d\n"),
pgmnames[i], (int)exc);
else
r_exitcodes[i] = (int)exc;
ec = GPG_ERR_GENERAL;
}
else
{
if (r_exitcodes)
r_exitcodes[i] = 0;
}
}
break;
default:
_gpgrt_log_debug ("WaitForMultipleObjects returned unexpected code %d\n",
code);
ec = GPG_ERR_GENERAL;
break;
}
leave:
return ec;
}
/* See gpgrt-int.h for a description. */
gpg_err_code_t
_gpgrt_spawn_process_detached (const char *pgmname, const char *argv[],
const char *envp[] )
{
gpg_err_code_t err;
SECURITY_ATTRIBUTES sec_attr;
PROCESS_INFORMATION pi =
{
NULL, /* Returns process handle. */
0, /* Returns primary thread handle. */
0, /* Returns pid. */
0 /* Returns tid. */
};
STARTUPINFO si;
int cr_flags;
char *cmdline;
int ret;
/* We don't use ENVP. */
(void)envp;
if (access (pgmname, X_OK))
return _gpg_err_code_from_syserror ();
/* Prepare security attributes. */
memset (&sec_attr, 0, sizeof sec_attr );
sec_attr.nLength = sizeof sec_attr;
sec_attr.bInheritHandle = FALSE;
/* Build the command line. */
err = build_w32_commandline (pgmname, argv, &cmdline);
if (err)
return err;
/* Start the process. */
memset (&si, 0, sizeof si);
si.cb = sizeof (si);
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE;
cr_flags = (CREATE_DEFAULT_ERROR_MODE
| GetPriorityClass (GetCurrentProcess ())
| CREATE_NEW_PROCESS_GROUP
| DETACHED_PROCESS);
_gpgrt_log_debug ("CreateProcess(detached), path='%s' cmdline='%s'\n",
pgmname, cmdline);
ret = CreateProcess (pgmname, /* Program to start. */
cmdline, /* Command line arguments. */
&sec_attr, /* Process security attributes. */
&sec_attr, /* Thread security attributes. */
FALSE, /* Inherit handles. */
cr_flags, /* Creation flags. */
NULL, /* Environment. */
NULL, /* Use current drive/directory. */
&si, /* Startup information. */
&pi /* Returns process information. */
);
if (!ret)
{
_gpgrt_log_error ("CreateProcess(detached) failed: ec=%d\n",
(int)GetLastError ());
xfree (cmdline);
return GPG_ERR_GENERAL;
}
xfree (cmdline);
cmdline = NULL;
_gpgrt_log_debug ("CreateProcess(detached) ready: hProcess=%p hThread=%p"
" dwProcessID=%d dwThreadId=%d\n",
pi.hProcess, pi.hThread,
(int) pi.dwProcessId, (int) pi.dwThreadId);
CloseHandle (pi.hThread);
CloseHandle (pi.hProcess);
return 0;
}
/* Kill a process; that is send an appropriate signal to the process.
gnupg_wait_process must be called to actually remove the process
from the system. An invalid PID is ignored. */
void
_gpgrt_kill_process (pid_t pid)
{
if (pid != (pid_t) INVALID_HANDLE_VALUE)
{
HANDLE process = (HANDLE) pid;
/* Arbitrary error code. */
_gpgrt_pre_syscall ();
TerminateProcess (process, 1);
_gpgrt_post_syscall ();
}
}
void
_gpgrt_release_process (pid_t pid)
{
if (pid != (pid_t)INVALID_HANDLE_VALUE)
{
HANDLE process = (HANDLE)pid;
CloseHandle (process);
}
}
diff --git a/src/visibility.c b/src/visibility.c
index 573a5a4..d754032 100644
--- a/src/visibility.c
+++ b/src/visibility.c
@@ -1,1181 +1,1193 @@
/* visibility.c - Wrapper for all public functions.
* Copyright (C) 2014 g10 Code GmbH
*
* This file is part of libgpg-error.
*
* libgpg-error is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* libgpg-error 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 .
* SPDX-License-Identifier: LGPL-2.1+
*/
#include
#include
#include /* For abort(). */
#define _GPGRT_INCL_BY_VISIBILITY_C 1
#include "gpgrt-int.h"
const char *
gpg_strerror (gpg_error_t err)
{
return _gpg_strerror (err);
}
int
gpg_strerror_r (gpg_error_t err, char *buf, size_t buflen)
{
return _gpg_strerror_r (err, buf, buflen);
}
const char *
gpg_strsource (gpg_error_t err)
{
return _gpg_strsource (err);
}
gpg_err_code_t
gpg_err_code_from_errno (int err)
{
return _gpg_err_code_from_errno (err);
}
int
gpg_err_code_to_errno (gpg_err_code_t code)
{
return _gpg_err_code_to_errno (code);
}
gpg_err_code_t
gpg_err_code_from_syserror (void)
{
return _gpg_err_code_from_syserror ();
}
void
gpg_err_set_errno (int err)
{
_gpg_err_set_errno (err);
}
gpg_error_t
gpg_err_init (void)
{
return _gpg_err_init ();
}
void
gpg_err_deinit (int mode)
{
_gpg_err_deinit (mode);
}
+void
+gpgrt_add_emergency_cleanup (void (*f)(void))
+{
+ _gpgrt_add_emergency_cleanup (f);
+}
+
+void
+gpgrt_abort (void)
+{
+ _gpgrt_abort ();
+}
+
const char *
gpg_error_check_version (const char *req_version)
{
return _gpg_error_check_version (req_version);
}
const char *
gpgrt_check_version (const char *req_version)
{
return _gpg_error_check_version (req_version);
}
void
gpgrt_set_syscall_clamp (void (*pre)(void), void (*post)(void))
{
_gpgrt_set_syscall_clamp (pre, post);
}
void
gpgrt_get_syscall_clamp (void (**r_pre)(void), void (**r_post)(void))
{
_gpgrt_get_syscall_clamp (r_pre, r_post);
}
void
gpgrt_set_alloc_func (void *(*f)(void *a, size_t n))
{
_gpgrt_set_alloc_func (f);
}
gpg_err_code_t
gpgrt_lock_init (gpgrt_lock_t *lockhd)
{
return _gpgrt_lock_init (lockhd);
}
gpg_err_code_t
gpgrt_lock_lock (gpgrt_lock_t *lockhd)
{
return _gpgrt_lock_lock (lockhd);
}
gpg_err_code_t
gpgrt_lock_trylock (gpgrt_lock_t *lockhd)
{
return _gpgrt_lock_trylock (lockhd);
}
gpg_err_code_t
gpgrt_lock_unlock (gpgrt_lock_t *lockhd)
{
return _gpgrt_lock_unlock (lockhd);
}
gpg_err_code_t
gpgrt_lock_destroy (gpgrt_lock_t *lockhd)
{
return _gpgrt_lock_destroy (lockhd);
}
gpg_err_code_t
gpgrt_yield (void)
{
return _gpgrt_yield ();
}
estream_t
gpgrt_fopen (const char *_GPGRT__RESTRICT path,
const char *_GPGRT__RESTRICT mode)
{
return _gpgrt_fopen (path, mode);
}
estream_t
gpgrt_mopen (void *_GPGRT__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 *_GPGRT__RESTRICT mode)
{
return _gpgrt_mopen (data, data_n, data_len, grow, func_realloc, func_free,
mode);
}
estream_t
gpgrt_fopenmem (size_t memlimit, const char *_GPGRT__RESTRICT mode)
{
return _gpgrt_fopenmem (memlimit, mode);
}
estream_t
gpgrt_fopenmem_init (size_t memlimit, const char *_GPGRT__RESTRICT mode,
const void *data, size_t datalen)
{
return _gpgrt_fopenmem_init (memlimit, mode, data, datalen);
}
estream_t
gpgrt_fdopen (int filedes, const char *mode)
{
return _gpgrt_fdopen (filedes, mode);
}
estream_t
gpgrt_fdopen_nc (int filedes, const char *mode)
{
return _gpgrt_fdopen_nc (filedes, mode);
}
estream_t
gpgrt_sysopen (es_syshd_t *syshd, const char *mode)
{
return _gpgrt_sysopen (syshd, mode);
}
estream_t
gpgrt_sysopen_nc (es_syshd_t *syshd, const char *mode)
{
return _gpgrt_sysopen_nc (syshd, mode);
}
estream_t
gpgrt_fpopen (FILE *fp, const char *mode)
{
return _gpgrt_fpopen (fp, mode);
}
estream_t
gpgrt_fpopen_nc (FILE *fp, const char *mode)
{
return _gpgrt_fpopen_nc (fp, mode);
}
estream_t
gpgrt_freopen (const char *_GPGRT__RESTRICT path,
const char *_GPGRT__RESTRICT mode,
estream_t _GPGRT__RESTRICT stream)
{
return _gpgrt_freopen (path, mode, stream);
}
estream_t
gpgrt_fopencookie (void *_GPGRT__RESTRICT cookie,
const char *_GPGRT__RESTRICT mode,
gpgrt_cookie_io_functions_t functions)
{
return _gpgrt_fopencookie (cookie, mode, functions);
}
int
gpgrt_fclose (estream_t stream)
{
return _gpgrt_fclose (stream);
}
int
gpgrt_fclose_snatch (estream_t stream, void **r_buffer, size_t *r_buflen)
{
return _gpgrt_fclose_snatch (stream, r_buffer, r_buflen);
}
int
gpgrt_onclose (estream_t stream, int mode,
void (*fnc) (estream_t, void*), void *fnc_value)
{
return _gpgrt_onclose (stream, mode, fnc, fnc_value);
}
int
gpgrt_fileno (estream_t stream)
{
return _gpgrt_fileno (stream);
}
int
gpgrt_fileno_unlocked (estream_t stream)
{
return _gpgrt_fileno_unlocked (stream);
}
int
gpgrt_syshd (estream_t stream, es_syshd_t *syshd)
{
return _gpgrt_syshd (stream, syshd);
}
int
gpgrt_syshd_unlocked (estream_t stream, es_syshd_t *syshd)
{
return _gpgrt_syshd_unlocked (stream, syshd);
}
void
_gpgrt_set_std_fd (int no, int fd)
{
_gpgrt__set_std_fd (no, fd); /* (double dash in name) */
}
estream_t
_gpgrt_get_std_stream (int fd)
{
return _gpgrt__get_std_stream (fd); /* (double dash in name) */
}
void
gpgrt_flockfile (estream_t stream)
{
_gpgrt_flockfile (stream);
}
int
gpgrt_ftrylockfile (estream_t stream)
{
return _gpgrt_ftrylockfile (stream);
}
void
gpgrt_funlockfile (estream_t stream)
{
_gpgrt_funlockfile (stream);
}
int
_gpgrt_pending (estream_t stream)
{
return _gpgrt__pending (stream);
}
int
_gpgrt_pending_unlocked (estream_t stream)
{
return _gpgrt__pending_unlocked (stream);
}
int
gpgrt_feof (estream_t stream)
{
return _gpgrt_feof (stream);
}
int
gpgrt_feof_unlocked (estream_t stream)
{
return _gpgrt_feof_unlocked (stream);
}
int
gpgrt_ferror (estream_t stream)
{
return _gpgrt_ferror (stream);
}
int
gpgrt_ferror_unlocked (estream_t stream)
{
return _gpgrt_ferror_unlocked (stream);
}
void
gpgrt_clearerr (estream_t stream)
{
_gpgrt_clearerr (stream);
}
void
gpgrt_clearerr_unlocked (estream_t stream)
{
_gpgrt_clearerr_unlocked (stream);
}
int
gpgrt_fflush (estream_t stream)
{
return _gpgrt_fflush (stream);
}
int
gpgrt_fseek (estream_t stream, long int offset, int whence)
{
return _gpgrt_fseek (stream, offset, whence);
}
int
gpgrt_fseeko (estream_t stream, gpgrt_off_t offset, int whence)
{
return _gpgrt_fseeko (stream, offset, whence);
}
long int
gpgrt_ftell (estream_t stream)
{
return _gpgrt_ftell (stream);
}
gpgrt_off_t
gpgrt_ftello (estream_t stream)
{
return _gpgrt_ftello (stream);
}
void
gpgrt_rewind (estream_t stream)
{
_gpgrt_rewind (stream);
}
int
gpgrt_ftruncate (estream_t stream, gpgrt_off_t length)
{
return _gpgrt_ftruncate (stream, length);
}
int
gpgrt_fgetc (estream_t stream)
{
return _gpgrt_fgetc (stream);
}
int
_gpgrt_getc_underflow (estream_t stream)
{
return _gpgrt__getc_underflow (stream);
}
int
gpgrt_fputc (int c, estream_t stream)
{
return _gpgrt_fputc (c, stream);
}
int
_gpgrt_putc_overflow (int c, estream_t stream)
{
return _gpgrt__putc_overflow (c, stream);
}
int
gpgrt_ungetc (int c, estream_t stream)
{
return _gpgrt_ungetc (c, stream);
}
int
gpgrt_read (estream_t _GPGRT__RESTRICT stream,
void *_GPGRT__RESTRICT buffer, size_t bytes_to_read,
size_t *_GPGRT__RESTRICT bytes_read)
{
return _gpgrt_read (stream, buffer, bytes_to_read, bytes_read);
}
int
gpgrt_write (estream_t _GPGRT__RESTRICT stream,
const void *_GPGRT__RESTRICT buffer, size_t bytes_to_write,
size_t *_GPGRT__RESTRICT bytes_written)
{
return _gpgrt_write (stream, buffer, bytes_to_write, bytes_written);
}
int
gpgrt_write_sanitized (estream_t _GPGRT__RESTRICT stream,
const void * _GPGRT__RESTRICT buffer, size_t length,
const char * delimiters,
size_t * _GPGRT__RESTRICT bytes_written)
{
return _gpgrt_write_sanitized (stream, buffer, length, delimiters,
bytes_written);
}
int
gpgrt_write_hexstring (estream_t _GPGRT__RESTRICT stream,
const void *_GPGRT__RESTRICT buffer, size_t length,
int reserved, size_t *_GPGRT__RESTRICT bytes_written )
{
return _gpgrt_write_hexstring (stream, buffer, length, reserved,
bytes_written);
}
size_t
gpgrt_fread (void *_GPGRT__RESTRICT ptr, size_t size, size_t nitems,
estream_t _GPGRT__RESTRICT stream)
{
return _gpgrt_fread (ptr, size, nitems, stream);
}
size_t
gpgrt_fwrite (const void *_GPGRT__RESTRICT ptr, size_t size, size_t nitems,
estream_t _GPGRT__RESTRICT stream)
{
return _gpgrt_fwrite (ptr, size, nitems, stream);
}
char *
gpgrt_fgets (char *_GPGRT__RESTRICT buffer, int length,
estream_t _GPGRT__RESTRICT stream)
{
return _gpgrt_fgets (buffer, length, stream);
}
int
gpgrt_fputs (const char *_GPGRT__RESTRICT s, estream_t _GPGRT__RESTRICT stream)
{
return _gpgrt_fputs (s, stream);
}
int
gpgrt_fputs_unlocked (const char *_GPGRT__RESTRICT s,
estream_t _GPGRT__RESTRICT stream)
{
return _gpgrt_fputs_unlocked (s, stream);
}
gpgrt_ssize_t
gpgrt_getline (char *_GPGRT__RESTRICT *_GPGRT__RESTRICT lineptr,
size_t *_GPGRT__RESTRICT n, estream_t _GPGRT__RESTRICT stream)
{
return _gpgrt_getline (lineptr, n, stream);
}
gpgrt_ssize_t
gpgrt_read_line (estream_t stream,
char **addr_of_buffer, size_t *length_of_buffer,
size_t *max_length)
{
return _gpgrt_read_line (stream, addr_of_buffer, length_of_buffer,
max_length);
}
int
gpgrt_vfprintf (estream_t _GPGRT__RESTRICT stream,
const char *_GPGRT__RESTRICT format,
va_list ap)
{
return _gpgrt_vfprintf (stream, NULL, NULL, format, ap);
}
int
gpgrt_vfprintf_unlocked (estream_t _GPGRT__RESTRICT stream,
const char *_GPGRT__RESTRICT format,
va_list ap)
{
return _gpgrt_vfprintf_unlocked (stream, NULL, NULL, format, ap);
}
int
gpgrt_printf (const char *_GPGRT__RESTRICT format, ...)
{
va_list ap;
int rc;
va_start (ap, format);
rc = _gpgrt_vfprintf (es_stdout, NULL, NULL, format, ap);
va_end (ap);
return rc;
}
int
gpgrt_printf_unlocked (const char *_GPGRT__RESTRICT format, ...)
{
va_list ap;
int rc;
va_start (ap, format);
rc = _gpgrt_vfprintf_unlocked (es_stdout, NULL, NULL, format, ap);
va_end (ap);
return rc;
}
int
gpgrt_fprintf (estream_t _GPGRT__RESTRICT stream,
const char *_GPGRT__RESTRICT format, ...)
{
va_list ap;
int rc;
va_start (ap, format);
rc = _gpgrt_vfprintf (stream, NULL, NULL, format, ap);
va_end (ap);
return rc;
}
int
gpgrt_fprintf_unlocked (estream_t _GPGRT__RESTRICT stream,
const char *_GPGRT__RESTRICT format, ...)
{
va_list ap;
int rc;
va_start (ap, format);
rc = _gpgrt_vfprintf_unlocked (stream, NULL, NULL, format, ap);
va_end (ap);
return rc;
}
int
gpgrt_fprintf_sf (estream_t _GPGRT__RESTRICT stream,
gpgrt_string_filter_t sf, void *sfvalue,
const char *_GPGRT__RESTRICT format, ...)
{
va_list ap;
int rc;
va_start (ap, format);
rc = _gpgrt_vfprintf (stream, sf, sfvalue, format, ap);
va_end (ap);
return rc;
}
int
gpgrt_fprintf_sf_unlocked (estream_t _GPGRT__RESTRICT stream,
gpgrt_string_filter_t sf, void *sfvalue,
const char *_GPGRT__RESTRICT format, ...)
{
va_list ap;
int rc;
va_start (ap, format);
rc = _gpgrt_vfprintf_unlocked (stream, sf, sfvalue, format, ap);
va_end (ap);
return rc;
}
int
gpgrt_setvbuf (estream_t _GPGRT__RESTRICT stream,
char *_GPGRT__RESTRICT buf, int type, size_t size)
{
return _gpgrt_setvbuf (stream, buf, type, size);
}
void
gpgrt_setbuf (estream_t _GPGRT__RESTRICT stream, char *_GPGRT__RESTRICT buf)
{
_gpgrt_setvbuf (stream, buf, buf? _IOFBF : _IONBF, BUFSIZ);
}
void
gpgrt_set_binary (estream_t stream)
{
_gpgrt_set_binary (stream);
}
int
gpgrt_set_nonblock (estream_t stream, int onoff)
{
return _gpgrt_set_nonblock (stream, onoff);
}
int
gpgrt_get_nonblock (estream_t stream)
{
return _gpgrt_get_nonblock (stream);
}
int
gpgrt_poll (gpgrt_poll_t *fds, unsigned int nfds, int timeout)
{
return _gpgrt_poll (fds, nfds, timeout);
}
estream_t
gpgrt_tmpfile (void)
{
return _gpgrt_tmpfile ();
}
void
gpgrt_opaque_set (estream_t stream, void *opaque)
{
_gpgrt_opaque_set (stream, opaque);
}
void *
gpgrt_opaque_get (estream_t stream)
{
return _gpgrt_opaque_get (stream);
}
void
gpgrt_fname_set (estream_t stream, const char *fname)
{
_gpgrt_fname_set (stream, fname);
}
const char *
gpgrt_fname_get (estream_t stream)
{
return _gpgrt_fname_get (stream);
}
int
gpgrt_asprintf (char **r_buf, const char *_GPGRT__RESTRICT format, ...)
{
va_list ap;
int rc;
va_start (ap, format);
rc = _gpgrt_estream_vasprintf (r_buf, format, ap);
va_end (ap);
return rc;
}
int
gpgrt_vasprintf (char **r_buf, const char *_GPGRT__RESTRICT format, va_list ap)
{
return _gpgrt_estream_vasprintf (r_buf, format, ap);
}
char *
gpgrt_bsprintf (const char *_GPGRT__RESTRICT format, ...)
{
int rc;
va_list ap;
char *buf;
va_start (ap, format);
rc = _gpgrt_estream_vasprintf (&buf, format, ap);
va_end (ap);
if (rc < 0)
return NULL;
return buf;
}
char *
gpgrt_vbsprintf (const char *_GPGRT__RESTRICT format, va_list ap)
{
int rc;
char *buf;
rc = _gpgrt_estream_vasprintf (&buf, format, ap);
if (rc < 0)
return NULL;
return buf;
}
int
gpgrt_snprintf (char *buf, size_t bufsize, const char *format, ...)
{
int rc;
va_list arg_ptr;
va_start (arg_ptr, format);
rc = _gpgrt_estream_vsnprintf (buf, bufsize, format, arg_ptr);
va_end (arg_ptr);
return rc;
}
int
gpgrt_vsnprintf (char *buf, size_t bufsize,
const char *format, va_list arg_ptr)
{
return _gpgrt_estream_vsnprintf (buf, bufsize, format, arg_ptr);
}
void *
gpgrt_realloc (void *a, size_t n)
{
return _gpgrt_realloc (a, n);
}
void *
gpgrt_malloc (size_t n)
{
return _gpgrt_malloc (n);
}
void *
gpgrt_calloc (size_t n, size_t m)
{
return _gpgrt_calloc (n, m);
}
char *
gpgrt_strdup (const char *string)
{
return _gpgrt_strdup (string);
}
char *
gpgrt_strconcat (const char *s1, ...)
{
va_list arg_ptr;
char *result;
if (!s1)
result = _gpgrt_strdup ("");
else
{
va_start (arg_ptr, s1);
result = _gpgrt_strconcat_core (s1, arg_ptr);
va_end (arg_ptr);
}
return result;
}
void
gpgrt_free (void *a)
{
if (a)
_gpgrt_free (a);
}
char *
gpgrt_getenv (const char *name)
{
return _gpgrt_getenv (name);
}
gpg_err_code_t
gpgrt_setenv (const char *name, const char *value, int overwrite)
{
return _gpgrt_setenv (name, value, overwrite);
}
gpg_err_code_t
gpgrt_mkdir (const char *name, const char *modestr)
{
return _gpgrt_mkdir (name, modestr);
}
gpg_err_code_t
gpgrt_chdir (const char *name)
{
return _gpgrt_chdir (name);
}
char *
gpgrt_getcwd (void)
{
return _gpgrt_getcwd ();
}
gpgrt_b64state_t
gpgrt_b64enc_start (estream_t stream, const char *title)
{
return _gpgrt_b64enc_start (stream, title);
}
gpg_err_code_t
gpgrt_b64enc_write (gpgrt_b64state_t state, const void *buffer, size_t nbytes)
{
return _gpgrt_b64enc_write (state, buffer, nbytes);
}
gpg_err_code_t
gpgrt_b64enc_finish (gpgrt_b64state_t state)
{
return _gpgrt_b64enc_finish (state);
}
gpgrt_b64state_t
gpgrt_b64dec_start (const char *title)
{
return _gpgrt_b64dec_start (title);
}
gpg_error_t
gpgrt_b64dec_proc (gpgrt_b64state_t state, void *buffer,
size_t length, size_t *r_nbytes)
{
return _gpgrt_b64dec_proc (state, buffer, length, r_nbytes);
}
gpg_error_t
gpgrt_b64dec_finish (gpgrt_b64state_t state)
{
return _gpgrt_b64dec_finish (state);
}
int
gpgrt_get_errorcount (int clear)
{
return _gpgrt_get_errorcount (clear);
}
void
gpgrt_inc_errorcount (void)
{
_gpgrt_inc_errorcount ();
}
void
gpgrt_log_set_sink (const char *name, estream_t stream, int fd)
{
_gpgrt_log_set_sink (name, stream, fd);
}
void
gpgrt_log_set_socket_dir_cb (const char *(*fnc)(void))
{
_gpgrt_log_set_socket_dir_cb (fnc);
}
void
gpgrt_log_set_pid_suffix_cb (int (*cb)(unsigned long *r_value))
{
_gpgrt_log_set_pid_suffix_cb (cb);
}
void
gpgrt_log_set_prefix (const char *text, unsigned int flags)
{
_gpgrt_log_set_prefix (text, flags);
}
const char *
gpgrt_log_get_prefix (unsigned int *flags)
{
return _gpgrt_log_get_prefix (flags);
}
int
gpgrt_log_test_fd (int fd)
{
return _gpgrt_log_test_fd (fd);
}
int
gpgrt_log_get_fd (void)
{
return _gpgrt_log_get_fd ();
}
estream_t
gpgrt_log_get_stream (void)
{
return _gpgrt_log_get_stream ();
}
void
gpgrt_log (int level, const char *fmt, ...)
{
va_list arg_ptr ;
va_start (arg_ptr, fmt) ;
_gpgrt_logv (level, fmt, arg_ptr);
va_end (arg_ptr);
}
void
gpgrt_logv (int level, const char *fmt, va_list arg_ptr)
{
_gpgrt_logv (level, fmt, arg_ptr);
}
void
gpgrt_logv_prefix (int level, const char *prefix,
const char *fmt, va_list arg_ptr)
{
_gpgrt_logv_prefix (level, prefix, fmt, arg_ptr);
}
void
gpgrt_log_string (int level, const char *string)
{
_gpgrt_log_string (level, string);
}
void
gpgrt_log_info (const char *fmt, ...)
{
va_list arg_ptr;
va_start (arg_ptr, fmt);
_gpgrt_logv (GPGRT_LOGLVL_INFO, fmt, arg_ptr);
va_end (arg_ptr);
}
void
gpgrt_log_error (const char *fmt, ...)
{
va_list arg_ptr;
va_start (arg_ptr, fmt);
_gpgrt_logv (GPGRT_LOGLVL_ERROR, fmt, arg_ptr);
va_end (arg_ptr);
}
void
gpgrt_log_fatal (const char *fmt, ...)
{
va_list arg_ptr;
va_start (arg_ptr, fmt);
_gpgrt_logv (GPGRT_LOGLVL_FATAL, fmt, arg_ptr);
va_end (arg_ptr);
- abort (); /* Never called; just to make the compiler happy. */
+ _gpgrt_abort (); /* Never called; just to make the compiler happy. */
}
void
gpgrt_log_bug (const char *fmt, ...)
{
va_list arg_ptr;
va_start (arg_ptr, fmt);
_gpgrt_logv (GPGRT_LOGLVL_BUG, fmt, arg_ptr);
va_end (arg_ptr);
- abort (); /* Never called; just to make the compiler happy. */
+ _gpgrt_abort (); /* Never called; just to make the compiler happy. */
}
void
gpgrt_log_debug (const char *fmt, ...)
{
va_list arg_ptr ;
va_start (arg_ptr, fmt);
_gpgrt_logv (GPGRT_LOGLVL_DEBUG, fmt, arg_ptr);
va_end (arg_ptr);
}
void
gpgrt_log_debug_string (const char *string, const char *fmt, ...)
{
va_list arg_ptr ;
va_start (arg_ptr, fmt);
_gpgrt_logv_internal (GPGRT_LOGLVL_DEBUG, 0, string, NULL, fmt, arg_ptr);
va_end (arg_ptr);
}
void
gpgrt_log_printf (const char *fmt, ...)
{
va_list arg_ptr;
va_start (arg_ptr, fmt);
_gpgrt_logv (fmt ? GPGRT_LOGLVL_CONT : GPGRT_LOGLVL_BEGIN, fmt, arg_ptr);
va_end (arg_ptr);
}
void
gpgrt_log_flush (void)
{
_gpgrt_log_flush ();
}
void
gpgrt_log_printhex (const void *buffer, size_t length, const char *fmt, ...)
{
va_list arg_ptr;
va_start (arg_ptr, fmt);
_gpgrt_logv_printhex (buffer, length, fmt, arg_ptr);
va_end (arg_ptr);
}
void
gpgrt_log_clock (const char *fmt, ...)
{
va_list arg_ptr;
va_start (arg_ptr, fmt);
_gpgrt_logv_clock (fmt, arg_ptr);
va_end (arg_ptr);
}
void
_gpgrt_log_assert (const char *expr, const char *file,
int line, const char *func)
{
#ifdef GPGRT_HAVE_MACRO_FUNCTION
_gpgrt__log_assert (expr, file, line, func);
#else
_gpgrt__log_assert (expr, file, line);
#endif
}
#if 0
gpg_err_code_t
gpgrt_make_pipe (int filedes[2], estream_t *r_fp, int direction, int nonblock)
{
return _gpgrt_make_pipe (filedes, r_fp, direction, nonblock);
}
gpg_err_code_t
gpgrt_spawn_process (const char *pgmname, const char *argv[],
int *except, void (*preexec)(void), unsigned int flags,
estream_t *r_infp, estream_t *r_outfp, estream_t *r_errfp,
pid_t *pid)
{
return _gpgrt_spawn_process (pgmname, argv, except, preexec, flags,
r_infp, r_outfp, r_errfp, pid);
}
gpg_err_code_t
gpgrt_spawn_process_fd (const char *pgmname, const char *argv[],
int infd, int outfd, int errfd, pid_t *pid)
{
return _gpgrt_spawn_process_fd (pgmname, argv, infd, outfd, errfd, pid);
}
gpg_err_code_t
gpgrt_spawn_process_detached (const char *pgmname, const char *argv[],
const char *envp[])
{
return _gpgrt_spawn_process_detached (pgmname, argv, envp);
}
gpg_err_code_t
gpgrt_wait_process (const char *pgmname, pid_t pid, int hang, int *r_exitcode)
{
return _gpgrt_wait_process (pgmname, pid, hang, r_exitcode);
}
gpg_err_code_t
gpgrt_wait_processes (const char **pgmnames, pid_t *pids,
size_t count, int hang, int *r_exitcodes)
{
return _gpgrt_wait_processes (pgmnames, pids, count, hang, r_exitcodes);
}
void
gpgrt_kill_process (pid_t pid)
{
_gpgrt_kill_process (pid);
}
void
gpgrt_release_process (pid_t pid)
{
_gpgrt_release_process (pid);
}
#endif /*0*/
int
gpgrt_argparse (estream_t fp, gpgrt_argparse_t *arg, gpgrt_opt_t *opts)
{
return _gpgrt_argparse (fp, arg, opts);
}
void
gpgrt_usage (int level)
{
_gpgrt_usage (level);
}
const char *
gpgrt_strusage (int level)
{
return _gpgrt_strusage (level);
}
void
gpgrt_set_strusage (const char *(*f)(int))
{
_gpgrt_set_strusage (f);
}
void
gpgrt_set_usage_outfnc (int (*f)(int, const char *))
{
_gpgrt_set_usage_outfnc (f);
}
void
gpgrt_set_fixed_string_mapper (const char *(*f)(const char*))
{
_gpgrt_set_fixed_string_mapper (f);
}
/* Compare program versions. */
int
gpgrt_cmp_version (const char *a, const char *b, int level)
{
return _gpgrt_cmp_version (a, b, level);
}
/* For consistency reasons we use function wrappers also for Windows
* specific function despite that they are technically not needed. */
#ifdef HAVE_W32_SYSTEM
char *
gpgrt_w32_reg_query_string (const char *root, const char *dir, const char *name)
{
return _gpgrt_w32_reg_query_string (root, dir, name);
}
#endif /*HAVE_W32_SYSTEM*/
diff --git a/src/visibility.h b/src/visibility.h
index 2dde522..28038d0 100644
--- a/src/visibility.h
+++ b/src/visibility.h
@@ -1,398 +1,402 @@
/* visibility.h - Set visibility attribute
* Copyright (C) 2014 g10 Code GmbH
*
* This file is part of libgpg-error.
*
* libgpg-error is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* libgpg-error 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 .
* SPDX-License-Identifier: LGPL-2.1+
*/
#ifndef _GPGRT_VISIBILITY_H
#define _GPGRT_VISIBILITY_H
/* Include the main header here so that public symbols are mapped to
the internal underscored ones. */
#ifdef _GPGRT_INCL_BY_VISIBILITY_C
# include "gpgrt-int.h"
#endif
/* Our use of the ELF visibility feature works by passing
-fvisibiliy=hidden on the command line and by explicitly marking
all exported functions as visible.
NOTE: When adding new functions, please make sure to add them to
gpg-error.vers and gpg-error.def.in as well. */
#ifdef _GPGRT_INCL_BY_VISIBILITY_C
# ifdef GPGRT_USE_VISIBILITY
# define MARK_VISIBLE(name) \
extern __typeof__ (name) name __attribute__ ((visibility("default")));
# else
# define MARK_VISIBLE(name) /* */
# endif
MARK_VISIBLE (gpg_strerror)
MARK_VISIBLE (gpg_strerror_r)
MARK_VISIBLE (gpg_strsource)
MARK_VISIBLE (gpg_err_code_from_errno)
MARK_VISIBLE (gpg_err_code_to_errno)
MARK_VISIBLE (gpg_err_code_from_syserror)
MARK_VISIBLE (gpg_err_set_errno)
MARK_VISIBLE (gpg_err_init)
MARK_VISIBLE (gpg_err_deinit)
+MARK_VISIBLE (gpgrt_add_emergency_cleanup)
+MARK_VISIBLE (gpgrt_abort)
MARK_VISIBLE (gpg_error_check_version)
MARK_VISIBLE (gpgrt_check_version)
MARK_VISIBLE (gpgrt_lock_init)
MARK_VISIBLE (gpgrt_lock_lock)
MARK_VISIBLE (gpgrt_lock_unlock)
MARK_VISIBLE (gpgrt_lock_destroy)
MARK_VISIBLE (gpgrt_yield)
MARK_VISIBLE (gpgrt_lock_trylock)
MARK_VISIBLE (gpgrt_fopen)
MARK_VISIBLE (gpgrt_mopen)
MARK_VISIBLE (gpgrt_fopenmem)
MARK_VISIBLE (gpgrt_fopenmem_init)
MARK_VISIBLE (gpgrt_fdopen)
MARK_VISIBLE (gpgrt_fdopen_nc)
MARK_VISIBLE (gpgrt_sysopen)
MARK_VISIBLE (gpgrt_sysopen_nc)
MARK_VISIBLE (gpgrt_fpopen)
MARK_VISIBLE (gpgrt_fpopen_nc)
MARK_VISIBLE (gpgrt_freopen)
MARK_VISIBLE (gpgrt_fopencookie)
MARK_VISIBLE (gpgrt_fclose)
MARK_VISIBLE (gpgrt_fclose_snatch)
MARK_VISIBLE (gpgrt_onclose)
MARK_VISIBLE (gpgrt_fileno)
MARK_VISIBLE (gpgrt_fileno_unlocked)
MARK_VISIBLE (gpgrt_syshd)
MARK_VISIBLE (gpgrt_syshd_unlocked)
MARK_VISIBLE (_gpgrt_set_std_fd)
MARK_VISIBLE (_gpgrt_get_std_stream)
MARK_VISIBLE (gpgrt_flockfile)
MARK_VISIBLE (gpgrt_ftrylockfile)
MARK_VISIBLE (gpgrt_funlockfile)
MARK_VISIBLE (_gpgrt_pending)
MARK_VISIBLE (_gpgrt_pending_unlocked)
MARK_VISIBLE (gpgrt_feof)
MARK_VISIBLE (gpgrt_feof_unlocked)
MARK_VISIBLE (gpgrt_ferror)
MARK_VISIBLE (gpgrt_ferror_unlocked)
MARK_VISIBLE (gpgrt_clearerr)
MARK_VISIBLE (gpgrt_clearerr_unlocked)
MARK_VISIBLE (gpgrt_fflush)
MARK_VISIBLE (gpgrt_fseek)
MARK_VISIBLE (gpgrt_fseeko)
MARK_VISIBLE (gpgrt_ftell)
MARK_VISIBLE (gpgrt_ftello)
MARK_VISIBLE (gpgrt_rewind)
MARK_VISIBLE (gpgrt_ftruncate)
MARK_VISIBLE (gpgrt_fgetc)
MARK_VISIBLE (_gpgrt_getc_underflow)
MARK_VISIBLE (gpgrt_fputc)
MARK_VISIBLE (_gpgrt_putc_overflow)
MARK_VISIBLE (gpgrt_ungetc)
MARK_VISIBLE (gpgrt_read)
MARK_VISIBLE (gpgrt_write)
MARK_VISIBLE (gpgrt_write_sanitized)
MARK_VISIBLE (gpgrt_write_hexstring)
MARK_VISIBLE (gpgrt_fread)
MARK_VISIBLE (gpgrt_fwrite)
MARK_VISIBLE (gpgrt_fgets)
MARK_VISIBLE (gpgrt_fputs)
MARK_VISIBLE (gpgrt_fputs_unlocked)
MARK_VISIBLE (gpgrt_getline)
MARK_VISIBLE (gpgrt_read_line)
MARK_VISIBLE (gpgrt_fprintf)
MARK_VISIBLE (gpgrt_fprintf_unlocked)
MARK_VISIBLE (gpgrt_fprintf_sf)
MARK_VISIBLE (gpgrt_fprintf_sf_unlocked)
MARK_VISIBLE (gpgrt_printf)
MARK_VISIBLE (gpgrt_printf_unlocked)
MARK_VISIBLE (gpgrt_vfprintf)
MARK_VISIBLE (gpgrt_vfprintf_unlocked)
MARK_VISIBLE (gpgrt_setvbuf)
MARK_VISIBLE (gpgrt_setbuf)
MARK_VISIBLE (gpgrt_set_binary)
MARK_VISIBLE (gpgrt_set_nonblock)
MARK_VISIBLE (gpgrt_get_nonblock)
MARK_VISIBLE (gpgrt_poll)
MARK_VISIBLE (gpgrt_tmpfile)
MARK_VISIBLE (gpgrt_opaque_set)
MARK_VISIBLE (gpgrt_opaque_get)
MARK_VISIBLE (gpgrt_fname_set)
MARK_VISIBLE (gpgrt_fname_get)
MARK_VISIBLE (gpgrt_asprintf)
MARK_VISIBLE (gpgrt_vasprintf)
MARK_VISIBLE (gpgrt_bsprintf)
MARK_VISIBLE (gpgrt_vbsprintf)
MARK_VISIBLE (gpgrt_snprintf)
MARK_VISIBLE (gpgrt_vsnprintf)
MARK_VISIBLE (gpgrt_set_syscall_clamp)
MARK_VISIBLE (gpgrt_get_syscall_clamp)
MARK_VISIBLE (gpgrt_set_alloc_func)
MARK_VISIBLE (gpgrt_realloc)
MARK_VISIBLE (gpgrt_malloc)
MARK_VISIBLE (gpgrt_calloc)
MARK_VISIBLE (gpgrt_strdup)
MARK_VISIBLE (gpgrt_strconcat)
MARK_VISIBLE (gpgrt_free)
MARK_VISIBLE (gpgrt_getenv)
MARK_VISIBLE (gpgrt_setenv)
MARK_VISIBLE (gpgrt_mkdir)
MARK_VISIBLE (gpgrt_chdir)
MARK_VISIBLE (gpgrt_getcwd)
MARK_VISIBLE (gpgrt_b64dec_start)
MARK_VISIBLE (gpgrt_b64dec_proc)
MARK_VISIBLE (gpgrt_b64dec_finish)
MARK_VISIBLE (gpgrt_b64enc_start)
MARK_VISIBLE (gpgrt_b64enc_write)
MARK_VISIBLE (gpgrt_b64enc_finish)
MARK_VISIBLE (gpgrt_get_errorcount)
MARK_VISIBLE (gpgrt_inc_errorcount)
MARK_VISIBLE (gpgrt_log_set_sink)
MARK_VISIBLE (gpgrt_log_set_socket_dir_cb)
MARK_VISIBLE (gpgrt_log_set_pid_suffix_cb)
MARK_VISIBLE (gpgrt_log_set_prefix)
MARK_VISIBLE (gpgrt_log_get_prefix)
MARK_VISIBLE (gpgrt_log_test_fd)
MARK_VISIBLE (gpgrt_log_get_fd)
MARK_VISIBLE (gpgrt_log_get_stream)
MARK_VISIBLE (gpgrt_log)
MARK_VISIBLE (gpgrt_logv)
MARK_VISIBLE (gpgrt_logv_prefix)
MARK_VISIBLE (gpgrt_log_string)
MARK_VISIBLE (gpgrt_log_bug)
MARK_VISIBLE (gpgrt_log_fatal)
MARK_VISIBLE (gpgrt_log_error)
MARK_VISIBLE (gpgrt_log_info)
MARK_VISIBLE (gpgrt_log_debug)
MARK_VISIBLE (gpgrt_log_debug_string)
MARK_VISIBLE (gpgrt_log_printf)
MARK_VISIBLE (gpgrt_log_printhex)
MARK_VISIBLE (gpgrt_log_clock)
MARK_VISIBLE (gpgrt_log_flush)
MARK_VISIBLE (_gpgrt_log_assert)
#if 0
MARK_VISIBLE (gpgrt_make_pipe)
MARK_VISIBLE (gpgrt_spawn_process)
MARK_VISIBLE (gpgrt_spawn_process_fd)
MARK_VISIBLE (gpgrt_spawn_process_detached)
MARK_VISIBLE (gpgrt_wait_process)
MARK_VISIBLE (gpgrt_wait_processes)
MARK_VISIBLE (gpgrt_kill_process)
MARK_VISIBLE (gpgrt_release_process)
#endif
MARK_VISIBLE (gpgrt_argparse)
MARK_VISIBLE (gpgrt_usage)
MARK_VISIBLE (gpgrt_strusage)
MARK_VISIBLE (gpgrt_set_strusage)
MARK_VISIBLE (gpgrt_set_fixed_string_mapper);
MARK_VISIBLE (gpgrt_set_usage_outfnc);
MARK_VISIBLE (gpgrt_cmp_version);
#undef MARK_VISIBLE
#else /*!_GPGRT_INCL_BY_VISIBILITY_C*/
/* To avoid accidental use of the public functions inside Libgpg-error,
we redefine them to catch such errors. */
#define gpg_strerror _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpg_strerror_r _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpg_strsource _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpg_err_code_from_errno _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpg_err_code_to_errno _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpg_err_code_from_syserror _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpg_err_set_errno _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpg_err_init _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpg_err_deinit _gpgrt_USE_UNDERSCORED_FUNCTION
+#define gpgrt_add_emergency_cleanup _gpgrt_USE_UNDERSCORED_FUNCTION
+#define gpgrt_abort _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpg_error_check_version _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_check_version _gpgrt_USE_OTHER_FUNCTION
#define gpgrt_lock_init _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_lock_lock _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_lock_unlock _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_lock_destroy _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_yield _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_lock_trylock _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_fopen _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_mopen _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_fopenmem _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_fopenmem_init _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_fdopen _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_fdopen_nc _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_sysopen _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_sysopen_nc _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_fpopen _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_fpopen_nc _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_freopen _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_fopencookie _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_fclose _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_fclose_snatch _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_onclose _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_fileno _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_fileno_unlocked _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_syshd _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_syshd_unlocked _gpgrt_USE_UNDERSCORED_FUNCTION
#define _gpgrt_set_std_fd _gpgrt_USE_UNDERSCORED_FUNCTION
#define _gpgrt_get_std_stream _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_flockfile _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_ftrylockfile _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_funlockfile _gpgrt_USE_UNDERSCORED_FUNCTION
#define _gpgrt_pending _gpgrt_USE_UNDERSCORED_FUNCTION
#define _gpgrt_pending_unlocked _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_feof _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_feof_unlocked _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_ferror _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_ferror_unlocked _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_clearerr _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_clearerr_unlocked _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_fflush _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_fseek _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_fseeko _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_ftell _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_ftello _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_rewind _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_ftruncate _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_fgetc _gpgrt_USE_UNDERSCORED_FUNCTION
#define _gpgrt_getc_underflow _gpgrt_USE_DBLUNDERSCO_FUNCTION
#define gpgrt_fputc _gpgrt_USE_UNDERSCORED_FUNCTION
#define _gpgrt_putc_overflow _gpgrt_USE_DBLUNDERSCO_FUNCTION
#define gpgrt_ungetc _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_read _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_write _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_write_sanitized _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_write_hexstring _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_fread _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_fwrite _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_fgets _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_fputs _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_fputs_unlocked _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_getline _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_read_line _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_fprintf _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_fprintf_unlocked _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_fprintf_sf _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_fprintf_sf_unlocked _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_printf _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_printf_unlocked _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_vfprintf _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_vfprintf_unlocked _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_setvbuf _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_setbuf _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_set_binary _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_set_nonblock _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_get_nonblock _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_poll _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_tmpfile _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_opaque_set _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_opaque_get _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_fname_set _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_fname_get _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_asprintf _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_vasprintf _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_bsprintf _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_vbsprintf _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_snprintf _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_vsnprintf _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_realloc _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_malloc _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_calloc _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_strdup _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_strconcat _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_free _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_getenv _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_setenv _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_mkdir _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_chdir _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_getcwd _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_set_syscall_clamp _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_get_syscall_clamp _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_set_alloc_func _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_b64enc_start _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_b64enc_write _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_b64enc_finish _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_b64dec_start _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_b64dec_proc _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_b64dec_finish _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_get_errorcount _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_inc_errorcount _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_log_set_sink _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_log_set_socket_dir_cb _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_log_set_pid_suffix_cb _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_log_set_prefix _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_log_get_prefix _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_log_test_fd _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_log_get_fd _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_log_get_stream _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_log _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_logv _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_logv_prefix _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_log_string _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_log_bug _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_log_fatal _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_log_error _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_log_info _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_log_debug _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_log_debug_string _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_log_printf _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_log_printhex _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_log_clock _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_log_flush _gpgrt_USE_UNDERSCORED_FUNCTION
#define _gpgrt_log_assert _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_make_pipe _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_spawn_process _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_spawn_process_fd _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_spawn_process_detached _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_wait_process _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_wait_processes _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_kill_process _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_release_process _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_argparse _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_usage _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_set_strusage _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_strusage _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_set_usage_outfnc _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_set_fixed_string_mapper _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_cmp_version _gpgrt_USE_UNDERSCORED_FUNCTION
/* Windows specific functions. */
#define gpgrt_w32_reg_query_string _gpgrt_USE_UNDERSCORED_FUNCTION
#endif /*!_GPGRT_INCL_BY_VISIBILITY_C*/
#endif /*_GPGRT_VISIBILITY_H*/
diff --git a/src/w32-estream.c b/src/w32-estream.c
index 5d29b2c..9e33cdd 100644
--- a/src/w32-estream.c
+++ b/src/w32-estream.c
@@ -1,1065 +1,1064 @@
/* w32-estream.c - es_poll support on W32.
* Copyright (C) 2000 Werner Koch (dd9jn)
* Copyright (C) 2001, 2002, 2003, 2004, 2007, 2010, 2016 g10 Code GmbH
*
* This file is part of libgpg-error.
*
* libgpg-error is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* libgpg-error 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 .
*/
/*
* This file is based on GPGME's w32-io.c started in 2001.
*/
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include
#include
-#include
#include
#include
#ifdef HAVE_SYS_TIME_H
# include
#endif
#ifdef HAVE_SYS_TYPES_H
# include
#endif
#include
#include
/* Enable tracing. The value is the module name to be printed. */
/*#define ENABLE_TRACING "estream" */
#include "gpgrt-int.h"
/*
* In order to support es_poll on Windows, we create a proxy shim that
* we use as the estream I/O functions. This shim creates reader and
* writer threads that use the original I/O functions.
*/
/* Calculate array dimension. */
#ifndef DIM
#define DIM(array) (sizeof (array) / sizeof (*array))
#endif
#define READBUF_SIZE 4096
#define WRITEBUF_SIZE 4096
typedef struct estream_cookie_w32_pollable *estream_cookie_w32_pollable_t;
struct reader_context_s
{
estream_cookie_w32_pollable_t pcookie;
HANDLE thread_hd;
CRITICAL_SECTION mutex;
int stop_me;
int eof;
int eof_shortcut;
int error;
int error_code;
/* This is manually reset. */
HANDLE have_data_ev;
/* This is automatically reset. */
HANDLE have_space_ev;
/* This is manually reset but actually only triggered once. */
HANDLE close_ev;
size_t readpos, writepos;
char buffer[READBUF_SIZE];
};
struct writer_context_s
{
estream_cookie_w32_pollable_t pcookie;
HANDLE thread_hd;
CRITICAL_SECTION mutex;
int stop_me;
int error;
int error_code;
/* This is manually reset. */
HANDLE have_data;
HANDLE is_empty;
HANDLE close_ev;
size_t nbytes;
char buffer[WRITEBUF_SIZE];
};
/* Cookie for pollable objects. */
struct estream_cookie_w32_pollable
{
unsigned int modeflags;
struct cookie_io_functions_s next_functions;
void *next_cookie;
struct reader_context_s *reader;
struct writer_context_s *writer;
};
static DWORD CALLBACK
reader (void *arg)
{
struct reader_context_s *ctx = arg;
int nbytes;
ssize_t nread;
trace (("%p: reader starting", ctx));
for (;;)
{
EnterCriticalSection (&ctx->mutex);
/* Leave a 1 byte gap so that we can see whether it is empty or
full. */
while ((ctx->writepos + 1) % READBUF_SIZE == ctx->readpos)
{
/* Wait for space. */
if (!ResetEvent (ctx->have_space_ev))
trace (("%p: ResetEvent failed: ec=%d", ctx, (int)GetLastError()));
LeaveCriticalSection (&ctx->mutex);
trace (("%p: waiting for space", ctx));
WaitForSingleObject (ctx->have_space_ev, INFINITE);
trace (("%p: got space", ctx));
EnterCriticalSection (&ctx->mutex);
}
- assert (((ctx->writepos + 1) % READBUF_SIZE != ctx->readpos));
+ gpgrt_assert (((ctx->writepos + 1) % READBUF_SIZE != ctx->readpos));
if (ctx->stop_me)
{
LeaveCriticalSection (&ctx->mutex);
break;
}
nbytes = (ctx->readpos + READBUF_SIZE
- ctx->writepos - 1) % READBUF_SIZE;
- assert (nbytes);
+ gpgrt_assert (nbytes);
if (nbytes > READBUF_SIZE - ctx->writepos)
nbytes = READBUF_SIZE - ctx->writepos;
LeaveCriticalSection (&ctx->mutex);
trace (("%p: reading up to %d bytes", ctx, nbytes));
nread = ctx->pcookie->next_functions.public.func_read
(ctx->pcookie->next_cookie, ctx->buffer + ctx->writepos, nbytes);
trace (("%p: got %d bytes", ctx, nread));
if (nread < 0)
{
ctx->error_code = (int) errno;
/* NOTE (W32CE): Do not ignore ERROR_BUSY! Check at
least stop_me if that happens. */
if (ctx->error_code == ERROR_BROKEN_PIPE)
{
ctx->eof = 1;
trace (("%p: got EOF (broken pipe)", ctx));
}
else
{
ctx->error = 1;
trace (("%p: read error: ec=%d", ctx, ctx->error_code));
}
break;
}
EnterCriticalSection (&ctx->mutex);
if (ctx->stop_me)
{
LeaveCriticalSection (&ctx->mutex);
break;
}
if (!nread)
{
ctx->eof = 1;
trace (("%p: got eof", ctx));
LeaveCriticalSection (&ctx->mutex);
break;
}
ctx->writepos = (ctx->writepos + nread) % READBUF_SIZE;
if (!SetEvent (ctx->have_data_ev))
trace (("%p: SetEvent (%p) failed: ec=%d",
ctx, ctx->have_data_ev, (int)GetLastError ()));
LeaveCriticalSection (&ctx->mutex);
}
/* Indicate that we have an error or EOF. */
if (!SetEvent (ctx->have_data_ev))
trace (("%p: SetEvent (%p) failed: ec=%d",
ctx, ctx->have_data_ev, (int)GetLastError ()));
trace (("%p: waiting for close", ctx));
WaitForSingleObject (ctx->close_ev, INFINITE);
CloseHandle (ctx->close_ev);
CloseHandle (ctx->have_data_ev);
CloseHandle (ctx->have_space_ev);
CloseHandle (ctx->thread_hd);
DeleteCriticalSection (&ctx->mutex);
free (ctx); /* Standard free! See comment in create_reader. */
return 0;
}
static struct reader_context_s *
create_reader (estream_cookie_w32_pollable_t pcookie)
{
struct reader_context_s *ctx;
SECURITY_ATTRIBUTES sec_attr;
DWORD tid;
memset (&sec_attr, 0, sizeof sec_attr);
sec_attr.nLength = sizeof sec_attr;
sec_attr.bInheritHandle = FALSE;
/* The CTX must be allocated in standard system memory so that we
* won't use any custom allocation handler which may use our lock
* primitives for its implementation. The problem here is that the
* syscall clamp mechanism (e.g. nPth) would be called recursively:
* 1. For example by the caller of _gpgrt_w32_poll and 2. by
* gpgrt_lock_lock on behalf of the the custom allocation and free
* functions. */
ctx = calloc (1, sizeof *ctx);
if (!ctx)
{
return NULL;
}
ctx->pcookie = pcookie;
ctx->have_data_ev = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
if (ctx->have_data_ev)
ctx->have_space_ev = CreateEvent (&sec_attr, FALSE, TRUE, NULL);
if (ctx->have_space_ev)
ctx->close_ev = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
if (!ctx->have_data_ev || !ctx->have_space_ev || !ctx->close_ev)
{
trace (("%p: CreateEvent failed: ec=%d", ctx, (int)GetLastError ()));
if (ctx->have_data_ev)
CloseHandle (ctx->have_data_ev);
if (ctx->have_space_ev)
CloseHandle (ctx->have_space_ev);
if (ctx->close_ev)
CloseHandle (ctx->close_ev);
_gpgrt_free (ctx);
return NULL;
}
InitializeCriticalSection (&ctx->mutex);
#ifdef HAVE_W32CE_SYSTEM
ctx->thread_hd = CreateThread (&sec_attr, 64 * 1024, reader, ctx,
STACK_SIZE_PARAM_IS_A_RESERVATION, &tid);
#else
ctx->thread_hd = CreateThread (&sec_attr, 0, reader, ctx, 0, &tid);
#endif
if (!ctx->thread_hd)
{
trace (("%p: CreateThread failed: ec=%d", ctx, (int)GetLastError ()));
DeleteCriticalSection (&ctx->mutex);
if (ctx->have_data_ev)
CloseHandle (ctx->have_data_ev);
if (ctx->have_space_ev)
CloseHandle (ctx->have_space_ev);
if (ctx->close_ev)
CloseHandle (ctx->close_ev);
_gpgrt_free (ctx);
return NULL;
}
else
{
#if 0
/* We set the priority of the thread higher because we know that
it only runs for a short time. This greatly helps to
increase the performance of the I/O. */
SetThreadPriority (ctx->thread_hd, get_desired_thread_priority ());
#endif
}
return ctx;
}
/* Prepare destruction of the reader thread for CTX. Returns 0 if a
call to this function is sufficient and destroy_reader_finish shall
not be called. */
static void
destroy_reader (struct reader_context_s *ctx)
{
EnterCriticalSection (&ctx->mutex);
ctx->stop_me = 1;
if (ctx->have_space_ev)
SetEvent (ctx->have_space_ev);
LeaveCriticalSection (&ctx->mutex);
#ifdef HAVE_W32CE_SYSTEM
/* Scenario: We never create a full pipe, but already started
reading. Then we need to unblock the reader in the pipe driver
to make our reader thread notice that we want it to go away. */
if (ctx->file_hd != INVALID_HANDLE_VALUE)
{
if (!DeviceIoControl (ctx->file_hd, GPGCEDEV_IOCTL_UNBLOCK,
NULL, 0, NULL, 0, NULL, NULL))
{
trace (("%p: unblock control call failed: ec=%d",
ctx, (int)GetLastError ()));
}
}
#endif
/* XXX is it feasible to unblock the thread? */
/* After setting this event CTX is void. */
SetEvent (ctx->close_ev);
}
/*
* Read function for pollable objects.
*/
static gpgrt_ssize_t
func_w32_pollable_read (void *cookie, void *buffer, size_t count)
{
estream_cookie_w32_pollable_t pcookie = cookie;
gpgrt_ssize_t nread;
struct reader_context_s *ctx;
trace (("%p: enter buffer=%p count=%u", cookie, buffer, count));
/* FIXME: implement pending check if COUNT==0 */
ctx = pcookie->reader;
if (ctx == NULL)
{
pcookie->reader = ctx = create_reader (pcookie);
if (!ctx)
{
_gpg_err_set_errno (EBADF);
nread = -1;
goto leave;
}
trace (("%p: new reader %p", cookie, pcookie->reader));
}
if (ctx->eof_shortcut)
{
nread = 0;
goto leave;
}
EnterCriticalSection (&ctx->mutex);
trace (("%p: readpos: %d, writepos %d", cookie, ctx->readpos, ctx->writepos));
if (ctx->readpos == ctx->writepos && !ctx->error)
{
/* No data available. */
int eof = ctx->eof;
LeaveCriticalSection (&ctx->mutex);
if (pcookie->modeflags & O_NONBLOCK && ! eof)
{
_gpg_err_set_errno (EAGAIN);
nread = -1;
goto leave;
}
trace (("%p: waiting for data", cookie));
WaitForSingleObject (ctx->have_data_ev, INFINITE);
trace (("%p: data available", cookie));
EnterCriticalSection (&ctx->mutex);
}
if (ctx->readpos == ctx->writepos || ctx->error)
{
LeaveCriticalSection (&ctx->mutex);
ctx->eof_shortcut = 1;
if (ctx->eof)
return 0;
if (!ctx->error)
{
trace (("%p: EOF but ctx->eof flag not set", cookie));
nread = 0;
goto leave;
}
_gpg_err_set_errno (ctx->error_code);
return -1;
}
nread = ctx->readpos < ctx->writepos
? ctx->writepos - ctx->readpos
: READBUF_SIZE - ctx->readpos;
if (nread > count)
nread = count;
memcpy (buffer, ctx->buffer + ctx->readpos, nread);
ctx->readpos = (ctx->readpos + nread) % READBUF_SIZE;
if (ctx->readpos == ctx->writepos && !ctx->eof)
{
if (!ResetEvent (ctx->have_data_ev))
{
trace (("%p: ResetEvent failed: ec=%d",
cookie, (int)GetLastError ()));
LeaveCriticalSection (&ctx->mutex);
/* FIXME: Should translate the error code. */
_gpg_err_set_errno (EIO);
nread = -1;
goto leave;
}
}
if (!SetEvent (ctx->have_space_ev))
{
trace (("%p: SetEvent (%p) failed: ec=%d",
cookie, ctx->have_space_ev, (int)GetLastError ()));
LeaveCriticalSection (&ctx->mutex);
/* FIXME: Should translate the error code. */
_gpg_err_set_errno (EIO);
nread = -1;
goto leave;
}
LeaveCriticalSection (&ctx->mutex);
leave:
trace_errno (nread==-1,("%p: leave nread=%d", cookie, (int)nread));
return nread;
}
/* The writer does use a simple buffering strategy so that we are
informed about write errors as soon as possible (i. e. with the the
next call to the write function. */
static DWORD CALLBACK
writer (void *arg)
{
struct writer_context_s *ctx = arg;
ssize_t nwritten;
trace (("%p: writer starting", ctx));
for (;;)
{
EnterCriticalSection (&ctx->mutex);
if (ctx->stop_me && !ctx->nbytes)
{
LeaveCriticalSection (&ctx->mutex);
break;
}
if (!ctx->nbytes)
{
if (!SetEvent (ctx->is_empty))
trace (("%p: SetEvent failed: ec=%d", ctx, (int)GetLastError ()));
if (!ResetEvent (ctx->have_data))
trace (("%p: ResetEvent failed: ec=%d", ctx, (int)GetLastError ()));
LeaveCriticalSection (&ctx->mutex);
trace (("%p: idle", ctx));
WaitForSingleObject (ctx->have_data, INFINITE);
trace (("%p: got data to write", ctx));
EnterCriticalSection (&ctx->mutex);
}
if (ctx->stop_me && !ctx->nbytes)
{
LeaveCriticalSection (&ctx->mutex);
break;
}
LeaveCriticalSection (&ctx->mutex);
trace (("%p: writing up to %d bytes", ctx, ctx->nbytes));
nwritten = ctx->pcookie->next_functions.public.func_write
(ctx->pcookie->next_cookie, ctx->buffer, ctx->nbytes);
trace (("%p: wrote %d bytes", ctx, nwritten));
if (nwritten < 1)
{
/* XXX */
if (errno == ERROR_BUSY)
{
/* Probably stop_me is set now. */
trace (("%p: pipe busy (unblocked?)", ctx));
continue;
}
ctx->error_code = errno;
ctx->error = 1;
trace (("%p: write error: ec=%d", ctx, ctx->error_code));
break;
}
EnterCriticalSection (&ctx->mutex);
ctx->nbytes -= nwritten;
LeaveCriticalSection (&ctx->mutex);
}
/* Indicate that we have an error. */
if (!SetEvent (ctx->is_empty))
trace (("%p: SetEvent failed: ec=%d", ctx, (int)GetLastError ()));
trace (("%p: waiting for close", ctx));
WaitForSingleObject (ctx->close_ev, INFINITE);
if (ctx->nbytes)
trace (("%p: still %d bytes in buffer at close time", ctx, ctx->nbytes));
CloseHandle (ctx->close_ev);
CloseHandle (ctx->have_data);
CloseHandle (ctx->is_empty);
CloseHandle (ctx->thread_hd);
DeleteCriticalSection (&ctx->mutex);
trace (("%p: writer is destroyed", ctx));
free (ctx); /* Standard free! See comment in create_writer. */
return 0;
}
static struct writer_context_s *
create_writer (estream_cookie_w32_pollable_t pcookie)
{
struct writer_context_s *ctx;
SECURITY_ATTRIBUTES sec_attr;
DWORD tid;
memset (&sec_attr, 0, sizeof sec_attr);
sec_attr.nLength = sizeof sec_attr;
sec_attr.bInheritHandle = FALSE;
/* See comment at create_reader. */
ctx = calloc (1, sizeof *ctx);
if (!ctx)
{
return NULL;
}
ctx->pcookie = pcookie;
ctx->have_data = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
if (ctx->have_data)
ctx->is_empty = CreateEvent (&sec_attr, TRUE, TRUE, NULL);
if (ctx->is_empty)
ctx->close_ev = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
if (!ctx->have_data || !ctx->is_empty || !ctx->close_ev)
{
trace (("%p: CreateEvent failed: ec=%d", ctx, (int)GetLastError ()));
if (ctx->have_data)
CloseHandle (ctx->have_data);
if (ctx->is_empty)
CloseHandle (ctx->is_empty);
if (ctx->close_ev)
CloseHandle (ctx->close_ev);
_gpgrt_free (ctx);
return NULL;
}
InitializeCriticalSection (&ctx->mutex);
#ifdef HAVE_W32CE_SYSTEM
ctx->thread_hd = CreateThread (&sec_attr, 64 * 1024, writer, ctx,
STACK_SIZE_PARAM_IS_A_RESERVATION, &tid);
#else
ctx->thread_hd = CreateThread (&sec_attr, 0, writer, ctx, 0, &tid );
#endif
if (!ctx->thread_hd)
{
trace (("%p: CreateThread failed: ec=%d", ctx, (int)GetLastError ()));
DeleteCriticalSection (&ctx->mutex);
if (ctx->have_data)
CloseHandle (ctx->have_data);
if (ctx->is_empty)
CloseHandle (ctx->is_empty);
if (ctx->close_ev)
CloseHandle (ctx->close_ev);
_gpgrt_free (ctx);
return NULL;
}
else
{
#if 0
/* We set the priority of the thread higher because we know
that it only runs for a short time. This greatly helps to
increase the performance of the I/O. */
SetThreadPriority (ctx->thread_hd, get_desired_thread_priority ());
#endif
}
return ctx;
}
static void
destroy_writer (struct writer_context_s *ctx)
{
trace (("%p: enter pollable_destroy_writer", ctx));
EnterCriticalSection (&ctx->mutex);
trace (("%p: setting stopme", ctx));
ctx->stop_me = 1;
if (ctx->have_data)
SetEvent (ctx->have_data);
LeaveCriticalSection (&ctx->mutex);
trace (("%p: waiting for empty", ctx));
/* Give the writer a chance to flush the buffer. */
WaitForSingleObject (ctx->is_empty, INFINITE);
#ifdef HAVE_W32CE_SYSTEM
/* Scenario: We never create a full pipe, but already started
writing more than the pipe buffer. Then we need to unblock the
writer in the pipe driver to make our writer thread notice that
we want it to go away. */
if (!DeviceIoControl (ctx->file_hd, GPGCEDEV_IOCTL_UNBLOCK,
NULL, 0, NULL, 0, NULL, NULL))
{
trace (("%p: unblock control call failed: ec=%d",
ctx, (int)GetLastError ()));
}
#endif
/* After setting this event CTX is void. */
trace (("%p: set close_ev", ctx));
SetEvent (ctx->close_ev);
trace (("%p: leave pollable_destroy_writer", ctx));
}
/*
* Write function for pollable objects.
*/
static gpgrt_ssize_t
func_w32_pollable_write (void *cookie, const void *buffer, size_t count)
{
estream_cookie_w32_pollable_t pcookie = cookie;
struct writer_context_s *ctx = pcookie->writer;
int nwritten;
trace (("%p: enter buffer: %p count: %d", cookie, buffer, count));
if (count == 0)
{
nwritten = 0;
goto leave;
}
if (ctx == NULL)
{
pcookie->writer = ctx = create_writer (pcookie);
if (!ctx)
{
nwritten = -1;
goto leave;
}
trace (("%p: new writer %p", cookie, pcookie->writer));
}
EnterCriticalSection (&ctx->mutex);
trace (("%p: buffer: %p, count: %d, nbytes: %d",
cookie, buffer, count, ctx->nbytes));
if (!ctx->error && ctx->nbytes)
{
/* Bytes are pending for send. */
/* Reset the is_empty event. Better safe than sorry. */
if (!ResetEvent (ctx->is_empty))
{
trace (("%p: ResetEvent failed: ec=%d",
cookie, (int)GetLastError ()));
LeaveCriticalSection (&ctx->mutex);
/* FIXME: Should translate the error code. */
_gpg_err_set_errno (EIO);
nwritten = -1;
goto leave;
}
LeaveCriticalSection (&ctx->mutex);
if (pcookie->modeflags & O_NONBLOCK)
{
trace (("%p: would block", cookie));
_gpg_err_set_errno (EAGAIN);
nwritten = -1;
goto leave;
}
trace (("%p: waiting for empty buffer", cookie));
WaitForSingleObject (ctx->is_empty, INFINITE);
trace (("%p: buffer is empty", cookie));
EnterCriticalSection (&ctx->mutex);
}
if (ctx->error)
{
LeaveCriticalSection (&ctx->mutex);
if (ctx->error_code == ERROR_NO_DATA)
_gpg_err_set_errno (EPIPE);
else
_gpg_err_set_errno (EIO);
nwritten = -1;
goto leave;
}
/* If no error occurred, the number of bytes in the buffer must be
zero. */
- assert (!ctx->nbytes);
+ gpgrt_assert (!ctx->nbytes);
if (count > WRITEBUF_SIZE)
count = WRITEBUF_SIZE;
memcpy (ctx->buffer, buffer, count);
ctx->nbytes = count;
/* We have to reset the is_empty event early, because it is also
used by the select() implementation to probe the channel. */
if (!ResetEvent (ctx->is_empty))
{
trace (("%p: ResetEvent failed: ec=%d", cookie, (int)GetLastError ()));
LeaveCriticalSection (&ctx->mutex);
/* FIXME: Should translate the error code. */
_gpg_err_set_errno (EIO);
nwritten = -1;
goto leave;
}
if (!SetEvent (ctx->have_data))
{
trace (("%p: SetEvent failed: ec=%d", cookie, (int)GetLastError ()));
LeaveCriticalSection (&ctx->mutex);
/* FIXME: Should translate the error code. */
_gpg_err_set_errno (EIO);
nwritten = -1;
goto leave;
}
LeaveCriticalSection (&ctx->mutex);
nwritten = count;
leave:
trace_errno (nwritten==-1,("%p: leave nwritten=%d", cookie, nwritten));
return nwritten;
}
/* This is the core of _gpgrt_poll. The caller needs to make sure that
* the syscall clamp has been engaged. */
int
_gpgrt_w32_poll (gpgrt_poll_t *fds, size_t nfds, int timeout)
{
HANDLE waitbuf[MAXIMUM_WAIT_OBJECTS];
int waitidx[MAXIMUM_WAIT_OBJECTS];
#ifdef ENABLE_TRACING
char waitinfo[MAXIMUM_WAIT_OBJECTS];
#endif
unsigned int code;
int nwait;
int i;
int any;
int count;
#if 0
restart:
#endif
any = 0;
nwait = 0;
count = 0;
for (i = 0; i < nfds; i++)
{
struct estream_cookie_w32_pollable *pcookie;
if (fds[i].ignore)
continue;
if (fds[i].stream->intern->kind != BACKEND_W32_POLLABLE)
{
/* This stream does not support polling. */
fds[i].got_err = 1;
continue;
}
pcookie = fds[i].stream->intern->cookie;
if (fds[i].want_read || fds[i].want_write)
{
/* XXX: What if one wants read and write, is that supported? */
if (fds[i].want_read)
{
struct reader_context_s *ctx = pcookie->reader;
if (ctx == NULL)
{
pcookie->reader = ctx = create_reader (pcookie);
if (!ctx)
{
/* FIXME: Is the error code appropriate? */
_gpg_err_set_errno (EBADF);
return -1;
}
trace (("%p: new reader %p", pcookie, pcookie->reader));
}
trace (("%p: using reader %p", pcookie, pcookie->reader));
if (nwait >= DIM (waitbuf))
{
trace (("oops: too many objects for WFMO"));
/* FIXME: Should translate the error code. */
_gpg_err_set_errno (EIO);
return -1;
}
waitidx[nwait] = i;
#ifdef ENABLE_TRACING
waitinfo[nwait] = 'r';
#endif /*ENABLE_TRACING*/
waitbuf[nwait++] = ctx->have_data_ev;
any = 1;
}
else if (fds[i].want_write)
{
struct writer_context_s *ctx = pcookie->writer;
if (ctx == NULL)
{
pcookie->writer = ctx = create_writer (pcookie);
if (!ctx)
{
trace (("oops: create writer failed"));
/* FIXME: Is the error code appropriate? */
_gpg_err_set_errno (EBADF);
return -1;
}
trace (("%p: new writer %p", pcookie, pcookie->writer));
}
trace (("%p: using writer %p", pcookie, pcookie->writer));
if (nwait >= DIM (waitbuf))
{
trace (("oops: Too many objects for WFMO"));
/* FIXME: Should translate the error code. */
_gpg_err_set_errno (EIO);
return -1;
}
waitidx[nwait] = i;
#ifdef ENABLE_TRACING
waitinfo[nwait] = 'w';
#endif /*ENABLE_TRACING*/
waitbuf[nwait++] = ctx->is_empty;
any = 1;
}
}
}
#ifdef ENABLE_TRACING
trace_start (("poll on [ "));
for (i = 0; i < nwait; i++)
trace_append (("%d/%c ", waitidx[i], waitinfo[i]));
trace_finish (("]"));
#endif /*ENABLE_TRACING*/
if (!any)
return 0;
code = WaitForMultipleObjects (nwait, waitbuf, 0,
timeout == -1 ? INFINITE : timeout);
if (code < WAIT_OBJECT_0 + nwait)
{
/* This WFMO is a really silly function: It does return either
the index of the signaled object or if 2 objects have been
signalled at the same time, the index of the object with the
lowest object is returned - so and how do we find out how
many objects have been signaled???. The only solution I can
imagine is to test each object starting with the returned
index individually - how dull. */
any = 0;
for (i = code - WAIT_OBJECT_0; i < nwait; i++)
{
if (WaitForSingleObject (waitbuf[i], 0) == WAIT_OBJECT_0)
{
- assert (waitidx[i] >=0 && waitidx[i] < nfds);
+ gpgrt_assert (waitidx[i] >=0 && waitidx[i] < nfds);
/* XXX: What if one wants read and write, is that
supported? */
if (fds[waitidx[i]].want_read)
fds[waitidx[i]].got_read = 1;
else if (fds[waitidx[i]].want_write)
fds[waitidx[i]].got_write = 1;
any = 1;
count++;
}
}
if (!any)
{
trace (("no signaled objects found after WFMO"));
count = -1;
}
}
else if (code == WAIT_TIMEOUT)
trace (("WFMO timed out"));
else if (code == WAIT_FAILED)
{
trace (("WFMO failed: ec=%d", (int)GetLastError ()));
#if 0
if (GetLastError () == ERROR_INVALID_HANDLE)
{
int k;
int j = handle_to_fd (waitbuf[i]);
trace (("WFMO invalid handle %d removed", j));
for (k = 0 ; k < nfds; k++)
{
if (fds[k].fd == j)
{
fds[k].want_read = fds[k].want_write = 0;
goto restart;
}
}
trace ((" oops, or not???"));
}
#endif
count = -1;
}
else
{
trace (("WFMO returned %u", code));
count = -1;
}
if (count > 0)
{
trace_start (("poll OK [ "));
for (i = 0; i < nfds; i++)
{
if (fds[i].ignore)
continue;
if (fds[i].got_read || fds[i].got_write)
trace_append (("%c%d ", fds[i].want_read ? 'r' : 'w', i));
}
trace_finish (("]"));
}
if (count < 0)
{
/* FIXME: Should determine a proper error code. */
_gpg_err_set_errno (EIO);
}
return count;
}
/*
* Implementation of pollable I/O on Windows.
*/
/*
* Constructor for pollable objects.
*/
int
_gpgrt_w32_pollable_create (void *_GPGRT__RESTRICT *_GPGRT__RESTRICT cookie,
unsigned int modeflags,
struct cookie_io_functions_s next_functions,
void *next_cookie)
{
estream_cookie_w32_pollable_t pcookie;
int err;
pcookie = _gpgrt_malloc (sizeof *pcookie);
if (!pcookie)
err = -1;
else
{
pcookie->modeflags = modeflags;
pcookie->next_functions = next_functions;
pcookie->next_cookie = next_cookie;
pcookie->reader = NULL;
pcookie->writer = NULL;
*cookie = pcookie;
err = 0;
}
trace_errno (err,("cookie=%p", *cookie));
return err;
}
/*
* Seek function for pollable objects.
*/
static int
func_w32_pollable_seek (void *cookie, gpgrt_off_t *offset, int whence)
{
estream_cookie_w32_pollable_t pcookie = cookie;
(void) pcookie;
(void) offset;
(void) whence;
/* XXX */
_gpg_err_set_errno (EOPNOTSUPP);
return -1;
}
/*
* The IOCTL function for pollable objects.
*/
static int
func_w32_pollable_ioctl (void *cookie, int cmd, void *ptr, size_t *len)
{
estream_cookie_w32_pollable_t pcookie = cookie;
cookie_ioctl_function_t func_ioctl = pcookie->next_functions.func_ioctl;
if (cmd == COOKIE_IOCTL_NONBLOCK)
{
if (ptr)
pcookie->modeflags |= O_NONBLOCK;
else
pcookie->modeflags &= ~O_NONBLOCK;
return 0;
}
if (func_ioctl)
return func_ioctl (pcookie->next_cookie, cmd, ptr, len);
_gpg_err_set_errno (EOPNOTSUPP);
return -1;
}
/*
* The destroy function for pollable objects.
*/
static int
func_w32_pollable_destroy (void *cookie)
{
estream_cookie_w32_pollable_t pcookie = cookie;
if (cookie)
{
if (pcookie->reader)
destroy_reader (pcookie->reader);
if (pcookie->writer)
destroy_writer (pcookie->writer);
pcookie->next_functions.public.func_close (pcookie->next_cookie);
_gpgrt_free (pcookie);
}
return 0;
}
/*
* Access object for the pollable functions.
*/
struct cookie_io_functions_s _gpgrt_functions_w32_pollable =
{
{
func_w32_pollable_read,
func_w32_pollable_write,
func_w32_pollable_seek,
func_w32_pollable_destroy,
},
func_w32_pollable_ioctl,
};
diff --git a/src/w32-lock.c b/src/w32-lock.c
index a55f932..feed1e6 100644
--- a/src/w32-lock.c
+++ b/src/w32-lock.c
@@ -1,161 +1,161 @@
/* w32-lock.c - GPGRT lock functions for Windows
Copyright (C) 2014 g10 Code GmbH
This file is part of libgpg-error.
libgpg-error is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
as published by the Free Software Foundation; either version 2.1 of
the License, or (at your option) any later version.
libgpg-error 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 .
*/
#if HAVE_CONFIG_H
#include
#endif
#ifndef HAVE_W32_SYSTEM
# error This module may only be build for Windows.
#endif
#include
#include
#include
#include
#define WIN32_LEAN_AND_MEAN
#include
#include "gpgrt-int.h"
#include "lock.h"
#include "w32-lock-obj.h"
static _gpgrt_lock_t *
get_lock_object (gpgrt_lock_t *lockhd)
{
_gpgrt_lock_t *lock = (_gpgrt_lock_t*)lockhd;
if (lock->vers != LOCK_ABI_VERSION)
- abort ();
+ _gpgrt_abort ();
return lock;
}
gpg_err_code_t
_gpgrt_lock_init (gpgrt_lock_t *lockhd)
{
_gpgrt_lock_t *lock = (_gpgrt_lock_t*)lockhd;
/* If VERS is zero we assume that no static initialization has been
done, so we setup our ABI version right here. The caller might
have called us to test whether lock support is at all available. */
if (!lock->vers)
{
if (sizeof (gpgrt_lock_t) < sizeof (_gpgrt_lock_t))
- abort ();
+ _gpgrt_abort ();
lock->vers = LOCK_ABI_VERSION;
}
else /* Run the usual check. */
{
lock = get_lock_object (lockhd);
if (sizeof (gpgrt_lock_t) < sizeof (_gpgrt_lock_t))
- abort ();
+ _gpgrt_abort ();
}
InitializeCriticalSection (&lock->csec);
lock->initdone = 1;
return 0;
}
gpg_err_code_t
_gpgrt_lock_lock (gpgrt_lock_t *lockhd)
{
_gpgrt_lock_t *lock = get_lock_object (lockhd);
if (!lock->initdone)
{
if (!InterlockedIncrement (&lock->started))
{
/* The new value of started is 0. Because the initial value
if the variable was -1 we known that this thread is the
first who needs this lock. Thus we initialize now. All
other threads won't get 0 back from InterlockedIncrement
and thus fall into the wait loop below. We ignore that
STARTED may in theory overflow if this thread starves for
too long. */
_gpgrt_lock_init (lockhd);
}
else
{
while (!lock->initdone)
Sleep (0);
}
}
_gpgrt_pre_syscall ();
EnterCriticalSection (&lock->csec);
_gpgrt_post_syscall ();
return 0;
}
gpg_err_code_t
_gpgrt_lock_trylock (gpgrt_lock_t *lockhd)
{
_gpgrt_lock_t *lock = get_lock_object (lockhd);
if (!lock->initdone)
{
if (!InterlockedIncrement (&lock->started))
{
_gpgrt_lock_init (lockhd);
}
else
{
while (!lock->initdone)
Sleep (0);
}
}
if (!TryEnterCriticalSection (&lock->csec))
return GPG_ERR_EBUSY;
return 0;
}
gpg_err_code_t
_gpgrt_lock_unlock (gpgrt_lock_t *lockhd)
{
_gpgrt_lock_t *lock = get_lock_object (lockhd);
if (!lock->initdone)
return GPG_ERR_INV_LOCK_OBJ;
LeaveCriticalSection (&lock->csec);
return 0;
}
/* Note: Use this function only if no other thread holds or waits for
this lock. */
gpg_err_code_t
_gpgrt_lock_destroy (gpgrt_lock_t *lockhd)
{
_gpgrt_lock_t *lock = get_lock_object (lockhd);
if (!lock->initdone)
return GPG_ERR_INV_LOCK_OBJ;
DeleteCriticalSection (&lock->csec);
lock->initdone = 0;
lock->started = -1;
return 0;
}