diff --git a/Makefile.am b/Makefile.am index 074b9a3..8897f17 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,176 +1,176 @@ # Makefile.am for libgpg-error. # Copyright (C) 2003, 2006, 2007, 2013 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-or-later # Location of the released tarball archives. This is prefixed by # the variable RELEASE_ARCHIVE in ~/.gnupg-autogen.rc. For example: # RELEASE_ARCHIVE=wk@somehost:archive/tarballs RELEASE_ARCHIVE_SUFFIX = libgpg-error # The variable RELEASE_SIGNING_KEY in ~/.gnupg-autogen.rc is used # to specify the key for signing. For example: # RELEASE_SIGNKEY=D8692123C4065DEA5E0F3AB5249B39D24F25E3B6 # Autoconf flags ACLOCAL_AMFLAGS = -I m4 DISTCHECK_CONFIGURE_FLAGS = --enable-doc # (A suitable gitlog-to-changelog script can be found in GnuPG master.) GITLOG_TO_CHANGELOG=gitlog-to-changelog EXTRA_DIST = autogen.sh autogen.rc libgpg-error.spec.in \ VERSION COPYING COPYING.LIB doc/HACKING ChangeLog-2011 \ - po/ChangeLog-2011 m4/ChangeLog-2011 contrib/ChangeLog-2011 \ + po/ChangeLog-2011 m4/ChangeLog-2011 \ build-aux/git-log-footer build-aux/git-log-fix if LANGUAGES_SOME lang_subdirs = lang else lang_subdirs = endif #if BUILD_GPGSCM #doc = gpgscm #else #doc = #endif if BUILD_DOC doc = doc else doc = endif if BUILD_TESTS tests = tests else tests = endif SUBDIRS = m4 src $(doc) $(tests) po $(lang_subdirs) dist-hook: gen-ChangeLog sed -e 's/@pkg_version@/$(VERSION)/g' \ $(top_srcdir)/libgpg-error.spec.in > $(distdir)/libgpg-error.spec distcheck-hook: set -e; ( \ pref="#+macro: $$(echo $(PACKAGE_NAME)|tr '-' '_')_" ;\ reldate="$$(date -u +%Y-%m-%d)" ;\ echo "$${pref}ver $(PACKAGE_VERSION)" ;\ echo "$${pref}date $${reldate}" ;\ list='$(DIST_ARCHIVES)'; for i in $$list; do \ case "$$i" in *.tar.bz2) \ echo "$${pref}size $$(wc -c <$$i|awk '{print int($$1/1024)}')k" ;\ echo "$${pref}sha1 $$(sha1sum <$$i|cut -d' ' -f1)" ;\ echo "$${pref}sha2 $$(sha256sum <$$i|cut -d' ' -f1)" ;;\ esac;\ done ) | tee $(distdir).swdb gen_start_date = 2011-12-01T00:00:00 .PHONY: gen-ChangeLog release sign-release gen-ChangeLog: set -e; \ if test -d $(top_srcdir)/.git; then \ (cd $(top_srcdir) && \ $(GITLOG_TO_CHANGELOG) --append-dot --tear-off \ --amend=build-aux/git-log-fix --tear-off \ --since=$(gen_start_date) ) > $(distdir)/cl-t; \ cat $(top_srcdir)/build-aux/git-log-footer >> $(distdir)/cl-t;\ rm -f $(distdir)/ChangeLog; \ mv $(distdir)/cl-t $(distdir)/ChangeLog; \ fi if HAVE_W32_SYSTEM install-data-hook: set -e; \ for i in $$(sed -e '/^#/d' -e 's/#.*//' $(top_srcdir)/po/LINGUAS); do \ $(MKDIR_P) "$(DESTDIR)$(localedir)/$$i/LC_MESSAGES" || true; \ rm -f "$(DESTDIR)$(localedir)/$$i/LC_MESSAGES/libgpg-error.mo" \ 2>/dev/null || true; \ msgfmt -o "$(DESTDIR)$(localedir)/$$i/LC_MESSAGES/libgpg-error.mo" \ $(top_srcdir)/po/$$i.po ; \ done endif stowinstall: $(MAKE) $(AM_MAKEFLAGS) install prefix=/usr/local/stow/libgpg-error # Macro to help the release target. RELEASE_NAME = $(PACKAGE_TARNAME)-$(PACKAGE_VERSION) release: +(set -e;\ if [ "$(abs_top_builddir)" = "$(abs_top_srcdir)" ]; then \ echo "error: build directory must not be the source directory" >&2;\ exit 2;\ fi ;\ echo "/* Build started at $$(date -uIseconds) */" ;\ cd $(top_srcdir); \ ./autogen.sh --force; \ cd $(abs_top_builddir); \ rm -rf dist; mkdir dist ; cd dist ; \ $(abs_top_srcdir)/configure --enable-maintainer-mode; \ $(MAKE) distcheck; \ echo "/* Build finished at $$(date -uIseconds) */" ;\ echo "/*" ;\ echo " * Please run the final step interactivly:" ;\ echo " * make sign-release" ;\ echo " */" ;\ ) 2>&1 | tee "$(RELEASE_NAME).buildlog" sign-release: +(set -e; \ test $$(pwd | sed 's,.*/,,') = dist || cd dist; \ x=$$(grep '^RELEASE_ARCHIVE=' $$HOME/.gnupg-autogen.rc|cut -d= -f2);\ if [ -z "$$x" ]; then \ echo "error: RELEASE_ARCHIVE missing in ~/.gnupg-autogen.rc">&2; \ exit 2;\ fi;\ myarchive="$$x/$(RELEASE_ARCHIVE_SUFFIX)";\ x=$$(grep '^RELEASE_SIGNKEY=' $$HOME/.gnupg-autogen.rc|cut -d= -f2);\ if [ -z "$$x" ]; then \ echo "error: RELEASE_SIGNKEY missing in ~/.gnupg-autogen.rc">&2; \ exit 2;\ fi;\ mysignkey="$$x";\ files1="$(RELEASE_NAME).tar.bz2 \ $(RELEASE_NAME).tar.gz" ; \ files2="$(RELEASE_NAME).tar.bz2.sig \ $(RELEASE_NAME).tar.gz.sig \ $(RELEASE_NAME).swdb \ $(RELEASE_NAME).buildlog" ;\ echo "/* Signing the source tarball ..." ;\ gpg -sbu $$mysignkey $(RELEASE_NAME).tar.bz2 ;\ gpg -sbu $$mysignkey $(RELEASE_NAME).tar.gz ;\ cat $(RELEASE_NAME).swdb >swdb.snippet;\ echo >>swdb.snippet ;\ sha1sum $${files1} >>swdb.snippet ;\ cat "../$(RELEASE_NAME).buildlog" swdb.snippet \ | gzip >$(RELEASE_NAME).buildlog ;\ echo "Copying to archive $$myarchive ..." ;\ scp -p $${files1} $${files2} $${myarchive}/ || true;\ echo '/*' ;\ echo ' * All done; for checksums see dist/swdb.snippet' ;\ echo ' */' ;\ ) diff --git a/autogen.sh b/autogen.sh index 8f33ece..9ea0612 100755 --- a/autogen.sh +++ b/autogen.sh @@ -1,513 +1,499 @@ #! /bin/sh # autogen.sh -# Copyright (C) 2003, 2014, 2017, 2018 g10 Code GmbH +# Copyright (C) 2003, 2014, 2017, 2018, 2022 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 program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY, to the extent permitted by law; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # # This is a generic script to create the configure script and handle cross # build environments. It requires the presence of a autogen.rc file to # configure it for the respective package. It is maintained as part of # GnuPG and source copied by other packages. # -# Version: 2018-07-10 +# Version: 2022-06-28 configure_ac="configure.ac" cvtver () { awk 'NR==1 {split($NF,A,".");X=1000000*A[1]+1000*A[2]+A[3];print X;exit 0}' } check_version () { if [ $(( `("$1" --version || echo "0") | cvtver` >= $2 )) = 1 ]; then return 0 fi echo "**Error**: "\`$1\'" not installed or too old." >&2 echo ' Version '$3' or newer is required.' >&2 [ -n "$4" ] && echo ' Note that this is part of '\`$4\''.' >&2 DIE="yes" return 1 } fatal () { echo "autogen.sh:" "$*" >&2 DIE=yes } info () { if [ -z "${SILENT}" ]; then echo "autogen.sh:" "$*" >&2 fi } die_p () { if [ "$DIE" = "yes" ]; then echo "autogen.sh: Stop." >&2 exit 1 fi } replace_sysroot () { configure_opts=$(echo $configure_opts | sed "s#@SYSROOT@#${w32root}#g") extraoptions=$(echo $extraoptions | sed "s#@SYSROOT@#${w32root}#g") } # Allow to override the default tool names AUTOCONF=${AUTOCONF_PREFIX}${AUTOCONF:-autoconf}${AUTOCONF_SUFFIX} AUTOHEADER=${AUTOCONF_PREFIX}${AUTOHEADER:-autoheader}${AUTOCONF_SUFFIX} AUTOMAKE=${AUTOMAKE_PREFIX}${AUTOMAKE:-automake}${AUTOMAKE_SUFFIX} ACLOCAL=${AUTOMAKE_PREFIX}${ACLOCAL:-aclocal}${AUTOMAKE_SUFFIX} GETTEXT=${GETTEXT_PREFIX}${GETTEXT:-gettext}${GETTEXT_SUFFIX} MSGMERGE=${GETTEXT_PREFIX}${MSGMERGE:-msgmerge}${GETTEXT_SUFFIX} DIE=no FORCE= SILENT= PRINT_HOST=no PRINT_BUILD=no tmp=$(dirname "$0") tsdir=$(cd "${tmp}"; pwd) if [ -n "${AUTOGEN_SH_SILENT}" ]; then SILENT=" --silent" fi if test x"$1" = x"--help"; then echo "usage: ./autogen.sh [OPTIONS] [ARGS]" echo " Options:" echo " --silent Silent operation" echo " --force Pass --force to autoconf" echo " --find-version Helper for configure.ac" echo " --git-build Run all commands to build from a Git" echo " --print-host Print only the host triplet" echo " --print-build Print only the build platform triplet" echo " --build-TYPE Configure to cross build for TYPE" echo "" echo " ARGS are passed to configure in --build-TYPE mode." echo " Configuration for this script is expected in autogen.rc" exit 0 fi if test x"$1" = x"--silent"; then SILENT=" --silent" shift fi if test x"$1" = x"--force"; then FORCE=" --force" shift fi if test x"$1" = x"--print-host"; then PRINT_HOST=yes shift fi if test x"$1" = x"--print-build"; then PRINT_BUILD=yes shift fi # Reject unsafe characters in $HOME, $tsdir and cwd. We consider spaces # as unsafe because it is too easy to get scripts wrong in this regard. am_lf=' ' case `pwd` in *[\;\\\"\#\$\&\'\`$am_lf\ \ ]*) fatal "unsafe working directory name" ;; esac case $tsdir in *[\;\\\"\#\$\&\'\`$am_lf\ \ ]*) fatal "unsafe source directory: \`$tsdir'" ;; esac case $HOME in *[\;\\\"\#\$\&\'\`$am_lf\ \ ]*) fatal "unsafe home directory: \`$HOME'" ;; esac die_p # List of variables sourced from autogen.rc. The strings '@SYSROOT@' in # these variables are replaced by the actual system root. configure_opts= extraoptions= # List of optional variables sourced from autogen.rc and ~/.gnupg-autogen.rc w32_toolprefixes= w32_extraoptions= -w32ce_toolprefixes= -w32ce_extraoptions= w64_toolprefixes= w64_extraoptions= amd64_toolprefixes= # End list of optional variables sourced from ~/.gnupg-autogen.rc # What follows are variables which are sourced but default to # environment variables or lacking them hardcoded values. #w32root= -#w32ce_root= #w64root= #amd64root= # Convenience option to use certain configure options for some hosts. myhost="" myhostsub="" case "$1" in --find-version) myhost="find-version" SILENT=" --silent" shift ;; --git-build) myhost="git-build" shift ;; --build-w32) myhost="w32" shift ;; - --build-w32ce) - myhost="w32" - myhostsub="ce" - shift - ;; --build-w64) myhost="w32" myhostsub="64" shift ;; --build-amd64) myhost="amd64" shift ;; --build*) fatal "**Error**: invalid build option $1" shift ;; *) ;; esac die_p # **** GIT BUILD **** # This is a helper to build from git. if [ "$myhost" = "git-build" ]; then tmp="$(pwd)" cd "$tsdir" || fatal "error cd-ing to $tsdir" ./autogen.sh || fatal "error running ./autogen.sh" cd "$tmp" || fatal "error cd-ing back to $tmp" die_p "$tsdir"/configure || fatal "error running $tsdir/configure" die_p make || fatal "error running make" die_p make check || fatal "error running make check" die_p exit 0 fi # **** end GIT BUILD **** # Source our configuration if [ -f "${tsdir}/autogen.rc" ]; then . "${tsdir}/autogen.rc" fi # Source optional site specific configuration if [ -f "$HOME/.gnupg-autogen.rc" ]; then info "sourcing extra definitions from $HOME/.gnupg-autogen.rc" . "$HOME/.gnupg-autogen.rc" fi # **** FIND VERSION **** # This is a helper for the configure.ac M4 magic # Called # ./autogen.sh --find-version PACKAGE MAJOR MINOR [MICRO] # returns a complete version string with automatic beta numbering. if [ "$myhost" = "find-version" ]; then package="$1" major="$2" minor="$3" micro="$4" if [ -z "$package" -o -z "$major" -o -z "$minor" ]; then echo "usage: ./autogen.sh --find-version PACKAGE MAJOR MINOR [MICRO]" >&2 exit 1 fi if [ -z "$micro" ]; then matchstr1="$package-$major.[0-9]*" matchstr2="$package-$major-base" vers="$major.$minor" else matchstr1="$package-$major.$minor.[0-9]*" matchstr2="$package-$major.$minor-base" vers="$major.$minor.$micro" fi beta=no if [ -e .git ]; then ingit=yes tmp=$(git describe --match "${matchstr1}" --long 2>/dev/null) tmp=$(echo "$tmp" | sed s/^"$package"//) if [ -n "$tmp" ]; then tmp=$(echo "$tmp" | sed s/^"$package"// \ | awk -F- '$3!=0 && $3 !~ /^beta/ {print"-beta"$3}') else tmp=$(git describe --match "${matchstr2}" --long 2>/dev/null \ | awk -F- '$4!=0{print"-beta"$4}') fi [ -n "$tmp" ] && beta=yes rev=$(git rev-parse --short HEAD | tr -d '\n\r') rvd=$((0x$(echo ${rev} | dd bs=1 count=4 2>/dev/null))) else ingit=no beta=yes tmp="-unknown" rev="0000000" rvd="0" fi echo "$package-$vers$tmp:$beta:$ingit:$vers$tmp:$vers:$tmp:$rev:$rvd:" exit 0 fi # **** end FIND VERSION **** if [ ! -f "$tsdir/build-aux/config.guess" ]; then fatal "$tsdir/build-aux/config.guess not found" exit 1 fi build=`$tsdir/build-aux/config.guess` if [ $PRINT_BUILD = yes ]; then echo "$build" exit 0 fi # ****************** # W32 build script # ****************** if [ "$myhost" = "w32" ]; then case $myhostsub in - ce) - w32root="$w32ce_root" - [ -z "$w32root" ] && w32root="$HOME/w32ce_root" - toolprefixes="$w32ce_toolprefixes arm-mingw32ce" - extraoptions="$extraoptions $w32ce_extraoptions" - ;; 64) w32root="$w64root" [ -z "$w32root" ] && w32root="$HOME/w64root" toolprefixes="$w64_toolprefixes x86_64-w64-mingw32" extraoptions="$extraoptions $w64_extraoptions" ;; *) [ -z "$w32root" ] && w32root="$HOME/w32root" toolprefixes="$w32_toolprefixes i686-w64-mingw32 i586-mingw32msvc" toolprefixes="$toolprefixes i386-mingw32msvc mingw32" extraoptions="$extraoptions $w32_extraoptions" ;; esac info "Using $w32root as standard install directory" replace_sysroot # Locate the cross compiler crossbindir= for host in $toolprefixes; do if ${host}-gcc --version >/dev/null 2>&1 ; then crossbindir=/usr/${host}/bin conf_CC="CC=${host}-gcc" break; fi done if [ -z "$crossbindir" ]; then fatal "cross compiler kit not installed" if [ -z "$myhostsub" ]; then info "Under Debian GNU/Linux, you may install it using" info " apt-get install mingw32 mingw32-runtime mingw32-binutils" fi die_p fi if [ $PRINT_HOST = yes ]; then echo "$host" exit 0 fi if [ -f "$tsdir/config.log" ]; then if ! head $tsdir/config.log | grep "$host" >/dev/null; then fatal "Please run a 'make distclean' first" die_p fi fi $tsdir/configure --enable-maintainer-mode ${SILENT} \ --prefix=${w32root} \ --host=${host} --build=${build} SYSROOT=${w32root} \ PKG_CONFIG_LIBDIR=${w32root}/lib/pkgconfig \ ${configure_opts} ${extraoptions} "$@" rc=$? exit $rc fi # ***** end W32 build script ******* # ***** AMD64 cross build script ******* # Used to cross-compile for AMD64 (for testing) if [ "$myhost" = "amd64" ]; then [ -z "$amd64root" ] && amd64root="$HOME/amd64root" info "Using $amd64root as standard install directory" replace_sysroot toolprefixes="$amd64_toolprefixes x86_64-linux-gnu amd64-linux-gnu" # Locate the cross compiler crossbindir= for host in $toolprefixes ; do if ${host}-gcc --version >/dev/null 2>&1 ; then crossbindir=/usr/${host}/bin conf_CC="CC=${host}-gcc" break; fi done if [ -z "$crossbindir" ]; then echo "Cross compiler kit not installed" >&2 echo "Stop." >&2 exit 1 fi if [ $PRINT_HOST = yes ]; then echo "$host" exit 0 fi if [ -f "$tsdir/config.log" ]; then if ! head $tsdir/config.log | grep "$host" >/dev/null; then echo "Please run a 'make distclean' first" >&2 exit 1 fi fi $tsdir/configure --enable-maintainer-mode ${SILENT} \ --prefix=${amd64root} \ --host=${host} --build=${build} \ ${configure_opts} ${extraoptions} "$@" rc=$? exit $rc fi # ***** end AMD64 cross build script ******* # Grep the required versions from configure.ac autoconf_vers=`sed -n '/^AC_PREREQ(/ { s/^.*(\(.*\))/\1/p q }' ${configure_ac}` autoconf_vers_num=`echo "$autoconf_vers" | cvtver` automake_vers=`sed -n '/^min_automake_version=/ { s/^.*="\(.*\)"/\1/p q }' ${configure_ac}` automake_vers_num=`echo "$automake_vers" | cvtver` if [ -d "${tsdir}/po" ]; then gettext_vers=`sed -n '/^AM_GNU_GETTEXT_VERSION(/ { s/^.*\[\(.*\)])/\1/p q }' ${configure_ac}` gettext_vers_num=`echo "$gettext_vers" | cvtver` else gettext_vers="n/a" fi if [ -z "$autoconf_vers" -o -z "$automake_vers" -o -z "$gettext_vers" ] then echo "**Error**: version information not found in "\`${configure_ac}\'"." >&2 exit 1 fi if check_version $AUTOCONF $autoconf_vers_num $autoconf_vers ; then check_version $AUTOHEADER $autoconf_vers_num $autoconf_vers autoconf fi if check_version $AUTOMAKE $automake_vers_num $automake_vers; then check_version $ACLOCAL $automake_vers_num $autoconf_vers automake fi if [ "$gettext_vers" != "n/a" ]; then if check_version $GETTEXT $gettext_vers_num $gettext_vers; then check_version $MSGMERGE $gettext_vers_num $gettext_vers gettext fi fi if [ "$DIE" = "yes" ]; then cat </dev/null 2>/dev/null; then [ -z "${SILENT}" ] && CP="$CP -v" fi if [ -f .git/hooks/pre-commit.sample -a ! -f .git/hooks/pre-commit ] ; then [ -z "${SILENT}" ] && cat <. # SPDX-License-Identifier: LGPL-2.1+ # (Process this file with autoconf to produce a configure script.) # The following lines are used by ./autogen.sh. AC_PREREQ([2.69]) min_automake_version="1.14" # To build a release you need to create a tag with the version number # (git tag -s libgpg-error-n.m) and run "./autogen.sh --force". # Please bump the version number immediately after the release, do # another commit, and a push so that the git magic is able to work. # See below for the LT versions. m4_define([mym4_package],[libgpg-error]) m4_define([mym4_major], [1]) m4_define([mym4_minor], [46]) # Below is m4 magic to extract and compute the revision number, the # decimalized short revision number, a beta version string, and a flag # indicating a development version (mym4_isgit). Note that the m4 # processing is done by autoconf and not during the configure run. m4_define([mym4_verslist], m4_split(m4_esyscmd([./autogen.sh --find-version] \ mym4_package mym4_major mym4_minor),[:])) m4_define([mym4_isbeta], m4_argn(2, mym4_verslist)) m4_define([mym4_version], m4_argn(4, mym4_verslist)) m4_define([mym4_revision], m4_argn(7, mym4_verslist)) m4_define([mym4_revision_dec], m4_argn(8, mym4_verslist)) m4_esyscmd([echo ]mym4_version[>VERSION]) AC_INIT([mym4_package],[mym4_version],[https://bugs.gnupg.org]) # LT Version numbers, remember to change them just *before* a release. # (Code changed: REVISION++) # (Interfaces added/removed/changed: CURRENT++, REVISION=0) # (Interfaces added: AGE++) # (Interfaces removed: AGE=0) # Note that added error codes don't constitute an interface change. LIBGPG_ERROR_LT_CURRENT=33 LIBGPG_ERROR_LT_AGE=33 LIBGPG_ERROR_LT_REVISION=0 ################################################ AC_SUBST(LIBGPG_ERROR_LT_CURRENT) AC_SUBST(LIBGPG_ERROR_LT_AGE) AC_SUBST(LIBGPG_ERROR_LT_REVISION) VERSION_NUMBER=m4_esyscmd(printf "0x%02x%02x00" mym4_major mym4_minor) AC_SUBST(VERSION_NUMBER) AC_CONFIG_AUX_DIR([build-aux]) AM_INIT_AUTOMAKE([serial-tests dist-bzip2]) AM_MAINTAINER_MODE AC_CONFIG_SRCDIR([src/err-sources.h.in]) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_MACRO_DIR([m4]) AM_SILENT_RULES # We need to know about the host architecture to avoid spurious # warnings. AC_CANONICAL_HOST AB_INIT # Checks for programs. AC_PROG_CC AM_PROG_CC_C_O AC_PROG_CPP AC_PROG_AWK AC_CHECK_TOOL(AR, ar, :) AC_USE_SYSTEM_EXTENSIONS AM_LANGINFO_CODESET # Taken from mpfr-4.0.1, then modified for LDADD_FOR_TESTS_KLUDGE dnl Under Linux, make sure that the old dtags are used if LD_LIBRARY_PATH dnl is defined. The issue is that with the new dtags, LD_LIBRARY_PATH has dnl the precedence over the run path, so that if a compatible MPFR library dnl is installed in some directory from $LD_LIBRARY_PATH, then the tested dnl MPFR library will be this library instead of the MPFR library from the dnl build tree. Other OS with the same issue might be added later. dnl dnl References: dnl https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=859732 dnl http://lists.gnu.org/archive/html/libtool/2017-05/msg00000.html dnl dnl We need to check whether --disable-new-dtags is supported as alternate dnl linkers may be used (e.g., with tcc: CC=tcc LD=tcc). dnl case $host in *-*-linux*) if test -n "$LD_LIBRARY_PATH"; then saved_LDFLAGS="$LDFLAGS" LDADD_FOR_TESTS_KLUDGE="-Wl,--disable-new-dtags" LDFLAGS="$LDFLAGS $LDADD_FOR_TESTS_KLUDGE" AC_MSG_CHECKING(whether --disable-new-dtags is supported by the linker) AC_LINK_IFELSE([AC_LANG_SOURCE([[ int main (void) { return 0; } ]])], [AC_MSG_RESULT(yes (use it since LD_LIBRARY_PATH is set))], [AC_MSG_RESULT(no) LDADD_FOR_TESTS_KLUDGE="" ]) LDFLAGS="$saved_LDFLAGS" fi ;; esac AC_SUBST([LDADD_FOR_TESTS_KLUDGE]) # Set some variables depending on the platform for later use. have_w32_system=no have_w64_system=no -have_w32ce_system=no case "${host}" in x86_64-*mingw32*) have_w32_system=yes have_w64_system=yes ;; - *-mingw32ce*) - have_w32_system=yes - have_w32ce_system=yes - ;; *-mingw32*) have_w32_system=yes ;; *-apple-darwin*) # This is the equivalent of the _GNU_SOURCE feature-test-macro # on GNU libc systems. AC_DEFINE(_DARWIN_C_SOURCE, 1, Expose all libc features (__DARWIN_C_FULL).) ;; *) ;; esac if test "$have_w32_system" != yes; then gl_THREADLIB_EARLY fi # We build libgpg-error with large file support so that we have a 64 # bit off_t. Our external interface uses the gpgrt_off_t which is # anyway specified as 64 bit. Thus the same libgpg-error can be used # by software which is not build with large file support. AC_SYS_LARGEFILE LT_PREREQ([2.2.6]) LT_INIT([win32-dll disable-static]) LT_LANG([Windows Resource]) # We need to compile and run a program on the build machine. AX_CC_FOR_BUILD AH_BOTTOM([ /* Force using of NLS for W32 even if no libintl has been found. This is okay because we have our own gettext implementation for W32. */ #if defined(HAVE_W32_SYSTEM) && !defined(ENABLE_NLS) #define ENABLE_NLS 1 #endif /* Connect the generic estream-printf.c to our framework. */ #define _ESTREAM_PRINTF_REALLOC _gpgrt_realloc #define _ESTREAM_PRINTF_EXTRA_INCLUDE "gpgrt-int.h" /* For building we need to define these macro. */ #define GPG_ERR_ENABLE_GETTEXT_MACROS 1 #define GPG_ERR_ENABLE_ERRNO_MACROS 1 #define GPGRT_ENABLE_ES_MACROS 1 #define GPGRT_ENABLE_LOG_MACROS 1 #define GPGRT_ENABLE_ARGPARSE_MACROS 1 ]) # Note, that autogen.sh greps for the next line. AM_GNU_GETTEXT_VERSION([0.19.3]) AM_GNU_GETTEXT([external]) # Checks for header files. AC_CHECK_HEADERS([locale.h stdint.h sys/select.h sys/time.h \ signal.h poll.h pwd.h]) AC_FUNC_STRERROR_R case "${host_os}" in mingw32*) INSTALLSHELLPATH=/bin/sh AC_CHECK_FUNC([strerror_s]) ;; solaris*) INSTALLSHELLPATH=/usr/xpg4/bin/sh # All versions of Solaris from 2.4 have a thread-safe strerror(). # Since Solaris 10, in addition strerror_r() exists. ;; *) INSTALLSHELLPATH=/bin/sh AC_CHECK_FUNC([strerror_r], [], AC_MSG_WARN([[Without strerror_r, gpg_strerror_r might not be thread-safe]])) ;; esac AC_SUBST(INSTALLSHELLPATH) AC_FUNC_FORK AC_CHECK_FUNCS([flockfile vasprintf mmap rand strlwr stpcpy setenv stat \ getrlimit getpwnam getpwuid getpwnam_r getpwuid_r inet_pton]) # # Checks for typedefs, structures, and compiler characteristics. # AC_C_CONST AC_CHECK_SIZEOF(int) AC_CHECK_SIZEOF(long) AC_CHECK_SIZEOF(long long) AC_HEADER_TIME AC_CHECK_SIZEOF(time_t,,[[ #include #if TIME_WITH_SYS_TIME # include # include #else # if HAVE_SYS_TIME_H # include # else # include # endif #endif ]]) GNUPG_FUNC_MKDIR_TAKES_ONE_ARG # Find a 64 bit integer type to be used instead of off_t. We prefer # the standard integer types over int64_t and finally try long long. if test "$ac_cv_sizeof_int" = "8"; then replacement_for_off_t="int" elif test "$ac_cv_sizeof_long" = "8"; then replacement_for_off_t="long" elif test "$ac_cv_header_stdint_h" = yes; then replacement_for_off_t="int64_t" elif test "$ac_cv_sizeof_long_long" = "8"; then replacement_for_off_t="long long" else AC_MSG_ERROR([[ *** *** No 64 bit signed integer type found. Can't build this library. ***]]) fi AC_DEFINE_UNQUOTED(REPLACEMENT_FOR_OFF_T, "$replacement_for_off_t", [Used by mkheader to insert the replacement type.]) # # Setup gcc specific options # AC_MSG_NOTICE([checking for cc features]) if test "$GCC" = yes; then # Check whether gcc does not emit a diagnostic for unknown -Wno-* # options. This is the case for gcc >= 4.6 AC_MSG_CHECKING([if gcc ignores unknown -Wno-* options]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6 ) #kickerror #endif]],[])],[_gcc_silent_wno=yes],[_gcc_silent_wno=no]) AC_MSG_RESULT($_gcc_silent_wno) # Note that it is okay to use CFLAGS here because these are just # warning options and the user should have a chance of overriding # them. if test "$USE_MAINTAINER_MODE" = "yes"; then M_CFLAGS="-O3 -Wall -Wcast-align -Wshadow -Wstrict-prototypes" M_CFLAGS="$M_CFLAGS -Wformat -Wno-format-y2k -Wformat-security" if test x"$_gcc_silent_wno" = xyes ; then _gcc_wopt=yes else AC_MSG_CHECKING([if gcc supports -Wno-missing-field-initializers]) _gcc_cflags_save=$CFLAGS CFLAGS="-Wno-missing-field-initializers" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])], [_gcc_wopt=yes],[_gcc_wopt=no]) AC_MSG_RESULT($_gcc_wopt) CFLAGS=$_gcc_cflags_save; fi if test x"$_gcc_wopt" = xyes ; then M_CFLAGS="$M_CFLAGS -W -Wno-sign-compare -Wno-missing-field-initializers" fi AC_MSG_CHECKING([if gcc supports -Wdeclaration-after-statement]) _gcc_cflags_save=$CFLAGS CFLAGS="-Wdeclaration-after-statement" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])],_gcc_wopt=yes,_gcc_wopt=no) AC_MSG_RESULT($_gcc_wopt) CFLAGS=$_gcc_cflags_save; if test x"$_gcc_wopt" = xyes ; then M_CFLAGS="$M_CFLAGS -Wdeclaration-after-statement" fi # Prepend the maintainer-cflags so that the user can override # them, e.g. to override the optimization flags for debugging. CFLAGS="$M_CFLAGS $CFLAGS" else CFLAGS="$CFLAGS -Wall" fi AC_MSG_CHECKING([if gcc supports -Wpointer-arith]) _gcc_cflags_save=$CFLAGS CFLAGS="-Wpointer-arith" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])],_gcc_psign=yes,_gcc_psign=no) AC_MSG_RESULT($_gcc_psign) CFLAGS=$_gcc_cflags_save; if test x"$_gcc_psign" = xyes ; then CFLAGS="$CFLAGS -Wpointer-arith" fi # The undocumented option -Wno-psabi suppresses the annoying # "the ABI of passing union with long double has changed in GCC 4.4" # which is emitted in estream-printf.c but entirely irrelvant # because that union is local to the file. if test x"$_gcc_silent_wno" = xyes ; then CFLAGS="$CFLAGS -Wno-psabi" fi fi # # Check whether the compiler supports the GCC style aligned attribute # AC_CACHE_CHECK([whether the GCC style aligned attribute is supported], [gcry_cv_gcc_attribute_aligned], [gcry_cv_gcc_attribute_aligned=no AC_COMPILE_IFELSE([AC_LANG_SOURCE( [[struct { int a; } foo __attribute__ ((aligned (16)));]])], [gcry_cv_gcc_attribute_aligned=yes])]) if test "$gcry_cv_gcc_attribute_aligned" = "yes" ; then AC_DEFINE(HAVE_GCC_ATTRIBUTE_ALIGNED,1, [Defined if a GCC style "__attribute__ ((aligned (n))" is supported]) fi # # Check for ELF visibility support. # AC_CACHE_CHECK(whether the visibility attribute is supported, gcry_cv_visibility_attribute, [gcry_cv_visibility_attribute=no AC_LANG_CONFTEST([AC_LANG_SOURCE( [[int foo __attribute__ ((visibility ("hidden"))) = 1; int bar __attribute__ ((visibility ("protected"))) = 1; ]])]) if ${CC-cc} -Werror -S conftest.c -o conftest.s \ 1>&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD ; then if grep '\.hidden.*foo' conftest.s >/dev/null 2>&1 ; then if grep '\.protected.*bar' conftest.s >/dev/null 2>&1; then gcry_cv_visibility_attribute=yes fi fi fi ]) if test "$gcry_cv_visibility_attribute" = "yes"; then AC_CACHE_CHECK(for broken visibility attribute, gcry_cv_broken_visibility_attribute, [gcry_cv_broken_visibility_attribute=yes AC_LANG_CONFTEST([AC_LANG_SOURCE( [[int foo (int x); int bar (int x) __asm__ ("foo") __attribute__ ((visibility ("hidden"))); int bar (int x) { return x; } ]])]) if ${CC-cc} -Werror -S conftest.c -o conftest.s \ 1>&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD ; then if grep '\.hidden@<:@ _@:>@foo' conftest.s >/dev/null 2>&1; then gcry_cv_broken_visibility_attribute=no fi fi ]) fi if test "$gcry_cv_visibility_attribute" = "yes"; then AC_CACHE_CHECK(for broken alias attribute, gcry_cv_broken_alias_attribute, [gcry_cv_broken_alias_attribute=yes AC_LANG_CONFTEST([AC_LANG_SOURCE( [[extern int foo (int x) __asm ("xyzzy"); int bar (int x) { return x; } extern __typeof (bar) foo __attribute ((weak, alias ("bar"))); extern int dfoo; extern __typeof (dfoo) dfoo __asm ("abccb"); int dfoo = 1; ]])]) if ${CC-cc} -Werror -S conftest.c -o conftest.s \ 1>&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD ; then if grep 'xyzzy' conftest.s >/dev/null 2>&1 && \ grep 'abccb' conftest.s >/dev/null 2>&1; then gcry_cv_broken_alias_attribute=no fi fi ]) fi if test "$gcry_cv_visibility_attribute" = "yes"; then AC_CACHE_CHECK(if gcc supports -fvisibility=hidden, gcry_cv_gcc_has_f_visibility, [gcry_cv_gcc_has_f_visibility=no _gcc_cflags_save=$CFLAGS CFLAGS="-fvisibility=hidden" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])], gcry_cv_gcc_has_f_visibility=yes) CFLAGS=$_gcc_cflags_save; ]) fi if test "$gcry_cv_visibility_attribute" = "yes" \ && test "$gcry_cv_broken_visibility_attribute" != "yes" \ && test "$gcry_cv_broken_alias_attribute" != "yes" \ && test "$gcry_cv_gcc_has_f_visibility" = "yes" then AC_DEFINE(GPGRT_USE_VISIBILITY, 1, [Define to use the GNU C visibility attribute.]) CFLAGS="$CFLAGS -fvisibility=hidden" fi # # Check whether ld supports a version script. # (Actually not a check but a list of systems which are known to support it.) # have_ld_version_script=no case "${host}" in *-*-linux*) have_ld_version_script=yes ;; *-*-gnu*) have_ld_version_script=yes ;; esac AC_ARG_ENABLE([ld-version-script], AS_HELP_STRING([--enable-ld-version-script], [enable/disable use of linker version script. (default is system dependent)]), [have_ld_version_script=$enableval], [ : ] ) AM_CONDITIONAL(HAVE_LD_VERSION_SCRIPT, test "$have_ld_version_script" = "yes") # # Check for thread library. # # Windows has always thread support; thus we don't bother to test for # it as it may lead to false results when cross building. if test "$have_w32_system" = yes; then GPG_ERROR_CONFIG_LIBS_PRIVATE=-lws2_32 AC_DEFINE([USE_WINDOWS_THREADS], [1]) LIBTHREAD= LTLIBTHREAD= LIBMULTITHREAD= LTLIBMULTITHREAD= THREADLIB_CPPFLAGS="" AC_SUBST([LIBTHREAD]) AC_SUBST([LTLIBTHREAD]) AC_SUBST([LIBMULTITHREAD]) AC_SUBST([LTLIBMULTITHREAD]) else GPG_ERROR_CONFIG_LIBS_PRIVATE="" gl_LOCK if test "$gl_threads_api" = posix; then AC_CHECK_SIZEOF(pthread_mutex_t,,[AC_INCLUDES_DEFAULT #include ]) AC_CHECK_HEADERS([sys/single_threaded.h]) fi fi # Default value for GPG_ERROR_CONFIG_LIBS config_libs="-lgpg-error" # # Check for other libraries # LIB_SCHED_YIELD= AC_SUBST([LIB_SCHED_YIELD]) AC_SEARCH_LIBS([sched_yield], [rt posix4], [if test "$ac_cv_search_sched_yield" != "none required"; then LIB_SCHED_YIELD=$ac_cv_search_sched_yield config_libs="$config_libs $LIB_SCHED_YIELD" fi]) LIB_NETWORK= AC_SUBST([LIB_NETWORK]) AC_SEARCH_LIBS([inet_addr], [nsl], [if test "$ac_cv_search_inet_addr" != "none required"; then LIB_NETWORK=$ac_cv_search_inet_addr fi]) AC_SEARCH_LIBS([socket], [socket], [if test "$ac_cv_search_socket" != "none required"; then LIB_NETWORK="$ac_cv_search_socket $LIB_NETWORK" fi], [], [$LIB_NETWORK]) if test "x$LIB_NETWORK" != x; then config_libs="$config_libs $LIB_NETWORK" fi # Check for optional readline support GNUPG_CHECK_READLINE # # Prepare building of estream # estream_INIT # # Substitution used for gpg-error-config # GPG_ERROR_CONFIG_LIBS="$config_libs" if test "x$LIBTHREAD" != x; then GPG_ERROR_CONFIG_LIBS="${GPG_ERROR_CONFIG_LIBS} ${LIBTHREAD}" fi if test "x$LIBMULTITHREAD" != x; then GPG_ERROR_CONFIG_MT_LIBS="${LIBMULTITHREAD}" else GPG_ERROR_CONFIG_MT_LIBS="" fi -if test "$have_w32ce_system" = yes; then - GPG_ERROR_CONFIG_CFLAGS="-idirafter \${includedir}/gpg-extra" -else - GPG_ERROR_CONFIG_CFLAGS="" -fi +GPG_ERROR_CONFIG_CFLAGS="" if test "x$THREADLIB_CPPFLAGS" != x; then GPG_ERROR_CONFIG_MT_CFLAGS="${THREADLIB_CPPFLAGS}" else GPG_ERROR_CONFIG_MT_CFLAGS="" fi GPG_ERROR_CONFIG_HOST="$host" case "$includedir" in /usr/include|/include) ;; '${prefix}/include') if test "$prefix" != / -a "$prefix" != /usr; then if test -z "$GPG_ERROR_CONFIG_CFLAGS"; then GPG_ERROR_CONFIG_CFLAGS="-I\${includedir}" else GPG_ERROR_CONFIG_CFLAGS="-I\${includedir} $GPG_ERROR_CONFIG_CFLAGS" fi fi ;; *) if test -z "$GPG_ERROR_CONFIG_CFLAGS"; then GPG_ERROR_CONFIG_CFLAGS="-I\${includedir}" else GPG_ERROR_CONFIG_CFLAGS="-I\${includedir} $GPG_ERROR_CONFIG_CFLAGS" fi ;; esac case "$libdir" in /usr/lib|/usr/lib64|/lib|/lib64) ;; '${exec_prefix}/lib') if test "$exec_prefix" = "NONE"; then if test "$prefix" != / -a "$prefix" != /usr; then GPG_ERROR_CONFIG_LIBS="-L\${libdir} $GPG_ERROR_CONFIG_LIBS" fi elif test "$exec_prefix" != / -a "$exec_prefix" != /usr; then GPG_ERROR_CONFIG_LIBS="-L\${libdir} $GPG_ERROR_CONFIG_LIBS" fi ;; *) GPG_ERROR_CONFIG_LIBS="-L\${libdir} $GPG_ERROR_CONFIG_LIBS" ;; esac AC_SUBST(GPG_ERROR_CONFIG_LIBS) AC_SUBST(GPG_ERROR_CONFIG_CFLAGS) AC_SUBST(GPG_ERROR_CONFIG_MT_LIBS) AC_SUBST(GPG_ERROR_CONFIG_MT_CFLAGS) AC_SUBST(GPG_ERROR_CONFIG_LIBS_PRIVATE) AC_SUBST(GPG_ERROR_CONFIG_HOST) # # Special defines for certain platforms # force_use_syscfg=no if test "$have_w32_system" = yes; then AC_DEFINE(HAVE_W32_SYSTEM,1,[Defined if we run on a W32 API based system]) if test "$have_w64_system" = yes; then AC_DEFINE(HAVE_W64_SYSTEM,1,[Defined if we run on 64 bit W32 API system]) fi - if test "$have_w32ce_system" = yes; then - AC_DEFINE(HAVE_W32CE_SYSTEM,1,[Defined if we run on WindowsCE]) - fi force_use_syscfg=yes fi if test x"$gl_use_threads" = xno; then lock_obj_h_generated=yes if test ! -d src; then mkdir src; fi host=$host $srcdir/src/gen-lock-obj.sh --disable-threads \ >src/lock-obj-pub.native.h AC_MSG_NOTICE([generated src/lock-obj-pub.native.h for $host]) elif test x$cross_compiling = xyes; then case $host in *-*-linux-gnu* | *-*-linux-musl*) AC_CHECK_TOOL(OBJDUMP, [objdump]) if test -n "$OBJDUMP"; then lock_obj_h_generated=yes if test ! -d src; then mkdir src; fi LOCK_ABI_VERSION=1 host=$host host_alias=$host_alias \ CC=$CC OBJDUMP=$OBJDUMP \ ac_ext=$ac_ext ac_objext=$ac_objext \ AWK=$AWK $srcdir/src/gen-lock-obj.sh \ >src/lock-obj-pub.native.h AC_MSG_NOTICE([generated src/lock-obj-pub.native.h using $host_alias-objdump and $AWK]) else force_use_syscfg=yes fi ;; *) force_use_syscfg=yes ;; esac fi AM_CONDITIONAL(HAVE_W32_SYSTEM, test "$have_w32_system" = yes) AM_CONDITIONAL(HAVE_W64_SYSTEM, test "$have_w64_system" = yes) -AM_CONDITIONAL(HAVE_W32CE_SYSTEM, test "$have_w32ce_system" = yes) AM_CONDITIONAL(CROSS_COMPILING, test x$cross_compiling = xyes) AM_CONDITIONAL(FORCE_USE_SYSCFG, test x$force_use_syscfg = xyes) AM_CONDITIONAL(HAVE_GENERATED_LOCK_OBJ_H, test x$lock_obj_h_generated = xyes) have_lock_optimization=no if test "$gl_threads_api" = posix; then if test x$ac_cv_header_sys_single_threaded_h = xyes; then have_lock_optimization=yes else case "${host_os}" in *-gnu*) have_lock_optimization=yes ;; * ) ;; esac fi fi AM_CONDITIONAL(HAVE_LOCK_OPTIMIZATION, test "$have_lock_optimization" = yes) AC_DEFINE_UNQUOTED(HOST_TRIPLET_STRING, "$host", [The host triplet]) # # gpgrt_log_clock may require linking with extra libaries. As long as # we don't have a good test for this we require the use of this # configure option to enabling printing of a timestamp. # AC_MSG_CHECKING([whether to enable log_clock]) AC_ARG_ENABLE(log_clock, AS_HELP_STRING([--enable-log-clock], [enable log_clock timestamps]), enable_log_clock=$enableval, enable_log_clock=no) AC_MSG_RESULT($enable_log_clock) if test "$enable_log_clock" = yes ; then AC_DEFINE(ENABLE_LOG_CLOCK,1,[Defined to use log_clock timestamps]) fi # # For now we do not build gpgscm by default. # Eventually we will reverse the meaning of that option. # build_gpgscm=no #AC_MSG_CHECKING([whether to build gpgscm]) #AC_ARG_ENABLE(gpgscm, # AS_HELP_STRING([--enable-gpgscm], # [build the gpgscm tool]), # build_gpgscm=$enableval, build_gpgscm=no) #AC_MSG_RESULT($build_gpgscm) AM_CONDITIONAL([BUILD_GPGSCM], [test "x$build_gpgscm" != xno]) # # Add -Werror to CFLAGS. This hack can be used to avoid problems with # misbehaving autoconf tests in case the user supplied -Werror. # AC_ARG_ENABLE(werror, AS_HELP_STRING([--enable-werror], [append -Werror to CFLAGS]), [if test $enableval = yes ; then CFLAGS="$CFLAGS -Werror" fi]) # # Provide information about the build. # BUILD_REVISION="mym4_revision" AC_SUBST(BUILD_REVISION) AC_DEFINE_UNQUOTED(BUILD_REVISION, "$BUILD_REVISION", [GIT commit id revision used to build this package]) changequote(,)dnl BUILD_VERSION=`echo "$PACKAGE_VERSION"|sed 's/\([0-9.]*\).*/\1./'` changequote([,])dnl BUILD_VERSION="${BUILD_VERSION}0.mym4_revision_dec" BUILD_FILEVERSION=`echo "${BUILD_VERSION}" | tr . ,` AC_SUBST(BUILD_VERSION) AC_SUBST(BUILD_FILEVERSION) AC_ARG_ENABLE([build-timestamp], AS_HELP_STRING([--enable-build-timestamp], [set an explicit build timestamp for reproducibility. (default is the current time in ISO-8601 format)]), [if test "$enableval" = "yes"; then BUILD_TIMESTAMP=`date -u +%Y-%m-%dT%H:%M+0000 2>/dev/null || date` else BUILD_TIMESTAMP="$enableval" fi], [BUILD_TIMESTAMP=""]) AC_SUBST(BUILD_TIMESTAMP) AC_DEFINE_UNQUOTED(BUILD_TIMESTAMP, "$BUILD_TIMESTAMP", [The time this package was configured for a build]) AC_ARG_ENABLE(languages, AS_HELP_STRING([--disable-languages], [do not build support for other languages than C])) AM_CONDITIONAL([LANGUAGES_SOME], [test "x$enable_languages" != xno]) build_doc=yes AC_ARG_ENABLE([doc], AS_HELP_STRING([--disable-doc], [do not build the documentation]), build_doc=$enableval, build_doc=yes) AM_CONDITIONAL([BUILD_DOC], [test "x$build_doc" != xno]) build_tests=yes AC_ARG_ENABLE([tests], AS_HELP_STRING([--disable-tests], [do not build the tests]), build_tests=$enableval, build_tests=yes) AM_CONDITIONAL([BUILD_TESTS], [test "x$build_tests" != xno]) # # Substitution # AC_CONFIG_FILES([Makefile]) AC_CONFIG_FILES([doc/Makefile po/Makefile.in m4/Makefile]) AC_CONFIG_FILES([src/Makefile tests/Makefile]) AC_CONFIG_FILES([lang/Makefile lang/cl/Makefile lang/cl/gpg-error.asd]) AC_CONFIG_FILES([src/versioninfo.rc src/gpg-error.w32-manifest]) AC_CONFIG_FILES([src/gpg-error.pc]) AC_CONFIG_FILES([src/gpg-error-config-old:src/gpg-error-config.in], [chmod +x src/gpg-error-config-old]) AC_CONFIG_FILES([src/gpgrt-config], [chmod +x src/gpgrt-config]) AC_CONFIG_FILES([src/gpg-error-config-test.sh], [chmod +x src/gpg-error-config-test.sh]) AC_OUTPUT tmp= if test "$have_w32_system" != yes; then if test x"$gl_use_threads" = xno; then tmp=" NO-THREADS" fi fi echo " $PACKAGE_NAME v$PACKAGE_VERSION has been configured as follows: Revision: mym4_revision (mym4_revision_dec) Platform: $host$tmp " if test "$gcry_cv_gcc_attribute_aligned" != "yes" ; then cat < - - NB: ChangeLog files are no longer manually maintained. Starting - on December 1st, 2011 we put change information only in the GIT - commit log, and generate a top-level ChangeLog file from logs at - "make dist". See doc/HACKING for details. - -2010-11-15 Marcus Brinkmann - - * conf-w32ce-msc/stdint.h: New file. - * conf-w32ce-msc/build.mk (conf_sources): Add stdint.h. - -2010-11-15 Werner Koch - - * conf-w32ce-msc/build.mk (clean): New. - -2010-11-02 Werner Koch - - * conf-w32ce-msc/build.mk: Change directory layout. Provide - install target. - -2010-10-28 Werner Koch - - * contrib/conf-w32ce-msc/build.mk: New. - * contrib/conf-w32ce-msc/config.h: New. diff --git a/contrib/conf-w32ce-msc/build.mk b/contrib/conf-w32ce-msc/build.mk deleted file mode 100755 index 0909509..0000000 --- a/contrib/conf-w32ce-msc/build.mk +++ /dev/null @@ -1,144 +0,0 @@ -# build.mk - Makefile to build libgpg-error using Visual-C -# Copyright 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. - -# This is a helper make script to build libgpg-error for WindowsCE -# using the Microsoft Visual C compiler. - -# The target build directory where we run the Visual C compiler/ This -# needs to be an absolute directory name. Further we expect this -# structure of the tree: -# -# TARGET/src - Source directories: One directory for each project -# /bin - Installed DLLs -# /lib - Installed import libs. -# /include - Instaled header files. - -targetdir = /home/smb/xppro-gnu -targetsrc = $(targetdir)/src - -# Install directories (relative) -bindir = ../../../bin -libdir = ../../../lib -incdir = ../../../include - -help: - @echo "Run " - @echo " make -f ../contrib/conf-w32ce-msc/build.mk copy-source" - @echo "on the POSIX system and then" - @echo " nmake -f build.mk all" - @echo " nmake -f build.mk install" - @echo "on the Windows system" - -ce_defines = -DWINCE -D_WIN32_WCE=0x502 -DUNDER_CE \ - -DWIN32_PLATFORM_PSPC -D_UNICODE -DUNICODE \ - -D_CONSOLE -DARM -D_ARM_ -#-D_DEBUG -DDEBUG - -# Some options of Visual-C: -# -W3 Set warning level 3 -# -Zi Generate debug info -# -Od Disable optimization -# -Gm Enable minimal rebuild (for C++) -# -EHsc Exception handling model sc -# -MTd Create a debug multithreaded executable -# -fp: Floating point behaviour -# -GR- Disable runtime type information -# -Os Favor small code -# -LD Create a DLL -# -Fe Set executable output name (may be only a directory) -CFLAGS = -nologo -W3 -fp:fast -Os $(ce_defines) \ - -DHAVE_CONFIG_H -DDLL_EXPORT -I. -Igpg-extra - -LDFLAGS = - -# Standard source files -sources = \ - init.c init.h \ - strsource.c \ - strerror.c \ - code-to-errno.c \ - code-from-errno.c \ - w32-gettext.c \ - gettext.h \ - err-sources.h \ - err-codes.h - -# Sources files in this directory inclduing this Makefile -conf_sources = \ - build.mk \ - config.h \ - stdint.h - -# Source files built by running the standard build system. -built_sources = \ - code-from-errno.h \ - code-to-errno.h \ - err-codes-sym.h \ - err-sources-sym.h \ - errnos-sym.h \ - gpg-error.h \ - mkerrcodes.h \ - mkw32errmap.map.c \ - gpg-error.def \ - gpg-extra/errno.h - -copy-static-source: - @if [ ! -f ./w32-gettext.c ]; then \ - echo "Please cd to the src/ directory first"; \ - exit 1; \ - fi - cp -t $(targetsrc)/libgpg-error/src $(sources); - cd ../contrib/conf-w32ce-msc ; \ - cp -t $(targetsrc)/libgpg-error/src $(conf_sources) - - -copy-built-source: - @if [ ! -f ./mkw32errmap.map.c ]; then \ - echo "Please build using ./autogen.sh --build-w32ce first"; \ - exit 1; \ - fi - cp -t $(targetsrc)/libgpg-error/src $(built_sources) - -mkdir $(targetsrc)/libgpg-error/src/gpg-extra - mv $(targetsrc)/libgpg-error/src/errno.h \ - $(targetsrc)/libgpg-error/src/gpg-extra - -copy-source: copy-static-source copy-built-source - - -all: $(sources) $(conf_sources) $(built_sources) - $(CC) $(CFLAGS) -c w32-gettext.c - $(CC) $(CFLAGS) -c init.c - $(CC) $(CFLAGS) -c strsource.c - $(CC) $(CFLAGS) -c strerror.c - $(CC) $(CFLAGS) -c code-to-errno.c - $(CC) $(CFLAGS) -c code-from-errno.c - link.exe /DLL /IMPLIB:libgpg-error-0-msc.lib \ - /OUT:libgpg-error-0-msc.dll \ - /DEF:gpg-error.def /NOLOGO /MANIFEST:NO \ - /NODEFAULTLIB:"oldnames.lib" /DYNAMICBASE:NO \ - w32-gettext.obj init.obj strsource.obj strerror.obj \ - code-to-errno.obj code-from-errno.obj \ - coredll.lib corelibc.lib ole32.lib oleaut32.lib uuid.lib \ - commctrl.lib /subsystem:windowsce,5.02 - -install: all - -mkdir $(bindir:/=\) - -mkdir $(libdir:/=\) - -mkdir $(incdir:/=\) - -mkdir $(incdir:/=\)\gpg-extra - copy /y gpg-error.h $(incdir:/=\) - copy /y gpg-extra\errno.h $(incdir:/=\)\gpg-extra - copy /y libgpg-error-0-msc.dll $(bindir:/=\) - copy /y libgpg-error-0-msc.lib $(libdir:/=\) - -clean: - del *.obj libgpg-error-0-msc.lib \ - libgpg-error-0-msc.dll libgpg-error-0-msc.exe diff --git a/contrib/conf-w32ce-msc/config.h b/contrib/conf-w32ce-msc/config.h deleted file mode 100755 index 1d8132c..0000000 --- a/contrib/conf-w32ce-msc/config.h +++ /dev/null @@ -1,153 +0,0 @@ -/* config.h for building with Visual-C for WindowsCE. - * Copyright 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. - */ - -/* This file was originally created by running - * ./autogen.sh --build-w32ce - * on svn revision 253 (libgpg-error 1.10) and then adjusted to work - * with Visual-C. - */ - -/* Define to the version of this package. */ -#define PACKAGE_VERSION "1.11-msc1" - - -/* Name of package */ -#define PACKAGE "libgpg-error" - -/* Define to the address where bug reports for this package should be sent. */ -#define PACKAGE_BUGREPORT "bug-gnupg@gnupg.org" - -/* Define to the full name of this package. */ -#define PACKAGE_NAME "libgpg-error" - -/* Define to the full name and version of this package. */ -#define PACKAGE_STRING "libgpg-error " PACKAGE_VERSION - -/* Define to the one symbol short name of this package. */ -#define PACKAGE_TARNAME "libgpg-error" - -/* Define to the home page for this package. */ -#define PACKAGE_URL "" - - - -/* Define to 1 if translation of program messages to the user's native - language is requested. */ -/* #undef ENABLE_NLS */ - -/* Define to 1 if you have the MacOS X function CFLocaleCopyCurrent in the - CoreFoundation framework. */ -/* #undef HAVE_CFLOCALECOPYCURRENT */ - -/* Define to 1 if you have the MacOS X function CFPreferencesCopyAppValue in - the CoreFoundation framework. */ -/* #undef HAVE_CFPREFERENCESCOPYAPPVALUE */ - -/* Define if the GNU dcgettext() function is already present or preinstalled. - */ -/* #undef HAVE_DCGETTEXT */ - -/* Define to 1 if you have the declaration of `strerror_r', and to 0 if you - don't. */ -#define HAVE_DECL_STRERROR_R 0 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_DLFCN_H */ - -/* Define if the GNU gettext() function is already present or preinstalled. */ -/* #undef HAVE_GETTEXT */ - -/* Define if you have the iconv() function and it works. */ -/* #undef HAVE_ICONV */ - -/* Define to 1 if you have the header file. */ -#define HAVE_INTTYPES_H 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_LOCALE_H */ - -/* Define to 1 if you have the header file. */ -#define HAVE_MEMORY_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STDINT_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STDLIB_H 1 - -/* Define to 1 if you have the `strerror_r' function. */ -/* #undef HAVE_STRERROR_R */ - -/* Define to 1 if you have the header file. */ -#define HAVE_STRINGS_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STRING_H 1 - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_STAT_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_TYPES_H - -/* Define to 1 if you have the header file. */ -#define HAVE_UNISTD_H 1 - -/* Defined if we run on WindowsCE */ -#define HAVE_W32CE_SYSTEM 1 - -/* Defined if we run on a W32 API based system */ -#define HAVE_W32_SYSTEM 1 - -/* Define to the sub-directory in which libtool stores uninstalled libraries. - */ -#define LT_OBJDIR ".libs/" - -/* Define to 1 if your C compiler doesn't accept -c and -o together. */ -/* #undef NO_MINUS_C_MINUS_O */ - -/* Define to 1 if you have the ANSI C header files. */ -#define STDC_HEADERS 1 - -/* Define to 1 if strerror_r returns char *. */ -/* #undef STRERROR_R_CHAR_P */ - - -/* Version number of package */ -#define VERSION PACKAGE_VERSION - -/* Define to 1 if on MINIX. */ -/* #undef _MINIX */ - -/* Define to 2 if the system does not provide POSIX.1 features except with - this defined. */ -/* #undef _POSIX_1_SOURCE */ - -/* Define to 1 if you need to in order for `stat' and other things to work. */ -/* #undef _POSIX_SOURCE */ - -/* Define to empty if `const' does not conform to ANSI C. */ -/* #undef const */ - - -/* Force using of NLS for W32 even if no libintl has been found. This is - okay because we have our own gettext implementation for W32. */ -#if defined(HAVE_W32_SYSTEM) && !defined(ENABLE_NLS) -#define ENABLE_NLS 1 -#endif -/* For building we need to define these macro. */ -#define GPG_ERR_ENABLE_GETTEXT_MACROS 1 -#define GPG_ERR_ENABLE_ERRNO_MACROS 1 - -/* snprintf is not part of oldnames.lib thus we redefine it here. */ -#define snprintf _snprintf - diff --git a/contrib/conf-w32ce-msc/stdint.h b/contrib/conf-w32ce-msc/stdint.h deleted file mode 100755 index 0a821b7..0000000 --- a/contrib/conf-w32ce-msc/stdint.h +++ /dev/null @@ -1,9 +0,0 @@ -typedef unsigned long long uint64_t; -typedef long long int64_t; -typedef unsigned int uint32_t; -typedef int int32_t; -typedef unsigned short uint16_t; -typedef short int16_t; -typedef unsigned int uintptr_t; -typedef int intptr_t; - diff --git a/src/Makefile.am b/src/Makefile.am index 34e0476..ded4f0b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,367 +1,323 @@ # Makefile.am for libgpg-error. # Copyright (C) 2003, 2004, 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+ # # We distribute the generated sources err-sources.h and err-codes.h, # because they are needed to build the po directory, and they don't # depend on the configuration anyway. # -if HAVE_W32CE_SYSTEM -gpg_extra_headers = gpg-extra/errno.h -extra_cppflags = -idirafter gpg-extra -else -gpg_extra_headers = -extra_cppflags = -endif - localedir = $(datadir)/locale bin_PROGRAMS = gpg-error if HAVE_W32_SYSTEM noinst_PROGRAMS = gen-w32-lock-obj else noinst_PROGRAMS = gen-posix-lock-obj endif # Distributed lock object definitions for cross compilation. lock_obj_pub = \ syscfg/lock-obj-pub.aarch64-unknown-linux-gnu.h \ syscfg/lock-obj-pub.aarch64-unknown-linux-gnu_ilp32.h \ syscfg/lock-obj-pub.aarch64-apple-darwin.h \ syscfg/lock-obj-pub.alpha-unknown-linux-gnu.h \ syscfg/lock-obj-pub.arm-unknown-linux-androideabi.h \ syscfg/lock-obj-pub.arm-unknown-linux-gnueabi.h \ syscfg/lock-obj-pub.arm-apple-darwin.h \ syscfg/lock-obj-pub.hppa-unknown-linux-gnu.h \ syscfg/lock-obj-pub.i386-apple-darwin.h \ syscfg/lock-obj-pub.i686-unknown-gnu.h \ syscfg/lock-obj-pub.i686-unknown-kfreebsd-gnu.h \ syscfg/lock-obj-pub.i686-unknown-linux-gnu.h \ syscfg/lock-obj-pub.m68k-unknown-linux-gnu.h \ syscfg/lock-obj-pub.mips-unknown-linux-gnu.h \ syscfg/lock-obj-pub.mips64el-unknown-linux-gnuabi64.h \ syscfg/lock-obj-pub.mips64-unknown-linux-gnuabi64.h \ syscfg/lock-obj-pub.mipsel-unknown-linux-gnu.h \ syscfg/lock-obj-pub.nios2-unknown-linux-gnu.h \ syscfg/lock-obj-pub.or1k-unknown-linux-gnu.h \ syscfg/lock-obj-pub.powerpc-unknown-linux-gnu.h \ syscfg/lock-obj-pub.powerpc64-unknown-linux-gnu.h \ syscfg/lock-obj-pub.powerpc64le-unknown-linux-gnu.h \ syscfg/lock-obj-pub.powerpc-unknown-linux-gnuspe.h \ syscfg/lock-obj-pub.riscv64-unknown-linux-gnu.h \ syscfg/lock-obj-pub.riscv32-unknown-linux-gnu.h \ syscfg/lock-obj-pub.s390x-unknown-linux-gnu.h \ syscfg/lock-obj-pub.sh3-unknown-linux-gnu.h \ syscfg/lock-obj-pub.sh4-unknown-linux-gnu.h \ syscfg/lock-obj-pub.sparc-unknown-linux-gnu.h \ syscfg/lock-obj-pub.sparc64-unknown-linux-gnu.h \ syscfg/lock-obj-pub.x86_64-apple-darwin.h \ syscfg/lock-obj-pub.x86_64-unknown-kfreebsd-gnu.h \ syscfg/lock-obj-pub.x86_64-unknown-linux-gnu.h \ syscfg/lock-obj-pub.x86_64-unknown-linux-gnux32.h \ syscfg/lock-obj-pub.x86_64-unknown-linux-musl.h \ syscfg/lock-obj-pub.tilegx-unknown-linux-gnu.h \ syscfg/lock-obj-pub.ia64-unknown-linux-gnu.h \ syscfg/lock-obj-pub.mingw32.h lib_LTLIBRARIES = libgpg-error.la nodist_include_HEADERS = gpg-error.h gpgrt.h bin_SCRIPTS = gpgrt-config gpg-error-config m4datadir = $(datadir)/aclocal m4data_DATA = gpg-error.m4 gpgrt.m4 pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = gpg-error.pc EXTRA_DIST = mkstrtable.awk err-sources.h.in err-codes.h.in \ mkerrnos.awk errnos.in README \ mkerrcodes.awk mkerrcodes1.awk mkerrcodes2.awk mkerrcodes.c \ - mkheader.c gpg-error.h.in mkw32errmap.c w32-add.h w32ce-add.h \ + mkheader.c gpg-error.h.in w32-add.h \ err-sources.h err-codes.h gpg-error-config.in gpg-error.m4 gpgrt.m4 \ gpg-error.vers gpg-error.def.in \ versioninfo.rc.in gpg-error.w32-manifest.in \ gpgrt-config.in gpg-error-config-test.sh.in gpg-error.pc.in \ gen-lock-obj.sh $(lock_obj_pub) BUILT_SOURCES = $(srcdir)/err-sources.h $(srcdir)/err-codes.h \ code-to-errno.h code-from-errno.h \ err-sources-sym.h err-codes-sym.h errnos-sym.h gpg-error.h gpgrt.h \ - gpg-error.def mkw32errmap.map.c + gpg-error.def -tmp_files = _mkerrcodes.h _gpg-error.def.h mkw32errmap.tab.h mkw32errmap.map.c +tmp_files = _mkerrcodes.h _gpg-error.def.h CLEANFILES = code-to-errno.h code-from-errno.h \ gpg-error.h gpgrt.h \ - mkerrcodes$(EXEEXT_FOR_BUILD) mkerrcodes.h gpg-error.def mkw32errmap.tab.h \ - mkw32errmap.map.c err-sources-sym.h err-codes-sym.h errnos-sym.h \ + mkerrcodes$(EXEEXT_FOR_BUILD) mkerrcodes.h gpg-error.def \ + err-sources-sym.h err-codes-sym.h errnos-sym.h \ gpg-extra/errno.h mkheader$(EXEEXT_FOR_BUILD) \ gpgrt-config gpg-error-config-old gpg-error-config-test.sh \ gpg-error-config gpg-error-config-test.log \ $(tmp_files) lock-obj-pub.native.h MAINTAINERCLEANFILES = $(srcdir)/err-sources.h $(srcdir)/err-codes.h TESTS = gpg-error-config-test.sh # # {{{ Begin Windows part # if HAVE_W32_SYSTEM arch_sources = w32-gettext.c w32-lock.c w32-lock-obj.h w32-thread.c \ w32-iconv.c w32-estream.c w32-reg.c spawn-w32.c RCCOMPILE = $(RC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ - -DLOCALEDIR=\"$(localedir)\" $(AM_CPPFLAGS) $(CPPFLAGS) + $(libgpg_error_la_CPPFLAGS) $(AM_CPPFLAGS) $(CPPFLAGS) LTRCCOMPILE = $(LIBTOOL) --mode=compile --tag=RC $(RCCOMPILE) SUFFIXES = .rc .lo .rc.lo: $(LTRCCOMPILE) -i "$<" -o "$@" gpg_error_res = versioninfo.lo export_symbols = -export-symbols gpg-error.def # i686-w64-mingw32.gcc version 4.9.1 takes the long long helper # functions from libgcc_s_sjlj-1.dll and not from a static libgcc. As # a plain C program we do not use exception handler and thus there is # no need to use this DLL. Thus we force gcc to link that statically. extra_ltoptions = -XCClinker -static-libgcc versioninfo.lo : gpg-error.w32-manifest install-def-file: gpg-error.def -$(INSTALL) -d $(DESTDIR)$(libdir) $(INSTALL) gpg-error.def $(DESTDIR)$(libdir)/gpg-error.def uninstall-def-file: -rm $(DESTDIR)$(libdir)/gpg-error.def libgpg_error_la_DEPENDENCIES = $(gpg_error_res) gpg-error.def intllibs = # # }}} End Windows part # else # # {{{ Begin Unix part # arch_sources = posix-lock.c posix-lock-obj.h posix-thread.c spawn-posix.c gpg_error_res = export_symbols = extra_ltoptions = install-def-file: uninstall-def-file: intllibs = @LTLIBINTL@ endif # # }}} End Unix part # socklibs = @GPG_ERROR_CONFIG_LIBS_PRIVATE@ if HAVE_LD_VERSION_SCRIPT libgpg_error_vers_opt = -Wl,--version-script=$(srcdir)/gpg-error.vers else libgpg_error_vers_opt = endif libgpg_error_la_LDFLAGS = \ -no-undefined $(export_symbols) $(libgpg_error_vers_opt) \ $(extra_ltoptions) -version-info \ @LIBGPG_ERROR_LT_CURRENT@:@LIBGPG_ERROR_LT_REVISION@:@LIBGPG_ERROR_LT_AGE@ libgpg_error_la_SOURCES = gettext.h $(arch_sources) \ gpgrt-int.h protos.h init.c init.h version.c lock.h thread.h \ estream.c estream-printf.c estream-printf.h \ strsource.c strerror.c code-to-errno.c code-from-errno.c \ visibility.c visibility.h \ sysutils.c \ stringutils.c \ syscall-clamp.c \ logging.c \ b64dec.c b64enc.c \ argparse.c nodist_libgpg_error_la_SOURCES = gpg-error.h # libgpg_error_la_DEPENDENCIES = \ # $(srcdir)/gpg-error.vers -# Note that RCCOMPILE needs the same defines as ..._la_CPPFLAGS but -# without the extra_cppflags because they may include am -idirafter -# which is not supported by the RC compiler. -libgpg_error_la_CPPFLAGS = -DLOCALEDIR=\"$(localedir)\" $(extra_cppflags) +libgpg_error_la_CPPFLAGS = -DLOCALEDIR=\"$(localedir)\" libgpg_error_la_LIBADD = $(gpg_error_res) $(intllibs) $(socklibs) $(LIBTHREAD) gpg_error_SOURCES = strsource-sym.c strerror-sym.c gpg-error.c gpg_error_CPPFLAGS = -DPKGDATADIR=\"$(pkgdatadir)\" \ - -DLOCALEDIR=\"$(localedir)\" $(extra_cppflags) + -DLOCALEDIR=\"$(localedir)\" gpg_error_LDADD = libgpg-error.la $(LTLIBINTL) # We build err-sources.h and err-codes.h in the source directory. # This is needed because gettext does only look into the source # directory to find the files listed in po/POTFILE.in. To make these # rules work we also need to depend on Makefile.am and not on the # generated files Makefile.in or Makefile. $(srcdir)/err-sources.h: Makefile.am mkstrtable.awk err-sources.h.in $(AWK) -f $(srcdir)/mkstrtable.awk -v textidx=3 \ $(srcdir)/err-sources.h.in >$@ err-sources-sym.h: Makefile mkstrtable.awk err-sources.h.in $(AWK) -f $(srcdir)/mkstrtable.awk -v textidx=2 -v nogettext=1 \ $(srcdir)/err-sources.h.in >$@ $(srcdir)/err-codes.h: Makefile.am mkstrtable.awk err-codes.h.in $(AWK) -f $(srcdir)/mkstrtable.awk -v textidx=3 \ $(srcdir)/err-codes.h.in >$@ err-codes-sym.h: Makefile mkstrtable.awk err-codes.h.in $(AWK) -f $(srcdir)/mkstrtable.awk -v textidx=2 -v nogettext=1 \ $(srcdir)/err-codes.h.in >$@ code-to-errno.h: Makefile mkerrnos.awk errnos.in $(AWK) -f $(srcdir)/mkerrnos.awk $(srcdir)/errnos.in >$@ # It is correct to use $(CPP). We want the host's idea of the error codes. -mkerrcodes.h: Makefile mkerrcodes.awk $(gpg_extra_headers) +mkerrcodes.h: Makefile mkerrcodes.awk $(AWK) -f $(srcdir)/mkerrcodes1.awk $(srcdir)/errnos.in >_$@ - $(CPP) $(CPPFLAGS) $(extra_cppflags) -P _$@ | grep GPG_ERR_ | \ + $(CPP) $(CPPFLAGS) -P _$@ | grep GPG_ERR_ | \ $(AWK) -f $(srcdir)/mkerrcodes.awk >$@ -rm _$@ -if HAVE_W32CE_SYSTEM -# It is correct to use $(CPP). We want the host's idea of the error codes. -mkw32errmap.tab.h: Makefile mkw32errmap.c - $(CPP) -DRESOLVE_MACROS $(srcdir)/mkw32errmap.c | \ - grep '{&mkw32errmap_marker' >$@ -mkw32errmap.map.c: mkw32errmap$(EXEEXT_FOR_BUILD) - ./mkw32errmap$(EXEEXT_FOR_BUILD) --map > $@ -gpg-extra/errno.h: mkw32errmap$(EXEEXT_FOR_BUILD) - -$(MKDIR_P) gpg-extra - ./mkw32errmap$(EXEEXT_FOR_BUILD) > $@ -else -mkw32errmap.map.c: - echo "/*dummy*/" > $@ -endif - # We use CC proper for preprocessing thus we have to convince it that # the data is really to be preprocessed. gpg-error.def: Makefile gpg-error.def.in cat $(srcdir)/gpg-error.def.in >_$@.h - $(CPP) $(DEFAULT_INCLUDES) $(INCLUDES) $(extra_cppflags) _$@.h | \ + $(CPP) $(DEFAULT_INCLUDES) $(INCLUDES) _$@.h | \ grep -v '^#' >$@ -rm _$@.h # It is correct to use $(CC_FOR_BUILD) here. We want to run the # program at build time. mkerrcodes$(EXEEXT_FOR_BUILD): mkerrcodes.c mkerrcodes.h Makefile $(CC_FOR_BUILD) $(CFLAGS_FOR_BUILD) $(LDFLAGS_FOR_BUILD) \ $(CPPFLAGS_FOR_BUILD) -I. -I$(srcdir) -o $@ $(srcdir)/mkerrcodes.c -if HAVE_W32CE_SYSTEM -# It is correct to use $(CC_FOR_BUILD) here. We want to run the -# program at build time. -mkw32errmap$(EXEEXT_FOR_BUILD): mkw32errmap.c mkw32errmap.tab.h Makefile - $(CC_FOR_BUILD) $(CFLAGS_FOR_BUILD) $(LDFLAGS_FOR_BUILD) \ - $(CPPFLAGS_FOR_BUILD) -I. -I$(srcdir) -o $@ $(srcdir)/mkw32errmap.c -endif - code-from-errno.h: mkerrcodes$(EXEEXT_FOR_BUILD) Makefile ./mkerrcodes$(EXEEXT_FOR_BUILD) | $(AWK) -f $(srcdir)/mkerrcodes2.awk >$@ errnos-sym.h: Makefile mkstrtable.awk errnos.in $(AWK) -f $(srcdir)/mkstrtable.awk -v textidx=2 -v nogettext=1 \ -v prefix=GPG_ERR_ -v pkg_namespace=errnos_ \ $(srcdir)/errnos.in >$@ mkheader$(EXEEXT_FOR_BUILD): mkheader.c Makefile $(CC_FOR_BUILD) $(CFLAGS_FOR_BUILD) $(LDFLAGS_FOR_BUILD) \ $(CPPFLAGS_FOR_BUILD) -g -I. -I$(srcdir) -o $@ $(srcdir)/mkheader.c parts_of_gpg_error_h = \ gpg-error.h.in \ err-sources.h.in \ err-codes.h.in \ errnos.in \ w32-add.h \ - w32ce-add.h \ $(lock_obj_pub) # If we are cross-compiling or building on Windows we better make sure # that no stale native lock include file will be found by mkheader. if FORCE_USE_SYSCFG pre_mkheader_cmds = if test -f lock-obj-pub.native.h; \ then rm lock-obj-pub.native.h; fi mkheader_opts = --cross else if HAVE_GENERATED_LOCK_OBJ_H # lock-obj-pub.native.h is generated at configure time pre_mkheader_cmds = : mkheader_opts = parts_of_gpg_error_h += ./lock-obj-pub.native.h else pre_mkheader_cmds = : mkheader_opts = parts_of_gpg_error_h += ./lock-obj-pub.native.h ./lock-obj-pub.native.h: Makefile gen-posix-lock-obj$(EXEEXT) posix-lock-obj.h ./gen-posix-lock-obj >$@ endif endif # We also depend on versioninfo.rc because that is build by # config.status and thus has up-to-date version numbers. gpg-error.h: Makefile mkheader$(EXEEXT_FOR_BUILD) $(parts_of_gpg_error_h) \ versioninfo.rc ../config.h $(pre_mkheader_cmds) ./mkheader$(EXEEXT_FOR_BUILD) $(mkheader_opts) \ $(host_triplet) $(srcdir)/gpg-error.h.in \ ../config.h $(PACKAGE_VERSION) $(VERSION_NUMBER) >$@ gpgrt.h: gpg-error.h cp gpg-error.h gpgrt.h gpg-error-config: gpgrt-config gpg-error-config-old gpg-error-config-test.sh @echo $(ECHO_N) "Confirm gpg-error-config works... $(ECHO_C)" @if ./gpg-error-config-test.sh --old-new; then \ echo "good"; \ else \ echo "no"; \ echo "*** Please report to with gpg-error-config-test.log"; \ exit 1; \ fi cp gpg-error-config-old $@ - -install-data-local: -if HAVE_W32CE_SYSTEM - -$(MKDIR_P) "$(DESTDIR)$(includedir)/gpg-extra" - $(INSTALL_DATA) gpg-extra/errno.h \ - "$(DESTDIR)$(includedir)/gpg-extra/errno.h" -else - : -endif diff --git a/src/estream-printf.c b/src/estream-printf.c index fe25657..831af55 100644 --- a/src/estream-printf.c +++ b/src/estream-printf.c @@ -1,1904 +1,1894 @@ /* 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 #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 +#define _set_errno(a) do { errno = (a); } while (0) /* 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; 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; gpgrt_assert (argidx < argspecs_len); argidx++; /* Apply indirect field width and precision values. */ if (arg->width == STAR_FIELD_VALUE) { 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) { 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 { gpgrt_assert (arg->vt == valuetable[arg->arg_pos-1].vt); value = valuetable[arg->arg_pos-1].value; } switch (arg->conspec) { 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; } 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 32f8f48..07a5d97 100644 --- a/src/estream.c +++ b/src/estream.c @@ -1,5796 +1,5764 @@ /* 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_TIME_H # include #endif #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_W32_SYSTEM # ifdef HAVE_WINSOCK2_H # include # endif # include #else # ifdef HAVE_POLL_H # include # else # ifdef HAVE_SYS_SELECT_H # include # endif # endif #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 +#define _set_errno(a) do { errno = (a); } while (0) #define IS_INVALID_FD(a) ((a) == -1) /* 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. That actually belong into sysutils but to allow * standalone use of estream we keep it here. */ #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; /* ReactOS uses EACCES ("Permission denied") and * is likely right because they used an * undocumented function to associate the error * codes. However we have always used EPERM * ("Operation not permitted", e.g. function is * required to be called by root) and we better * stick to that to avoid surprising bugs. */ case ERROR_INVALID_HANDLE: return EBADF; case ERROR_INVALID_BLOCK: return ENOMEM; case ERROR_NOT_ENOUGH_MEMORY: return ENOMEM; case ERROR_NO_DATA: return EPIPE; case ERROR_ALREADY_EXISTS: return EEXIST; /* This mapping has been taken from reactOS. */ case ERROR_TOO_MANY_OPEN_FILES: return EMFILE; case ERROR_ARENA_TRASHED: return ENOMEM; case ERROR_BAD_ENVIRONMENT: return E2BIG; case ERROR_BAD_FORMAT: return ENOEXEC; case ERROR_INVALID_DRIVE: return ENOENT; case ERROR_CURRENT_DIRECTORY: return EACCES; case ERROR_NOT_SAME_DEVICE: return EXDEV; case ERROR_NO_MORE_FILES: return ENOENT; case ERROR_WRITE_PROTECT: return EACCES; case ERROR_BAD_UNIT: return EACCES; case ERROR_NOT_READY: return EACCES; case ERROR_BAD_COMMAND: return EACCES; case ERROR_CRC: return EACCES; case ERROR_BAD_LENGTH: return EACCES; case ERROR_SEEK: return EACCES; case ERROR_NOT_DOS_DISK: return EACCES; case ERROR_SECTOR_NOT_FOUND: return EACCES; case ERROR_OUT_OF_PAPER: return EACCES; case ERROR_WRITE_FAULT: return EACCES; case ERROR_READ_FAULT: return EACCES; case ERROR_GEN_FAILURE: return EACCES; case ERROR_SHARING_VIOLATION: return EACCES; case ERROR_LOCK_VIOLATION: return EACCES; case ERROR_WRONG_DISK: return EACCES; case ERROR_SHARING_BUFFER_EXCEEDED: return EACCES; case ERROR_BAD_NETPATH: return ENOENT; case ERROR_NETWORK_ACCESS_DENIED: return EACCES; case ERROR_BAD_NET_NAME: return ENOENT; case ERROR_FILE_EXISTS: return EEXIST; case ERROR_CANNOT_MAKE: return EACCES; case ERROR_FAIL_I24: return EACCES; case ERROR_NO_PROC_SLOTS: return EAGAIN; case ERROR_DRIVE_LOCKED: return EACCES; case ERROR_BROKEN_PIPE: return EPIPE; case ERROR_DISK_FULL: return ENOSPC; case ERROR_INVALID_TARGET_HANDLE: return EBADF; case ERROR_WAIT_NO_CHILDREN: return ECHILD; case ERROR_CHILD_NOT_COMPLETE: return ECHILD; case ERROR_DIRECT_ACCESS_HANDLE: return EBADF; case ERROR_SEEK_ON_DEVICE: return EACCES; case ERROR_DIR_NOT_EMPTY: return ENOTEMPTY; case ERROR_NOT_LOCKED: return EACCES; case ERROR_BAD_PATHNAME: return ENOENT; case ERROR_MAX_THRDS_REACHED: return EAGAIN; case ERROR_LOCK_FAILED: return EACCES; case ERROR_INVALID_STARTING_CODESEG: return ENOEXEC; case ERROR_INVALID_STACKSEG: return ENOEXEC; case ERROR_INVALID_MODULETYPE: return ENOEXEC; case ERROR_INVALID_EXE_SIGNATURE: return ENOEXEC; case ERROR_EXE_MARKED_INVALID: return ENOEXEC; case ERROR_BAD_EXE_FORMAT: return ENOEXEC; case ERROR_ITERATED_DATA_EXCEEDS_64k: return ENOEXEC; case ERROR_INVALID_MINALLOCSIZE: return ENOEXEC; case ERROR_DYNLINK_FROM_INVALID_RING: return ENOEXEC; case ERROR_IOPL_NOT_ENABLED: return ENOEXEC; case ERROR_INVALID_SEGDPL: return ENOEXEC; case ERROR_AUTODATASEG_EXCEEDS_64k: return ENOEXEC; case ERROR_RING2SEG_MUST_BE_MOVABLE: return ENOEXEC; case ERROR_RELOC_CHAIN_XEEDS_SEGLIM: return ENOEXEC; case ERROR_INFLOOP_IN_RELOC_CHAIN: return ENOEXEC; case ERROR_FILENAME_EXCED_RANGE: return ENOENT; case ERROR_NESTING_NOT_ALLOWED: return EAGAIN; case ERROR_NOT_ENOUGH_QUOTA: return ENOMEM; default: return EIO; } } /* Wrapper to be used by other modules to set ERRNO from the Windows * error. EC may be -1 to get the last error. */ void _gpgrt_w32_set_errno (int ec) { if (ec == -1) ec = GetLastError (); _set_errno (map_w32_to_errno (ec)); } gpg_err_code_t _gpgrt_w32_get_last_err_code (void) { int ec = GetLastError (); errno = map_w32_to_errno (ec); return _gpg_err_code_from_errno (errno); } #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) { if (item_prev) item_prev->next = item->next; else 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; } 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; } 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; gpgrt_assert (mem_cookie->memory_size >= mem_cookie->offset); nleft = mem_cookie->memory_size - mem_cookie->offset; 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; } 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, }; #ifdef HAVE_W32_SYSTEM /* * Implementation of SOCKET based I/O. */ /* Cookie for SOCKET objects. */ typedef struct estream_cookie_sock { SOCKET sock; /* The SOCKET 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_sock_t; /* * Create function for objects indentified by a libc file descriptor. */ static int func_sock_create (void **cookie, SOCKET sock, unsigned int modeflags, int no_close) { estream_cookie_sock_t sock_cookie; int err; trace (("enter: sock=%d mf=%x nc=%d", (int)sock, modeflags, no_close)); sock_cookie = mem_alloc (sizeof (*sock_cookie)); if (! sock_cookie) err = -1; else { sock_cookie->sock = sock; sock_cookie->no_close = no_close; sock_cookie->nonblock = !!(modeflags & O_NONBLOCK); *cookie = sock_cookie; err = 0; } trace_errno (err, ("leave: cookie=%p err=%d", *cookie, err)); return err; } /* * Read function for SOCKET objects. */ static gpgrt_ssize_t func_sock_read (void *cookie, void *buffer, size_t size) { estream_cookie_sock_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->sock)) { _gpgrt_yield (); bytes_read = 0; } else { _gpgrt_pre_syscall (); do { bytes_read = recv (file_cookie->sock, buffer, size, 0); } 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 SOCKET objects. */ static gpgrt_ssize_t func_sock_write (void *cookie, const void *buffer, size_t size) { estream_cookie_sock_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->sock)) { _gpgrt_yield (); bytes_written = size; /* Yeah: Success writing to the bit bucket. */ } else if (buffer) { _gpgrt_pre_syscall (); do { bytes_written = send (file_cookie->sock, buffer, size, 0); } 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 SOCKET objects. */ static int func_sock_seek (void *cookie, gpgrt_off_t *offset, int whence) { (void)cookie; (void)offset; (void)whence; _set_errno (ESPIPE); return -1; } /* * The IOCTL function for SOCKET objects. */ static int func_sock_ioctl (void *cookie, int cmd, void *ptr, size_t *len) { estream_cookie_sock_t sock_cookie = cookie; int ret; if (cmd == COOKIE_IOCTL_NONBLOCK && !len) { sock_cookie->nonblock = !!ptr; if (IS_INVALID_FD (sock_cookie->sock)) { _set_errno (EINVAL); ret = -1; } else { u_long mode = 0; if (sock_cookie->nonblock) mode = 1; ret = ioctlsocket (sock_cookie->sock, FIONBIO, &mode); } } else { _set_errno (EINVAL); ret = -1; } return ret; } /* * The destroy function for SOCKET objects. */ static int func_sock_destroy (void *cookie) { estream_cookie_sock_t sock_cookie = cookie; int err; trace (("enter: cookie=%p", cookie)); if (sock_cookie) { if (IS_INVALID_FD (sock_cookie->sock)) err = 0; else err = sock_cookie->no_close? 0 : closesocket (sock_cookie->sock); mem_free (sock_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_sock = { { func_sock_read, func_sock_write, func_sock_seek, func_sock_destroy, }, func_sock_ioctl, }; /* * Implementation of W32 handle based I/O. */ /* 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. */ #ifdef HAVE_W32_SYSTEM static int any8bitchar (const char *string) { if (string) for ( ; *string; string++) if ((*string & 0x80)) return 1; return 0; } #endif /*HAVE_W32_SYSTEM*/ /* 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; } #ifdef HAVE_W32_SYSTEM if (any8bitchar (path)) { wchar_t *wpath; wpath = _gpgrt_utf8_to_wchar (path); if (!wpath) fd = -1; else { fd = _wopen (wpath, modeflags, cmode); _gpgrt_free_wchar (wpath); } } else /* Avoid unnecessary conversion. */ fd = open (path, modeflags, cmode); #else fd = open (path, modeflags, cmode); #endif 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; } /* Create function for objects identified by a file name. Windows * version to use CreateFile. */ #ifdef HAVE_W32_SYSTEM static int func_file_create_w32 (void **cookie, HANDLE *rethd, const char *path, unsigned int modeflags, unsigned int cmode) { estream_cookie_w32_t hd_cookie; wchar_t *wpath = NULL; int err = 0; HANDLE hd; DWORD desired_access; DWORD share_mode; DWORD creation_distribution; (void)cmode; hd_cookie = mem_alloc (sizeof *hd_cookie); if (!hd_cookie) { err = -1; goto leave; } wpath = _gpgrt_fname_to_wchar (path); if (!wpath) { err = -1; goto leave; } if ((modeflags & O_WRONLY)) { desired_access = GENERIC_WRITE; share_mode = FILE_SHARE_WRITE; } else if ((modeflags & O_RDWR)) { desired_access = GENERIC_READ | GENERIC_WRITE; share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE; } else { desired_access = GENERIC_READ; share_mode = FILE_SHARE_READ; } creation_distribution = 0; if ((modeflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) creation_distribution |= CREATE_NEW; else if ((modeflags & O_TRUNC) == O_TRUNC) { if ((modeflags & O_CREAT) == O_CREAT) creation_distribution |= CREATE_ALWAYS; else if ((modeflags & O_RDONLY) != O_RDONLY) creation_distribution |= TRUNCATE_EXISTING; } else if ((modeflags & O_APPEND) == O_APPEND) creation_distribution |= OPEN_EXISTING; else if ((modeflags & O_CREAT) == O_CREAT) creation_distribution |= OPEN_ALWAYS; else creation_distribution |= OPEN_EXISTING; hd = CreateFileW (wpath, desired_access, share_mode, NULL, /* security attributes */ creation_distribution, 0, /* flags and attributes */ NULL); /* template file */ if (hd == INVALID_HANDLE_VALUE) { _set_errno (map_w32_to_errno (GetLastError ())); err = -1; goto leave; } hd_cookie->hd = hd; hd_cookie->no_close = 0; hd_cookie->no_syscall_clamp = 0; *cookie = hd_cookie; *rethd = hd; leave: _gpgrt_free_wchar (wpath); if (err) mem_free (hd_cookie); return err; } #endif /*HAVE_W32_SYSTEM*/ /* 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 GPGRT_SYSHD_HANDLE mode. On POSIX this * is a NOP but under Windows the direct W32 API functions (HANDLE) * are used instead of their libc counterparts (fd). This flag * also allows to use file names longer than MAXPATH. Note that * gpgrt_fileno does not not work for such a stream under Windows. * * 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; 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) { 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 stored 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. With CANCEL_MODE set * try to cancel as much as possible (see _gpgrt_fcancel). */ static int do_close (estream_t stream, int cancel_mode, 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); if (cancel_mode) { stream->flags.writing = 0; es_empty (stream); } 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, 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 = 0; estream_t stream = NULL; void *cookie = NULL; int err; struct cookie_io_functions_s *functions; es_syshd_t syshd; int kind; err = parse_mode (mode, &modeflags, &xmode, &cmode); if (err) goto leave; /* Convenience hack so that we can use /dev/null on Windows. */ #ifdef HAVE_W32_SYSTEM if (path && !strcmp (path, "/dev/null")) path = "nul"; #endif #ifdef HAVE_W32_SYSTEM if ((xmode & X_SYSOPEN)) { kind = BACKEND_W32; functions = &estream_functions_w32; syshd.type = ES_SYSHD_HANDLE; err = func_file_create_w32 (&cookie, &syshd.u.handle, path, modeflags, cmode); } else #endif /* W32 */ { kind = BACKEND_FD; functions = &estream_functions_fd; syshd.type = ES_SYSHD_FD; err = func_file_create (&cookie, &syshd.u.fd, path, modeflags, cmode); } if (err) goto leave; create_called = 1; err = create_stream (&stream, cookie, &syshd, kind, *functions, modeflags, xmode, 0); if (err) goto leave; if (stream && path) fname_set_internal (stream, path, 1); leave: if (err && create_called) functions->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 static estream_t do_sockopen (SOCKET sock, 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 sockopen. */ _set_errno (EINVAL); err = -1; goto out; } err = func_sock_create (&cookie, sock, modeflags, no_close); if (err) goto out; syshd.type = ES_SYSHD_SOCK; syshd.u.sock = sock; create_called = 1; err = create_stream (&stream, cookie, &syshd, BACKEND_SOCK, estream_functions_sock, 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_sock.public.func_close) (cookie); return stream; } 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 * separate 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: #ifndef HAVE_W32_SYSTEM case ES_SYSHD_SOCK: #endif stream = do_fdopen (syshd->u.fd, mode, no_close, 0); break; #ifdef HAVE_W32_SYSTEM case ES_SYSHD_SOCK: stream = do_sockopen (syshd->u.sock, mode, no_close, 0); break; 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)); _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; /* Convenience hack so that we can use /dev/null on Windows. */ #ifdef HAVE_W32_SYSTEM if (!strcmp (path, "/dev/null")) path = "nul"; #endif 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, 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, 0); stream = NULL; } return stream; } int _gpgrt_fclose (estream_t stream) { int err; err = do_close (stream, 0, 0); return err; } /* gpgrt_fcancel does the same as gpgrt_fclose but tries to avoid * flushing out any data still held in internal buffers. It may or * may not remove a new file created for that stream by the open * function. */ int _gpgrt_fcancel (estream_t stream) { int err; err = do_close (stream, 1, 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, 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. If gpgrt_fcancel is used, the cancellation of internal buffers is done before the notifications. The notification handler 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-initialized 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 ((intptr_t)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 # ifdef HAVE_POLL_H struct pollfd *poll_fds = NULL; nfds_t poll_nfds; # else fd_set readfds, writefds, exceptfds; int any_readfd, any_writefd, any_exceptfd; int max_fd; #endif 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 */ } if (count) goto leave; /* 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*/ # ifdef HAVE_POLL_H poll_fds = xtrymalloc (sizeof (*poll_fds)*nfds); if (!poll_fds) { count = -1; goto leave; } poll_nfds = 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 || item->want_write || item->want_oob) { poll_fds[poll_nfds].fd = fd; poll_fds[poll_nfds].events = ((item->want_read ? POLLIN : 0) |(item->want_write ? POLLOUT : 0) |(item->want_oob ? POLLPRI : 0)); poll_fds[poll_nfds].revents = 0; poll_nfds++; } } _gpgrt_pre_syscall (); do ret = poll (poll_fds, poll_nfds, timeout); while (ret == -1 && (errno == EINTR || errno == EAGAIN)); _gpgrt_post_syscall (); # else /* !HAVE_POLL_H */ 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 (); # endif if (ret == -1) { # ifdef HAVE_POLL_H trace_errno (1, ("poll failed: ")); # else trace_errno (1, ("select failed: ")); # endif 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; } # ifdef HAVE_POLL_H poll_nfds = 0; 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 && (poll_fds[poll_nfds].revents & (POLLIN|POLLHUP))) { item->got_read = 1; any = 1; } if (item->want_write && (poll_fds[poll_nfds].revents & POLLOUT)) { item->got_write = 1; any = 1; } if (item->want_oob && (poll_fds[poll_nfds].revents & ~(POLLIN|POLLOUT))) { item->got_oob = 1; any = 1; } if (item->want_read || item->want_write || item->want_oob) poll_nfds++; if (any) count++; } # else 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 #endif /*!HAVE_W32_SYSTEM*/ leave: #ifndef HAVE_W32_SYSTEM # ifdef HAVE_POLL_H xfree (poll_fds); # endif #endif #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 c75b317..28e9ef3 100644 --- a/src/gpg-error.def.in +++ b/src/gpg-error.def.in @@ -1,250 +1,246 @@ /* 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 gpgrt_abort @175 gpgrt_set_confdir @176 gpgrt_argparser @177 gpgrt_fnameconcat @178 gpgrt_absfnameconcat @179 gpgrt_reallocarray @180 gpgrt_fclose @181 gpgrt_fcancel @182 gpgrt_access @183 gpgrt_free_wchar @184 gpgrt_fname_to_wchar @185 gpgrt_utf8_to_wchar @186 gpgrt_wchar_to_utf8 @187 ;; end of file with public symbols for Windows. diff --git a/src/gpgrt-int.h b/src/gpgrt-int.h index a541620..cb14191 100644 --- a/src/gpgrt-int.h +++ b/src/gpgrt-int.h @@ -1,848 +1,848 @@ /* 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" #include "protos.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_reallocarray (void *a, size_t oldnmemb, size_t nmemb, size_t size); 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 xtryreallocarray(a,b,c,d) _gpgrt_reallocarray ((a),(b),(c),(d)) #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_SOCK, 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. */ /* BUFSIZ on Windows is 512 but on current Linux it is 8k. We better * use the 8k for Windows as well. */ #ifdef HAVE_W32_SYSTEM # define BUFFER_BLOCK_SIZE 8192 #else # define BUFFER_BLOCK_SIZE BUFSIZ #endif #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_fcancel (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. */ extern 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; unsigned int stop_seen:1; unsigned int invalid_encoding:1; unsigned 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. + * This flag is only useful under W32 systems, so that no new + * console is created and pops up a console window when starting + * the server. * * 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. + * On W32 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); int _gpgrt_argparser (gpgrt_argparse_t *arg, gpgrt_opt_t *opts, const char *confname); 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*)); void _gpgrt_set_confdir (int what, const char *name); /* * 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); /* Wrapper for Windows to allow utf8 file names. */ gpg_err_code_t _gpgrt_access (const char *fname, int mode); /* Return the home directory of user NAME. */ char *_gpgrt_getpwdir (const char *name); /* Return the account name of the current user. */ char *_gpgrt_getusername (void); /* Expand and concat file name parts. */ char *_gpgrt_vfnameconcat (int want_abs, const char *first_part, va_list arg_ptr); char *_gpgrt_fnameconcat (const char *first_part, ... ) GPGRT_ATTR_SENTINEL(0); char *_gpgrt_absfnameconcat (const char *first_part, ... ) GPGRT_ATTR_SENTINEL(0); /* * Platform specific functions (Windows) */ #ifdef HAVE_W32_SYSTEM char *_gpgrt_w32_reg_query_string (const char *root, const char *dir, const char *name); char *_gpgrt_w32_reg_get_string (const char *key); wchar_t *_gpgrt_fname_to_wchar (const char *fname); #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 2d9f7ab..493e3ba 100644 --- a/src/init.c +++ b/src/init.c @@ -1,753 +1,690 @@ /* 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. */ _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); } /* This is safe version of realloc useful for reallocing a calloced * array. There are two ways to call it: The first example * reallocates the array A to N elements each of SIZE but does not * clear the newly allocated elements: * * p = gpgrt_reallocarray (a, n, n, nsize); * * Note that when NOLD is larger than N no cleaning is needed anyway. * The second example reallocates an array of size NOLD to N elements * each of SIZE but clear the newly allocated elements: * * p = gpgrt_reallocarray (a, nold, n, nsize); * * Note that gpgrt_reallocarray (NULL, 0, n, nsize) is equivalent to * _gpgrt_calloc (n, nsize). * */ void * _gpgrt_reallocarray (void *a, size_t oldnmemb, size_t nmemb, size_t size) { size_t oldbytes, bytes; char *p; bytes = nmemb * size; /* size_t is unsigned so the behavior on overflow * is defined. */ if (size && bytes / size != nmemb) { _gpg_err_set_errno (ENOMEM); return NULL; } p = _gpgrt_realloc (a, bytes); if (p && oldnmemb < nmemb) { /* OLDNMEMBS is lower than NMEMB thus the user asked for a calloc. Clear all newly allocated members. */ oldbytes = oldnmemb * size; if (size && oldbytes / size != oldnmemb) { xfree (p); _gpg_err_set_errno (ENOMEM); return NULL; } memset (p + oldbytes, 0, bytes - oldbytes); } return p; } /* 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) { int save_errno; if (!a) return; /* Shortcut */ /* In case ERRNO is set we better save it so that the free machinery * may not accidentally change ERRNO. We restore it only if it was * already set to comply with the usual C semantic for ERRNO. * See also https://dev.gnupg.org/T5393#146261 */ save_errno = errno; _gpgrt_realloc (a, 0); if (save_errno && save_errno != errno) _gpg_err_set_errno (save_errno); } 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. */ _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/init.h b/src/init.h index 90a716a..ea7154d 100644 --- a/src/init.h +++ b/src/init.h @@ -1,70 +1,61 @@ /* init.h - Declarations for init.c Copyright (C) 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 . */ #ifndef INIT_H #define INIT_H #if HAVE_W32_SYSTEM /* Forward declaration - defined in w32-gettext.c. */ struct loaded_domain; /* An item for a linked list of loaded domains. */ struct domainlist_s { struct domainlist_s *next; char *dname; /* Directory name for the mo file. */ char *fname; /* File name for the MO file. */ int load_failed; /* True if loading the domain failed. */ struct loaded_domain *domain; /* NULL if not loaded. Never changed once set to non-NULL. */ char name[1]; /* Name of the domain. Never changed once set. */ }; -/* 119 bytes for an error message should be enough. With this size we - can assume that the allocation does not take up more than 128 bytes - per thread. Note that this is only used for W32CE. */ -#define STRBUFFER_SIZE 120 - /* The TLS space definition. */ struct tls_space_s { /* Flag used by w32-gettext. */ int gt_use_utf8; - -#ifdef HAVE_W32CE_SYSTEM - char strerror_buffer[STRBUFFER_SIZE]; -#endif }; /* Return the TLS. */ struct tls_space_s *get_tls (void); /* Explicit constructor for w32-gettext.c */ #ifndef DLL_EXPORT void _gpg_w32__init_gettext_module (void); #endif #endif /*HAVE_W32_SYSTEM*/ #endif /*INIT_H*/ diff --git a/src/mkheader.c b/src/mkheader.c index 154d79f..e05dafd 100644 --- a/src/mkheader.c +++ b/src/mkheader.c @@ -1,787 +1,782 @@ /* mkheader.c - Create a header file for libgpg-error * Copyright (C) 2010 Free Software Foundation, Inc. * Copyright (C) 2014 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. */ #include #include #include #include #include #define PGM "mkheader" #define LINESIZE 1024 static char *host_triplet; /* malloced. */ static char *host_os; /* points into host_triplet. */ static char *srcdir; static const char *hdr_version; static const char *hdr_version_number; static int cross_building; /* Command line flag. */ /* Values take from the supplied config.h. */ static int have_stdint_h; static int have_sys_types_h; static int have_w32_system; static int have_w64_system; static char *replacement_for_off_type; static int use_posix_threads; /* Various state flags. */ static int stdint_h_included; static int sys_types_h_included; /* The usual free wrapper. */ static void xfree (void *a) { if (a) free (a); } static char * xmalloc (size_t n) { char *p; p = malloc (n); if (!p) { fputs (PGM ": out of core\n", stderr); exit (1); } return p; } static char * xstrdup (const char *string) { char *p; size_t len = strlen (string) + 1; p = xmalloc (len); memcpy (p, string, len); return p; } /* Return a malloced string with TRIPLET. If TRIPLET has an alias * return that instead. In general build-aux/config.sub should do the * aliasing but some returned triplets are anyway identical and thus * we use this function to map it to the canonical form. A pointer to * the OS part of the returned value is stored at R_OS. * NO_VENDOR_HACK is for internal use; caller must call with 0. */ static char * canon_host_triplet (const char *triplet, int no_vendor_hack, char **r_os) { struct { const char *name; const char *alias; } tbl[] = { {"i486-pc-linux-gnu", "i686-unknown-linux-gnu" }, {"i586-pc-linux-gnu" }, {"i686-pc-linux-gnu" }, {"arc-oe-linux-gnu" }, /* Other CPU but same struct. */ {"arc-oe-linux-uclibc" }, /* and uclibc is also the same. */ {"i486-pc-gnu", "i686-unknown-gnu"}, {"i586-pc-gnu"}, {"i686-pc-gnu"}, {"i486-pc-kfreebsd-gnu", "i686-unknown-kfreebsd-gnu"}, {"i586-pc-kfreebsd-gnu"}, {"i686-pc-kfreebsd-gnu"}, {"x86_64-pc-linux-gnuhardened1", "x86_64-unknown-linux-gnu" }, {"x86_64-pc-linux-gnu" }, {"powerpc-unknown-linux-gnuspe", "powerpc-unknown-linux-gnu" }, {"arm-unknown-linux-gnueabihf", "arm-unknown-linux-gnueabi" }, {"armv7-unknown-linux-gnueabihf" }, {"armv7a-unknown-linux-gnueabihf" }, {"armv5-unknown-linux-musleabi" }, {"armv6-unknown-linux-musleabihf" }, { NULL } }; int i; const char *lastalias = NULL; const char *s; char *p; char *result; for (i=0; tbl[i].name; i++) { if (tbl[i].alias) lastalias = tbl[i].alias; if (!strcmp (tbl[i].name, triplet)) { if (!lastalias) break; /* Ooops: first entry has no alias. */ result = xstrdup (lastalias); goto leave; } } for (i=0, s=triplet; *s; s++) if (*s == '-') i++; if (i > 2 && !no_vendor_hack) { /* We have a 4 part "triplet": CPU-VENDOR-KERNEL-SYSTEM where * the last two parts replace the OS part of a real triplet. * The VENDOR part is then in general useless because * KERNEL-SYSTEM is specific enough. We now do a second pass by * replacing VENDOR with "unknown". */ char *buf = xmalloc (strlen (triplet) + 7 + 1); for (p=buf,s=triplet,i=0; *s; s++) { *p++ = *s; if (*s == '-' && ++i == 1) { memcpy (p, "unknown-",8); p += 8; for (s++; *s != '-'; s++) ; } } *p = 0; result = canon_host_triplet (buf, 1, NULL); xfree (buf); goto leave; } result = xstrdup (triplet); leave: /* Find the OS part. */ if (r_os) { *r_os = result + strlen (result); /* Default to the empty string. */ for (i=0, p=result; *p; p++) if (*p == '-' && ++i == 2) { *r_os = p+1; break; } } return result; } /* Parse the supplied config.h file and extract required info. Returns 0 on success. */ static int parse_config_h (const char *fname) { FILE *fp; char line[LINESIZE]; int lnr = 0; char *p1; fp = fopen (fname, "r"); if (!fp) { fprintf (stderr, "%s:%d: can't open file: %s\n", fname, lnr, strerror (errno)); return 1; } while (fgets (line, LINESIZE, fp)) { size_t n = strlen (line); lnr++; if (!n || line[n-1] != '\n') { fprintf (stderr, "%s:%d: trailing linefeed missing, line too long or " "embedded nul character\n", fname, lnr); break; } line[--n] = 0; if (strncmp (line, "#define ", 8)) continue; /* We are only interested in define lines. */ p1 = strtok (line + 8, " \t"); if (!*p1) continue; /* oops */ if (!strcmp (p1, "HAVE_STDINT_H")) have_stdint_h = 1; else if (!strcmp (p1, "HAVE_SYS_TYPES_H")) have_sys_types_h = 1; else if (!strcmp (p1, "HAVE_W32_SYSTEM")) have_w32_system = 1; else if (!strcmp (p1, "HAVE_W64_SYSTEM")) have_w64_system = 1; else if (!strcmp (p1, "REPLACEMENT_FOR_OFF_T")) { p1 = strtok (NULL, "\""); if (!*p1) continue; /* oops */ xfree (replacement_for_off_type); replacement_for_off_type = xstrdup (p1); } else if (!strcmp (p1, "USE_POSIX_THREADS")) use_posix_threads = 1; } if (ferror (fp)) { fprintf (stderr, "%s:%d: error reading file: %s\n", fname, lnr, strerror (errno)); fclose (fp); return 1; } fclose (fp); return 0; } /* Write LINE to stdout. The function is allowed to modify LINE. */ static void write_str (char *line) { if (fputs (line, stdout) == EOF) { fprintf (stderr, PGM ": error writing to stdout: %s\n", strerror (errno)); exit (1); } } static void write_line (char *line) { if (puts (line) == EOF) { fprintf (stderr, PGM ": error writing to stdout: %s\n", strerror (errno)); exit (1); } } /* Write SOURCE or CODES line to stdout. The function is allowed to modify LINE. Trailing white space is already removed. Passing NULL resets the internal state. */ static void write_sources_or_codes (char *line) { static int in_intro; char *p1, *p2; if (!line) { in_intro = 1; return; } if (!*line) return; if (in_intro) { if (!strchr ("0123456789", *line)) return; in_intro = 0; } p1 = strtok (line, " \t"); p2 = p1? strtok (NULL, " \t") : NULL; if (p1 && p2 && strchr ("0123456789", *p1) && *p2) { write_str (" "); write_str (p2); write_str (" = "); write_str (p1); write_str (",\n"); } } /* Write system errnos to stdout. The function is allowed to modify LINE. Trailing white space is already removed. Passing NULL resets the internal state. */ static void write_errnos_in (char *line) { static int state; char *p1, *p2; if (!line) { state = 0; return; } if (!*line) return; if (!state && strchr ("0123456789", *line)) state = 1; else if (state == 1 && !strchr ("0123456789", *line)) state = 2; if (state != 1) return; p1 = strtok (line, " \t"); p2 = p1? strtok (NULL, " \t") : NULL; if (p1 && p2 && strchr ("0123456789", *p1) && *p2) { write_str (" GPG_ERR_"); write_str (p2); write_str (" = GPG_ERR_SYSTEM_ERROR | "); write_str (p1); write_str (",\n"); } } /* Create the full file name for NAME and return a newly allocated string with it. If name contains a '&' and REPL is not NULL replace '&' with REPL. */ static char * mk_include_name (const char *name, const char *repl) { FILE *fp; char *incfname, *p; const char *s; incfname = malloc (strlen (srcdir) + strlen (name) + (repl?strlen (repl):0) + 1); if (!incfname) { fputs (PGM ": out of core\n", stderr); exit (1); } if (*name == '.' && name[1] == '/') *incfname = 0; else strcpy (incfname, srcdir); p = incfname + strlen (incfname); for (s=name; *s; s++) { if (*s == '&' && repl) { while (*repl) *p++ = *repl++; repl = NULL; /* Replace only once. */ } else *p++ = *s; } *p = 0; return incfname; } /* Include the file NAME from the source directory. The included file is not further expanded. It may have comments indicated by a double hash mark at the begin of a line. OUTF is called for each read line and passed a buffer with the content of line sans line line endings. If NAME is prefixed with "./" it is included from the current directory and not from the source directory. */ static void include_file (const char *fname, int lnr, const char *name, void (*outf)(char*)) { FILE *fp; char *incfname; int inclnr; char line[LINESIZE]; int repl_flag; repl_flag = !!strchr (name, '&'); incfname = mk_include_name (name, repl_flag? host_triplet : NULL); fp = fopen (incfname, "r"); if (!fp && repl_flag) { /* Try again using the OS string. */ free (incfname); incfname = mk_include_name (name, host_os); fp = fopen (incfname, "r"); } if (!fp) { fprintf (stderr, "%s:%d: error including `%s': %s\n", fname, lnr, incfname, strerror (errno)); exit (1); } if (repl_flag) fprintf (stderr,"%s:%d: note: including '%s'\n", fname, lnr, incfname); inclnr = 0; while (fgets (line, LINESIZE, fp)) { size_t n = strlen (line); inclnr++; if (!n || line[n-1] != '\n') { fprintf (stderr, "%s:%d: trailing linefeed missing, line too long or " "embedded nul character\n", incfname, inclnr); fprintf (stderr,"%s:%d: note: file '%s' included from here\n", fname, lnr, incfname); exit (1); } line[--n] = 0; while (line[n] == ' ' || line[n] == '\t' || line[n] == '\r') { line[n] = 0; if (!n) break; n--; } if (line[0] == '#' && line[1] == '#') { if (!strncmp (line+2, "EOF##", 5)) break; /* Forced EOF. */ } else outf (line); } if (ferror (fp)) { fprintf (stderr, "%s:%d: error reading `%s': %s\n", fname, lnr, incfname, strerror (errno)); exit (1); } fclose (fp); free (incfname); } /* Try to include the file NAME. Returns true if it does not exist. */ static int try_include_file (const char *fname, int lnr, const char *name, void (*outf)(char*)) { int rc; char *incfname; int repl_flag; repl_flag = !!strchr (name, '&'); incfname = mk_include_name (name, repl_flag? host_triplet : NULL); rc = access (incfname, R_OK); if (rc && repl_flag) { free (incfname); incfname = mk_include_name (name, host_os); rc = access (incfname, R_OK); } if (!rc) include_file (fname, lnr, name, outf); free (incfname); return rc; } static int write_special (const char *fname, int lnr, const char *tag) { if (!strcmp (tag, "version")) { putchar ('\"'); fputs (hdr_version, stdout); putchar ('\"'); } else if (!strcmp (tag, "version-number")) { fputs (hdr_version_number, stdout); } else if (!strcmp (tag, "define:gpgrt_off_t")) { if (!replacement_for_off_type) { fprintf (stderr, "%s:%d: replacement for off_t not defined\n", fname, lnr); exit (1); } else { if (!strcmp (replacement_for_off_type, "int64_t") && !stdint_h_included && have_stdint_h) { fputs ("#include \n\n", stdout); stdint_h_included = 1; } printf ("typedef %s gpgrt_off_t;\n", replacement_for_off_type); } } else if (!strcmp (tag, "define:gpgrt_ssize_t")) { if (have_w64_system) { if (!stdint_h_included && have_stdint_h) { fputs ("# include \n", stdout); stdint_h_included = 1; } fputs ("typedef int64_t gpgrt_ssize_t;\n", stdout); } else if (have_w32_system) { fputs ("typedef long gpgrt_ssize_t;\n", stdout); } else { if (!sys_types_h_included) { fputs ("#include \n", stdout); sys_types_h_included = 1; } fputs ("typedef ssize_t gpgrt_ssize_t;\n", stdout); } } else if (!strcmp (tag, "api_ssize_t")) { if (have_w32_system) fputs ("gpgrt_ssize_t", stdout); else fputs ("ssize_t", stdout); } else if (!strcmp (tag, "SOCKET_t")) { if (have_w32_system) fputs ("uintptr_t", stdout); else fputs ("int", stdout); } else if (!strcmp (tag, "define:pid_t")) { if (have_sys_types_h) { if (!sys_types_h_included) { fputs ("#include \n", stdout); sys_types_h_included = 1; } } else if (have_w64_system) { if (!stdint_h_included && have_stdint_h) { fputs ("#include \n", stdout); stdint_h_included = 1; } fputs ("typedef int64_t pid_t\n", stdout); } else { fputs ("typedef int pid_t\n", stdout); } } else if (!strcmp (tag, "include:err-sources")) { write_sources_or_codes (NULL); include_file (fname, lnr, "err-sources.h.in", write_sources_or_codes); } else if (!strcmp (tag, "include:err-codes")) { write_sources_or_codes (NULL); include_file (fname, lnr, "err-codes.h.in", write_sources_or_codes); } else if (!strcmp (tag, "include:errnos")) { include_file (fname, lnr, "errnos.in", write_errnos_in); } else if (!strcmp (tag, "include:os-add")) { if (!strcmp (host_os, "mingw32")) { include_file (fname, lnr, "w32-add.h", write_line); } - else if (!strcmp (host_os, "mingw32ce")) - { - include_file (fname, lnr, "w32-add.h", write_line); - include_file (fname, lnr, "w32ce-add.h", write_line); - } } else if (!strcmp (tag, "include:lock-obj")) { /* If we are not cross compiling and the native file exists we * prefer that over one from syscfg. */ if (cross_building || try_include_file (fname, lnr, "./lock-obj-pub.native.h", write_line)) include_file (fname, lnr, "syscfg/lock-obj-pub.&.h", write_line); } else return 0; /* Unknown tag. */ return 1; /* Tag processed. */ } int main (int argc, char **argv) { FILE *fp = NULL; char line[LINESIZE]; int lnr = 0; const char *fname, *s; char *p1, *p2; const char *config_h; const char *host_triplet_raw; if (argc) { argc--; argv++; } if (argc && !strcmp (argv[0], "--cross")) { cross_building = 1; argc--; argv++; } if (argc == 1) { /* Print just the canonicalized host triplet. */ host_triplet = canon_host_triplet (argv[0], 0, &host_os); printf ("%s\n", host_triplet); goto leave; } else if (argc == 5) ; /* Standard operation. */ else { fputs ("usage: " PGM " host_triplet template.h config.h version version_number\n" " " PGM " host_triplet\n", stderr); return 1; } host_triplet_raw = argv[0]; fname = argv[1]; config_h = argv[2]; hdr_version = argv[3]; hdr_version_number = argv[4]; host_triplet = canon_host_triplet (host_triplet_raw, 0, &host_os); srcdir = malloc (strlen (fname) + 2 + 1); if (!srcdir) { fputs (PGM ": out of core\n", stderr); return 1; } strcpy (srcdir, fname); p1 = strrchr (srcdir, '/'); if (p1) p1[1] = 0; else strcpy (srcdir, "./"); if (parse_config_h (config_h)) return 1; fp = fopen (fname, "r"); if (!fp) { fprintf (stderr, "%s:%d: can't open file: %s\n", fname, lnr, strerror (errno)); return 1; } while (fgets (line, LINESIZE, fp)) { size_t n = strlen (line); lnr++; if (!n || line[n-1] != '\n') { fprintf (stderr, "%s:%d: trailing linefeed missing, line too long or " "embedded nul character\n", fname, lnr); break; } line[--n] = 0; p1 = strchr (line, '@'); p2 = p1? strchr (p1+1, '@') : NULL; if (!p1 || !p2 || p2-p1 == 1) { puts (line); continue; } *p1++ = 0; *p2++ = 0; fputs (line, stdout); if (!strcmp (p1, "configure_input")) { s = strrchr (fname, '/'); printf ("Do not edit. Generated from %s for:\n%*s", s? s+1 : fname, (int)(p1 - line) + 13, ""); if (!strcmp (host_triplet, host_triplet_raw)) printf ("%s", host_triplet); else printf ("%s (%s)", host_triplet, host_triplet_raw); if (!use_posix_threads && !have_w32_system && !have_w64_system) fputs (" NO-THREADS", stdout); fputs (p2, stdout); } else if (!write_special (fname, lnr, p1)) { putchar ('@'); fputs (p1, stdout); putchar ('@'); fputs (p2, stdout); } else if (*p2) { fputs (p2, stdout); } putchar ('\n'); } if (ferror (fp)) { fprintf (stderr, "%s:%d: error reading file: %s\n", fname, lnr, strerror (errno)); return 1; } fputs ("/*\n" "Loc" "al Variables:\n" "buffer-read-only: t\n" "End:\n" "*/\n", stdout); leave: if (ferror (stdout)) { fprintf (stderr, PGM ": error writing to stdout: %s\n", strerror (errno)); return 1; } if (fp) fclose (fp); xfree (host_triplet); return 0; } diff --git a/src/mkw32errmap.c b/src/mkw32errmap.c deleted file mode 100644 index 508a513..0000000 --- a/src/mkw32errmap.c +++ /dev/null @@ -1,178 +0,0 @@ -/* mkw32errmap.c - Generate mapping sources for Windows. - Copyright (C) 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 . - */ - -#ifdef RESOLVE_MACROS -# include -#endif -#include -#include - -static const char header_gpg_extra_errno_h[] = - "/* errno.h - WindowsCE errno.h substitute\n" - " Copyright (C) 2010 g10 Code GmbH\n" - "\n" - " This file is free software; as a special exception the author gives\n" - " unlimited permission to copy and/or distribute it, with or without\n" - " modifications, as long as this notice is preserved.\n" - "\n" - " This file is distributed in the hope that it will be useful, but\n" - " WITHOUT ANY WARRANTY, to the extent permitted by law; without even\n" - " the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR\n" - " PURPOSE.\n" - "\n" - " +++ Do not edit! File has been generated by mkw32errmap.c +++\n" - "\n" - " This file is intended to be used with ming32ce-gcc to implement an\n" - " errno substitute under WindowsCE. It must be included via gcc's\n" - " -idirafter option. The gpg-error-config script emits the\n" - " appropriate option snippet. The actual implementation of the errno\n" - " related functions are part of libgpg-error. A separate header file\n" - " is required because errno.h is often included before gpg-error.h.\n" - " */\n" - "\n" - "#ifndef _GPG_ERROR_EXTRA_ERRNO_H\n" - "#define _GPG_ERROR_EXTRA_ERRNO_H\n" - "\n" - "/* Due to peculiarities in W32 we can't implement ERRNO as an\n" - " writable lvalue. This also allows us to easily find places\n" - " where ERRNO is being written to. See also gpg_err_set_errno. */\n" - "int _gpg_w32ce_get_errno (void);\n" - "#define errno (_gpg_w32ce_get_errno ())\n" - "\n"; -static const char footer_gpg_extra_errno_h[] = - "\n" - "#endif /*_GPG_ERROR_EXTRA_ERRNO_H*/\n"; - - -/* The table below is used in two modes. First we run the host - preprocessor over it to generate a new include file. This include - file has the same content but the Windows error macros are - resolved. This is so that we don't need to include winerror.h into - the generated errno.h. The mkw32errmap_marker variable is only - here to have something to grep for after preprocessing. */ -static int mkw32errmap_marker; -struct table_s -{ - int *dummy; - const char *name; - int w32code; - int w32code2; -}; - -struct table_s table[] = - { -#ifdef RESOLVE_MACROS -#define X(a,b,c) \ - {&mkw32errmap_marker, (a), (b), (c)} - X( "EPERM", ERROR_CANNOT_MAKE , 0 ), - X( "ENOENT", ERROR_FILE_NOT_FOUND , ERROR_PATH_NOT_FOUND ), - X( "EINTR", ERROR_INVALID_AT_INTERRUPT_TIME , 0 ), - X( "EIO", ERROR_IO_DEVICE , 0 ), - X( "ENXIO", ERROR_FILE_INVALID , 0 ), - X( "EBADF", ERROR_INVALID_HANDLE , 0 ), - X( "EAGAIN", ERROR_MORE_DATA , WSAEWOULDBLOCK ), - - X( "ENOMEM", ERROR_NOT_ENOUGH_MEMORY , 0 ), - X( "EACCES", ERROR_ACCESS_DENIED , 0 ), - X( "EFAULT", ERROR_PROCESS_ABORTED , 0 ), - X( "EBUSY", ERROR_BUSY , 0 ), - X( "EEXIST", ERROR_FILE_EXISTS , WSAEADDRINUSE ), - - X( "EXDEV", ERROR_NOT_SAME_DEVICE , 0 ), - X( "ENODEV", ERROR_BAD_DEVICE , ERROR_DEV_NOT_EXIST ), - - X( "ENOTDIR",ERROR_DIRECTORY , 0 ), - X( "EINVAL", ERROR_INVALID_PARAMETER , 0 ), - X( "ENFILE", ERROR_NO_MORE_FILES , 0 ), - X( "EMFILE", ERROR_TOO_MANY_OPEN_FILES , 0 ), - X( "ENOSPC", ERROR_DISK_FULL , 0 ), - X( "EROFS", ERROR_WRITE_PROTECT , 0 ), - X( "EPIPE", ERROR_BROKEN_PIPE , 0 ), - X( "ERANGE", ERROR_ARITHMETIC_OVERFLOW , 0 ), - X( "EDEADLOCK",ERROR_POSSIBLE_DEADLOCK , 0 ), - X( "ENAMETOOLONG", ERROR_FILENAME_EXCED_RANGE, 0 ), - X( "ENOLCK", ERROR_SHARING_BUFFER_EXCEEDED , 0 ), - X( "ENOSYS", ERROR_NOT_SUPPORTED , 0 ), - X( "ENOTEMPTY",ERROR_DIR_NOT_EMPTY , 0 ), - X( "ESPIPE", ERROR_SEEK_ON_DEVICE , 0 ), -#if 0 /* FIXME: Find appropriate mappings. */ - X( "EILSEQ", ), - X( "EDOM", ), - X( "EMLINK", ), - X( "ESRCH", ), /* No such process */ - X( "E2BIG", ), /* Arg list too long */ - X( "ENOEXEC", ), /* Exec format error */ - X( "ECHILD", ), /* No child processes */ - X( "EISDIR", ), /* Is a directory */ - X( "ENOTTY", ), /* Inappropriate I/O control operation */ - X( "EFBIG", ), /* File too large */ -#endif -#undef X -#else /*!RESOLVE_MACROS*/ -# include "mkw32errmap.tab.h" -#endif /*!RESOLVE_MACROS*/ - { NULL, 0 } - }; - - - -static int -compare_table (const void *a_v, const void *b_v) -{ - const struct table_s *a = a_v; - const struct table_s *b = b_v; - - return (a->w32code - b->w32code); -} - - -int -main (int argc, char **argv) -{ - int idx; - - for (idx=0; table[idx].name; idx++) - ; - qsort (table, idx, sizeof *table, compare_table); - - if (argc == 2 && !strcmp (argv[1], "--map")) - { - fputs ("static int\n" - "map_w32codes (int err)\n" - "{\n" - " switch (err)\n" - " {\n", stdout ); - for (idx=0; table[idx].name; idx++) - if (table[idx].w32code2) - printf (" case %d: return %d;\n", - table[idx].w32code2, table[idx].w32code); - fputs (" default: return err;\n" - " }\n" - "}\n", stdout); - } - else - { - fputs (header_gpg_extra_errno_h, stdout); - for (idx=0; table[idx].name; idx++) - printf ("#define %-12s %5d\n", table[idx].name, table[idx].w32code); - fputs (footer_gpg_extra_errno_h, stdout); - } - - return 0; -} diff --git a/src/spawn-posix.c b/src/spawn-posix.c index d33bade..54a6a68 100644 --- a/src/spawn-posix.c +++ b/src/spawn-posix.c @@ -1,891 +1,891 @@ /* 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) +#if defined(HAVE_W32_SYSTEM) #error This code is only used on POSIX #endif #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; /* Skip invalid PID. */ if (pids[i] == (pid_t)(-1)) { r_exitcodes[i] = -1; left -= 1; continue; } /* 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", (int)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 1b5f085..90c0538 100644 --- a/src/spawn-w32.c +++ b/src/spawn-w32.c @@ -1,923 +1,923 @@ /* 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) +#if !defined(HAVE_W32_SYSTEM) #error This code is only used on W32. #endif #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 /* For HANDLE and the internal file descriptor (fd) of this module: * HANDLE can be represented by an intptr_t which should be true for * all systems (HANDLE is defined as void *). Further, we assume that * -1 denotes an invalid handle. * * Note that a C run-time file descriptor (the exposed one to API) is * always represented by an int. */ #define fd_to_handle(a) ((HANDLE)(a)) #define handle_to_fd(a) ((intptr_t)(a)) /* For pid_t and HANDLE: * We assume that a HANDLE can be represented by an int which should * be true for all i386 systems. * * On 64-bit machine, it is no longer true, as a type, however, as * long as the range of the value in the type HANDLE can be * represented by an int, it works. * * FIXME with original MinGW: Breaking ABI for pid_t will be needed * when the value won't fit within 32-bit range on 64-bit machine. * * Note that pid_t is 64-bit integer in sys/types.h with MinGW-w64. * So, no problem with MinGW-w64. */ #define pid_to_handle(a) ((HANDLE)(a)) #define handle_to_pid(a) ((pid_t)(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)INVALID_HANDLE_VALUE; /* 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)INVALID_HANDLE_VALUE; /* 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)INVALID_HANDLE_VALUE) 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; gpg_err_code_t ec; /* We don't use ENVP. */ (void)envp; ec = _gpgrt_access (pgmname, X_OK); if (ec) return ec; /* 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/sysutils.c b/src/sysutils.c index bc3a68d..1251ed6 100644 --- a/src/sysutils.c +++ b/src/sysutils.c @@ -1,611 +1,607 @@ /* sysutils.c - Platform specific helper functions * Copyright (C) 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+ */ #include #include #include #include #include #include #ifdef HAVE_W32_SYSTEM # include #endif #ifdef HAVE_STAT # include #endif #include #include #ifdef HAVE_PWD_H # include #endif #include "gpgrt-int.h" /* Return true if FD is valid. */ int _gpgrt_fd_valid_p (int fd) { int d = dup (fd); if (d < 0) return 0; close (d); return 1; } /* Our variant of getenv. The returned string must be freed. If the * environment variable does not exists NULL is returned and ERRNO set * to 0. */ char * _gpgrt_getenv (const char *name) { if (!name || !*name || strchr (name, '=')) { _gpg_err_set_errno (EINVAL); return NULL; } #ifdef HAVE_W32_SYSTEM { int len, size; char *result; len = GetEnvironmentVariable (name, NULL, 0); if (!len && GetLastError () == ERROR_ENVVAR_NOT_FOUND) { _gpg_err_set_errno (0); return NULL; } again: size = len; result = _gpgrt_malloc (size); if (!result) return NULL; len = GetEnvironmentVariable (name, result, size); if (len >= size) { /* Changed in the meantime - retry. */ _gpgrt_free (result); goto again; } if (!len && GetLastError () == ERROR_ENVVAR_NOT_FOUND) { /* Deleted in the meantime. */ _gpgrt_free (result); _gpg_err_set_errno (0); return NULL; } if (!len) { /* Other error. FIXME: We need mapping fucntion. */ _gpgrt_free (result); _gpg_err_set_errno (EIO); return NULL; } return result; } #else /*!HAVE_W32_SYSTEM*/ { const char *s = getenv (name); if (!s) { _gpg_err_set_errno (0); return NULL; } return _gpgrt_strdup (s); } #endif /*!HAVE_W32_SYSTEM*/ } /* Wrapper around setenv so that we can have the same function in * Windows and Unix. In contrast to the standard setenv passing a * VALUE as NULL and setting OVERWRITE will remove the envvar. */ gpg_err_code_t _gpgrt_setenv (const char *name, const char *value, int overwrite) { if (!name || !*name || strchr (name, '=')) return GPG_ERR_EINVAL; #ifdef HAVE_W32_SYSTEM /* Windows maintains (at least) two sets of environment variables. * One set can be accessed by GetEnvironmentVariable and * SetEnvironmentVariable. This set is inherited by the children. * The other set is maintained in the C runtime, and is accessed * using getenv and putenv. We try to keep them in sync by * modifying both sets. Note that gpgrt_getenv ignores the libc * values - however, too much existing code still uses getenv. */ { int exists; char tmpbuf[10]; char *buf; if (!value && overwrite) { if (!SetEnvironmentVariable (name, NULL)) return GPG_ERR_EINVAL; if (getenv (name)) { /* Ugly: Leaking memory. */ buf = _gpgrt_strdup (name); if (!buf) return _gpg_err_code_from_syserror (); if (putenv (buf)) return _gpg_err_code_from_syserror (); } return 0; } exists = GetEnvironmentVariable (name, tmpbuf, sizeof tmpbuf); if ((! exists || overwrite) && !SetEnvironmentVariable (name, value)) return GPG_ERR_EINVAL; /* (Might also be ENOMEM.) */ if (overwrite || !getenv (name)) { /* Ugly: Leaking memory. */ buf = _gpgrt_strconcat (name, "=", value, NULL); if (!buf) return _gpg_err_code_from_syserror (); if (putenv (buf)) return _gpg_err_code_from_syserror (); } return 0; } #else /*!HAVE_W32_SYSTEM*/ # ifdef HAVE_SETENV { if (!value && overwrite) { if (unsetenv (name)) return _gpg_err_code_from_syserror (); } else { if (setenv (name, value ? value : "", overwrite)) return _gpg_err_code_from_syserror (); } return 0; } # else /*!HAVE_SETENV*/ # if __GNUC__ # warning no setenv - using putenv but leaking memory. # endif { char *buf; if (!value && overwrite) { if (getenv (name)) { buf = _gpgrt_strdup (name); if (!buf) return _gpg_err_code_from_syserror (); if (putenv (buf)) return -1; } } else if (overwrite || !getenv (name)) { buf = _gpgrt_strconcat (name, "=", value, NULL); if (!buf) return _gpg_err_code_from_syserror (); if (putenv (buf)) return _gpg_err_code_from_syserror (); } return 0; } # endif /*!HAVE_SETENV*/ #endif /*!HAVE_W32_SYSTEM*/ } #ifdef HAVE_W32_SYSTEM /* Convert an UTF-8 encode file name to wchar. If the file name is * close to the limit of MAXPATH the API functions will fail. The * method to overcome this API limitation is to use a prefix which * bypasses the checking by CreateFile. This also required to first * convert the name to an absolute file name. */ wchar_t * _gpgrt_fname_to_wchar (const char *fname) { wchar_t *wname; wchar_t *wfullpath = NULL; int success = 0; wname = _gpgrt_utf8_to_wchar (fname); if (!wname) return NULL; if (!strncmp (fname, "\\\\?\\", 4)) success = 1; /* Already translated. */ else if (wcslen (wname) > 230) { int wlen = 1024; int extralen; DWORD res; wchar_t *w; try_again: wfullpath = xtrymalloc (wlen * sizeof *wfullpath); if (!wfullpath) goto leave; if (*fname == '\\' && fname[1] == '\\' && fname[2]) { wcscpy (wfullpath, L"\\\\?\\UNC\\"); extralen = 8; } else { wcscpy (wfullpath, L"\\\\?\\"); extralen = 4; } res = GetFullPathNameW (wname, wlen-extralen, wfullpath+extralen, NULL); if (!res) { _gpgrt_w32_set_errno (-1); goto leave; } else if (res >= wlen - extralen) { /* Truncated - increase to the desired length. */ if (wlen > 1024) { /* We should never get to here. */ errno = ENAMETOOLONG; goto leave; } /* GetFullPathNameW indicated the required buffer length. */ _gpgrt_free_wchar (wfullpath); wfullpath = NULL; wlen = res + extralen; goto try_again; } _gpgrt_free_wchar (wname); wname = wfullpath; wfullpath = NULL; /* Need to make sure that all slashes are mapped. */ for (w = wname; *w; w++) if (*w == L'/') *w = L'\\'; success = 1; } else success = 1; leave: _gpgrt_free_wchar (wfullpath); if (!success) { _gpgrt_free_wchar (wname); wname = NULL; } return wname; } #endif /*HAVE_W32_SYSTEM*/ #ifndef HAVE_W32_SYSTEM static mode_t modestr_to_mode (const char *modestr) { mode_t mode = 0; if (modestr && *modestr) { modestr++; if (*modestr && *modestr++ == 'r') mode |= S_IRUSR; if (*modestr && *modestr++ == 'w') mode |= S_IWUSR; if (*modestr && *modestr++ == 'x') mode |= S_IXUSR; if (*modestr && *modestr++ == 'r') mode |= S_IRGRP; if (*modestr && *modestr++ == 'w') mode |= S_IWGRP; if (*modestr && *modestr++ == 'x') mode |= S_IXGRP; if (*modestr && *modestr++ == 'r') mode |= S_IROTH; if (*modestr && *modestr++ == 'w') mode |= S_IWOTH; if (*modestr && *modestr++ == 'x') mode |= S_IXOTH; } return mode; } #endif /* A wrapper around mkdir which takes a string for the mode argument. * This makes it easier to handle the mode argument which is not * defined on all systems. The format of the modestring is * * "-rwxrwxrwx" * * '-' is a don't care or not set. 'r', 'w', 'x' are read allowed, * write allowed, execution allowed with the first group for the user, * the second for the group and the third for all others. If the * string is shorter than above the missing mode characters are meant * to be not set. * * Note that in addition to returning an gpg-error error code ERRNO is * also set by this function. */ gpg_err_code_t _gpgrt_mkdir (const char *name, const char *modestr) { #ifdef HAVE_W32_SYSTEM wchar_t *wname; gpg_err_code_t ec; (void)modestr; /* Note: Fixme: We should set appropriate permissions. */ wname = _gpgrt_fname_to_wchar (name); if (!wname) return _gpg_err_code_from_syserror (); if (!CreateDirectoryW (wname, NULL)) { _gpgrt_w32_set_errno (-1); ec = _gpg_err_code_from_syserror (); } else ec = 0; _gpgrt_free_wchar (wname); return ec; #elif MKDIR_TAKES_ONE_ARG (void)modestr; if (mkdir (name)) return _gpg_err_code_from_syserror (); return 0; #else if (mkdir (name, modestr_to_mode (modestr))) return _gpg_err_code_from_syserror (); return 0; #endif } /* A simple wrapper around chdir. NAME is expected to be utf8 * encoded. Note that in addition to returning an gpg-error error * code ERRNO is also set by this function. */ gpg_err_code_t _gpgrt_chdir (const char *name) { #ifdef HAVE_W32_SYSTEM wchar_t *wname; gpg_err_code_t ec; /* Note that the \\?\ trick does not work with SetCurrentDirectoryW * Thus we use the plain conversion function. */ wname = _gpgrt_utf8_to_wchar (name); if (!wname) return _gpg_err_code_from_syserror (); if (!SetCurrentDirectoryW (wname)) { _gpgrt_w32_set_errno (-1); ec = _gpg_err_code_from_syserror (); } else ec = 0; _gpgrt_free_wchar (wname); return ec; #else /*!HAVE_W32_SYSTEM*/ if (chdir (name)) return _gpg_err_code_from_syserror (); return 0; #endif /*!HAVE_W32_SYSTEM*/ } /* Return the current working directory as a malloced string. Return * NULL and sets ERRNO on error. */ char * _gpgrt_getcwd (void) { -#ifdef HAVE_W32CE_SYSTEM - - return xtrystrdup ("/"); - -#elif defined(HAVE_W32_SYSTEM) +#if defined(HAVE_W32_SYSTEM) wchar_t wbuffer[MAX_PATH + sizeof(wchar_t)]; DWORD wlen; char *buf, *p; wlen = GetCurrentDirectoryW (MAX_PATH, wbuffer); if (!wlen) { _gpgrt_w32_set_errno (-1); return NULL; } else if (wlen > MAX_PATH) { /* FWIW: I tried to use GetFullPathNameW (L".") but found no way * to execute a test program at a too long cwd. */ _gpg_err_set_errno (ENAMETOOLONG); return NULL; } buf = _gpgrt_wchar_to_utf8 (wbuffer, wlen); if (buf) { for (p=buf; *p; p++) if (*p == '\\') *p = '/'; } return buf; #else /*Unix*/ char *buffer; size_t size = 100; for (;;) { buffer = xtrymalloc (size+1); if (!buffer) return NULL; if (getcwd (buffer, size) == buffer) return buffer; xfree (buffer); if (errno != ERANGE) return NULL; size *= 2; } #endif /*Unix*/ } /* Wrapper around access to handle file name encoding under Windows. * Returns 0 if FNAME can be accessed in MODE or an error code. ERRNO * is also set on error. */ gpg_err_code_t _gpgrt_access (const char *fname, int mode) { gpg_err_code_t ec; #ifdef HAVE_W32_SYSTEM wchar_t *wfname; DWORD attribs; wfname = _gpgrt_fname_to_wchar (fname); if (!wfname) return _gpg_err_code_from_syserror (); attribs = GetFileAttributesW (wfname); if (attribs == (DWORD)(-1)) ec = _gpgrt_w32_get_last_err_code (); else { if ((mode & W_OK) && (attribs & FILE_ATTRIBUTE_READONLY)) { _gpg_err_set_errno (EACCES); ec = _gpg_err_code_from_syserror (); } else ec = 0; } _gpgrt_free_wchar (wfname); #else /* Unix */ ec = access (fname, mode)? _gpg_err_code_from_syserror () : 0; #endif /* Unix */ return ec; } /* Get the standard home directory for user NAME. If NAME is NULL the * directory for the current user is returned. Caller must release * the returned string. */ char * _gpgrt_getpwdir (const char *name) { char *result = NULL; #ifdef HAVE_PWD_H struct passwd *pwd = NULL; if (name) { #ifdef HAVE_GETPWNAM /* Fixme: We should use getpwnam_r if available. */ pwd = getpwnam (name); #endif } else { #ifdef HAVE_GETPWUID /* Fixme: We should use getpwuid_r if available. */ pwd = getpwuid (getuid()); #endif } if (pwd) { result = _gpgrt_strdup (pwd->pw_dir); } #else /*!HAVE_PWD_H*/ /* No support at all. */ (void)name; #endif /*HAVE_PWD_H*/ return result; } /* Return a malloced copy of the current user's account name; this may * return NULL on memory failure. */ char * _gpgrt_getusername (void) { char *result = NULL; #ifdef HAVE_W32_SYSTEM wchar_t wtmp[1]; wchar_t *wbuf; DWORD wsize = 1; char *buf; GetUserNameW (wtmp, &wsize); wbuf = _gpgrt_malloc (wsize * sizeof *wbuf); if (!wbuf) { _gpgrt_w32_set_errno (-1); return NULL; } if (!GetUserNameW (wbuf, &wsize)) { _gpgrt_w32_set_errno (-1); xfree (wbuf); return NULL; } buf = _gpgrt_wchar_to_utf8 (wbuf, wsize); xfree (wbuf); return buf; #else /* !HAVE_W32_SYSTEM */ # if defined(HAVE_PWD_H) && defined(HAVE_GETPWUID) struct passwd *pwd; pwd = getpwuid (getuid()); if (pwd) { result = _gpgrt_strdup (pwd->pw_name); } # endif /*HAVE_PWD_H*/ #endif /* !HAVE_W32_SYSTEM */ return result; } diff --git a/src/w32-estream.c b/src/w32-estream.c index e17ea2c..06851a5 100644 --- a/src/w32-estream.c +++ b/src/w32-estream.c @@ -1,1082 +1,1040 @@ /* 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 #ifdef HAVE_SYS_TIME_H # include #endif #ifdef HAVE_SYS_TYPES_H # include #endif #include #include #ifndef EOPNOTSUPP # define EOPNOTSUPP ENOSYS #endif /* 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 8192 #define WRITEBUF_SIZE 8192 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); } 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; 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. */ 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) { /* WFMO needs at least one object, thus we use use sleep here. * INFINITE wait does not make any sense in this case, so we * error out. */ if (timeout == -1) { _gpg_err_set_errno (EINVAL); return -1; } if (timeout) Sleep (timeout); code = WAIT_TIMEOUT; } else 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) { 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-reg.c b/src/w32-reg.c index d1d2ca6..5510df8 100644 --- a/src/w32-reg.c +++ b/src/w32-reg.c @@ -1,203 +1,201 @@ /* w32-reg.c - Windows registry support * Copyright (C) 2002, 2005, 2010, 2012, 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+ */ #if HAVE_CONFIG_H #include #endif #ifndef HAVE_W32_SYSTEM # error This module may only be build for Windows. #endif #include #include #include #include #include #define WIN32_LEAN_AND_MEAN #include #include "gpgrt-int.h" /* Return a string from the W32 Registry or NULL in case of error. * Caller must release the return value. A NULL for root is an alias * for HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE in turn. The returned * string is UTF-8 encoded; ROOT, DIR, and NAME must be plain * ASCII. */ char * _gpgrt_w32_reg_query_string (const char *root, const char *dir, const char *name) { HKEY root_key, key_handle; DWORD n1, nbytes, type; char *result = NULL; if (!root) root_key = HKEY_CURRENT_USER; else if (!strcmp (root, "HKEY_CLASSES_ROOT") || !strcmp (root, "HKCR")) root_key = HKEY_CLASSES_ROOT; else if (!strcmp (root, "HKEY_CURRENT_USER") || !strcmp (root, "HKCU")) root_key = HKEY_CURRENT_USER; else if (!strcmp (root, "HKEY_LOCAL_MACHINE") || !strcmp (root, "HKLM")) root_key = HKEY_LOCAL_MACHINE; else if (!strcmp (root, "HKEY_USERS") || !strcmp (root, "HKU")) root_key = HKEY_USERS; else if (!strcmp (root, "HKEY_PERFORMANCE_DATA")) root_key = HKEY_PERFORMANCE_DATA; else if (!strcmp (root, "HKEY_CURRENT_CONFIG") || !strcmp (root, "HKCC")) root_key = HKEY_CURRENT_CONFIG; else return NULL; if (RegOpenKeyExA (root_key, dir, 0, KEY_READ, &key_handle)) { if (root) return NULL; /* No need for a RegClose, so return direct. */ /* It seems to be common practise to fall back to HKLM. */ if (RegOpenKeyExA (HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &key_handle)) return NULL; /* still no need for a RegClose, so return direct */ } /* FIXME: Use wide functions and convert to utf-8. */ nbytes = 1; if (RegQueryValueExA (key_handle, name, 0, NULL, NULL, &nbytes)) { if (root) goto leave; /* Try to fallback to HKLM also for a missing value. */ RegCloseKey (key_handle); if (RegOpenKeyExA (HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &key_handle)) return NULL; /* Nope. */ if (RegQueryValueExA (key_handle, name, 0, NULL, NULL, &nbytes)) goto leave; } n1 = nbytes + 1; result = xtrymalloc (n1); if (!result) goto leave; if (RegQueryValueExA (key_handle, name, 0, &type, (LPBYTE) result, &n1)) { xfree (result); result = NULL; goto leave; } result[nbytes] = 0; /* Make sure it is really a string. */ -#ifndef HAVE_W32CE_SYSTEM /* (Windows CE has no environment.) */ if (type == REG_EXPAND_SZ && strchr (result, '%')) { char *tmp; n1 += 1000; tmp = xtrymalloc (n1 + 1); if (!tmp) goto leave; nbytes = ExpandEnvironmentStrings (result, tmp, n1); if (nbytes && nbytes > n1) { xfree (tmp); n1 = nbytes; tmp = xtrymalloc (n1 + 1); if (!tmp) goto leave; nbytes = ExpandEnvironmentStrings (result, tmp, n1); if (nbytes && nbytes > n1) { xfree (tmp); /* Oops - truncated, better don't expand at all. */ goto leave; } tmp[nbytes] = 0; xfree (result); result = tmp; } else if (nbytes) /* Okay, reduce the length. */ { tmp[nbytes] = 0; xfree (result); result = xtrymalloc (strlen (tmp)+1); if (!result) result = tmp; else { strcpy (result, tmp); xfree (tmp); } } else /* Error - don't expand. */ { xfree (tmp); } } -#endif leave: RegCloseKey (key_handle); return result; } /* Compact version of gpgrt_w32_reg_query_string. This version * expects a single string as key described here using an example: * * HKCU\Software\GNU\GnuPG:HomeDir * * HKCU := the class, other supported classes are HKLM, HKCR, HKU, and * HKCC. If no class is given and the string thus starts with * a backslash HKCU with a fallback to HKLM is used. * Software\GNU\GnuPG := The actual key. * HomeDir := the name of the item. The name is optional to use the default * value. * * Note that the first backslash and the first colon act as delimiters. * * Returns a malloced string or NULL if not found. */ char * _gpgrt_w32_reg_get_string (const char *key_arg) { char *key; char *p1, *p2; char *result; if (!key_arg) return NULL; key = xtrystrdup (key_arg); if (!key) { _gpgrt_log_info ("warning: malloc failed while reading registry key\n"); return NULL; } p1 = strchr (key, '\\'); if (!p1) { xfree (key); return NULL; } *p1++ = 0; p2 = strchr (p1, ':'); if (p2) *p2++ = 0; result = _gpgrt_w32_reg_query_string (*key? key : NULL, p1, p2); xfree (key); return result; } diff --git a/src/w32ce-add.h b/src/w32ce-add.h deleted file mode 100644 index c6207bb..0000000 --- a/src/w32ce-add.h +++ /dev/null @@ -1,8 +0,0 @@ -## w32ce-add.h - Snippet to be be included into gpg-error.h. -## (Comments are indicated by a double hash mark) - -/* Substitute for strerror - this one is thread safe. */ -char *_gpg_w32ce_strerror (int err); -#ifdef GPG_ERR_ENABLE_ERRNO_MACROS -# define strerror(a) _gpg_w32ce_strerror (a) -#endif diff --git a/tests/Makefile.am b/tests/Makefile.am index 9652982..61453ea 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,48 +1,42 @@ # Makefile.am for libgpg-error/tests. # Copyright (C) 2003 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, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA ## Process this file with automake to produce Makefile.in -if HAVE_W32CE_SYSTEM -extra_includes = -idirafter $(top_builddir)/src/gpg-extra -else -extra_includes = -endif - EXTRA_DIST = t-argparse.conf etc/t-argparse.conf gpg_error_lib = ../src/libgpg-error.la TESTS = t-version t-strerror t-syserror t-lock t-printf t-poll t-b64 \ t-argparse t-logging t-stringutils t-malloc if HAVE_LOCK_OPTIMIZATION TESTS += t-lock-single-posix endif -AM_CPPFLAGS = -I$(top_builddir)/src $(extra_includes) +AM_CPPFLAGS = -I$(top_builddir)/src AM_LDFLAGS = -no-install LDADD = $(gpg_error_lib) @LDADD_FOR_TESTS_KLUDGE@ noinst_PROGRAMS = $(TESTS) noinst_HEADERS = t-common.h t_lock_LDADD = $(LDADD) $(LIBMULTITHREAD) t_poll_LDADD = $(LDADD) $(LIBMULTITHREAD)