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)