summaryrefslogtreecommitdiff
path: root/rpmio
diff options
context:
space:
mode:
Diffstat (limited to 'rpmio')
-rw-r--r--rpmio/Makefile.am40
-rw-r--r--rpmio/Makefile.in689
-rw-r--r--rpmio/argv.c225
-rw-r--r--rpmio/argv.h179
-rw-r--r--rpmio/base64.c255
-rw-r--r--rpmio/base64.h37
-rw-r--r--rpmio/digest.c246
-rw-r--r--rpmio/digest.h49
-rw-r--r--rpmio/macro.c1611
-rw-r--r--rpmio/rpmfileutil.c777
-rw-r--r--rpmio/rpmfileutil.h151
-rw-r--r--rpmio/rpmhook.c262
-rw-r--r--rpmio/rpmhook.h37
-rw-r--r--rpmio/rpmio.c1900
-rw-r--r--rpmio/rpmio.h169
-rw-r--r--rpmio/rpmio_internal.h44
-rw-r--r--rpmio/rpmkeyring.c231
-rw-r--r--rpmio/rpmkeyring.h96
-rw-r--r--rpmio/rpmlog.c230
-rw-r--r--rpmio/rpmlog.h281
-rw-r--r--rpmio/rpmlua.c844
-rw-r--r--rpmio/rpmlua.h92
-rw-r--r--rpmio/rpmmacro.h156
-rw-r--r--rpmio/rpmmalloc.c85
-rw-r--r--rpmio/rpmpgp.c1669
-rw-r--r--rpmio/rpmpgp.h1201
-rw-r--r--rpmio/rpmsq.c366
-rw-r--r--rpmio/rpmsq.h106
-rw-r--r--rpmio/rpmstring.c192
-rw-r--r--rpmio/rpmstring.h179
-rw-r--r--rpmio/rpmsw.c131
-rw-r--r--rpmio/rpmsw.h104
-rw-r--r--rpmio/rpmurl.h52
-rw-r--r--rpmio/rpmutil.h159
-rw-r--r--rpmio/url.c127
35 files changed, 12972 insertions, 0 deletions
diff --git a/rpmio/Makefile.am b/rpmio/Makefile.am
new file mode 100644
index 0000000..b1003c5
--- /dev/null
+++ b/rpmio/Makefile.am
@@ -0,0 +1,40 @@
+# Makefile for rpm library.
+
+AM_CPPFLAGS = -I$(top_builddir) -I$(top_srcdir) -I$(top_builddir)/include/
+AM_CPPFLAGS += @WITH_NSS_INCLUDE@
+AM_CPPFLAGS += @WITH_POPT_INCLUDE@
+AM_CPPFLAGS += -I$(top_srcdir)/misc
+AM_CPPFLAGS += -DRPMCONFIGDIR="\"@RPMCONFIGDIR@\""
+AM_CPPFLAGS += -DLOCALSTATEDIR="\"$(localstatedir)\""
+
+usrlibdir = $(libdir)
+usrlib_LTLIBRARIES = librpmio.la
+librpmio_la_SOURCES = \
+ argv.c base64.h base64.c digest.h digest.c macro.c \
+ rpmhook.c rpmio.c rpmlog.c rpmmalloc.c \
+ rpmpgp.c rpmsq.c rpmsw.c url.c \
+ rpmio_internal.h rpmhook.h \
+ rpmstring.c rpmfileutil.c \
+ rpmkeyring.c
+
+librpmio_la_LDFLAGS = -version-info 2:1:0
+librpmio_la_LIBADD = \
+ ../misc/libmisc.la \
+ @WITH_NSS_LIB@ \
+ @WITH_BZ2_LIB@ \
+ @WITH_ZLIB_LIB@ \
+ @WITH_LIBELF_LIB@ \
+ @WITH_POPT_LIB@ \
+ @WITH_LZMA_LIB@ \
+ -lpthread
+
+if WITH_LUA
+AM_CPPFLAGS += -I$(top_srcdir)/luaext/
+AM_CPPFLAGS += @LUA_CFLAGS@
+librpmio_la_SOURCES += rpmlua.c rpmlua.h
+librpmio_la_LIBADD += @LUA_LIBS@
+librpmio_la_LIBADD += $(top_builddir)/luaext/libluaext.la
+endif
+
+check_PROGRAMS =
+
diff --git a/rpmio/Makefile.in b/rpmio/Makefile.in
new file mode 100644
index 0000000..68cbd8a
--- /dev/null
+++ b/rpmio/Makefile.in
@@ -0,0 +1,689 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Makefile for rpm library.
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+@WITH_LUA_TRUE@am__append_1 = -I$(top_srcdir)/luaext/ @LUA_CFLAGS@
+@WITH_LUA_TRUE@am__append_2 = rpmlua.c rpmlua.h
+@WITH_LUA_TRUE@am__append_3 = @LUA_LIBS@ \
+@WITH_LUA_TRUE@ $(top_builddir)/luaext/libluaext.la
+check_PROGRAMS =
+subdir = rpmio
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/gettext.m4 \
+ $(top_srcdir)/m4/iconv.m4 $(top_srcdir)/m4/lib-ld.m4 \
+ $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \
+ $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__installdirs = "$(DESTDIR)$(usrlibdir)"
+LTLIBRARIES = $(usrlib_LTLIBRARIES)
+@WITH_LUA_TRUE@am__DEPENDENCIES_1 = \
+@WITH_LUA_TRUE@ $(top_builddir)/luaext/libluaext.la
+librpmio_la_DEPENDENCIES = ../misc/libmisc.la $(am__DEPENDENCIES_1)
+am__librpmio_la_SOURCES_DIST = argv.c base64.h base64.c digest.h \
+ digest.c macro.c rpmhook.c rpmio.c rpmlog.c rpmmalloc.c \
+ rpmpgp.c rpmsq.c rpmsw.c url.c rpmio_internal.h rpmhook.h \
+ rpmstring.c rpmfileutil.c rpmkeyring.c rpmlua.c rpmlua.h
+@WITH_LUA_TRUE@am__objects_1 = rpmlua.lo
+am_librpmio_la_OBJECTS = argv.lo base64.lo digest.lo macro.lo \
+ rpmhook.lo rpmio.lo rpmlog.lo rpmmalloc.lo rpmpgp.lo rpmsq.lo \
+ rpmsw.lo url.lo rpmstring.lo rpmfileutil.lo rpmkeyring.lo \
+ $(am__objects_1)
+librpmio_la_OBJECTS = $(am_librpmio_la_OBJECTS)
+librpmio_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(librpmio_la_LDFLAGS) $(LDFLAGS) -o $@
+DEFAULT_INCLUDES =
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+ $(LDFLAGS) -o $@
+SOURCES = $(librpmio_la_SOURCES)
+DIST_SOURCES = $(am__librpmio_la_SOURCES_DIST)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AR = @AR@
+AS = @AS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOM4TE = @AUTOM4TE@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DOXYGEN = @DOXYGEN@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FIXPERMS = @FIXPERMS@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GREP = @GREP@
+HAVE_DOT = @HAVE_DOT@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+LUA_CFLAGS = @LUA_CFLAGS@
+LUA_LIBS = @LUA_LIBS@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+POSUB = @POSUB@
+PYTHON = @PYTHON@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RPMCANONARCH = @RPMCANONARCH@
+RPMCANONCOLOR = @RPMCANONCOLOR@
+RPMCANONGNU = @RPMCANONGNU@
+RPMCANONOS = @RPMCANONOS@
+RPMCANONVENDOR = @RPMCANONVENDOR@
+RPMCONFIGDIR = @RPMCONFIGDIR@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WITH_ACL_LIB = @WITH_ACL_LIB@
+WITH_BZ2_LIB = @WITH_BZ2_LIB@
+WITH_CAP_LIB = @WITH_CAP_LIB@
+WITH_DB_LIB = @WITH_DB_LIB@
+WITH_LIBELF_LIB = @WITH_LIBELF_LIB@
+WITH_LZMA_LIB = @WITH_LZMA_LIB@
+WITH_MAGIC_INCLUDE = @WITH_MAGIC_INCLUDE@
+WITH_MAGIC_LIB = @WITH_MAGIC_LIB@
+WITH_NSS_INCLUDE = @WITH_NSS_INCLUDE@
+WITH_NSS_LIB = @WITH_NSS_LIB@
+WITH_POPT_INCLUDE = @WITH_POPT_INCLUDE@
+WITH_POPT_LIB = @WITH_POPT_LIB@
+WITH_PYTHON_INCLUDE = @WITH_PYTHON_INCLUDE@
+WITH_PYTHON_LIB = @WITH_PYTHON_LIB@
+WITH_SELINUX_LIB = @WITH_SELINUX_LIB@
+WITH_SEMANAGE_LIB = @WITH_SEMANAGE_LIB@
+WITH_ZLIB_INCLUDE = @WITH_ZLIB_INCLUDE@
+WITH_ZLIB_LIB = @WITH_ZLIB_LIB@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+YACC = @YACC@
+YFLAGS = @YFLAGS@
+__BZIP2 = @__BZIP2@
+__CAT = @__CAT@
+__CC = @__CC@
+__CHGRP = @__CHGRP@
+__CHMOD = @__CHMOD@
+__CHOWN = @__CHOWN@
+__CP = @__CP@
+__CPIO = @__CPIO@
+__CURL = @__CURL@
+__FAKECHROOT = @__FAKECHROOT@
+__FILE = @__FILE@
+__GPG = @__GPG@
+__GREP = @__GREP@
+__GZIP = @__GZIP@
+__ID = @__ID@
+__INSTALL = @__INSTALL@
+__LD = @__LD@
+__LRZIP = @__LRZIP@
+__LZIP = @__LZIP@
+__MAKE = @__MAKE@
+__MKDIR = @__MKDIR@
+__MKDIR_P = @__MKDIR_P@
+__MV = @__MV@
+__NM = @__NM@
+__OBJCOPY = @__OBJCOPY@
+__OBJDUMP = @__OBJDUMP@
+__PATCH = @__PATCH@
+__PERL = @__PERL@
+__PGP = @__PGP@
+__PYTHON = @__PYTHON@
+__RESTORECON = @__RESTORECON@
+__RM = @__RM@
+__RSH = @__RSH@
+__SED = @__SED@
+__SEMODULE = @__SEMODULE@
+__SSH = @__SSH@
+__STRIP = @__STRIP@
+__TAR = @__TAR@
+__UNZIP = @__UNZIP@
+__XZ = @__XZ@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+dirstamp = @dirstamp@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+subdirs = @subdirs@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+AM_CPPFLAGS = -I$(top_builddir) -I$(top_srcdir) \
+ -I$(top_builddir)/include/ @WITH_NSS_INCLUDE@ \
+ @WITH_POPT_INCLUDE@ -I$(top_srcdir)/misc \
+ -DRPMCONFIGDIR="\"@RPMCONFIGDIR@\"" \
+ -DLOCALSTATEDIR="\"$(localstatedir)\"" $(am__append_1)
+usrlibdir = $(libdir)
+usrlib_LTLIBRARIES = librpmio.la
+librpmio_la_SOURCES = argv.c base64.h base64.c digest.h digest.c \
+ macro.c rpmhook.c rpmio.c rpmlog.c rpmmalloc.c rpmpgp.c \
+ rpmsq.c rpmsw.c url.c rpmio_internal.h rpmhook.h rpmstring.c \
+ rpmfileutil.c rpmkeyring.c $(am__append_2)
+librpmio_la_LDFLAGS = -version-info 2:1:0
+librpmio_la_LIBADD = ../misc/libmisc.la @WITH_NSS_LIB@ @WITH_BZ2_LIB@ \
+ @WITH_ZLIB_LIB@ @WITH_LIBELF_LIB@ @WITH_POPT_LIB@ \
+ @WITH_LZMA_LIB@ -lpthread $(am__append_3)
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign rpmio/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign rpmio/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-usrlibLTLIBRARIES: $(usrlib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ test -z "$(usrlibdir)" || $(MKDIR_P) "$(DESTDIR)$(usrlibdir)"
+ @list='$(usrlib_LTLIBRARIES)'; test -n "$(usrlibdir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(usrlibdir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(usrlibdir)"; \
+ }
+
+uninstall-usrlibLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(usrlib_LTLIBRARIES)'; test -n "$(usrlibdir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(usrlibdir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(usrlibdir)/$$f"; \
+ done
+
+clean-usrlibLTLIBRARIES:
+ -test -z "$(usrlib_LTLIBRARIES)" || rm -f $(usrlib_LTLIBRARIES)
+ @list='$(usrlib_LTLIBRARIES)'; for p in $$list; do \
+ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+ test "$$dir" != "$$p" || dir=.; \
+ echo "rm -f \"$${dir}/so_locations\""; \
+ rm -f "$${dir}/so_locations"; \
+ done
+librpmio.la: $(librpmio_la_OBJECTS) $(librpmio_la_DEPENDENCIES)
+ $(librpmio_la_LINK) -rpath $(usrlibdir) $(librpmio_la_OBJECTS) $(librpmio_la_LIBADD) $(LIBS)
+
+clean-checkPROGRAMS:
+ @list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/argv.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/base64.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/digest.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/macro.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rpmfileutil.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rpmhook.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rpmio.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rpmkeyring.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rpmlog.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rpmlua.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rpmmalloc.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rpmpgp.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rpmsq.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rpmstring.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rpmsw.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/url.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ set x; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+ $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS)
+check: check-am
+all-am: Makefile $(LTLIBRARIES)
+installdirs:
+ for dir in "$(DESTDIR)$(usrlibdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-checkPROGRAMS clean-generic clean-libtool \
+ clean-usrlibLTLIBRARIES mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-usrlibLTLIBRARIES
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-usrlibLTLIBRARIES
+
+.MAKE: check-am install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean \
+ clean-checkPROGRAMS clean-generic clean-libtool \
+ clean-usrlibLTLIBRARIES ctags distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ install-usrlibLTLIBRARIES installcheck installcheck-am \
+ installdirs maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags uninstall \
+ uninstall-am uninstall-usrlibLTLIBRARIES
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/rpmio/argv.c b/rpmio/argv.c
new file mode 100644
index 0000000..cf6e299
--- /dev/null
+++ b/rpmio/argv.c
@@ -0,0 +1,225 @@
+/** \ingroup rpmio
+ * \file rpmio/argv.c
+ */
+
+#include "system.h"
+
+#include <stdlib.h>
+#include <rpm/argv.h>
+#include <rpm/rpmstring.h>
+
+#include "debug.h"
+
+void argvPrint(const char * msg, ARGV_const_t argv, FILE * fp)
+{
+ ARGV_const_t av;
+
+ if (fp == NULL) fp = stderr;
+
+ if (msg)
+ fprintf(fp, "===================================== %s\n", msg);
+
+ if (argv)
+ for (av = argv; *av; av++)
+ fprintf(fp, "%s\n", *av);
+
+}
+
+ARGV_t argvNew(void)
+{
+ ARGV_t argv = xcalloc(1, sizeof(*argv));
+ return argv;
+}
+
+ARGI_t argiFree(ARGI_t argi)
+{
+ if (argi) {
+ argi->nvals = 0;
+ argi->vals = _free(argi->vals);
+ }
+ argi = _free(argi);
+ return NULL;
+}
+
+ARGV_t argvFree(ARGV_t argv)
+{
+ ARGV_t av;
+
+ if (argv)
+ for (av = argv; *av; av++)
+ *av = _free(*av);
+ argv = _free(argv);
+ return NULL;
+}
+
+int argiCount(ARGI_const_t argi)
+{
+ int nvals = 0;
+ if (argi)
+ nvals = argi->nvals;
+ return nvals;
+}
+
+ARGint_t argiData(ARGI_const_t argi)
+{
+ ARGint_t vals = NULL;
+ if (argi && argi->nvals > 0)
+ vals = argi->vals;
+ return vals;
+}
+
+int argvCount(ARGV_const_t argv)
+{
+ int argc = 0;
+ if (argv)
+ while (argv[argc] != NULL)
+ argc++;
+ return argc;
+}
+
+ARGV_t argvData(ARGV_t argv)
+{
+ return argv;
+}
+
+int argvCmp(const void * a, const void * b)
+{
+ const char *astr = *(ARGV_t)a;
+ const char *bstr = *(ARGV_t)b;
+ return strcmp(astr, bstr);
+}
+
+int argvSort(ARGV_t argv, int (*compar)(const void *, const void *))
+{
+ if (compar == NULL)
+ compar = argvCmp;
+ qsort(argv, argvCount(argv), sizeof(*argv), compar);
+ return 0;
+}
+
+ARGV_t argvSearch(ARGV_const_t argv, const char *val,
+ int (*compar)(const void *, const void *))
+{
+ if (argv == NULL)
+ return NULL;
+ if (compar == NULL)
+ compar = argvCmp;
+ return bsearch(&val, argv, argvCount(argv), sizeof(*argv), compar);
+}
+
+int argiAdd(ARGI_t * argip, int ix, int val)
+{
+ ARGI_t argi;
+
+ if (argip == NULL)
+ return -1;
+ if (*argip == NULL)
+ *argip = xcalloc(1, sizeof(**argip));
+ argi = *argip;
+ if (ix < 0)
+ ix = argi->nvals;
+ if (ix >= argi->nvals) {
+ argi->vals = xrealloc(argi->vals, (ix + 1) * sizeof(*argi->vals));
+ memset(argi->vals + argi->nvals, 0,
+ (ix - argi->nvals) * sizeof(*argi->vals));
+ argi->nvals = ix + 1;
+ }
+ argi->vals[ix] = val;
+ return 0;
+}
+
+int argvAdd(ARGV_t * argvp, const char *val)
+{
+ ARGV_t argv;
+ int argc;
+
+ if (argvp == NULL)
+ return -1;
+ argc = argvCount(*argvp);
+ *argvp = xrealloc(*argvp, (argc + 1 + 1) * sizeof(**argvp));
+ argv = *argvp;
+ argv[argc++] = xstrdup(val);
+ argv[argc ] = NULL;
+ return 0;
+}
+
+int argvAddNum(ARGV_t *argvp, int val)
+{
+ char *valstr = NULL;
+ int rc;
+ rasprintf(&valstr, "%d", val);
+ rc = argvAdd(argvp, valstr);
+ free(valstr);
+ return rc;
+}
+
+int argvAppend(ARGV_t * argvp, ARGV_const_t av)
+{
+ ARGV_t argv = *argvp;
+ int argc = argvCount(argv);
+ int ac = argvCount(av);
+ int i;
+
+ argv = xrealloc(argv, (argc + ac + 1) * sizeof(*argv));
+ for (i = 0; i < ac; i++)
+ argv[argc + i] = xstrdup(av[i]);
+ argv[argc + ac] = NULL;
+ *argvp = argv;
+ return 0;
+}
+
+ARGV_t argvSplitString(const char * str, const char * seps, argvFlags flags)
+{
+ char *dest = NULL;
+ ARGV_t argv;
+ int argc = 1;
+ const char * s;
+ char * t;
+ int c;
+
+ if (str == NULL || seps == NULL)
+ return NULL;
+
+ dest = xmalloc(strlen(str) + 1);
+ for (argc = 1, s = str, t = dest; (c = *s); s++, t++) {
+ if (strchr(seps, c)) {
+ argc++;
+ c = '\0';
+ }
+ *t = c;
+ }
+ *t = '\0';
+
+ argv = xmalloc( (argc + 1) * sizeof(*argv));
+
+ for (c = 0, s = dest; s < t; s+= strlen(s) + 1) {
+ if (*s == '\0' && (flags & ARGV_SKIPEMPTY))
+ continue;
+ argv[c] = xstrdup(s);
+ c++;
+ }
+ argv[c] = NULL;
+ free(dest);
+ return argv;
+}
+
+/* Backwards compatibility */
+int argvSplit(ARGV_t * argvp, const char * str, const char * seps)
+{
+ if (argvp) {
+ *argvp = argvSplitString(str, seps, ARGV_SKIPEMPTY);
+ }
+ return 0;
+}
+
+char *argvJoin(ARGV_const_t argv, const char *sep)
+{
+ char *dest = NULL;
+ char * const *arg;
+
+ for (arg = argv; arg && *arg; arg++) {
+ rstrscat(&dest, *arg, *(arg+1) ? sep : "", NULL);
+ }
+ return dest;
+}
+
diff --git a/rpmio/argv.h b/rpmio/argv.h
new file mode 100644
index 0000000..d73a3c3
--- /dev/null
+++ b/rpmio/argv.h
@@ -0,0 +1,179 @@
+#ifndef _H_ARGV_
+#define _H_ARGV_
+
+/** \ingroup rpmargv
+ * \file rpmio/argv.h
+ */
+
+#include <stdio.h>
+#include <rpm/rpmtypes.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef char ** ARGV_t;
+typedef char * const *ARGV_const_t;
+
+typedef int * ARGint_t;
+struct ARGI_s {
+ unsigned nvals;
+ ARGint_t vals;
+};
+typedef struct ARGI_s * ARGI_t;
+typedef struct ARGI_s const * const ARGI_const_t;
+
+/** \ingroup rpmargv
+ * Print argv array elements.
+ * @param msg output message prefix (or NULL)
+ * @param argv argv array
+ * @param fp output file handle (NULL uses stderr)
+ */
+void argvPrint(const char * msg, ARGV_const_t argv, FILE * fp);
+
+/** \ingroup rpmargv
+ * Destroy an argi array.
+ * @param argi argi array
+ * @return NULL always
+ */
+ARGI_t argiFree(ARGI_t argi);
+
+
+/** \ingroup rpmargv
+ * Create an empty argv array.
+ * @return pointer to empty argv
+ */
+ARGV_t argvNew(void);
+
+/** \ingroup rpmargv
+ * Destroy an argv array.
+ * @param argv argv array
+ * @return NULL always
+ */
+ARGV_t argvFree(ARGV_t argv);
+
+/** \ingroup rpmargv
+ * Return no. of elements in argi array.
+ * @param argi argi array
+ * @return no. of elements
+ */
+int argiCount(ARGI_const_t argi);
+
+/** \ingroup rpmargv
+ * Return data from argi array.
+ * @param argi argi array
+ * @return argi array data address
+ */
+ARGint_t argiData(ARGI_const_t argi);
+
+/** \ingroup rpmargv
+ * Return no. of elements in argv array.
+ * @param argv argv array
+ * @return no. of elements
+ */
+int argvCount(ARGV_const_t argv);
+
+/** \ingroup rpmargv
+ * Return data from argv array.
+ * @param argv argv array
+ * @return argv array data address
+ */
+ARGV_t argvData(ARGV_t argv);
+
+/** \ingroup rpmargv
+ * Compare argv arrays (qsort/bsearch).
+ * @param a 1st instance address
+ * @param b 2nd instance address
+ * @return result of comparison
+ */
+int argvCmp(const void * a, const void * b);
+
+/** \ingroup rpmargv
+ * Sort an argv array.
+ * @param argv argv array
+ * @param compar strcmp-like comparison function, or NULL for argvCmp()
+ * @return 0 always
+ */
+int argvSort(ARGV_t argv, int (*compar)(const void *, const void *));
+
+/** \ingroup rpmargv
+ * Find an element in an argv array.
+ * @param argv argv array
+ * @param val string to find
+ * @param compar strcmp-like comparison function, or NULL for argvCmp()
+ * @return found string (NULL on failure)
+ */
+ARGV_t argvSearch(ARGV_const_t argv, const char *val,
+ int (*compar)(const void *, const void *));
+
+/** \ingroup rpmargv
+ * Add an int to an argi array.
+ * @retval *argip argi array
+ * @param ix argi array index (or -1 to append)
+ * @param val int arg to add
+ * @return 0 always
+ */
+int argiAdd(ARGI_t * argip, int ix, int val);
+
+/** \ingroup rpmargv
+ * Add a string to an argv array.
+ * @retval *argvp argv array
+ * @param val string arg to append
+ * @return 0 always
+ */
+int argvAdd(ARGV_t * argvp, const char *val);
+
+/** \ingroup rpmargv
+ * Add a number to an argv array (converting to a string).
+ * @retval *argvp argv array
+ * @param val numeric arg to append
+ * @return 0 always
+ */
+int argvAddNum(ARGV_t * argvp, int val);
+
+/** \ingroup rpmargv
+ * Append one argv array to another.
+ * @retval *argvp argv array
+ * @param av argv array to append
+ * @return 0 always
+ */
+int argvAppend(ARGV_t * argvp, ARGV_const_t av);
+
+enum argvFlags_e {
+ ARGV_NONE = 0,
+ ARGV_SKIPEMPTY = (1 << 0), /* omit empty strings from result */
+};
+
+typedef rpmFlags argvFlags;
+
+/** \ingroup rpmargv
+ * Split a string into an argv array.
+ * @param str string arg to split
+ * @param seps seperator characters
+ * @param flags flags to control behavior
+ * @return argv array
+ */
+ARGV_t argvSplitString(const char * str, const char * seps, argvFlags flags);
+
+/** \ingroup rpmargv
+ * Split a string into an argv array.
+ * @retval *argvp argv array
+ * @param str string arg to split
+ * @param seps seperator characters
+ * @return 0 always
+ */
+int argvSplit(ARGV_t * argvp, const char * str, const char * seps);
+
+/** \ingroup rpmargv
+ * Join an argv array into a string.
+ * @param *argv argv array to join
+ * @param sep seperator string to use
+ * @return malloc'ed string
+ */
+char *argvJoin(ARGV_const_t argv, const char *sep);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _H_ARGV_ */
diff --git a/rpmio/base64.c b/rpmio/base64.c
new file mode 100644
index 0000000..6b006e1
--- /dev/null
+++ b/rpmio/base64.c
@@ -0,0 +1,255 @@
+/* base64 encoder/decoder based on public domain implementation
+ * by Chris Venter */
+
+#include <arpa/inet.h>
+#include <stdlib.h>
+
+#include "rpmio/base64.h"
+
+
+static char base64_encode_value(char value_in)
+{
+ static const char encoding[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ if (value_in > 63) return '=';
+ return encoding[(int)value_in];
+}
+
+static char *base64_encode_block(const char *plaintext_in, int length_in, char *codechar)
+{
+ const char *plainchar = plaintext_in;
+ const char *const plaintextend = plaintext_in + length_in;
+ char result;
+ char fragment;
+
+ while (1) {
+ if (plainchar == plaintextend) {
+ return codechar;
+ }
+ fragment = *plainchar++;
+ result = (fragment & 0x0fc) >> 2;
+ *codechar++ = base64_encode_value(result);
+ result = (fragment & 0x003) << 4;
+ if (plainchar == plaintextend)
+ {
+ *codechar++ = base64_encode_value(result);
+ *codechar++ = '=';
+ *codechar++ = '=';
+ return codechar;
+ }
+ fragment = *plainchar++;
+ result |= (fragment & 0x0f0) >> 4;
+ *codechar++ = base64_encode_value(result);
+ result = (fragment & 0x00f) << 2;
+ if (plainchar == plaintextend)
+ {
+ *codechar++ = base64_encode_value(result);
+ *codechar++ = '=';
+ return codechar;
+ }
+ fragment = *plainchar++;
+ result |= (fragment & 0x0c0) >> 6;
+ *codechar++ = base64_encode_value(result);
+ result = (fragment & 0x03f) >> 0;
+ *codechar++ = base64_encode_value(result);
+ }
+ /* control should not reach here */
+ return codechar;
+}
+
+#define BASE64_DEFAULT_LINE_LENGTH 64
+
+char *b64encode(const void *data, size_t len, int linelen)
+{
+ size_t encodedlen;
+ const char *dataptr = data;
+ char *output;
+ char *outptr;
+
+ if (data == NULL)
+ return NULL;
+
+ if (linelen < 0)
+ linelen = BASE64_DEFAULT_LINE_LENGTH;
+
+ linelen /= 4;
+ encodedlen = ((len + 2) / 3) * 4;
+ if (linelen > 0) {
+ encodedlen += encodedlen/(linelen * 4) + 1;
+ }
+ ++encodedlen; /* for zero termination */
+
+ output = malloc(encodedlen);
+ if (output == NULL)
+ return NULL;
+
+ outptr = output;
+ while (len > 0) {
+ if (linelen > 0 && len > linelen * 3) {
+ outptr = base64_encode_block(dataptr, linelen * 3, outptr);
+ len -= linelen * 3;
+ dataptr += linelen * 3;
+ } else {
+ outptr = base64_encode_block(dataptr, len, outptr);
+ len = 0;
+ }
+ if (linelen > 0) {
+ *outptr++ = '\n';
+ }
+ }
+ *outptr = '\0';
+ return output;
+}
+
+static int base64_decode_value(unsigned char value_in)
+{
+ static const int decoding[] = {62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-2,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51};
+ value_in -= 43;
+ if (value_in > sizeof(decoding)/sizeof(int))
+ return -1;
+ return decoding[value_in];
+}
+
+static size_t base64_decode_block(const char *code_in, const size_t length_in, char *plaintext_out)
+{
+ const char *codechar = code_in;
+ char *plainchar = plaintext_out;
+ int fragment;
+
+ *plainchar = 0;
+
+ while (1)
+ {
+ do {
+ if (codechar == code_in+length_in)
+ {
+ return plainchar - plaintext_out;
+ }
+ fragment = base64_decode_value(*codechar++);
+ } while (fragment < 0);
+ *plainchar = (char)((fragment & 0x03f) << 2);
+
+ do {
+ if (codechar == code_in+length_in)
+ {
+ return plainchar - plaintext_out;
+ }
+ fragment = base64_decode_value(*codechar++);
+ } while (fragment < 0);
+ *plainchar++ |= (char)((fragment & 0x030) >> 4);
+ *plainchar = (char)((fragment & 0x00f) << 4);
+
+ do {
+ if (codechar == code_in+length_in)
+ {
+ return plainchar - plaintext_out;
+ }
+ fragment = base64_decode_value(*codechar++);
+ } while (fragment < 0);
+ *plainchar++ |= (char)((fragment & 0x03c) >> 2);
+ *plainchar = (char)((fragment & 0x003) << 6);
+
+ do {
+ if (codechar == code_in+length_in)
+ {
+ return plainchar - plaintext_out;
+ }
+ fragment = base64_decode_value(*codechar++);
+ } while (fragment < 0);
+ *plainchar++ |= (char)(fragment & 0x03f);
+ }
+ /* control should not reach here */
+ return plainchar - plaintext_out;
+}
+
+int b64decode(const char *in, void **out, size_t *outlen)
+{
+ size_t outcnt = 0;
+ const char *inptr = in;
+
+ *out = NULL;
+
+ if (in == NULL) {
+ return 1;
+ }
+
+ while (*inptr != '\0') {
+ /* assume all ASCII control chars as whitespace */
+ if (*inptr > 32) {
+ if (base64_decode_value(*inptr) != -1) {
+ ++outcnt;
+ } else {
+ return 3;
+ }
+ }
+ ++inptr;
+ }
+
+ if (outcnt % 4 != 0)
+ return 2;
+
+ outcnt = (outcnt / 4) * 3;
+
+ *out = malloc(outcnt + 1); /* base64_decode_block can write one extra character */
+
+ if (*out == NULL)
+ return 4;
+
+ *outlen = base64_decode_block(in, inptr - in, *out);
+
+ return 0;
+}
+
+#define CRC24_INIT 0xb704ce
+#define CRC24_POLY 0x1864cfb
+
+char *b64crc(const unsigned char *data, size_t len)
+{
+ uint32_t crc = CRC24_INIT;
+ int i;
+
+ while (len--) {
+ crc ^= (*data++) << 16;
+ for (i = 0; i < 8; i++) {
+ crc <<= 1;
+ if (crc & 0x1000000)
+ crc ^= CRC24_POLY;
+ }
+ }
+ crc = htonl(crc & 0xffffff);
+ data = (unsigned char *)&crc;
+ ++data;
+ return b64encode(data, 3, 0);
+}
+
+#ifdef BASE64_TEST
+#include <stdio.h>
+#include <string.h>
+
+int main(int argc, char *argv[])
+{
+ static char tst[]="wtrt8122čLýáj\x20s ~ýhž\t4\x02šjjmBvž^%$RTš#á.íěj\x1hčýčŤc+";
+ char *encoded;
+ void *decoded;
+ size_t size;
+ int err;
+ printf("Original: %lu\n%s\n", sizeof(tst)-1, tst);
+ encoded = b64encode(tst, sizeof(tst)-1, 64);
+ printf("Encoded: %lu\n%s\n", strlen(encoded), encoded);
+ if ((err = b64decode(encoded, &decoded, &size)) != 0) {
+ fprintf(stderr, "Error in decode: %d\n", err);
+ return 1;
+ }
+ printf("Decoded:\n%.*s\n", (int)size, (char *)decoded);
+ if (size != sizeof(tst)-1) {
+ fprintf(stderr, "Size differs orig: %lu new: %lu\n", sizeof(tst)-1, size);
+ return 1;
+ }
+ if (memcmp(tst, decoded, size) != 0) {
+ fprintf(stderr, "Decoded data differs.\n");
+ return 1;
+ }
+ fprintf(stderr, "OK\n");
+ return 0;
+}
+#endif
+
diff --git a/rpmio/base64.h b/rpmio/base64.h
new file mode 100644
index 0000000..d572856
--- /dev/null
+++ b/rpmio/base64.h
@@ -0,0 +1,37 @@
+/* base64 encoder/decoder based on public domain implementation
+ * by Chris Venter */
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* returns malloced base64 encoded string
+ * lines are split with \n characters to be nearest lower multiple of linelen
+ * if linelen/4 == 0 lines are not split
+ * if linelen < 0 default line length (64) is used
+ * the returned string is empty when len == 0
+ * returns NULL on failures
+ */
+char *b64encode(const void *data, size_t len, int linelen);
+
+/* decodes from zero terminated base64 encoded string to a newly malloced buffer
+ * ignores whitespace characters in the input string
+ * return values:
+ * 0 - OK
+ * 1 - input is NULL
+ * 2 - invalid length
+ * 3 - invalid characters on input
+ * 4 - malloc failed
+ */
+int b64decode(const char *in, void **out, size_t *outlen);
+
+/* counts CRC24 and base64 encodes it in a malloced string
+ * returns NULL on failures
+ */
+char *b64crc(const unsigned char *data, size_t len);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/rpmio/digest.c b/rpmio/digest.c
new file mode 100644
index 0000000..66f27b5
--- /dev/null
+++ b/rpmio/digest.c
@@ -0,0 +1,246 @@
+/** \ingroup signature
+ * \file rpmio/digest.c
+ */
+
+#include "system.h"
+
+#include "rpmio/digest.h"
+
+#include "debug.h"
+
+#ifdef SHA_DEBUG
+#define DPRINTF(_a) fprintf _a
+#else
+#define DPRINTF(_a)
+#endif
+
+
+/**
+ * MD5/SHA1 digest private data.
+ */
+struct DIGEST_CTX_s {
+ rpmDigestFlags flags; /*!< Bit(s) to control digest operation. */
+ HASHContext *hashctx; /*!< Internal NSS hash context. */
+ int algo; /*!< Used hash algorithm */
+};
+
+#define DIGESTS_MAX 11
+struct rpmDigestBundle_s {
+ int index_min; /*!< Smallest index of active digest */
+ int index_max; /*!< Largest index of active digest */
+ off_t nbytes; /*!< Length of total input data */
+ DIGEST_CTX digests[DIGESTS_MAX]; /*!< Digest contexts indexed by algo */
+};
+
+rpmDigestBundle rpmDigestBundleNew(void)
+{
+ rpmDigestBundle bundle = xcalloc(1, sizeof(*bundle));
+ return bundle;
+}
+
+rpmDigestBundle rpmDigestBundleFree(rpmDigestBundle bundle)
+{
+ if (bundle) {
+ for (int i = bundle->index_min; i <= bundle->index_max ; i++) {
+ if (bundle->digests[i] == NULL)
+ continue;
+ rpmDigestFinal(bundle->digests[i], NULL, NULL, 0);
+ bundle->digests[i] = NULL;
+ }
+ memset(bundle, 0, sizeof(*bundle));
+ free(bundle);
+ }
+ return NULL;
+}
+
+int rpmDigestBundleAdd(rpmDigestBundle bundle, int algo,
+ rpmDigestFlags flags)
+{
+ DIGEST_CTX ctx = NULL;
+ if (bundle && algo > 0 && algo < DIGESTS_MAX) {
+ if (bundle->digests[algo] == NULL) {
+ ctx = rpmDigestInit(algo, flags);
+ if (ctx) {
+ bundle->digests[algo] = ctx;
+ if (algo < bundle->index_min) {
+ bundle->index_min = algo;
+ }
+ if (algo > bundle->index_max) {
+ bundle->index_max = algo;
+ }
+ }
+ }
+ }
+ return (ctx != NULL);
+}
+
+int rpmDigestBundleUpdate(rpmDigestBundle bundle, const void *data, size_t len)
+{
+ int rc = 0;
+ if (bundle && data && len > 0) {
+ for (int i = bundle->index_min; i <= bundle->index_max; i++) {
+ DIGEST_CTX ctx = bundle->digests[i];
+ if (ctx == NULL)
+ continue;
+ rc += rpmDigestUpdate(ctx, data, len);
+ }
+ bundle->nbytes += len;
+ }
+ return rc;
+}
+
+int rpmDigestBundleFinal(rpmDigestBundle bundle,
+ int algo, void ** datap, size_t * lenp, int asAscii)
+{
+ int rc = 0;
+ if (bundle && algo >= bundle->index_min && algo <= bundle->index_max) {
+ rc = rpmDigestFinal(bundle->digests[algo], datap, lenp, asAscii);
+ bundle->digests[algo] = NULL;
+ }
+ return rc;
+}
+
+DIGEST_CTX rpmDigestBundleDupCtx(rpmDigestBundle bundle, int algo)
+{
+ DIGEST_CTX dup = NULL;
+ if (bundle && algo >= bundle->index_min && algo <= bundle->index_max) {
+ dup = rpmDigestDup(bundle->digests[algo]);
+ }
+ return dup;
+}
+
+DIGEST_CTX
+rpmDigestDup(DIGEST_CTX octx)
+{
+ DIGEST_CTX nctx = NULL;
+ if (octx) {
+ HASHContext *hctx = HASH_Clone(octx->hashctx);
+ if (hctx) {
+ nctx = memcpy(xcalloc(1, sizeof(*nctx)), octx, sizeof(*nctx));
+ nctx->hashctx = hctx;
+ }
+ }
+ return nctx;
+}
+
+RPM_GNUC_PURE
+static HASH_HashType getHashType(int hashalgo)
+{
+ switch (hashalgo) {
+ case PGPHASHALGO_MD5:
+ return HASH_AlgMD5;
+ break;
+ case PGPHASHALGO_MD2:
+ return HASH_AlgMD2;
+ break;
+ case PGPHASHALGO_SHA1:
+ return HASH_AlgSHA1;
+ break;
+ case PGPHASHALGO_SHA256:
+ return HASH_AlgSHA256;
+ break;
+ case PGPHASHALGO_SHA384:
+ return HASH_AlgSHA384;
+ break;
+ case PGPHASHALGO_SHA512:
+ return HASH_AlgSHA512;
+ break;
+ case PGPHASHALGO_RIPEMD160:
+ case PGPHASHALGO_TIGER192:
+ case PGPHASHALGO_HAVAL_5_160:
+ default:
+ return HASH_AlgNULL;
+ break;
+ }
+}
+
+size_t
+rpmDigestLength(int hashalgo)
+{
+ return HASH_ResultLen(getHashType(hashalgo));
+}
+
+DIGEST_CTX
+rpmDigestInit(int hashalgo, rpmDigestFlags flags)
+{
+ HASH_HashType type = getHashType(hashalgo);
+ HASHContext *hashctx = NULL;
+ DIGEST_CTX ctx = NULL;
+
+ if (type == HASH_AlgNULL || rpmInitCrypto() < 0)
+ goto exit;
+
+ if ((hashctx = HASH_Create(type)) != NULL) {
+ ctx = xcalloc(1, sizeof(*ctx));
+ ctx->flags = flags;
+ ctx->algo = hashalgo;
+ ctx->hashctx = hashctx;
+ HASH_Begin(ctx->hashctx);
+ }
+
+DPRINTF((stderr, "*** Init(%x) ctx %p hashctx %p\n", flags, ctx, ctx->hashctx));
+exit:
+ return ctx;
+}
+
+int
+rpmDigestUpdate(DIGEST_CTX ctx, const void * data, size_t len)
+{
+ size_t partlen;
+ const unsigned char *ptr = data;
+
+ if (ctx == NULL)
+ return -1;
+
+DPRINTF((stderr, "*** Update(%p,%p,%zd) hashctx %p \"%s\"\n", ctx, data, len, ctx->hashctx, ((char *)data)));
+ partlen = ~(unsigned int)0xFF;
+ while (len > 0) {
+ if (len < partlen) {
+ partlen = len;
+ }
+ HASH_Update(ctx->hashctx, ptr, partlen);
+ ptr += partlen;
+ len -= partlen;
+ }
+ return 0;
+}
+
+int
+rpmDigestFinal(DIGEST_CTX ctx, void ** datap, size_t *lenp, int asAscii)
+{
+ unsigned char * digest;
+ unsigned int digestlen;
+
+ if (ctx == NULL)
+ return -1;
+ digestlen = HASH_ResultLenContext(ctx->hashctx);
+ digest = xmalloc(digestlen);
+
+DPRINTF((stderr, "*** Final(%p,%p,%p,%zd) hashctx %p digest %p\n", ctx, datap, lenp, asAscii, ctx->hashctx, digest));
+/* FIX: check rc */
+ HASH_End(ctx->hashctx, digest, (unsigned int *) &digestlen, digestlen);
+
+ /* Return final digest. */
+ if (!asAscii) {
+ if (lenp) *lenp = digestlen;
+ if (datap) {
+ *datap = digest;
+ digest = NULL;
+ }
+ } else {
+ if (lenp) *lenp = (2*digestlen) + 1;
+ if (datap) {
+ const uint8_t * s = (const uint8_t *) digest;
+ *datap = pgpHexStr(s, digestlen);
+ }
+ }
+ if (digest) {
+ memset(digest, 0, digestlen); /* In case it's sensitive */
+ free(digest);
+ }
+ HASH_Destroy(ctx->hashctx);
+ memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */
+ free(ctx);
+ return 0;
+}
+
diff --git a/rpmio/digest.h b/rpmio/digest.h
new file mode 100644
index 0000000..0f44619
--- /dev/null
+++ b/rpmio/digest.h
@@ -0,0 +1,49 @@
+#ifndef _RPMDIGEST_H
+#define _RPMDIGEST_H
+
+#include <nss.h>
+#include <sechash.h>
+#include <keyhi.h>
+#include <cryptohi.h>
+
+#include <rpm/rpmpgp.h>
+#include "rpmio/base64.h"
+
+
+/** \ingroup rpmio
+ * Values parsed from OpenPGP signature/pubkey packet(s).
+ */
+struct pgpDigParams_s {
+ char * userid;
+ uint8_t * hash;
+ char * params[4];
+ uint8_t tag;
+
+ uint8_t version; /*!< version number. */
+ pgpTime_t time; /*!< time that the key was created. */
+ uint8_t pubkey_algo; /*!< public key algorithm. */
+
+ uint8_t hash_algo;
+ uint8_t sigtype;
+ uint8_t hashlen;
+ uint8_t signhash16[2];
+ pgpKeyID_t signid;
+ uint8_t saved;
+#define PGPDIG_SAVED_TIME (1 << 0)
+#define PGPDIG_SAVED_ID (1 << 1)
+
+};
+
+/** \ingroup rpmio
+ * Container for values parsed from an OpenPGP signature and public key.
+ */
+struct pgpDig_s {
+ struct pgpDigParams_s signature;
+ struct pgpDigParams_s pubkey;
+
+ /* DSA/RSA parameters */
+ SECKEYPublicKey *keydata;
+ SECItem *sigdata;
+};
+
+#endif /* _RPMDIGEST_H */
diff --git a/rpmio/macro.c b/rpmio/macro.c
new file mode 100644
index 0000000..70ed88e
--- /dev/null
+++ b/rpmio/macro.c
@@ -0,0 +1,1611 @@
+/** \ingroup rpmrc rpmio
+ * \file rpmio/macro.c
+ */
+
+#include "system.h"
+#include <stdarg.h>
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#else
+extern char *optarg;
+extern int optind;
+#endif
+
+#if !defined(isblank)
+#define isblank(_c) ((_c) == ' ' || (_c) == '\t')
+#endif
+#define iseol(_c) ((_c) == '\n' || (_c) == '\r')
+
+#define STREQ(_t, _f, _fn) ((_fn) == (sizeof(_t)-1) && rstreqn((_t), (_f), (_fn)))
+
+#define MACROBUFSIZ (BUFSIZ * 2)
+
+#include <rpm/rpmio.h>
+#include <rpm/rpmstring.h>
+#include <rpm/rpmfileutil.h>
+#include <rpm/rpmurl.h>
+#include <rpm/rpmlog.h>
+#include <rpm/rpmmacro.h>
+#include <rpm/argv.h>
+
+#ifdef WITH_LUA
+#include "rpmio/rpmlua.h"
+#endif
+
+#include "debug.h"
+
+/*! The structure used to store a macro. */
+struct rpmMacroEntry_s {
+ struct rpmMacroEntry_s *prev;/*!< Macro entry stack. */
+ char *name; /*!< Macro name. */
+ char *opts; /*!< Macro parameters (a la getopt) */
+ char *body; /*!< Macro body. */
+ int used; /*!< No. of expansions. */
+ int level; /*!< Scoping level. */
+};
+
+/*! The structure used to store the set of macros in a context. */
+struct rpmMacroContext_s {
+ rpmMacroEntry *macroTable; /*!< Macro entry table for context. */
+ int macrosAllocated;/*!< No. of allocated macros. */
+ int firstFree; /*!< No. of macros. */
+};
+
+
+static struct rpmMacroContext_s rpmGlobalMacroContext_s;
+rpmMacroContext rpmGlobalMacroContext = &rpmGlobalMacroContext_s;
+
+static struct rpmMacroContext_s rpmCLIMacroContext_s;
+rpmMacroContext rpmCLIMacroContext = &rpmCLIMacroContext_s;
+
+/**
+ * Macro expansion state.
+ */
+typedef struct MacroBuf_s {
+ char * buf; /*!< Expansion buffer. */
+ size_t tpos; /*!< Current position in expansion buffer */
+ size_t nb; /*!< No. bytes remaining in expansion buffer. */
+ int depth; /*!< Current expansion depth. */
+ int macro_trace; /*!< Pre-print macro to expand? */
+ int expand_trace; /*!< Post-print macro expansion? */
+ rpmMacroContext mc;
+} * MacroBuf;
+
+#define _MAX_MACRO_DEPTH 16
+static int max_macro_depth = _MAX_MACRO_DEPTH;
+
+#define _PRINT_MACRO_TRACE 0
+static int print_macro_trace = _PRINT_MACRO_TRACE;
+
+#define _PRINT_EXPAND_TRACE 0
+static int print_expand_trace = _PRINT_EXPAND_TRACE;
+
+#define MACRO_CHUNK_SIZE 16
+
+/* forward ref */
+static int expandMacro(MacroBuf mb, const char *src, size_t slen);
+
+/* =============================================================== */
+
+/**
+ * Compare macro entries by name (qsort/bsearch).
+ * @param ap 1st macro entry
+ * @param bp 2nd macro entry
+ * @return result of comparison
+ */
+static int
+compareMacroName(const void * ap, const void * bp)
+{
+ rpmMacroEntry ame = *((const rpmMacroEntry *)ap);
+ rpmMacroEntry bme = *((const rpmMacroEntry *)bp);
+
+ if (ame == NULL && bme == NULL)
+ return 0;
+ if (ame == NULL)
+ return 1;
+ if (bme == NULL)
+ return -1;
+ return strcmp(ame->name, bme->name);
+}
+
+/**
+ * Enlarge macro table.
+ * @param mc macro context
+ */
+static void
+expandMacroTable(rpmMacroContext mc)
+{
+ if (mc->macroTable == NULL) {
+ mc->macrosAllocated = MACRO_CHUNK_SIZE;
+ mc->macroTable = (rpmMacroEntry *)
+ xmalloc(sizeof(*(mc->macroTable)) * mc->macrosAllocated);
+ mc->firstFree = 0;
+ } else {
+ mc->macrosAllocated += MACRO_CHUNK_SIZE;
+ mc->macroTable = (rpmMacroEntry *)
+ xrealloc(mc->macroTable, sizeof(*(mc->macroTable)) *
+ mc->macrosAllocated);
+ }
+ memset(&mc->macroTable[mc->firstFree], 0, MACRO_CHUNK_SIZE * sizeof(*(mc->macroTable)));
+}
+
+/**
+ * Sort entries in macro table.
+ * @param mc macro context
+ */
+static void
+sortMacroTable(rpmMacroContext mc)
+{
+ int i;
+
+ if (mc == NULL || mc->macroTable == NULL)
+ return;
+
+ qsort(mc->macroTable, mc->firstFree, sizeof(*(mc->macroTable)),
+ compareMacroName);
+
+ /* Empty pointers are now at end of table. Reset first free index. */
+ for (i = 0; i < mc->firstFree; i++) {
+ if (mc->macroTable[i] != NULL)
+ continue;
+ mc->firstFree = i;
+ break;
+ }
+}
+
+void
+rpmDumpMacroTable(rpmMacroContext mc, FILE * fp)
+{
+ int nempty = 0;
+ int nactive = 0;
+
+ if (mc == NULL) mc = rpmGlobalMacroContext;
+ if (fp == NULL) fp = stderr;
+
+ fprintf(fp, "========================\n");
+ if (mc->macroTable != NULL) {
+ int i;
+ for (i = 0; i < mc->firstFree; i++) {
+ rpmMacroEntry me;
+ if ((me = mc->macroTable[i]) == NULL) {
+ /* XXX this should never happen */
+ nempty++;
+ continue;
+ }
+ fprintf(fp, "%3d%c %s", me->level,
+ (me->used > 0 ? '=' : ':'), me->name);
+ if (me->opts && *me->opts)
+ fprintf(fp, "(%s)", me->opts);
+ if (me->body && *me->body)
+ fprintf(fp, "\t%s", me->body);
+ fprintf(fp, "\n");
+ nactive++;
+ }
+ }
+ fprintf(fp, _("======================== active %d empty %d\n"),
+ nactive, nempty);
+}
+
+/**
+ * Find entry in macro table.
+ * @param mc macro context
+ * @param name macro name
+ * @param namelen no. of bytes
+ * @return address of slot in macro table with name (or NULL)
+ */
+static rpmMacroEntry *
+findEntry(rpmMacroContext mc, const char * name, size_t namelen)
+{
+ rpmMacroEntry key, *ret;
+ struct rpmMacroEntry_s keybuf;
+ char namebuf[namelen+1];
+ const char *mname = name;
+
+ if (mc == NULL) mc = rpmGlobalMacroContext;
+ if (mc->macroTable == NULL || mc->firstFree == 0)
+ return NULL;
+
+ if (namelen > 0) {
+ strncpy(namebuf, name, namelen);
+ namebuf[namelen] = '\0';
+ mname = namebuf;
+ }
+
+ key = &keybuf;
+ memset(key, 0, sizeof(*key));
+ key->name = (char *)mname;
+ ret = (rpmMacroEntry *) bsearch(&key, mc->macroTable, mc->firstFree,
+ sizeof(*(mc->macroTable)), compareMacroName);
+ /* XXX TODO: find 1st empty slot and return that */
+ return ret;
+}
+
+/* =============================================================== */
+
+/**
+ * fgets(3) analogue that reads \ continuations. Last newline always trimmed.
+ * @param buf input buffer
+ * @param size inbut buffer size (bytes)
+ * @param fd file handle
+ * @return buffer, or NULL on end-of-file
+ */
+static char *
+rdcl(char * buf, size_t size, FILE *f)
+{
+ char *q = buf - 1; /* initialize just before buffer. */
+ size_t nb = 0;
+ size_t nread = 0;
+ int pc = 0, bc = 0;
+ char *p = buf;
+
+ if (f != NULL)
+ do {
+ *(++q) = '\0'; /* terminate and move forward. */
+ if (fgets(q, size, f) == NULL) /* read next line. */
+ break;
+ nb = strlen(q);
+ nread += nb; /* trim trailing \r and \n */
+ for (q += nb - 1; nb > 0 && iseol(*q); q--)
+ nb--;
+ for (; p <= q; p++) {
+ switch (*p) {
+ case '\\':
+ switch (*(p+1)) {
+ case '\0': break;
+ default: p++; break;
+ }
+ break;
+ case '%':
+ switch (*(p+1)) {
+ case '{': p++, bc++; break;
+ case '(': p++, pc++; break;
+ case '%': p++; break;
+ }
+ break;
+ case '{': if (bc > 0) bc++; break;
+ case '}': if (bc > 0) bc--; break;
+ case '(': if (pc > 0) pc++; break;
+ case ')': if (pc > 0) pc--; break;
+ }
+ }
+ if (nb == 0 || (*q != '\\' && !bc && !pc) || *(q+1) == '\0') {
+ *(++q) = '\0'; /* trim trailing \r, \n */
+ break;
+ }
+ q++; p++; nb++; /* copy newline too */
+ size -= nb;
+ if (*q == '\r') /* XXX avoid \r madness */
+ *q = '\n';
+ } while (size > 0);
+ return (nread > 0 ? buf : NULL);
+}
+
+/**
+ * Return text between pl and matching pr characters.
+ * @param p start of text
+ * @param pl left char, i.e. '[', '(', '{', etc.
+ * @param pr right char, i.e. ']', ')', '}', etc.
+ * @return address of last char before pr (or NULL)
+ */
+static const char *
+matchchar(const char * p, char pl, char pr)
+{
+ int lvl = 0;
+ char c;
+
+ while ((c = *p++) != '\0') {
+ if (c == '\\') { /* Ignore escaped chars */
+ p++;
+ continue;
+ }
+ if (c == pr) {
+ if (--lvl <= 0) return --p;
+ } else if (c == pl)
+ lvl++;
+ }
+ return (const char *)NULL;
+}
+
+/**
+ * Pre-print macro expression to be expanded.
+ * @param mb macro expansion state
+ * @param s current expansion string
+ * @param se end of string
+ */
+static void
+printMacro(MacroBuf mb, const char * s, const char * se)
+{
+ const char *senl;
+ const char *ellipsis;
+ int choplen;
+
+ if (s >= se) { /* XXX just in case */
+ fprintf(stderr, _("%3d>%*s(empty)"), mb->depth,
+ (2 * mb->depth + 1), "");
+ return;
+ }
+
+ if (s[-1] == '{')
+ s--;
+
+ /* Print only to first end-of-line (or end-of-string). */
+ for (senl = se; *senl && !iseol(*senl); senl++)
+ {};
+
+ /* Limit trailing non-trace output */
+ choplen = 61 - (2 * mb->depth);
+ if ((senl - s) > choplen) {
+ senl = s + choplen;
+ ellipsis = "...";
+ } else
+ ellipsis = "";
+
+ /* Substitute caret at end-of-macro position */
+ fprintf(stderr, "%3d>%*s%%%.*s^", mb->depth,
+ (2 * mb->depth + 1), "", (int)(se - s), s);
+ if (se[1] != '\0' && (senl - (se+1)) > 0)
+ fprintf(stderr, "%-.*s%s", (int)(senl - (se+1)), se+1, ellipsis);
+ fprintf(stderr, "\n");
+}
+
+/**
+ * Post-print expanded macro expression.
+ * @param mb macro expansion state
+ * @param t current expansion string result
+ * @param te end of string
+ */
+static void
+printExpansion(MacroBuf mb, const char * t, const char * te)
+{
+ const char *ellipsis;
+ int choplen;
+
+ if (!(te > t)) {
+ rpmlog(RPMLOG_DEBUG, _("%3d<%*s(empty)\n"), mb->depth, (2 * mb->depth + 1), "");
+ return;
+ }
+
+ /* Shorten output which contains newlines */
+ while (te > t && iseol(te[-1]))
+ te--;
+ ellipsis = "";
+ if (mb->depth > 0) {
+ const char *tenl;
+
+ /* Skip to last line of expansion */
+ while ((tenl = strchr(t, '\n')) && tenl < te)
+ t = ++tenl;
+
+ /* Limit expand output */
+ choplen = 61 - (2 * mb->depth);
+ if ((te - t) > choplen) {
+ te = t + choplen;
+ ellipsis = "...";
+ }
+ }
+
+ rpmlog(RPMLOG_DEBUG,"%3d<%*s", mb->depth, (2 * mb->depth + 1), "");
+ if (te > t)
+ rpmlog(RPMLOG_DEBUG, "%.*s%s", (int)(te - t), t, ellipsis);
+ rpmlog(RPMLOG_DEBUG, "\n");
+}
+
+#define SKIPBLANK(_s, _c) \
+ while (((_c) = *(_s)) && isblank(_c)) \
+ (_s)++; \
+
+#define SKIPNONBLANK(_s, _c) \
+ while (((_c) = *(_s)) && !(isblank(_c) || iseol(_c))) \
+ (_s)++; \
+
+#define COPYNAME(_ne, _s, _c) \
+ { SKIPBLANK(_s,_c); \
+ while(((_c) = *(_s)) && (risalnum(_c) || (_c) == '_')) \
+ *(_ne)++ = *(_s)++; \
+ *(_ne) = '\0'; \
+ }
+
+#define COPYOPTS(_oe, _s, _c) \
+ { \
+ while(((_c) = *(_s)) && (_c) != ')') \
+ *(_oe)++ = *(_s)++; \
+ *(_oe) = '\0'; \
+ }
+
+/**
+ * Macro-expand string src, return result in dynamically allocated buffer.
+ * @param mb macro expansion state
+ * @param src string to expand
+ * @param slen input string length (or 0 for strlen())
+ * @retval target pointer to expanded string (malloced)
+ * @return result of expansion
+ */
+static int
+expandThis(MacroBuf mb, const char * src, size_t slen, char **target)
+{
+ struct MacroBuf_s umb;
+ int rc;
+
+ /* Copy other state from "parent", but we want a buffer of our own */
+ umb = *mb;
+ umb.buf = NULL;
+ rc = expandMacro(&umb, src, slen);
+ *target = umb.buf;
+
+ return rc;
+}
+
+static void mbAppend(MacroBuf mb, char c)
+{
+ if (mb->nb < 1) {
+ mb->buf = xrealloc(mb->buf, mb->tpos + mb->nb + MACROBUFSIZ);
+ mb->nb += MACROBUFSIZ;
+ }
+ mb->buf[mb->tpos++] = c;
+ mb->nb--;
+}
+
+static void mbAppendStr(MacroBuf mb, const char *str)
+{
+ size_t len = strlen(str);
+ if (len > mb->nb) {
+ mb->buf = xrealloc(mb->buf, mb->tpos + mb->nb + MACROBUFSIZ + len);
+ mb->nb += MACROBUFSIZ + len;
+ }
+ memcpy(mb->buf+mb->tpos, str, len);
+ mb->tpos += len;
+ mb->nb -= len;
+}
+/**
+ * Expand output of shell command into target buffer.
+ * @param mb macro expansion state
+ * @param cmd shell command
+ * @param clen no. bytes in shell command
+ * @return result of expansion
+ */
+static int
+doShellEscape(MacroBuf mb, const char * cmd, size_t clen)
+{
+ char *buf = NULL;
+ FILE *shf;
+ int rc = 0;
+ int c;
+
+ rc = expandThis(mb, cmd, clen, &buf);
+ if (rc)
+ goto exit;
+
+ if ((shf = popen(buf, "r")) == NULL) {
+ rc = 1;
+ goto exit;
+ }
+ while((c = fgetc(shf)) != EOF) {
+ mbAppend(mb, c);
+ }
+ (void) pclose(shf);
+
+ /* XXX delete trailing \r \n */
+ while (iseol(mb->buf[mb->tpos-1])) {
+ mb->buf[mb->tpos--] = '\0';
+ mb->nb++;
+ }
+
+exit:
+ _free(buf);
+ return rc;
+}
+
+/**
+ * Parse (and execute) new macro definition.
+ * @param mb macro expansion state
+ * @param se macro definition to parse
+ * @param level macro recursion level
+ * @param expandbody should body be expanded?
+ * @return address to continue parsing
+ */
+static const char *
+doDefine(MacroBuf mb, const char * se, int level, int expandbody)
+{
+ const char *s = se;
+ size_t blen = MACROBUFSIZ;
+ char *buf = xmalloc(blen);
+ char *n = buf, *ne = n;
+ char *o = NULL, *oe;
+ char *b, *be, *ebody = NULL;
+ int c;
+ int oc = ')';
+
+ /* Copy name */
+ COPYNAME(ne, s, c);
+
+ /* Copy opts (if present) */
+ oe = ne + 1;
+ if (*s == '(') {
+ s++; /* skip ( */
+ o = oe;
+ COPYOPTS(oe, s, oc);
+ s++; /* skip ) */
+ }
+
+ /* Copy body, skipping over escaped newlines */
+ b = be = oe + 1;
+ SKIPBLANK(s, c);
+ if (c == '{') { /* XXX permit silent {...} grouping */
+ if ((se = matchchar(s, c, '}')) == NULL) {
+ rpmlog(RPMLOG_ERR,
+ _("Macro %%%s has unterminated body\n"), n);
+ se = s; /* XXX W2DO? */
+ goto exit;
+ }
+ s++; /* XXX skip { */
+ strncpy(b, s, (se - s));
+ b[se - s] = '\0';
+ be += strlen(b);
+ se++; /* XXX skip } */
+ s = se; /* move scan forward */
+ } else { /* otherwise free-field */
+ int bc = 0, pc = 0;
+ while (*s && (bc || pc || !iseol(*s))) {
+ switch (*s) {
+ case '\\':
+ switch (*(s+1)) {
+ case '\0': break;
+ default: s++; break;
+ }
+ break;
+ case '%':
+ switch (*(s+1)) {
+ case '{': *be++ = *s++; bc++; break;
+ case '(': *be++ = *s++; pc++; break;
+ case '%': *be++ = *s++; break;
+ }
+ break;
+ case '{': if (bc > 0) bc++; break;
+ case '}': if (bc > 0) bc--; break;
+ case '(': if (pc > 0) pc++; break;
+ case ')': if (pc > 0) pc--; break;
+ }
+ *be++ = *s++;
+ }
+ *be = '\0';
+
+ if (bc || pc) {
+ rpmlog(RPMLOG_ERR,
+ _("Macro %%%s has unterminated body\n"), n);
+ se = s; /* XXX W2DO? */
+ goto exit;
+ }
+
+ /* Trim trailing blanks/newlines */
+ while (--be >= b && (c = *be) && (isblank(c) || iseol(c)))
+ {};
+ *(++be) = '\0'; /* one too far */
+ }
+
+ /* Move scan over body */
+ while (iseol(*s))
+ s++;
+ se = s;
+
+ /* Names must start with alphabetic or _ and be at least 3 chars */
+ if (!((c = *n) && (risalpha(c) || c == '_') && (ne - n) > 2)) {
+ rpmlog(RPMLOG_ERR,
+ _("Macro %%%s has illegal name (%%define)\n"), n);
+ goto exit;
+ }
+
+ /* Options must be terminated with ')' */
+ if (o && oc != ')') {
+ rpmlog(RPMLOG_ERR, _("Macro %%%s has unterminated opts\n"), n);
+ goto exit;
+ }
+
+ if ((be - b) < 1) {
+ rpmlog(RPMLOG_ERR, _("Macro %%%s has empty body\n"), n);
+ goto exit;
+ }
+
+ if (expandbody) {
+ if (expandThis(mb, b, 0, &ebody)) {
+ rpmlog(RPMLOG_ERR, _("Macro %%%s failed to expand\n"), n);
+ goto exit;
+ }
+ b = ebody;
+ }
+
+ addMacro(mb->mc, n, o, b, (level - 1));
+
+exit:
+ _free(buf);
+ _free(ebody);
+ return se;
+}
+
+/**
+ * Parse (and execute) macro undefinition.
+ * @param mc macro context
+ * @param se macro name to undefine
+ * @return address to continue parsing
+ */
+static const char *
+doUndefine(rpmMacroContext mc, const char * se)
+{
+ const char *s = se;
+ char *buf = xmalloc(MACROBUFSIZ);
+ char *n = buf, *ne = n;
+ int c;
+
+ COPYNAME(ne, s, c);
+
+ /* Move scan over body */
+ while (iseol(*s))
+ s++;
+ se = s;
+
+ /* Names must start with alphabetic or _ and be at least 3 chars */
+ if (!((c = *n) && (risalpha(c) || c == '_') && (ne - n) > 2)) {
+ rpmlog(RPMLOG_ERR,
+ _("Macro %%%s has illegal name (%%undefine)\n"), n);
+ goto exit;
+ }
+
+ delMacro(mc, n);
+
+exit:
+ _free(buf);
+ return se;
+}
+
+/**
+ * Push new macro definition onto macro entry stack.
+ * @param mep address of macro entry slot
+ * @param n macro name
+ * @param o macro parameters (NULL if none)
+ * @param b macro body (NULL becomes "")
+ * @param level macro recursion level
+ */
+static void
+pushMacro(rpmMacroEntry * mep,
+ const char * n, const char * o,
+ const char * b, int level)
+{
+ rpmMacroEntry prev = (mep && *mep ? *mep : NULL);
+ rpmMacroEntry me = (rpmMacroEntry) xmalloc(sizeof(*me));
+
+ me->prev = prev;
+ me->name = (prev ? prev->name : xstrdup(n));
+ me->opts = (o ? xstrdup(o) : NULL);
+ me->body = xstrdup(b ? b : "");
+ me->used = 0;
+ me->level = level;
+ if (mep)
+ *mep = me;
+ else
+ me = _free(me);
+}
+
+/**
+ * Pop macro definition from macro entry stack.
+ * @param mep address of macro entry slot
+ */
+static void
+popMacro(rpmMacroEntry * mep)
+{
+ rpmMacroEntry me = (*mep ? *mep : NULL);
+
+ if (me) {
+ /* XXX cast to workaround const */
+ if ((*mep = me->prev) == NULL)
+ me->name = _free(me->name);
+ me->opts = _free(me->opts);
+ me->body = _free(me->body);
+ me = _free(me);
+ }
+}
+
+/**
+ * Free parsed arguments for parameterized macro.
+ * @param mb macro expansion state
+ */
+static void
+freeArgs(MacroBuf mb)
+{
+ rpmMacroContext mc = mb->mc;
+ int ndeleted = 0;
+ int i;
+
+ if (mc == NULL || mc->macroTable == NULL)
+ return;
+
+ /* Delete dynamic macro definitions */
+ for (i = 0; i < mc->firstFree; i++) {
+ rpmMacroEntry *mep, me;
+ int skiptest = 0;
+ mep = &mc->macroTable[i];
+ me = *mep;
+
+ if (me == NULL) /* XXX this should never happen */
+ continue;
+ if (me->level < mb->depth)
+ continue;
+ if (strlen(me->name) == 1 && strchr("#*0", *me->name)) {
+ if (*me->name == '*' && me->used > 0)
+ skiptest = 1; /* XXX skip test for %# %* %0 */
+ } else if (!skiptest && me->used <= 0) {
+#if NOTYET
+ rpmlog(RPMLOG_ERR,
+ _("Macro %%%s (%s) was not used below level %d\n"),
+ me->name, me->body, me->level);
+#endif
+ }
+ popMacro(mep);
+ if (!(mep && *mep))
+ ndeleted++;
+ }
+
+ /* If any deleted macros, sort macro table */
+ if (ndeleted)
+ sortMacroTable(mc);
+}
+
+/**
+ * Parse arguments (to next new line) for parameterized macro.
+ * @todo Use popt rather than getopt to parse args.
+ * @param mb macro expansion state
+ * @param me macro entry slot
+ * @param se arguments to parse
+ * @param lastc stop parsing at lastc
+ * @return address to continue parsing
+ */
+static const char *
+grabArgs(MacroBuf mb, const rpmMacroEntry me, const char * se,
+ const char * lastc)
+{
+ const char *opts;
+ char *args = NULL;
+ ARGV_t argv = NULL;
+ int argc = 0;
+ int c;
+
+ /* Copy macro name as argv[0] */
+ argvAdd(&argv, me->name);
+ addMacro(mb->mc, "0", NULL, me->name, mb->depth);
+
+ /*
+ * Make a copy of se up to lastc string that we can pass to argvSplit().
+ * Append the results to main argv.
+ */
+ { ARGV_t av = NULL;
+ char *s = xcalloc((lastc-se)+1, sizeof(*s));
+ memcpy(s, se, (lastc-se));
+
+ argvSplit(&av, s, " \t");
+ argvAppend(&argv, av);
+
+ argvFree(av);
+ free(s);
+ }
+
+ /*
+ * The macro %* analoguous to the shell's $* means "Pass all non-macro
+ * parameters." Consequently, there needs to be a macro that means "Pass all
+ * (including macro parameters) options". This is useful for verifying
+ * parameters during expansion and yet transparently passing all parameters
+ * through for higher level processing (e.g. %description and/or %setup).
+ * This is the (potential) justification for %{**} ...
+ */
+ args = argvJoin(argv + 1, " ");
+ addMacro(mb->mc, "**", NULL, args, mb->depth);
+ free(args);
+
+ /*
+ * POSIX states optind must be 1 before any call but glibc uses 0
+ * to (re)initialize getopt structures, eww.
+ */
+#ifdef __GLIBC__
+ optind = 0;
+#else
+ optind = 1;
+#endif
+
+ opts = me->opts;
+ argc = argvCount(argv);
+
+ /* Define option macros. */
+ while((c = getopt(argc, argv, opts)) != -1)
+ {
+ char *name = NULL, *body = NULL;
+ if (c == '?' || strchr(opts, c) == NULL) {
+ rpmlog(RPMLOG_ERR, _("Unknown option %c in %s(%s)\n"),
+ (char)c, me->name, opts);
+ goto exit;
+ }
+
+ rasprintf(&name, "-%c", c);
+ if (optarg) {
+ rasprintf(&body, "-%c %s", c, optarg);
+ } else {
+ rasprintf(&body, "-%c", c);
+ }
+ addMacro(mb->mc, name, NULL, body, mb->depth);
+ free(name);
+ free(body);
+
+ if (optarg) {
+ rasprintf(&name, "-%c*", c);
+ addMacro(mb->mc, name, NULL, optarg, mb->depth);
+ free(name);
+ }
+ }
+
+ /* Add argument count (remaining non-option items) as macro. */
+ { char *ac = NULL;
+ rasprintf(&ac, "%d", (argc - optind));
+ addMacro(mb->mc, "#", NULL, ac, mb->depth);
+ free(ac);
+ }
+
+ /* Add macro for each argument */
+ if (argc - optind) {
+ for (c = optind; c < argc; c++) {
+ char *name = NULL;
+ rasprintf(&name, "%d", (c - optind + 1));
+ addMacro(mb->mc, name, NULL, argv[c], mb->depth);
+ free(name);
+ }
+ }
+
+ /* Add concatenated unexpanded arguments as yet another macro. */
+ args = argvJoin(argv + optind, " ");
+ addMacro(mb->mc, "*", NULL, args ? args : "", mb->depth);
+ free(args);
+
+exit:
+ argvFree(argv);
+ return *lastc ? lastc + 1 : lastc;
+}
+
+/**
+ * Perform macro message output
+ * @param mb macro expansion state
+ * @param waserror use rpmlog()?
+ * @param msg message to ouput
+ * @param msglen no. of bytes in message
+ */
+static void
+doOutput(MacroBuf mb, int waserror, const char * msg, size_t msglen)
+{
+ char *buf = NULL;
+
+ (void) expandThis(mb, msg, msglen, &buf);
+ if (waserror)
+ rpmlog(RPMLOG_ERR, "%s\n", buf);
+ else
+ fprintf(stderr, "%s", buf);
+ _free(buf);
+}
+
+/**
+ * Execute macro primitives.
+ * @param mb macro expansion state
+ * @param negate should logic be inverted?
+ * @param f beginning of field f
+ * @param fn length of field f
+ * @param g beginning of field g
+ * @param gn length of field g
+ */
+static void
+doFoo(MacroBuf mb, int negate, const char * f, size_t fn,
+ const char * g, size_t gn)
+{
+ char *buf = NULL;
+ char *b = NULL, *be;
+ int c;
+
+ if (g != NULL) {
+ (void) expandThis(mb, g, gn, &buf);
+ } else {
+ buf = xmalloc(MACROBUFSIZ + fn + gn);
+ buf[0] = '\0';
+ }
+ if (STREQ("basename", f, fn)) {
+ if ((b = strrchr(buf, '/')) == NULL)
+ b = buf;
+ else
+ b++;
+#if NOTYET
+ /* XXX watchout for conflict with %dir */
+ } else if (STREQ("dirname", f, fn)) {
+ if ((b = strrchr(buf, '/')) != NULL)
+ *b = '\0';
+ b = buf;
+#endif
+ } else if (STREQ("suffix", f, fn)) {
+ if ((b = strrchr(buf, '.')) != NULL)
+ b++;
+ } else if (STREQ("expand", f, fn)) {
+ b = buf;
+ } else if (STREQ("verbose", f, fn)) {
+ if (negate)
+ b = (rpmIsVerbose() ? NULL : buf);
+ else
+ b = (rpmIsVerbose() ? buf : NULL);
+ } else if (STREQ("url2path", f, fn) || STREQ("u2p", f, fn)) {
+ (void)urlPath(buf, (const char **)&b);
+ if (*b == '\0') b = "/";
+ } else if (STREQ("uncompress", f, fn)) {
+ rpmCompressedMagic compressed = COMPRESSED_OTHER;
+ for (b = buf; (c = *b) && isblank(c);)
+ b++;
+ for (be = b; (c = *be) && !isblank(c);)
+ be++;
+ *be++ = '\0';
+ (void) rpmFileIsCompressed(b, &compressed);
+ switch(compressed) {
+ default:
+ case COMPRESSED_NOT:
+ sprintf(be, "%%__cat %s", b);
+ break;
+ case COMPRESSED_OTHER:
+ sprintf(be, "%%__gzip -dc %s", b);
+ break;
+ case COMPRESSED_BZIP2:
+ sprintf(be, "%%__bzip2 -dc %s", b);
+ break;
+ case COMPRESSED_ZIP:
+ sprintf(be, "%%__unzip %s", b);
+ break;
+ case COMPRESSED_LZMA:
+ case COMPRESSED_XZ:
+ sprintf(be, "%%__xz -dc %s", b);
+ break;
+ case COMPRESSED_LZIP:
+ sprintf(be, "%%__lzip -dc %s", b);
+ break;
+ case COMPRESSED_LRZIP:
+ sprintf(be, "%%__lrzip -dqo- %s", b);
+ break;
+ }
+ b = be;
+ } else if (STREQ("getenv", f, fn)) {
+ b = getenv(buf);
+ } else if (STREQ("getconfdir", f, fn)) {
+ sprintf(buf, "%s", rpmConfigDir());
+ b = buf;
+ } else if (STREQ("S", f, fn)) {
+ for (b = buf; (c = *b) && risdigit(c);)
+ b++;
+ if (!c) { /* digit index */
+ b++;
+ sprintf(b, "%%SOURCE%s", buf);
+ } else
+ b = buf;
+ } else if (STREQ("P", f, fn)) {
+ for (b = buf; (c = *b) && risdigit(c);)
+ b++;
+ if (!c) { /* digit index */
+ b++;
+ sprintf(b, "%%PATCH%s", buf);
+ } else
+ b = buf;
+ } else if (STREQ("F", f, fn)) {
+ b = buf + strlen(buf) + 1;
+ sprintf(b, "file%s.file", buf);
+ }
+
+ if (b) {
+ (void) expandMacro(mb, b, 0);
+ }
+ free(buf);
+}
+
+/**
+ * The main macro recursion loop.
+ * @param mb macro expansion state
+ * @param src string to expand
+ * @return 0 on success, 1 on failure
+ */
+static int
+expandMacro(MacroBuf mb, const char *src, size_t slen)
+{
+ rpmMacroEntry *mep;
+ rpmMacroEntry me;
+ const char *s = src, *se;
+ const char *f, *fe;
+ const char *g, *ge;
+ size_t fn, gn, tpos;
+ int c;
+ int rc = 0;
+ int negate;
+ const char * lastc;
+ int chkexist;
+ char *source = NULL;
+
+ /* Handle non-terminated substrings by creating a terminated copy */
+ if (!slen)
+ slen = strlen(src);
+ source = xmalloc(slen + 1);
+ strncpy(source, src, slen);
+ source[slen] = '\0';
+ s = source;
+
+ if (mb->buf == NULL) {
+ size_t blen = MACROBUFSIZ + strlen(s);
+ mb->buf = xcalloc(blen + 1, sizeof(*mb->buf));
+ mb->tpos = 0;
+ mb->nb = blen;
+ }
+ tpos = mb->tpos; /* save expansion pointer for printExpand */
+
+ if (++mb->depth > max_macro_depth) {
+ rpmlog(RPMLOG_ERR,
+ _("Too many levels of recursion in macro expansion. It is likely caused by recursive macro declaration.\n"));
+ mb->depth--;
+ mb->expand_trace = 1;
+ _free(source);
+ return 1;
+ }
+
+ while (rc == 0 && (c = *s) != '\0') {
+ s++;
+ /* Copy text until next macro */
+ switch(c) {
+ case '%':
+ if (*s) { /* Ensure not end-of-string. */
+ if (*s != '%')
+ break;
+ s++; /* skip first % in %% */
+ }
+ default:
+ mbAppend(mb, c);
+ continue;
+ break;
+ }
+
+ /* Expand next macro */
+ f = fe = NULL;
+ g = ge = NULL;
+ if (mb->depth > 1) /* XXX full expansion for outermost level */
+ tpos = mb->tpos; /* save expansion pointer for printExpand */
+ negate = 0;
+ lastc = NULL;
+ chkexist = 0;
+ switch ((c = *s)) {
+ default: /* %name substitution */
+ while (strchr("!?", *s) != NULL) {
+ switch(*s++) {
+ case '!':
+ negate = ((negate + 1) % 2);
+ break;
+ case '?':
+ chkexist++;
+ break;
+ }
+ }
+ f = se = s;
+ if (*se == '-')
+ se++;
+ while((c = *se) && (risalnum(c) || c == '_'))
+ se++;
+ /* Recognize non-alnum macros too */
+ switch (*se) {
+ case '*':
+ se++;
+ if (*se == '*') se++;
+ break;
+ case '#':
+ se++;
+ break;
+ default:
+ break;
+ }
+ fe = se;
+ /* For "%name " macros ... */
+ if ((c = *fe) && isblank(c))
+ if ((lastc = strchr(fe,'\n')) == NULL)
+ lastc = strchr(fe, '\0');
+ break;
+ case '(': /* %(...) shell escape */
+ if ((se = matchchar(s, c, ')')) == NULL) {
+ rpmlog(RPMLOG_ERR,
+ _("Unterminated %c: %s\n"), (char)c, s);
+ rc = 1;
+ continue;
+ }
+ if (mb->macro_trace)
+ printMacro(mb, s, se+1);
+
+ s++; /* skip ( */
+ rc = doShellEscape(mb, s, (se - s));
+ se++; /* skip ) */
+
+ s = se;
+ continue;
+ break;
+ case '{': /* %{...}/%{...:...} substitution */
+ if ((se = matchchar(s, c, '}')) == NULL) {
+ rpmlog(RPMLOG_ERR,
+ _("Unterminated %c: %s\n"), (char)c, s);
+ rc = 1;
+ continue;
+ }
+ f = s+1;/* skip { */
+ se++; /* skip } */
+ while (strchr("!?", *f) != NULL) {
+ switch(*f++) {
+ case '!':
+ negate = ((negate + 1) % 2);
+ break;
+ case '?':
+ chkexist++;
+ break;
+ }
+ }
+ for (fe = f; (c = *fe) && !strchr(" :}", c);)
+ fe++;
+ switch (c) {
+ case ':':
+ g = fe + 1;
+ ge = se - 1;
+ break;
+ case ' ':
+ lastc = se-1;
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+
+ /* XXX Everything below expects fe > f */
+ fn = (fe - f);
+ gn = (ge - g);
+ if ((fe - f) <= 0) {
+/* XXX Process % in unknown context */
+ c = '%'; /* XXX only need to save % */
+ mbAppend(mb, c);
+#if 0
+ rpmlog(RPMLOG_ERR,
+ _("A %% is followed by an unparseable macro\n"));
+#endif
+ s = se;
+ continue;
+ }
+
+ if (mb->macro_trace)
+ printMacro(mb, s, se);
+
+ /* Expand builtin macros */
+ if (STREQ("global", f, fn)) {
+ s = doDefine(mb, se, RMIL_GLOBAL, 1);
+ continue;
+ }
+ if (STREQ("define", f, fn)) {
+ s = doDefine(mb, se, mb->depth, 0);
+ continue;
+ }
+ if (STREQ("undefine", f, fn)) {
+ s = doUndefine(mb->mc, se);
+ continue;
+ }
+
+ if (STREQ("echo", f, fn) ||
+ STREQ("warn", f, fn) ||
+ STREQ("error", f, fn)) {
+ int waserror = 0;
+ if (STREQ("error", f, fn))
+ waserror = 1;
+ if (g != NULL && g < ge)
+ doOutput(mb, waserror, g, gn);
+ else
+ doOutput(mb, waserror, f, fn);
+ s = se;
+ continue;
+ }
+
+ if (STREQ("trace", f, fn)) {
+ /* XXX TODO restore expand_trace/macro_trace to 0 on return */
+ mb->expand_trace = mb->macro_trace = (negate ? 0 : mb->depth);
+ if (mb->depth == 1) {
+ print_macro_trace = mb->macro_trace;
+ print_expand_trace = mb->expand_trace;
+ }
+ s = se;
+ continue;
+ }
+
+ if (STREQ("dump", f, fn)) {
+ rpmDumpMacroTable(mb->mc, NULL);
+ while (iseol(*se))
+ se++;
+ s = se;
+ continue;
+ }
+
+#ifdef WITH_LUA
+ if (STREQ("lua", f, fn)) {
+ rpmlua lua = NULL; /* Global state. */
+ const char *ls = s+sizeof("{lua:")-1;
+ const char *lse = se-sizeof("}")+1;
+ char *scriptbuf = (char *)xmalloc((lse-ls)+1);
+ char *printbuf;
+ memcpy(scriptbuf, ls, lse-ls);
+ scriptbuf[lse-ls] = '\0';
+ rpmluaPushPrintBuffer(lua);
+ if (rpmluaRunScript(lua, scriptbuf, NULL) == -1)
+ rc = 1;
+ printbuf = rpmluaPopPrintBuffer(lua);
+ if (printbuf) {
+ mbAppendStr(mb, printbuf);
+ free(printbuf);
+ }
+ free(scriptbuf);
+ s = se;
+ continue;
+ }
+#endif
+
+ /* XXX necessary but clunky */
+ if (STREQ("basename", f, fn) ||
+ STREQ("suffix", f, fn) ||
+ STREQ("expand", f, fn) ||
+ STREQ("verbose", f, fn) ||
+ STREQ("uncompress", f, fn) ||
+ STREQ("url2path", f, fn) ||
+ STREQ("u2p", f, fn) ||
+ STREQ("getenv", f, fn) ||
+ STREQ("getconfdir", f, fn) ||
+ STREQ("S", f, fn) ||
+ STREQ("P", f, fn) ||
+ STREQ("F", f, fn)) {
+ /* FIX: verbose may be set */
+ doFoo(mb, negate, f, fn, g, gn);
+ s = se;
+ continue;
+ }
+
+ /* Expand defined macros */
+ mep = findEntry(mb->mc, f, fn);
+ me = (mep ? *mep : NULL);
+
+ /* XXX Special processing for flags */
+ if (*f == '-') {
+ if (me)
+ me->used++; /* Mark macro as used */
+ if ((me == NULL && !negate) || /* Without -f, skip %{-f...} */
+ (me != NULL && negate)) { /* With -f, skip %{!-f...} */
+ s = se;
+ continue;
+ }
+
+ if (g && g < ge) { /* Expand X in %{-f:X} */
+ rc = expandMacro(mb, g, gn);
+ } else
+ if (me && me->body && *me->body) {/* Expand %{-f}/%{-f*} */
+ rc = expandMacro(mb, me->body, 0);
+ }
+ s = se;
+ continue;
+ }
+
+ /* XXX Special processing for macro existence */
+ if (chkexist) {
+ if ((me == NULL && !negate) || /* Without -f, skip %{?f...} */
+ (me != NULL && negate)) { /* With -f, skip %{!?f...} */
+ s = se;
+ continue;
+ }
+ if (g && g < ge) { /* Expand X in %{?f:X} */
+ rc = expandMacro(mb, g, gn);
+ } else
+ if (me && me->body && *me->body) { /* Expand %{?f}/%{?f*} */
+ rc = expandMacro(mb, me->body, 0);
+ }
+ s = se;
+ continue;
+ }
+
+ if (me == NULL) { /* leave unknown %... as is */
+ /* XXX hack to permit non-overloaded %foo to be passed */
+ c = '%'; /* XXX only need to save % */
+ mbAppend(mb, c);
+ continue;
+ }
+
+ /* Setup args for "%name " macros with opts */
+ if (me && me->opts != NULL) {
+ if (lastc != NULL) {
+ se = grabArgs(mb, me, fe, lastc);
+ } else {
+ addMacro(mb->mc, "**", NULL, "", mb->depth);
+ addMacro(mb->mc, "*", NULL, "", mb->depth);
+ addMacro(mb->mc, "#", NULL, "0", mb->depth);
+ addMacro(mb->mc, "0", NULL, me->name, mb->depth);
+ }
+ }
+
+ /* Recursively expand body of macro */
+ if (me->body && *me->body) {
+ rc = expandMacro(mb, me->body, 0);
+ if (rc == 0)
+ me->used++; /* Mark macro as used */
+ }
+
+ /* Free args for "%name " macros with opts */
+ if (me->opts != NULL)
+ freeArgs(mb);
+
+ s = se;
+ }
+
+ mb->buf[mb->tpos] = '\0';
+ mb->depth--;
+ if (rc != 0 || mb->expand_trace)
+ printExpansion(mb, mb->buf+tpos, mb->buf+mb->tpos);
+ _free(source);
+ return rc;
+}
+
+
+/* =============================================================== */
+
+static int doExpandMacros(rpmMacroContext mc, const char *src, char **target)
+{
+ MacroBuf mb = xcalloc(1, sizeof(*mb));
+ int rc = 0;
+
+ if (mc == NULL) mc = rpmGlobalMacroContext;
+
+ mb->buf = NULL;
+ mb->depth = 0;
+ mb->macro_trace = print_macro_trace;
+ mb->expand_trace = print_expand_trace;
+ mb->mc = mc;
+
+ rc = expandMacro(mb, src, 0);
+
+ mb->buf[mb->tpos] = '\0'; /* XXX just in case */
+ /* expanded output is usually much less than alloced buffer, downsize */
+ *target = xrealloc(mb->buf, mb->tpos + 1);
+
+ _free(mb);
+ return rc;
+}
+
+int expandMacros(void * spec, rpmMacroContext mc, char * sbuf, size_t slen)
+{
+ char *target = NULL;
+ int rc = doExpandMacros(mc, sbuf, &target);
+ rstrlcpy(sbuf, target, slen);
+ free(target);
+ return rc;
+}
+
+void
+addMacro(rpmMacroContext mc,
+ const char * n, const char * o, const char * b, int level)
+{
+ rpmMacroEntry * mep;
+
+ if (mc == NULL) mc = rpmGlobalMacroContext;
+
+ /* If new name, expand macro table */
+ if ((mep = findEntry(mc, n, 0)) == NULL) {
+ if (mc->firstFree == mc->macrosAllocated)
+ expandMacroTable(mc);
+ if (mc->macroTable != NULL)
+ mep = mc->macroTable + mc->firstFree++;
+ }
+
+ if (mep != NULL) {
+ /* Push macro over previous definition */
+ pushMacro(mep, n, o, b, level);
+
+ /* If new name, sort macro table */
+ if ((*mep)->prev == NULL)
+ sortMacroTable(mc);
+ }
+}
+
+void
+delMacro(rpmMacroContext mc, const char * n)
+{
+ rpmMacroEntry * mep;
+
+ if (mc == NULL) mc = rpmGlobalMacroContext;
+ /* If name exists, pop entry */
+ if ((mep = findEntry(mc, n, 0)) != NULL) {
+ popMacro(mep);
+ /* If deleted name, sort macro table */
+ if (!(mep && *mep))
+ sortMacroTable(mc);
+ }
+}
+
+int
+rpmDefineMacro(rpmMacroContext mc, const char * macro, int level)
+{
+ MacroBuf mb = xcalloc(1, sizeof(*mb));
+
+ /* XXX just enough to get by */
+ mb->mc = (mc ? mc : rpmGlobalMacroContext);
+ (void) doDefine(mb, macro, level, 0);
+ _free(mb);
+ return 0;
+}
+
+void
+rpmLoadMacros(rpmMacroContext mc, int level)
+{
+
+ if (mc == NULL || mc == rpmGlobalMacroContext)
+ return;
+
+ if (mc->macroTable != NULL) {
+ int i;
+ for (i = 0; i < mc->firstFree; i++) {
+ rpmMacroEntry *mep, me;
+ mep = &mc->macroTable[i];
+ me = *mep;
+
+ if (me == NULL) /* XXX this should never happen */
+ continue;
+ addMacro(NULL, me->name, me->opts, me->body, (level - 1));
+ }
+ }
+}
+
+int
+rpmLoadMacroFile(rpmMacroContext mc, const char * fn)
+{
+ FILE *fd = fopen(fn, "r");
+ size_t blen = MACROBUFSIZ;
+ char *buf = xmalloc(blen);
+ int rc = -1;
+
+ if (fd == NULL || ferror(fd)) {
+ if (fd) (void) fclose(fd);
+ goto exit;
+ }
+
+ /* XXX Assume new fangled macro expansion */
+ max_macro_depth = 16;
+
+ buf[0] = '\0';
+ while(rdcl(buf, blen, fd) != NULL) {
+ char c, *n;
+
+ n = buf;
+ SKIPBLANK(n, c);
+
+ if (c != '%')
+ continue;
+ n++; /* skip % */
+ rc = rpmDefineMacro(mc, n, RMIL_MACROFILES);
+ }
+ rc = fclose(fd);
+
+exit:
+ _free(buf);
+ return rc;
+}
+
+void
+rpmInitMacros(rpmMacroContext mc, const char * macrofiles)
+{
+ ARGV_t pattern, globs = NULL;
+
+ if (macrofiles == NULL)
+ return;
+
+ argvSplit(&globs, macrofiles, ":");
+ for (pattern = globs; *pattern; pattern++) {
+ ARGV_t path, files = NULL;
+
+ /* Glob expand the macro file path element, expanding ~ to $HOME. */
+ if (rpmGlob(*pattern, NULL, &files) != 0) {
+ continue;
+ }
+
+ /* Read macros from each file. */
+ for (path = files; *path; path++) {
+ if (rpmFileHasSuffix(*path, ".rpmnew") ||
+ rpmFileHasSuffix(*path, ".rpmsave") ||
+ rpmFileHasSuffix(*path, ".rpmorig")) {
+ continue;
+ }
+ (void) rpmLoadMacroFile(mc, *path);
+ }
+ argvFree(files);
+ }
+ argvFree(globs);
+
+ /* Reload cmdline macros */
+ rpmLoadMacros(rpmCLIMacroContext, RMIL_CMDLINE);
+}
+
+void
+rpmFreeMacros(rpmMacroContext mc)
+{
+
+ if (mc == NULL) mc = rpmGlobalMacroContext;
+
+ if (mc->macroTable != NULL) {
+ int i;
+ for (i = 0; i < mc->firstFree; i++) {
+ rpmMacroEntry me;
+ while ((me = mc->macroTable[i]) != NULL) {
+ /* XXX cast to workaround const */
+ if ((mc->macroTable[i] = me->prev) == NULL)
+ me->name = _free(me->name);
+ me->opts = _free(me->opts);
+ me->body = _free(me->body);
+ me = _free(me);
+ }
+ }
+ mc->macroTable = _free(mc->macroTable);
+ }
+ memset(mc, 0, sizeof(*mc));
+}
+
+char *
+rpmExpand(const char *arg, ...)
+{
+ size_t blen = 0;
+ char *buf = NULL, *ret = NULL;
+ char *pe;
+ const char *s;
+ va_list ap;
+
+ if (arg == NULL) {
+ ret = xstrdup("");
+ goto exit;
+ }
+
+ /* precalculate unexpanded size */
+ va_start(ap, arg);
+ for (s = arg; s != NULL; s = va_arg(ap, const char *))
+ blen += strlen(s);
+ va_end(ap);
+
+ buf = xmalloc(blen + 1);
+ buf[0] = '\0';
+
+ va_start(ap, arg);
+ for (pe = buf, s = arg; s != NULL; s = va_arg(ap, const char *))
+ pe = stpcpy(pe, s);
+ va_end(ap);
+
+ (void) doExpandMacros(NULL, buf, &ret);
+
+ free(buf);
+exit:
+ return ret;
+}
+
+int
+rpmExpandNumeric(const char *arg)
+{
+ char *val;
+ int rc;
+
+ if (arg == NULL)
+ return 0;
+
+ val = rpmExpand(arg, NULL);
+ if (!(val && *val != '%'))
+ rc = 0;
+ else if (*val == 'Y' || *val == 'y')
+ rc = 1;
+ else if (*val == 'N' || *val == 'n')
+ rc = 0;
+ else {
+ char *end;
+ rc = strtol(val, &end, 0);
+ if (!(end && *end == '\0'))
+ rc = 0;
+ }
+ val = _free(val);
+
+ return rc;
+}
diff --git a/rpmio/rpmfileutil.c b/rpmio/rpmfileutil.c
new file mode 100644
index 0000000..3641341
--- /dev/null
+++ b/rpmio/rpmfileutil.c
@@ -0,0 +1,777 @@
+#include "system.h"
+
+#if HAVE_GELF_H
+
+#include <gelf.h>
+
+#if !defined(DT_GNU_PRELINKED)
+#define DT_GNU_PRELINKED 0x6ffffdf5
+#endif
+#if !defined(DT_GNU_LIBLIST)
+#define DT_GNU_LIBLIST 0x6ffffef9
+#endif
+
+#endif
+
+#if defined(HAVE_MMAP)
+#include <sys/mman.h>
+#endif
+
+#include <sys/wait.h>
+#include <errno.h>
+#include <popt.h>
+#include <ctype.h>
+
+#include <rpm/rpmfileutil.h>
+#include <rpm/rpmurl.h>
+#include <rpm/rpmmacro.h>
+#include <rpm/rpmlog.h>
+#include <rpm/argv.h>
+
+#include "rpmio/rpmio_internal.h"
+
+#include "debug.h"
+
+static const char *rpm_config_dir = NULL;
+
+static int open_dso(const char * path, pid_t * pidp, rpm_loff_t *fsizep)
+{
+ static const char * cmd = NULL;
+ static int initted = 0;
+ int fdno;
+
+ if (!initted) {
+ cmd = rpmExpand("%{?__prelink_undo_cmd}", NULL);
+ initted++;
+ }
+
+ if (pidp) *pidp = 0;
+
+ if (fsizep) {
+ struct stat sb, * st = &sb;
+ if (stat(path, st) < 0)
+ return -1;
+ *fsizep = st->st_size;
+ }
+
+ fdno = open(path, O_RDONLY);
+ if (fdno < 0)
+ return fdno;
+
+ if (!(cmd && *cmd))
+ return fdno;
+
+#if HAVE_GELF_H && HAVE_LIBELF
+ { Elf *elf = NULL;
+ Elf_Scn *scn = NULL;
+ Elf_Data *data = NULL;
+ GElf_Ehdr ehdr;
+ GElf_Shdr shdr;
+ GElf_Dyn dyn;
+ int bingo;
+
+ (void) elf_version(EV_CURRENT);
+
+ if ((elf = elf_begin (fdno, ELF_C_READ, NULL)) == NULL
+ || elf_kind(elf) != ELF_K_ELF
+ || gelf_getehdr(elf, &ehdr) == NULL
+ || !(ehdr.e_type == ET_DYN || ehdr.e_type == ET_EXEC))
+ goto exit;
+
+ bingo = 0;
+ while (!bingo && (scn = elf_nextscn(elf, scn)) != NULL) {
+ (void) gelf_getshdr(scn, &shdr);
+ if (shdr.sh_type != SHT_DYNAMIC)
+ continue;
+ while (!bingo && (data = elf_getdata (scn, data)) != NULL) {
+ int maxndx = data->d_size / shdr.sh_entsize;
+ int ndx;
+
+ for (ndx = 0; ndx < maxndx; ++ndx) {
+ (void) gelf_getdyn (data, ndx, &dyn);
+ if (!(dyn.d_tag == DT_GNU_PRELINKED || dyn.d_tag == DT_GNU_LIBLIST))
+ continue;
+ bingo = 1;
+ break;
+ }
+ }
+ }
+
+ if (pidp != NULL && bingo) {
+ int pipes[2];
+ pid_t pid;
+ int xx;
+
+ xx = close(fdno);
+ pipes[0] = pipes[1] = -1;
+ xx = pipe(pipes);
+ if (!(pid = fork())) {
+ ARGV_t av, lib;
+ argvSplit(&av, cmd, " ");
+
+ xx = close(pipes[0]);
+ xx = dup2(pipes[1], STDOUT_FILENO);
+ xx = close(pipes[1]);
+ if ((lib = argvSearch(av, "library", NULL)) != NULL) {
+ *lib = (char *) path;
+ unsetenv("MALLOC_CHECK_");
+ xx = execve(av[0], av+1, environ);
+ }
+ _exit(127);
+ }
+ *pidp = pid;
+ fdno = pipes[0];
+ xx = close(pipes[1]);
+ }
+
+exit:
+ if (elf) (void) elf_end(elf);
+ }
+#endif
+
+ return fdno;
+}
+
+int rpmDoDigest(int algo, const char * fn,int asAscii,
+ unsigned char * digest, rpm_loff_t * fsizep)
+{
+ const char * path;
+ urltype ut = urlPath(fn, &path);
+ unsigned char * dig = NULL;
+ size_t diglen;
+ unsigned char buf[32*BUFSIZ];
+ FD_t fd;
+ rpm_loff_t fsize = 0;
+ pid_t pid = 0;
+ int rc = 0;
+ int fdno;
+
+ fdno = open_dso(path, &pid, &fsize);
+ if (fdno < 0) {
+ rc = 1;
+ goto exit;
+ }
+
+ /* file to large (32 MB), do not mmap file */
+ if (fsize > (size_t) 32*1024*1024)
+ if (ut == URL_IS_PATH || ut == URL_IS_UNKNOWN)
+ ut = URL_IS_DASH; /* force fd io */
+
+ switch(ut) {
+ case URL_IS_PATH:
+ case URL_IS_UNKNOWN:
+#ifdef HAVE_MMAP
+ if (pid == 0) {
+ int xx;
+ DIGEST_CTX ctx;
+ void * mapped;
+
+ if (fsize) {
+ mapped = mmap(NULL, fsize, PROT_READ, MAP_SHARED, fdno, 0);
+ if (mapped == MAP_FAILED) {
+ xx = close(fdno);
+ rc = 1;
+ break;
+ }
+
+#ifdef MADV_SEQUENTIAL
+ xx = madvise(mapped, fsize, MADV_SEQUENTIAL);
+#endif
+ }
+
+ ctx = rpmDigestInit(algo, RPMDIGEST_NONE);
+ if (fsize)
+ xx = rpmDigestUpdate(ctx, mapped, fsize);
+ xx = rpmDigestFinal(ctx, (void **)&dig, &diglen, asAscii);
+ if (fsize)
+ xx = munmap(mapped, fsize);
+ xx = close(fdno);
+ break;
+ }
+#endif
+ case URL_IS_HTTPS:
+ case URL_IS_HTTP:
+ case URL_IS_FTP:
+ case URL_IS_HKP:
+ case URL_IS_DASH:
+ default:
+ /* Either use the pipe to prelink -y or open the URL. */
+ fd = (pid != 0) ? fdDup(fdno) : Fopen(fn, "r.ufdio");
+ (void) close(fdno);
+ if (fd == NULL || Ferror(fd)) {
+ rc = 1;
+ if (fd != NULL)
+ (void) Fclose(fd);
+ break;
+ }
+
+ fdInitDigest(fd, algo, 0);
+ fsize = 0;
+ while ((rc = Fread(buf, sizeof(buf[0]), sizeof(buf), fd)) > 0)
+ fsize += rc;
+ fdFiniDigest(fd, algo, (void **)&dig, &diglen, asAscii);
+ if (Ferror(fd))
+ rc = 1;
+
+ (void) Fclose(fd);
+ break;
+ }
+
+ /* Reap the prelink -y helper. */
+ if (pid) {
+ int status;
+ (void) waitpid(pid, &status, 0);
+ if (!WIFEXITED(status) || WEXITSTATUS(status))
+ rc = 1;
+ }
+
+exit:
+ if (fsizep)
+ *fsizep = fsize;
+ if (!rc)
+ memcpy(digest, dig, diglen);
+ dig = _free(dig);
+
+ return rc;
+}
+
+FD_t rpmMkTemp(char *templ)
+{
+ int sfd;
+ FD_t tfd = NULL;
+
+ sfd = mkstemp(templ);
+ if (sfd < 0) {
+ goto exit;
+ }
+
+ tfd = fdDup(sfd);
+ close(sfd);
+
+exit:
+ return tfd;
+}
+
+FD_t rpmMkTempFile(const char * prefix, char **fn)
+{
+ const char *tpmacro = "%{_tmppath}"; /* always set from rpmrc */
+ char *tempfn;
+ static int _initialized = 0;
+ FD_t tfd = NULL;
+
+ if (!prefix) prefix = "";
+
+ /* Create the temp directory if it doesn't already exist. */
+ if (!_initialized) {
+ _initialized = 1;
+ tempfn = rpmGenPath(prefix, tpmacro, NULL);
+ if (rpmioMkpath(tempfn, 0755, (uid_t) -1, (gid_t) -1))
+ goto exit;
+ free(tempfn);
+ }
+
+ tempfn = rpmGetPath(prefix, tpmacro, "/rpm-tmp.XXXXXX", NULL);
+ tfd = rpmMkTemp(tempfn);
+
+ if (tfd == NULL || Ferror(tfd)) {
+ rpmlog(RPMLOG_ERR, _("error creating temporary file %s: %m\n"), tempfn);
+ goto exit;
+ }
+
+exit:
+ if (tfd != NULL && fn)
+ *fn = tempfn;
+ else
+ free(tempfn);
+
+ return tfd;
+}
+
+int rpmioMkpath(const char * path, mode_t mode, uid_t uid, gid_t gid)
+{
+ char *d, *de;
+ int rc;
+
+ if (path == NULL || *path == '\0')
+ return -1;
+ d = rstrcat(NULL, path);
+ if (d[strlen(d)-1] != '/') {
+ rstrcat(&d,"/");
+ }
+ de = d;
+ for (;(de=strchr(de+1,'/'));) {
+ struct stat st;
+ *de = '\0';
+ rc = stat(d, &st);
+ if (rc) {
+ if (errno != ENOENT)
+ goto exit;
+ rc = mkdir(d, mode);
+ if (rc)
+ goto exit;
+ rpmlog(RPMLOG_DEBUG, "created directory(s) %s mode 0%o\n", path, mode);
+ if (!(uid == (uid_t) -1 && gid == (gid_t) -1)) {
+ rc = chown(d, uid, gid);
+ if (rc)
+ goto exit;
+ }
+ } else if (!S_ISDIR(st.st_mode)) {
+ rc = ENOTDIR;
+ goto exit;
+ }
+ *de = '/';
+ }
+ rc = 0;
+exit:
+ free(d);
+ return rc;
+}
+
+int rpmFileIsCompressed(const char * file, rpmCompressedMagic * compressed)
+{
+ FD_t fd;
+ ssize_t nb;
+ int rc = -1;
+ unsigned char magic[13];
+
+ *compressed = COMPRESSED_NOT;
+
+ fd = Fopen(file, "r.ufdio");
+ if (fd == NULL || Ferror(fd)) {
+ /* XXX Fstrerror */
+ rpmlog(RPMLOG_ERR, _("File %s: %s\n"), file, Fstrerror(fd));
+ if (fd) (void) Fclose(fd);
+ return 1;
+ }
+ nb = Fread(magic, sizeof(magic[0]), sizeof(magic), fd);
+ if (nb < 0) {
+ rpmlog(RPMLOG_ERR, _("File %s: %s\n"), file, Fstrerror(fd));
+ rc = 1;
+ } else if (nb < sizeof(magic)) {
+ rpmlog(RPMLOG_ERR, _("File %s is smaller than %u bytes\n"),
+ file, (unsigned)sizeof(magic));
+ rc = 0;
+ }
+ (void) Fclose(fd);
+ if (rc >= 0)
+ return rc;
+
+ rc = 0;
+
+ if ((magic[0] == 'B') && (magic[1] == 'Z')) {
+ *compressed = COMPRESSED_BZIP2;
+ } else if ((magic[0] == 'P') && (magic[1] == 'K') &&
+ (((magic[2] == 3) && (magic[3] == 4)) ||
+ ((magic[2] == '0') && (magic[3] == '0')))) { /* pkzip */
+ *compressed = COMPRESSED_ZIP;
+ } else if ((magic[0] == 0xfd) && (magic[1] == 0x37) &&
+ (magic[2] == 0x7a) && (magic[3] == 0x58) &&
+ (magic[4] == 0x5a) && (magic[5] == 0x00)) {
+ /* new style xz (lzma) with magic */
+ *compressed = COMPRESSED_XZ;
+ } else if ((magic[0] == 'L') && (magic[1] == 'Z') &&
+ (magic[2] == 'I') && (magic[3] == 'P')) {
+ *compressed = COMPRESSED_LZIP;
+ } else if ((magic[0] == 'L') && (magic[1] == 'R') &&
+ (magic[2] == 'Z') && (magic[3] == 'I')) {
+ *compressed = COMPRESSED_LRZIP;
+ } else if (((magic[0] == 0037) && (magic[1] == 0213)) || /* gzip */
+ ((magic[0] == 0037) && (magic[1] == 0236)) || /* old gzip */
+ ((magic[0] == 0037) && (magic[1] == 0036)) || /* pack */
+ ((magic[0] == 0037) && (magic[1] == 0240)) || /* SCO lzh */
+ ((magic[0] == 0037) && (magic[1] == 0235)) /* compress */
+ ) {
+ *compressed = COMPRESSED_OTHER;
+ } else if (rpmFileHasSuffix(file, ".lzma")) {
+ *compressed = COMPRESSED_LZMA;
+ }
+
+ return rc;
+}
+
+/* @todo "../sbin/./../bin/" not correct. */
+char *rpmCleanPath(char * path)
+{
+ const char *s;
+ char *se, *t, *te;
+ int begin = 1;
+
+ if (path == NULL)
+ return NULL;
+
+/*fprintf(stderr, "*** RCP %s ->\n", path); */
+ s = t = te = path;
+ while (*s != '\0') {
+/*fprintf(stderr, "*** got \"%.*s\"\trest \"%s\"\n", (t-path), path, s); */
+ switch(*s) {
+ case ':': /* handle url's */
+ if (s[1] == '/' && s[2] == '/') {
+ *t++ = *s++;
+ *t++ = *s++;
+ break;
+ }
+ begin=1;
+ break;
+ case '/':
+ /* Move parent dir forward */
+ for (se = te + 1; se < t && *se != '/'; se++)
+ {};
+ if (se < t && *se == '/') {
+ te = se;
+/*fprintf(stderr, "*** next pdir \"%.*s\"\n", (te-path), path); */
+ }
+ while (s[1] == '/')
+ s++;
+ while (t > path && t[-1] == '/')
+ t--;
+ break;
+ case '.':
+ /* Leading .. is special */
+ /* Check that it is ../, so that we don't interpret */
+ /* ..?(i.e. "...") or ..* (i.e. "..bogus") as "..". */
+ /* in the case of "...", this ends up being processed*/
+ /* as "../.", and the last '.' is stripped. This */
+ /* would not be correct processing. */
+ if (begin && s[1] == '.' && (s[2] == '/' || s[2] == '\0')) {
+/*fprintf(stderr, " leading \"..\"\n"); */
+ *t++ = *s++;
+ break;
+ }
+ /* Single . is special */
+ if (begin && s[1] == '\0') {
+ break;
+ }
+ /* Handle the ./ cases */
+ if (t > path && t[-1] == '/') {
+ /* Trim embedded ./ */
+ if (s[1] == '/') {
+ s+=2;
+ continue;
+ }
+ /* Trim trailing /. */
+ if (s[1] == '\0') {
+ s++;
+ continue;
+ }
+ }
+ /* Trim embedded /../ and trailing /.. */
+ if (!begin && t > path && t[-1] == '/' && s[1] == '.' && (s[2] == '/' || s[2] == '\0')) {
+ t = te;
+ /* Move parent dir forward */
+ if (te > path)
+ for (--te; te > path && *te != '/'; te--)
+ {};
+/*fprintf(stderr, "*** prev pdir \"%.*s\"\n", (te-path), path); */
+ s++;
+ s++;
+ continue;
+ }
+ break;
+ default:
+ begin = 0;
+ break;
+ }
+ *t++ = *s++;
+ }
+
+ /* Trim trailing / (but leave single / alone) */
+ if (t > &path[1] && t[-1] == '/')
+ t--;
+ *t = '\0';
+
+/*fprintf(stderr, "\t%s\n", path); */
+ return path;
+}
+
+/* Merge 3 args into path, any or all of which may be a url. */
+
+char * rpmGenPath(const char * urlroot, const char * urlmdir,
+ const char *urlfile)
+{
+ char * xroot = rpmGetPath(urlroot, NULL);
+ const char * root = xroot;
+ char * xmdir = rpmGetPath(urlmdir, NULL);
+ const char * mdir = xmdir;
+ char * xfile = rpmGetPath(urlfile, NULL);
+ const char * file = xfile;
+ char * result;
+ char * url = NULL;
+ int nurl = 0;
+ int ut;
+
+ ut = urlPath(xroot, &root);
+ if (url == NULL && ut > URL_IS_DASH) {
+ url = xroot;
+ nurl = root - xroot;
+ }
+ if (root == NULL || *root == '\0') root = "/";
+
+ ut = urlPath(xmdir, &mdir);
+ if (url == NULL && ut > URL_IS_DASH) {
+ url = xmdir;
+ nurl = mdir - xmdir;
+ }
+ if (mdir == NULL || *mdir == '\0') mdir = "/";
+
+ ut = urlPath(xfile, &file);
+ if (url == NULL && ut > URL_IS_DASH) {
+ url = xfile;
+ nurl = file - xfile;
+ }
+
+ if (url && nurl > 0) {
+ char *t = rstrcat(NULL, url);
+ t[nurl] = '\0';
+ url = t;
+ } else
+ url = xstrdup("");
+
+ result = rpmGetPath(url, root, "/", mdir, "/", file, NULL);
+
+ xroot = _free(xroot);
+ xmdir = _free(xmdir);
+ xfile = _free(xfile);
+ free(url);
+ return result;
+}
+
+/* Return concatenated and expanded canonical path. */
+
+char * rpmGetPath(const char *path, ...)
+{
+ va_list ap;
+ char *dest = NULL, *res;
+ const char *s;
+
+ if (path == NULL)
+ return xstrdup("");
+
+ va_start(ap, path);
+ for (s = path; s; s = va_arg(ap, const char *)) {
+ rstrcat(&dest, s);
+ }
+ va_end(ap);
+
+ res = rpmExpand(dest, NULL);
+ free(dest);
+
+ return rpmCleanPath(res);
+}
+
+int rpmGlob(const char * patterns, int * argcPtr, ARGV_t * argvPtr)
+{
+ int ac = 0;
+ const char ** av = NULL;
+ int argc = 0;
+ ARGV_t argv = NULL;
+ char * globRoot = NULL;
+ const char *home = getenv("HOME");
+ int gflags = 0;
+#ifdef ENABLE_NLS
+ char * old_collate = NULL;
+ char * old_ctype = NULL;
+ const char * t;
+#endif
+ size_t maxb, nb;
+ int i, j;
+ int rc;
+
+ if (home != NULL && strlen(home) > 0)
+ gflags |= GLOB_TILDE;
+
+ /* Can't use argvSplit() here, it doesn't handle whitespace etc escapes */
+ rc = poptParseArgvString(patterns, &ac, &av);
+ if (rc)
+ return rc;
+
+#ifdef ENABLE_NLS
+ t = setlocale(LC_COLLATE, NULL);
+ if (t)
+ old_collate = xstrdup(t);
+ t = setlocale(LC_CTYPE, NULL);
+ if (t)
+ old_ctype = xstrdup(t);
+ (void) setlocale(LC_COLLATE, "C");
+ (void) setlocale(LC_CTYPE, "C");
+#endif
+
+ if (av != NULL)
+ for (j = 0; j < ac; j++) {
+ char * globURL;
+ const char * path;
+ int ut = urlPath(av[j], &path);
+ int local = (ut == URL_IS_PATH) || (ut == URL_IS_UNKNOWN);
+ size_t plen = strlen(path);
+ int flags = gflags;
+ int dir_only = (plen > 0 && path[plen-1] == '/');
+ glob_t gl;
+
+ if (!local || (!glob_pattern_p(av[j], 0) && strchr(path, '~') == NULL)) {
+ argvAdd(&argv, av[j]);
+ continue;
+ }
+
+#ifdef GLOB_ONLYDIR
+ if (dir_only)
+ flags |= GLOB_ONLYDIR;
+#endif
+
+ gl.gl_pathc = 0;
+ gl.gl_pathv = NULL;
+
+ rc = glob(av[j], flags, NULL, &gl);
+ if (rc)
+ goto exit;
+
+ /* XXX Prepend the URL leader for globs that have stripped it off */
+ maxb = 0;
+ for (i = 0; i < gl.gl_pathc; i++) {
+ if ((nb = strlen(&(gl.gl_pathv[i][0]))) > maxb)
+ maxb = nb;
+ }
+
+ nb = ((ut == URL_IS_PATH) ? (path - av[j]) : 0);
+ maxb += nb;
+ maxb += 1;
+ globURL = globRoot = xmalloc(maxb);
+
+ switch (ut) {
+ case URL_IS_PATH:
+ case URL_IS_DASH:
+ strncpy(globRoot, av[j], nb);
+ break;
+ case URL_IS_HTTPS:
+ case URL_IS_HTTP:
+ case URL_IS_FTP:
+ case URL_IS_HKP:
+ case URL_IS_UNKNOWN:
+ default:
+ break;
+ }
+ globRoot += nb;
+ *globRoot = '\0';
+
+ for (i = 0; i < gl.gl_pathc; i++) {
+ const char * globFile = &(gl.gl_pathv[i][0]);
+
+ if (dir_only) {
+ struct stat sb;
+ if (lstat(gl.gl_pathv[i], &sb) || !S_ISDIR(sb.st_mode))
+ continue;
+ }
+
+ if (globRoot > globURL && globRoot[-1] == '/')
+ while (*globFile == '/') globFile++;
+ strcpy(globRoot, globFile);
+ argvAdd(&argv, globURL);
+ }
+ globfree(&gl);
+ globURL = _free(globURL);
+ }
+
+ argc = argvCount(argv);
+ if (argc > 0) {
+ if (argvPtr)
+ *argvPtr = argv;
+ if (argcPtr)
+ *argcPtr = argc;
+ rc = 0;
+ } else
+ rc = 1;
+
+
+exit:
+#ifdef ENABLE_NLS
+ if (old_collate) {
+ (void) setlocale(LC_COLLATE, old_collate);
+ old_collate = _free(old_collate);
+ }
+ if (old_ctype) {
+ (void) setlocale(LC_CTYPE, old_ctype);
+ old_ctype = _free(old_ctype);
+ }
+#endif
+ av = _free(av);
+ if (rc || argvPtr == NULL) {
+ argvFree(argv);
+ }
+ return rc;
+}
+
+char * rpmEscapeSpaces(const char * s)
+{
+ const char * se;
+ char * t;
+ char * te;
+ size_t nb = 0;
+
+ for (se = s; *se; se++) {
+ if (isspace(*se))
+ nb++;
+ nb++;
+ }
+ nb++;
+
+ t = te = xmalloc(nb);
+ for (se = s; *se; se++) {
+ if (isspace(*se))
+ *te++ = '\\';
+ *te++ = *se;
+ }
+ *te = '\0';
+ return t;
+}
+
+int rpmFileHasSuffix(const char *path, const char *suffix)
+{
+ size_t plen = strlen(path);
+ size_t slen = strlen(suffix);
+ return (plen >= slen && rstreq(path+plen-slen, suffix));
+}
+
+char * rpmGetCwd(void)
+{
+ int currDirLen = 0;
+ char * currDir = NULL;
+
+ do {
+ currDirLen += 128;
+ currDir = xrealloc(currDir, currDirLen);
+ memset(currDir, 0, currDirLen);
+ } while (getcwd(currDir, currDirLen) == NULL && errno == ERANGE);
+
+ return currDir;
+}
+
+int rpmMkdirs(const char *root, const char *pathstr)
+{
+ ARGV_t dirs = NULL;
+ int rc = 0;
+ argvSplit(&dirs, pathstr, ":");
+
+ for (char **d = dirs; *d; d++) {
+ char *path = rpmGetPath(root ? root : "", *d, NULL);
+ if ((rc = rpmioMkpath(path, 0755, -1, -1)) != 0) {
+ const char *msg = _("failed to create directory");
+ /* try to be more informative if the failing part was a macro */
+ if (**d == '%') {
+ rpmlog(RPMLOG_ERR, "%s %s: %s: %m\n", msg, *d, path);
+ } else {
+ rpmlog(RPMLOG_ERR, "%s %s: %m\n", msg, path);
+ }
+ }
+ free(path);
+ if (rc) break;
+ }
+ argvFree(dirs);
+ return rc;
+}
+
+const char *rpmConfigDir(void)
+{
+ if (rpm_config_dir == NULL) {
+ char *rpmenv = getenv("RPM_CONFIGDIR");
+ rpm_config_dir = rpmenv ? xstrdup(rpmenv) : RPMCONFIGDIR;
+ }
+ return rpm_config_dir;
+}
diff --git a/rpmio/rpmfileutil.h b/rpmio/rpmfileutil.h
new file mode 100644
index 0000000..28c6d8a
--- /dev/null
+++ b/rpmio/rpmfileutil.h
@@ -0,0 +1,151 @@
+#ifndef _RPMFILEUTIL_H
+#define _RPMFILEUTIL_H
+
+/** \ingroup rpmfileutil rpmio
+ * \file rpmio/rpmfileutil.h
+ * File and path manipulation helper functions.
+ */
+
+#include <rpm/rpmutil.h>
+#include <rpm/rpmio.h>
+#include <rpm/rpmpgp.h>
+#include <rpm/argv.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** \ingroup rpmfileutil
+ */
+typedef enum rpmCompressedMagic_e {
+ COMPRESSED_NOT = 0, /*!< not compressed */
+ COMPRESSED_OTHER = 1, /*!< gzip can handle */
+ COMPRESSED_BZIP2 = 2, /*!< bzip2 can handle */
+ COMPRESSED_ZIP = 3, /*!< unzip can handle */
+ COMPRESSED_LZMA = 4, /*!< lzma can handle */
+ COMPRESSED_XZ = 5, /*!< xz can handle */
+ COMPRESSED_LZIP = 6, /*!< lzip can handle */
+ COMPRESSED_LRZIP = 7 /*!< lrzip can handle */
+} rpmCompressedMagic;
+
+/** \ingroup rpmfileutil
+ * Calculate a file digest and size.
+ * @param algo digest algorithm
+ * @param fn file name
+ * @param asAscii return digest as ascii string?
+ * @retval digest address of calculated digest
+ * @retval *fsizep file size pointer (or NULL)
+ * @return 0 on success, 1 on error
+ */
+int rpmDoDigest(int algo, const char * fn,int asAscii,
+ unsigned char * digest, rpm_loff_t * fsizep);
+
+/** \ingroup rpmfileutil
+ * Thin wrapper for mkstemp(3).
+ * @param templ template for temporary filename
+ * @return file handle or NULL on error
+ */
+FD_t rpmMkTemp(char *templ);
+
+/** \ingroup rpmfileutil
+ * Return file handle for a temporaray file.
+ * A unique temporaray file path will be created in
+ * [prefix/]%{_tmppath} directory.
+ * The file name and the open file handle are returned.
+ *
+ * @param prefix leading part of temp file path
+ * @retval fn temp file name (or NULL)
+ * @return fdptr open file handle or NULL on error
+ */
+FD_t rpmMkTempFile(const char * prefix, char **fn);
+
+/** \ingroup rpmfileutil
+ * Insure that directories in path exist, creating as needed.
+ * @param path directory path
+ * @param mode directory mode (if created)
+ * @param uid directory uid (if created), or -1 to skip
+ * @param gid directory uid (if created), or -1 to skip
+ * @return 0 on success, errno (or -1) on error
+ */
+int rpmioMkpath(const char * path, mode_t mode, uid_t uid, gid_t gid);
+
+/** \ingroup rpmfileutil
+ * Create several directories (including parents if needed) in one go.
+ * Macros in pathstr will be expanded in the process.
+ * @param root leading root directory (or NULL for none)
+ * @param pathstr list of directories separated with :
+ * @return 0 if all directories were successfully created
+ * (or already existed), non-zero otherwise
+ */
+int rpmMkdirs(const char *root, const char *pathstr);
+
+/** \ingroup rpmfileutil
+ * Canonicalize file path.
+ * @param path path to canonicalize (in-place)
+ * @return pointer to path
+ */
+char * rpmCleanPath (char * path);
+
+/** \ingroup rpmfileutil
+ * Merge 3 args into path, any or all of which may be a url.
+ * The leading part of the first URL encountered is used
+ * for the result, other URL prefixes are discarded, permitting
+ * a primitive form of URL inheiritance.
+ * @param urlroot root URL (often path to chroot, or NULL)
+ * @param urlmdir directory URL (often a directory, or NULL)
+ * @param urlfile file URL (often a file, or NULL)
+ * @return expanded, merged, canonicalized path (malloc'ed)
+ */
+char * rpmGenPath (const char * urlroot,
+ const char * urlmdir,
+ const char * urlfile);
+
+/** \ingroup rpmfileutil
+ * Return (malloc'ed) expanded, canonicalized, file path.
+ * @param path macro(s) to expand (NULL terminates list)
+ * @return canonicalized path (malloc'ed)
+ */
+char * rpmGetPath (const char * path, ...) RPM_GNUC_NULL_TERMINATED;
+
+/** \ingroup rpmfileutil
+ * Return URL path(s) from a (URL prefixed) pattern glob.
+ * @param patterns glob pattern
+ * @retval *argcPtr no. of paths
+ * @retval *argvPtr ARGV_t array of paths
+ * @return 0 on success
+ */
+int rpmGlob(const char * patterns, int * argcPtr, ARGV_t * argvPtr);
+
+/** \ingroup rpmfileutil
+ * Escape isspace(3) characters in string.
+ * @param s string
+ * @return escaped string
+ */
+char * rpmEscapeSpaces(const char * s);
+
+/** \ingroup rpmfileutil
+ * Return type of compression used in file.
+ * @param file name of file
+ * @retval compressed address of compression type
+ * @return 0 on success, 1 on I/O error
+ */
+int rpmFileIsCompressed (const char * file, rpmCompressedMagic * compressed);
+
+/** \ingroup rpmfileutil
+ * Check if path (string) ends with given suffix
+ * @param path (path) string
+ * @param suffix suffix string to check for
+ * @return 1 if true, 0 otherwise
+ */
+int rpmFileHasSuffix(const char *path, const char *suffix);
+
+/** \ingroup rpmfileutil
+ * Like getcwd() but the result is malloced.
+ * @return current working directory (malloc'ed)
+ */
+char * rpmGetCwd(void);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _RPMFILEUTIL_H */
diff --git a/rpmio/rpmhook.c b/rpmio/rpmhook.c
new file mode 100644
index 0000000..9fe2a21
--- /dev/null
+++ b/rpmio/rpmhook.c
@@ -0,0 +1,262 @@
+#include "system.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include <rpm/rpmstring.h>
+#include "rpmio/rpmhook.h"
+
+#include "debug.h"
+
+#define RPMHOOK_TABLE_INITSIZE 256
+#define RPMHOOK_BUCKET_INITSIZE 5
+
+typedef struct rpmhookItem_s {
+ rpmhookFunc func;
+ void *data;
+ struct rpmhookItem_s *next;
+} * rpmhookItem;
+
+typedef struct rpmhookBucket_s {
+ unsigned long hash;
+ char *name;
+ rpmhookItem item;
+} * rpmhookBucket;
+
+typedef struct rpmhookTable_s {
+ int size;
+ int used;
+ struct rpmhookBucket_s bucket[1];
+} * rpmhookTable;
+
+
+rpmhookArgs rpmhookArgsNew(int argc)
+{
+ rpmhookArgs args = (rpmhookArgs) xcalloc(1,
+ sizeof(*args) + sizeof(args->argv) * (argc-1));
+ args->argc = argc;
+ return args;
+}
+
+rpmhookArgs rpmhookArgsFree(rpmhookArgs args)
+{
+ if (args != NULL)
+ free(args);
+ return NULL;
+}
+
+static rpmhookTable rpmhookTableNew(int size)
+{
+ rpmhookTable table = (rpmhookTable) xcalloc(1,
+ sizeof(*table) + sizeof(table->bucket) * (size-1));
+ table->size = size;
+ return table;
+}
+
+#if 0
+static rpmhookTable rpmhookTableFree(rpmhookTable table)
+{
+ rpmhookItem item, nextItem;
+ int i;
+ for (i = 0; i != table->size; i++) {
+ if (table->bucket[i].name == NULL)
+ continue;
+ free(table->bucket[i].name);
+ item = table->bucket[i].item;
+ while (item) {
+ nextItem = item->next;
+ free(item);
+ item = nextItem;
+ }
+ }
+ free(table);
+ return NULL;
+}
+#endif
+
+static void rpmhookTableRehash(rpmhookTable *table);
+
+static int rpmhookTableFindBucket(rpmhookTable *table, const char *name)
+{
+ /* Hash based on http://www.isthe.com/chongo/tech/comp/fnv/ */
+ unsigned long perturb;
+ unsigned long hash = 0;
+ unsigned char *bp = (unsigned char *)name;
+ unsigned char *be = bp + strlen(name);
+ rpmhookBucket bucket;
+ int ret;
+
+ if (((*table)->used/2)*3 > (*table)->size)
+ rpmhookTableRehash(table);
+ while (bp < be) {
+ hash ^= (unsigned long)*bp++;
+ hash *= (unsigned long)0x01000193;
+ }
+ perturb = hash;
+ ret = hash % (*table)->size;
+ bucket = &(*table)->bucket[ret];
+ while (bucket->name &&
+ (bucket->hash != hash || !rstreq(bucket->name, name))) {
+ /* Collision resolution based on Python's perturb scheme. */
+ ret = ((ret << 2) + ret + perturb + 1) % (*table)->size;
+ perturb >>= 5;
+ bucket = &(*table)->bucket[ret];
+ }
+ if (!bucket->name)
+ bucket->hash = hash;
+ return ret;
+}
+
+static void rpmhookTableRehash(rpmhookTable *table)
+{
+ rpmhookTable newtable = rpmhookTableNew((*table)->size*2);
+ int n, i = 0;
+
+ for (; i != (*table)->size; i++) {
+ if ((*table)->bucket[i].name == NULL)
+ continue;
+ n = rpmhookTableFindBucket(&newtable, (*table)->bucket[i].name);
+ newtable->bucket[n].name = (*table)->bucket[i].name;
+ newtable->bucket[n].item = (*table)->bucket[i].item;
+ }
+ newtable->used = (*table)->used;
+ free(*table);
+ *table = newtable;
+}
+
+static void rpmhookTableAddItem(rpmhookTable *table, const char *name,
+ rpmhookFunc func, void *data)
+{
+ int n = rpmhookTableFindBucket(table, name);
+ rpmhookBucket bucket = &(*table)->bucket[n];
+ rpmhookItem *item = &bucket->item;
+ if (!bucket->name) {
+ bucket->name = xstrdup(name);
+ (*table)->used++;
+ }
+ while (*item) item = &(*item)->next;
+ *item = xcalloc(1, sizeof(**item));
+ (*item)->func = func;
+ (*item)->data = data;
+}
+
+static void rpmhookTableDelItem(rpmhookTable *table, const char *name,
+ rpmhookFunc func, void *data,
+ int matchfunc, int matchdata)
+{
+ int n = rpmhookTableFindBucket(table, name);
+ rpmhookBucket bucket = &(*table)->bucket[n];
+ rpmhookItem item = bucket->item;
+ rpmhookItem lastItem = NULL;
+ rpmhookItem nextItem;
+ while (item) {
+ nextItem = item->next;
+ if ((!matchfunc || item->func == func) &&
+ (!matchdata || item->data == data)) {
+ free(item);
+ if (lastItem)
+ lastItem->next = nextItem;
+ else
+ bucket->item = nextItem;
+ } else {
+ lastItem = item;
+ }
+ item = nextItem;
+ }
+ if (!bucket->item) {
+ free(bucket->name);
+ bucket->name = NULL;
+ (*table)->used--;
+ }
+}
+
+static rpmhookArgs rpmhookArgsParse(const char *argt, va_list ap)
+{
+ rpmhookArgs args = rpmhookArgsNew(strlen(argt));
+ int i;
+
+ args->argt = argt;
+ for (i = 0; i != args->argc; i++) {
+ switch (argt[i]) {
+ case 's':
+ args->argv[i].s = va_arg(ap, char *);
+ break;
+ case 'i':
+ args->argv[i].i = va_arg(ap, int);
+ break;
+ case 'f':
+ args->argv[i].f = (float)va_arg(ap, double);
+ break;
+ case 'p':
+ args->argv[i].p = va_arg(ap, void *);
+ break;
+ default:
+ fprintf(stderr, "error: unsupported type '%c' as "
+ "a hook argument\n", argt[i]);
+ break;
+ }
+ }
+ return args;
+}
+
+static void rpmhookTableCallArgs(rpmhookTable *table, const char *name,
+ rpmhookArgs args)
+{
+ int n = rpmhookTableFindBucket(table, name);
+ rpmhookItem item = (*table)->bucket[n].item;
+ rpmhookItem next;
+ while (item) {
+ next = item->next;
+ if (item->func(args, item->data) != 0)
+ break;
+ item = next;
+ }
+}
+
+static rpmhookTable globalTable = NULL;
+
+void rpmhookRegister(const char *name, rpmhookFunc func, void *data)
+{
+ if (globalTable == NULL)
+ globalTable = rpmhookTableNew(RPMHOOK_TABLE_INITSIZE);
+ rpmhookTableAddItem(&globalTable, name, func, data);
+}
+
+void rpmhookUnregister(const char *name, rpmhookFunc func, void *data)
+{
+ if (globalTable != NULL)
+ rpmhookTableDelItem(&globalTable, name, func, data, 1, 1);
+}
+
+void rpmhookUnregisterAny(const char *name, rpmhookFunc func)
+{
+ if (globalTable != NULL)
+ rpmhookTableDelItem(&globalTable, name, func, NULL, 1, 0);
+}
+
+void rpmhookUnregisterAll(const char *name)
+{
+ if (globalTable != NULL)
+ rpmhookTableDelItem(&globalTable, name, NULL, NULL, 0, 0);
+}
+
+void rpmhookCall(const char *name, const char *argt, ...)
+{
+ if (globalTable != NULL) {
+ rpmhookArgs args;
+ va_list ap;
+ va_start(ap, argt);
+ args = rpmhookArgsParse(argt, ap);
+ rpmhookTableCallArgs(&globalTable, name, args);
+ (void) rpmhookArgsFree(args);
+ va_end(ap);
+ }
+}
+
+void rpmhookCallArgs(const char *name, rpmhookArgs args)
+{
+ if (globalTable != NULL)
+ rpmhookTableCallArgs(&globalTable, name, args);
+}
diff --git a/rpmio/rpmhook.h b/rpmio/rpmhook.h
new file mode 100644
index 0000000..52f5634
--- /dev/null
+++ b/rpmio/rpmhook.h
@@ -0,0 +1,37 @@
+#ifndef RPMHOOK_H
+#define RPMHOOK_H
+
+typedef union {
+ const char * s;
+ int i;
+ float f;
+ void * p;
+} rpmhookArgv;
+
+typedef struct rpmhookArgs_s {
+ int argc;
+ const char * argt;
+ rpmhookArgv argv[1];
+} * rpmhookArgs;
+
+typedef int (*rpmhookFunc) (rpmhookArgs args, void *data);
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+rpmhookArgs rpmhookArgsNew(int argc);
+rpmhookArgs rpmhookArgsFree(rpmhookArgs args);
+
+void rpmhookRegister(const char *name, rpmhookFunc func, void *data);
+void rpmhookUnregister(const char *name, rpmhookFunc func, void *data);
+void rpmhookUnregisterAny(const char *name, rpmhookFunc func);
+void rpmhookUnregisterAll(const char *name);
+void rpmhookCall(const char *name, const char *argt, ...);
+void rpmhookCallArgs(const char *name, rpmhookArgs args);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/rpmio/rpmio.c b/rpmio/rpmio.c
new file mode 100644
index 0000000..2fbbf91
--- /dev/null
+++ b/rpmio/rpmio.c
@@ -0,0 +1,1900 @@
+/** \ingroup rpmio
+ * \file rpmio/rpmio.c
+ */
+
+#include "system.h"
+#include <stdarg.h>
+#include <errno.h>
+
+#include <rpm/rpmlog.h>
+#include <rpm/rpmmacro.h>
+#include <rpm/rpmfileutil.h>
+#include <rpm/rpmsw.h>
+#include <rpm/rpmurl.h>
+
+#include "rpmio/rpmio_internal.h"
+
+#include "debug.h"
+
+#if HAVE_LIBIO_H && defined(_G_IO_IO_FILE_VERSION)
+#define _USE_LIBIO 1
+#endif
+
+typedef struct _FDSTACK_s {
+ FDIO_t io;
+ void * fp;
+ int fdno;
+} FDSTACK_t;
+
+/** \ingroup rpmio
+ * Cumulative statistics for a descriptor.
+ */
+typedef struct {
+ struct rpmop_s ops[FDSTAT_MAX]; /*!< Cumulative statistics. */
+} * FDSTAT_t;
+
+/** \ingroup rpmio
+ * The FD_t File Handle data structure.
+ */
+struct _FD_s {
+ int nrefs;
+ int flags;
+#define RPMIO_DEBUG_IO 0x40000000
+#define RPMIO_DEBUG_REFS 0x20000000
+ int magic;
+#define FDMAGIC 0x04463138
+ int nfps;
+ FDSTACK_t fps[8];
+ int urlType; /* ufdio: */
+
+ ssize_t bytesRemain; /* ufdio: */
+
+ int syserrno; /* last system errno encountered */
+ const char *errcookie; /* gzdio/bzdio/ufdio/xzdio: */
+
+ FDSTAT_t stats; /* I/O statistics */
+
+ rpmDigestBundle digests;
+};
+
+#define DBG(_f, _m, _x) \
+ \
+ if ((_rpmio_debug | ((_f) ? ((FD_t)(_f))->flags : 0)) & (_m)) fprintf _x \
+
+#define DBGIO(_f, _x) DBG((_f), RPMIO_DEBUG_IO, _x)
+
+static FDIO_t fdGetIo(FD_t fd)
+{
+ return (fd != NULL) ? fd->fps[fd->nfps].io : NULL;
+}
+
+static void fdSetIo(FD_t fd, FDIO_t io)
+{
+ if (fd)
+ fd->fps[fd->nfps].io = io;
+}
+
+static void * fdGetFp(FD_t fd)
+{
+ return (fd != NULL) ? fd->fps[fd->nfps].fp : NULL;
+}
+
+static void fdSetFp(FD_t fd, void * fp)
+{
+ if (fd)
+ fd->fps[fd->nfps].fp = fp;
+}
+
+static void fdSetFdno(FD_t fd, int fdno)
+{
+ if (fd)
+ fd->fps[fd->nfps].fdno = fdno;
+}
+
+static void fdPush(FD_t fd, FDIO_t io, void * fp, int fdno)
+{
+ if (fd == NULL || fd->nfps >= (sizeof(fd->fps)/sizeof(fd->fps[0]) - 1))
+ return;
+ fd->nfps++;
+ fdSetIo(fd, io);
+ fdSetFp(fd, fp);
+ fdSetFdno(fd, fdno);
+}
+
+static void fdPop(FD_t fd)
+{
+ if (fd == NULL || fd->nfps < 0) return;
+ fdSetIo(fd, NULL);
+ fdSetFp(fd, NULL);
+ fdSetFdno(fd, -1);
+ fd->nfps--;
+}
+
+static FD_t c2f(void * cookie)
+{
+ FD_t fd = (FD_t) cookie;
+ return (fd && fd->magic == FDMAGIC) ? fd : NULL;
+}
+
+void fdSetBundle(FD_t fd, rpmDigestBundle bundle)
+{
+ if (fd)
+ fd->digests = bundle;
+}
+
+rpmDigestBundle fdGetBundle(FD_t fd)
+{
+ return (fd != NULL) ? fd->digests : NULL;
+}
+
+static void * iotFileno(FD_t fd, FDIO_t iot)
+{
+ void * rc = NULL;
+
+ if (fd == NULL)
+ return NULL;
+
+ for (int i = fd->nfps; i >= 0; i--) {
+ FDSTACK_t * fps = &fd->fps[i];
+ if (fps->io != iot)
+ continue;
+ rc = fps->fp;
+ break;
+ }
+
+ return rc;
+}
+
+#define FDNREFS(fd) (fd ? ((FD_t)fd)->nrefs : -9)
+
+#define FDONLY(fd) assert(fdGetIo(fd) == fdio)
+#define GZDONLY(fd) assert(fdGetIo(fd) == gzdio)
+#define BZDONLY(fd) assert(fdGetIo(fd) == bzdio)
+#define LZDONLY(fd) assert(fdGetIo(fd) == xzdio || fdGetIo(fd) == lzdio)
+
+#define UFDONLY(fd) /* assert(fdGetIo(fd) == ufdio) */
+
+/**
+ */
+#if _USE_LIBIO
+static int noLibio = 0;
+#else
+static int noLibio = 1;
+#endif
+
+/** \ingroup rpmio
+ * \name RPMIO Vectors.
+ */
+typedef ssize_t (*fdio_read_function_t) (void *cookie, void *buf, size_t nbytes);
+typedef ssize_t (*fdio_write_function_t) (void *cookie, const void *buf, size_t nbytes);
+typedef int (*fdio_seek_function_t) (void *cookie, _libio_pos_t pos, int whence);
+typedef int (*fdio_close_function_t) (void *cookie);
+typedef FD_t (*fdio_ref_function_t) ( void * cookie);
+typedef FD_t (*fdio_deref_function_t) (FD_t fd);
+typedef FD_t (*fdio_new_function_t) (void);
+typedef int (*fdio_fileno_function_t) (void * cookie);
+typedef FD_t (*fdio_open_function_t) (const char * path, int flags, mode_t mode);
+typedef FD_t (*fdio_fopen_function_t) (const char * path, const char * fmode);
+typedef void * (*fdio_ffileno_function_t) (FD_t fd);
+typedef int (*fdio_fflush_function_t) (FD_t fd);
+
+/** \ingroup rpmio
+ */
+struct FDIO_s {
+ fdio_read_function_t read;
+ fdio_write_function_t write;
+ fdio_seek_function_t seek;
+ fdio_close_function_t close;
+
+ fdio_ref_function_t _fdref;
+ fdio_deref_function_t _fdderef;
+ fdio_new_function_t _fdnew;
+ fdio_fileno_function_t _fileno;
+
+ fdio_open_function_t _open;
+ fdio_fopen_function_t _fopen;
+ fdio_ffileno_function_t _ffileno;
+ fdio_fflush_function_t _fflush;
+};
+
+/* forward refs */
+static const FDIO_t fdio;
+static const FDIO_t fpio;
+static const FDIO_t ufdio;
+static const FDIO_t gzdio;
+static const FDIO_t bzdio;
+static const FDIO_t xzdio;
+static const FDIO_t lzdio;
+
+/** \ingroup rpmio
+ * Update digest(s) attached to fd.
+ */
+static void fdUpdateDigests(FD_t fd, const void * buf, size_t buflen);
+
+/**
+ */
+int _rpmio_debug = 0;
+
+/* =============================================================== */
+
+static const char * fdbg(FD_t fd)
+{
+ static char buf[BUFSIZ];
+ char *be = buf;
+ int i;
+
+ buf[0] = '\0';
+ if (fd == NULL)
+ return buf;
+
+ if (fd->bytesRemain != -1) {
+ sprintf(be, " clen %d", (int)fd->bytesRemain);
+ be += strlen(be);
+ }
+ *be++ = '\t';
+ for (i = fd->nfps; i >= 0; i--) {
+ FDSTACK_t * fps = &fd->fps[i];
+ if (i != fd->nfps)
+ *be++ = ' ';
+ *be++ = '|';
+ *be++ = ' ';
+ if (fps->io == fdio) {
+ sprintf(be, "FD %d fp %p", fps->fdno, fps->fp);
+ } else if (fps->io == ufdio) {
+ sprintf(be, "UFD %d fp %p", fps->fdno, fps->fp);
+ } else if (fps->io == gzdio) {
+ sprintf(be, "GZD %p fdno %d", fps->fp, fps->fdno);
+#if HAVE_BZLIB_H
+ } else if (fps->io == bzdio) {
+ sprintf(be, "BZD %p fdno %d", fps->fp, fps->fdno);
+#endif
+#if HAVE_LZMA_H
+ } else if (fps->io == xzdio) {
+ sprintf(be, "XZD %p fdno %d", fps->fp, fps->fdno);
+ } else if (fps->io == lzdio) {
+ sprintf(be, "LZD %p fdno %d", fps->fp, fps->fdno);
+#endif
+ } else if (fps->io == fpio) {
+ sprintf(be, "%s %p(%d) fdno %d",
+ (fps->fdno < 0 ? "LIBIO" : "FP"),
+ fps->fp, fileno(((FILE *)fps->fp)), fps->fdno);
+ } else {
+ sprintf(be, "??? io %p fp %p fdno %d ???",
+ fps->io, fps->fp, fps->fdno);
+ }
+ be += strlen(be);
+ *be = '\0';
+ }
+ return buf;
+}
+
+static void fdstat_enter(FD_t fd, fdOpX opx)
+{
+ if (fd == NULL) return;
+ if (fd->stats != NULL)
+ (void) rpmswEnter(fdOp(fd, opx), (ssize_t) 0);
+}
+
+static void fdstat_exit(FD_t fd, fdOpX opx, ssize_t rc)
+{
+ if (fd == NULL) return;
+ if (rc == -1)
+ fd->syserrno = errno;
+ else if (rc > 0 && fd->bytesRemain > 0)
+ switch (opx) {
+ case FDSTAT_READ:
+ case FDSTAT_WRITE:
+ fd->bytesRemain -= rc;
+ break;
+ default:
+ break;
+ }
+ if (fd->stats != NULL)
+ (void) rpmswExit(fdOp(fd, opx), rc);
+}
+
+static void fdstat_print(FD_t fd, const char * msg, FILE * fp)
+{
+ static const int usec_scale = (1000*1000);
+ int opx;
+
+ if (fd == NULL || fd->stats == NULL) return;
+ for (opx = 0; opx < 4; opx++) {
+ rpmop op = &fd->stats->ops[opx];
+ if (op->count <= 0) continue;
+ switch (opx) {
+ case FDSTAT_READ:
+ if (msg) fprintf(fp, "%s:", msg);
+ fprintf(fp, "%8d reads, %8ld total bytes in %d.%06d secs\n",
+ op->count, (long)op->bytes,
+ (int)(op->usecs/usec_scale), (int)(op->usecs%usec_scale));
+ break;
+ case FDSTAT_WRITE:
+ if (msg) fprintf(fp, "%s:", msg);
+ fprintf(fp, "%8d writes, %8ld total bytes in %d.%06d secs\n",
+ op->count, (long)op->bytes,
+ (int)(op->usecs/usec_scale), (int)(op->usecs%usec_scale));
+ break;
+ case FDSTAT_SEEK:
+ break;
+ case FDSTAT_CLOSE:
+ break;
+ }
+ }
+}
+
+/* =============================================================== */
+off_t fdSize(FD_t fd)
+{
+ struct stat sb;
+ off_t rc = -1;
+
+ if (fd != NULL && fstat(Fileno(fd), &sb) == 0)
+ rc = sb.st_size;
+ return rc;
+}
+
+FD_t fdDup(int fdno)
+{
+ FD_t fd;
+ int nfdno;
+
+ if ((nfdno = dup(fdno)) < 0)
+ return NULL;
+ fd = fdNew();
+ fdSetFdno(fd, nfdno);
+DBGIO(fd, (stderr, "==> fdDup(%d) fd %p %s\n", fdno, (fd ? fd : NULL), fdbg(fd)));
+ return fd;
+}
+
+static int fdSeekNot(void * cookie, _libio_pos_t pos, int whence)
+{
+ return -2;
+}
+
+/** \ingroup rpmio
+ */
+static int fdFileno(void * cookie)
+{
+ FD_t fd = c2f(cookie);
+ return (fd != NULL) ? fd->fps[0].fdno : -2;
+}
+
+FILE * fdGetFILE(FD_t fd)
+{
+ return ((FILE *)fdGetFp(fd));
+}
+
+/* =============================================================== */
+
+FD_t fdLink(void * cookie)
+{
+ FD_t fd = c2f(cookie);
+ if (fd)
+ fd->nrefs++;
+ return fd;
+}
+
+/**
+ */
+FD_t fdFree( FD_t fd)
+{
+ if (fd) {
+ if (--fd->nrefs > 0)
+ return fd;
+ fd->stats = _free(fd->stats);
+ if (fd->digests) {
+ fd->digests = rpmDigestBundleFree(fd->digests);
+ }
+ free(fd);
+ }
+ return NULL;
+}
+
+/**
+ */
+FD_t fdNew(void)
+{
+ FD_t fd = xcalloc(1, sizeof(*fd));
+ if (fd == NULL) /* XXX xmalloc never returns NULL */
+ return NULL;
+ fd->nrefs = 0;
+ fd->flags = 0;
+ fd->magic = FDMAGIC;
+ fd->urlType = URL_IS_UNKNOWN;
+
+ fd->nfps = 0;
+ memset(fd->fps, 0, sizeof(fd->fps));
+
+ fd->fps[0].io = fdio;
+ fd->fps[0].fp = NULL;
+ fd->fps[0].fdno = -1;
+
+ fd->bytesRemain = -1;
+ fd->syserrno = 0;
+ fd->errcookie = NULL;
+ fd->stats = xcalloc(1, sizeof(*fd->stats));
+ fd->digests = NULL;
+
+ return fdLink(fd);
+}
+
+/**
+ */
+static ssize_t fdRead(void * cookie, void * buf, size_t count)
+{
+ FD_t fd = c2f(cookie);
+ ssize_t rc;
+
+ if (fd == NULL || fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
+
+ fdstat_enter(fd, FDSTAT_READ);
+ rc = read(fdFileno(fd), buf, (count > fd->bytesRemain ? fd->bytesRemain : count));
+ fdstat_exit(fd, FDSTAT_READ, rc);
+
+ if (fd->digests && rc > 0) fdUpdateDigests(fd, buf, rc);
+
+DBGIO(fd, (stderr, "==>\tfdRead(%p,%p,%ld) rc %ld %s\n", cookie, buf, (long)count, (long)rc, fdbg(fd)));
+
+ return rc;
+}
+
+/**
+ */
+static ssize_t fdWrite(void * cookie, const void * buf, size_t count)
+{
+ FD_t fd = c2f(cookie);
+ int fdno = fdFileno(fd);
+ ssize_t rc;
+
+ if (fd == NULL || fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
+
+ if (fd->digests && count > 0) fdUpdateDigests(fd, buf, count);
+
+ if (count == 0) return 0;
+
+ fdstat_enter(fd, FDSTAT_WRITE);
+ rc = write(fdno, buf, (count > fd->bytesRemain ? fd->bytesRemain : count));
+ fdstat_exit(fd, FDSTAT_WRITE, rc);
+
+DBGIO(fd, (stderr, "==>\tfdWrite(%p,%p,%ld) rc %ld %s\n", cookie, buf, (long)count, (long)rc, fdbg(fd)));
+
+ return rc;
+}
+
+static int fdSeek(void * cookie, _libio_pos_t pos, int whence)
+{
+#ifdef USE_COOKIE_SEEK_POINTER
+ _IO_off64_t p = *pos;
+#else
+ off_t p = pos;
+#endif
+ FD_t fd = c2f(cookie);
+ off_t rc;
+
+ if (fd == NULL)
+ return -2;
+
+ assert(fd->bytesRemain == -1); /* XXX FIXME fadio only for now */
+ fdstat_enter(fd, FDSTAT_SEEK);
+ rc = lseek(fdFileno(fd), p, whence);
+ fdstat_exit(fd, FDSTAT_SEEK, rc);
+
+DBGIO(fd, (stderr, "==>\tfdSeek(%p,%ld,%d) rc %lx %s\n", cookie, (long)p, whence, (unsigned long)rc, fdbg(fd)));
+
+ return rc;
+}
+
+/**
+ */
+static int fdClose( void * cookie)
+{
+ FD_t fd = c2f(cookie);
+ int fdno;
+ int rc;
+
+ if (fd == NULL) return -2;
+ fdno = fdFileno(fd);
+
+ fdSetFdno(fd, -1);
+
+ fdstat_enter(fd, FDSTAT_CLOSE);
+ rc = ((fdno >= 0) ? close(fdno) : -2);
+ fdstat_exit(fd, FDSTAT_CLOSE, rc);
+
+DBGIO(fd, (stderr, "==>\tfdClose(%p) rc %lx %s\n", (fd ? fd : NULL), (unsigned long)rc, fdbg(fd)));
+
+ fd = fdFree(fd);
+ return rc;
+}
+
+/**
+ */
+static FD_t fdOpen(const char *path, int flags, mode_t mode)
+{
+ FD_t fd;
+ int fdno;
+
+ fdno = open(path, flags, mode);
+ if (fdno < 0) return NULL;
+ if (fcntl(fdno, F_SETFD, FD_CLOEXEC)) {
+ (void) close(fdno);
+ return NULL;
+ }
+ fd = fdNew();
+ fdSetFdno(fd, fdno);
+ fd->flags = flags;
+DBGIO(fd, (stderr, "==>\tfdOpen(\"%s\",%x,0%o) %s\n", path, (unsigned)flags, (unsigned)mode, fdbg(fd)));
+ return fd;
+}
+
+static const struct FDIO_s fdio_s = {
+ fdRead, fdWrite, fdSeek, fdClose, fdLink, fdFree, fdNew, fdFileno,
+ fdOpen, NULL, fdGetFp, NULL
+};
+static const FDIO_t fdio = &fdio_s ;
+
+int ufdCopy(FD_t sfd, FD_t tfd)
+{
+ char buf[BUFSIZ];
+ int itemsRead;
+ int itemsCopied = 0;
+ int rc = 0;
+
+ while (1) {
+ rc = Fread(buf, sizeof(buf[0]), sizeof(buf), sfd);
+ if (rc < 0)
+ break;
+ else if (rc == 0) {
+ rc = itemsCopied;
+ break;
+ }
+ itemsRead = rc;
+ rc = Fwrite(buf, sizeof(buf[0]), itemsRead, tfd);
+ if (rc < 0)
+ break;
+ if (rc != itemsRead) {
+ rc = -1;
+ break;
+ }
+
+ itemsCopied += itemsRead;
+ }
+
+ DBGIO(sfd, (stderr, "++ copied %d bytes\n", itemsCopied));
+
+ return rc;
+}
+
+/*
+ * Deal with remote url's by fetching them with a helper application
+ * and treat as local file afterwards.
+ * TODO:
+ * - better error checking + reporting
+ * - curl & friends don't know about hkp://, transform to http?
+ */
+
+static FD_t urlOpen(const char * url, int flags, mode_t mode)
+{
+ FD_t fd;
+ char *dest = NULL;
+ int rc = 1; /* assume failure */
+
+ fd = rpmMkTempFile(NULL, &dest);
+ if (fd == NULL) {
+ return NULL;
+ }
+ Fclose(fd);
+
+ rc = urlGetFile(url, dest);
+ if (rc == 0) {
+ fd = fdOpen(dest, flags, mode);
+ unlink(dest);
+ } else {
+ fd = NULL;
+ }
+ dest = _free(dest);
+
+ return fd;
+
+}
+static FD_t ufdOpen(const char * url, int flags, mode_t mode)
+{
+ FD_t fd = NULL;
+ const char * path;
+ urltype urlType = urlPath(url, &path);
+
+if (_rpmio_debug)
+fprintf(stderr, "*** ufdOpen(%s,0x%x,0%o)\n", url, (unsigned)flags, (unsigned)mode);
+
+ switch (urlType) {
+ case URL_IS_FTP:
+ case URL_IS_HTTPS:
+ case URL_IS_HTTP:
+ case URL_IS_HKP:
+ fd = urlOpen(url, flags, mode);
+ /* we're dealing with local file when urlOpen() returns */
+ urlType = URL_IS_UNKNOWN;
+ break;
+ case URL_IS_DASH:
+ if ((flags & O_ACCMODE) == O_RDWR) {
+ fd = NULL;
+ } else {
+ fd = fdDup((flags & O_ACCMODE) == O_WRONLY ?
+ STDOUT_FILENO : STDIN_FILENO);
+ }
+ break;
+ case URL_IS_PATH:
+ case URL_IS_UNKNOWN:
+ default:
+ fd = fdOpen(path, flags, mode);
+ break;
+ }
+
+ if (fd == NULL) return NULL;
+
+ fdSetIo(fd, ufdio);
+ fd->bytesRemain = -1;
+ fd->urlType = urlType;
+
+ if (Fileno(fd) < 0) {
+ (void) fdClose(fd);
+ return NULL;
+ }
+DBGIO(fd, (stderr, "==>\tufdOpen(\"%s\",%x,0%o) %s\n", url, (unsigned)flags, (unsigned)mode, fdbg(fd)));
+ return fd;
+}
+
+static const struct FDIO_s ufdio_s = {
+ fdRead, fdWrite, fdSeek, fdClose, fdLink, fdFree, fdNew, fdFileno,
+ ufdOpen, NULL, fdGetFp, NULL
+};
+static const FDIO_t ufdio = &ufdio_s ;
+
+ssize_t timedRead(FD_t fd, void * bufptr, size_t length)
+{
+ return ufdio->read(fd, bufptr, length);
+}
+
+/* =============================================================== */
+/* Support for GZIP library.
+ */
+#include <zlib.h>
+
+static void * gzdFileno(FD_t fd)
+{
+ return iotFileno(fd, gzdio);
+}
+
+static
+FD_t gzdOpen(const char * path, const char * fmode)
+{
+ FD_t fd;
+ gzFile gzfile;
+ if ((gzfile = gzopen(path, fmode)) == NULL)
+ return NULL;
+ fd = fdNew();
+ fdPop(fd); fdPush(fd, gzdio, gzfile, -1);
+
+DBGIO(fd, (stderr, "==>\tgzdOpen(\"%s\", \"%s\") fd %p %s\n", path, fmode, (fd ? fd : NULL), fdbg(fd)));
+ return fdLink(fd);
+}
+
+static FD_t gzdFdopen(void * cookie, const char *fmode)
+{
+ FD_t fd = c2f(cookie);
+ int fdno;
+ gzFile gzfile;
+
+ if (fd == NULL || fmode == NULL) return NULL;
+ fdno = fdFileno(fd);
+ fdSetFdno(fd, -1); /* XXX skip the fdio close */
+ if (fdno < 0) return NULL;
+ gzfile = gzdopen(fdno, fmode);
+ if (gzfile == NULL) return NULL;
+
+ fdPush(fd, gzdio, gzfile, fdno); /* Push gzdio onto stack */
+
+ return fdLink(fd);
+}
+
+static int gzdFlush(FD_t fd)
+{
+ gzFile gzfile;
+ gzfile = gzdFileno(fd);
+ if (gzfile == NULL) return -2;
+ return gzflush(gzfile, Z_SYNC_FLUSH); /* XXX W2DO? */
+}
+
+/* =============================================================== */
+static ssize_t gzdRead(void * cookie, void * buf, size_t count)
+{
+ FD_t fd = c2f(cookie);
+ gzFile gzfile;
+ ssize_t rc;
+
+ if (fd == NULL || fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
+
+ gzfile = gzdFileno(fd);
+ if (gzfile == NULL) return -2; /* XXX can't happen */
+
+ fdstat_enter(fd, FDSTAT_READ);
+ rc = gzread(gzfile, buf, count);
+DBGIO(fd, (stderr, "==>\tgzdRead(%p,%p,%u) rc %lx %s\n", cookie, buf, (unsigned)count, (unsigned long)rc, fdbg(fd)));
+ if (rc < 0) {
+ int zerror = 0;
+ fd->errcookie = gzerror(gzfile, &zerror);
+ if (zerror == Z_ERRNO) {
+ fd->syserrno = errno;
+ fd->errcookie = strerror(fd->syserrno);
+ }
+ } else if (rc >= 0) {
+ fdstat_exit(fd, FDSTAT_READ, rc);
+ if (fd->digests && rc > 0) fdUpdateDigests(fd, buf, rc);
+ }
+ return rc;
+}
+
+static ssize_t gzdWrite(void * cookie, const void * buf, size_t count)
+{
+ FD_t fd = c2f(cookie);
+ gzFile gzfile;
+ ssize_t rc;
+
+ if (fd == NULL || fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
+
+ if (fd->digests && count > 0) fdUpdateDigests(fd, buf, count);
+
+ gzfile = gzdFileno(fd);
+ if (gzfile == NULL) return -2; /* XXX can't happen */
+
+ fdstat_enter(fd, FDSTAT_WRITE);
+ rc = gzwrite(gzfile, (void *)buf, count);
+DBGIO(fd, (stderr, "==>\tgzdWrite(%p,%p,%u) rc %lx %s\n", cookie, buf, (unsigned)count, (unsigned long)rc, fdbg(fd)));
+ if (rc < 0) {
+ int zerror = 0;
+ fd->errcookie = gzerror(gzfile, &zerror);
+ if (zerror == Z_ERRNO) {
+ fd->syserrno = errno;
+ fd->errcookie = strerror(fd->syserrno);
+ }
+ } else if (rc > 0) {
+ fdstat_exit(fd, FDSTAT_WRITE, rc);
+ }
+ return rc;
+}
+
+/* XXX zlib-1.0.4 has not */
+static int gzdSeek(void * cookie, _libio_pos_t pos, int whence)
+{
+#ifdef USE_COOKIE_SEEK_POINTER
+ _IO_off64_t p = *pos;
+#else
+ off_t p = pos;
+#endif
+ int rc;
+#if HAVE_GZSEEK
+ FD_t fd = c2f(cookie);
+ gzFile gzfile;
+
+ if (fd == NULL) return -2;
+ assert(fd->bytesRemain == -1); /* XXX FIXME */
+
+ gzfile = gzdFileno(fd);
+ if (gzfile == NULL) return -2; /* XXX can't happen */
+
+ fdstat_enter(fd, FDSTAT_SEEK);
+ rc = gzseek(gzfile, p, whence);
+DBGIO(fd, (stderr, "==>\tgzdSeek(%p,%ld,%d) rc %lx %s\n", cookie, (long)p, whence, (unsigned long)rc, fdbg(fd)));
+ if (rc < 0) {
+ int zerror = 0;
+ fd->errcookie = gzerror(gzfile, &zerror);
+ if (zerror == Z_ERRNO) {
+ fd->syserrno = errno;
+ fd->errcookie = strerror(fd->syserrno);
+ }
+ } else if (rc >= 0) {
+ fdstat_exit(fd, FDSTAT_SEEK, rc);
+ }
+#else
+ rc = -2;
+#endif
+ return rc;
+}
+
+static int gzdClose( void * cookie)
+{
+ FD_t fd = c2f(cookie);
+ gzFile gzfile;
+ int rc;
+
+ gzfile = gzdFileno(fd);
+ if (gzfile == NULL) return -2; /* XXX can't happen */
+
+ fdstat_enter(fd, FDSTAT_CLOSE);
+ rc = gzclose(gzfile);
+
+ /* XXX TODO: preserve fd if errors */
+
+ if (fd) {
+DBGIO(fd, (stderr, "==>\tgzdClose(%p) zerror %d %s\n", cookie, rc, fdbg(fd)));
+ if (rc < 0) {
+ fd->errcookie = "gzclose error";
+ if (rc == Z_ERRNO) {
+ fd->syserrno = errno;
+ fd->errcookie = strerror(fd->syserrno);
+ }
+ } else if (rc >= 0) {
+ fdstat_exit(fd, FDSTAT_CLOSE, rc);
+ }
+ }
+
+DBGIO(fd, (stderr, "==>\tgzdClose(%p) rc %lx %s\n", cookie, (unsigned long)rc, fdbg(fd)));
+
+ if (_rpmio_debug || rpmIsDebug()) fdstat_print(fd, "GZDIO", stderr);
+ if (rc == 0)
+ fd = fdFree(fd);
+ return rc;
+}
+
+static const struct FDIO_s gzdio_s = {
+ gzdRead, gzdWrite, gzdSeek, gzdClose, fdLink, fdFree, fdNew, fdFileno,
+ NULL, gzdOpen, gzdFileno, gzdFlush
+};
+static const FDIO_t gzdio = &gzdio_s ;
+
+/* =============================================================== */
+/* Support for BZIP2 library.
+ */
+#if HAVE_BZLIB_H
+
+#include <bzlib.h>
+
+#ifdef HAVE_BZ2_1_0
+# define bzopen BZ2_bzopen
+# define bzclose BZ2_bzclose
+# define bzdopen BZ2_bzdopen
+# define bzerror BZ2_bzerror
+# define bzflush BZ2_bzflush
+# define bzread BZ2_bzread
+# define bzwrite BZ2_bzwrite
+#endif /* HAVE_BZ2_1_0 */
+
+static void * bzdFileno(FD_t fd)
+{
+ return iotFileno(fd, bzdio);
+}
+
+static FD_t bzdOpen(const char * path, const char * mode)
+{
+ FD_t fd;
+ BZFILE *bzfile;;
+ if ((bzfile = bzopen(path, mode)) == NULL)
+ return NULL;
+ fd = fdNew();
+ fdPop(fd); fdPush(fd, bzdio, bzfile, -1);
+ return fdLink(fd);
+}
+
+static FD_t bzdFdopen(void * cookie, const char * fmode)
+{
+ FD_t fd = c2f(cookie);
+ int fdno;
+ BZFILE *bzfile;
+
+ if (fd == NULL || fmode == NULL) return NULL;
+ fdno = fdFileno(fd);
+ fdSetFdno(fd, -1); /* XXX skip the fdio close */
+ if (fdno < 0) return NULL;
+ bzfile = bzdopen(fdno, fmode);
+ if (bzfile == NULL) return NULL;
+
+ fdPush(fd, bzdio, bzfile, fdno); /* Push bzdio onto stack */
+
+ return fdLink(fd);
+}
+
+static int bzdFlush(FD_t fd)
+{
+ return bzflush(bzdFileno(fd));
+}
+
+/* =============================================================== */
+static ssize_t bzdRead(void * cookie, void * buf, size_t count)
+{
+ FD_t fd = c2f(cookie);
+ BZFILE *bzfile;
+ ssize_t rc = 0;
+
+ if (fd == NULL || fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
+ bzfile = bzdFileno(fd);
+ fdstat_enter(fd, FDSTAT_READ);
+ if (bzfile)
+ rc = bzread(bzfile, buf, count);
+ if (rc == -1) {
+ int zerror = 0;
+ if (bzfile)
+ fd->errcookie = bzerror(bzfile, &zerror);
+ } else if (rc >= 0) {
+ fdstat_exit(fd, FDSTAT_READ, rc);
+ if (fd->digests && rc > 0) fdUpdateDigests(fd, buf, rc);
+ }
+ return rc;
+}
+
+static ssize_t bzdWrite(void * cookie, const void * buf, size_t count)
+{
+ FD_t fd = c2f(cookie);
+ BZFILE *bzfile;
+ ssize_t rc;
+
+ if (fd == NULL || fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
+
+ if (fd->digests && count > 0) fdUpdateDigests(fd, buf, count);
+
+ bzfile = bzdFileno(fd);
+ fdstat_enter(fd, FDSTAT_WRITE);
+ rc = bzwrite(bzfile, (void *)buf, count);
+ if (rc == -1) {
+ int zerror = 0;
+ fd->errcookie = bzerror(bzfile, &zerror);
+ } else if (rc > 0) {
+ fdstat_exit(fd, FDSTAT_WRITE, rc);
+ }
+ return rc;
+}
+
+static int bzdClose( void * cookie)
+{
+ FD_t fd = c2f(cookie);
+ BZFILE *bzfile;
+ int rc;
+
+ bzfile = bzdFileno(fd);
+
+ if (bzfile == NULL) return -2;
+ fdstat_enter(fd, FDSTAT_CLOSE);
+ /* FIX: check rc */
+ bzclose(bzfile);
+ rc = 0; /* XXX FIXME */
+
+ /* XXX TODO: preserve fd if errors */
+
+ if (fd) {
+ if (rc == -1) {
+ int zerror = 0;
+ fd->errcookie = bzerror(bzfile, &zerror);
+ } else if (rc >= 0) {
+ fdstat_exit(fd, FDSTAT_CLOSE, rc);
+ }
+ }
+
+DBGIO(fd, (stderr, "==>\tbzdClose(%p) rc %lx %s\n", cookie, (unsigned long)rc, fdbg(fd)));
+
+ if (_rpmio_debug || rpmIsDebug()) fdstat_print(fd, "BZDIO", stderr);
+ if (rc == 0)
+ fd = fdFree(fd);
+ return rc;
+}
+
+static const struct FDIO_s bzdio_s = {
+ bzdRead, bzdWrite, fdSeekNot, bzdClose, fdLink, fdFree, fdNew, fdFileno,
+ NULL, bzdOpen, bzdFileno, bzdFlush
+};
+static const FDIO_t bzdio = &bzdio_s ;
+
+#endif /* HAVE_BZLIB_H */
+
+/* =============================================================== */
+static const char * getFdErrstr (FD_t fd)
+{
+ const char *errstr = NULL;
+
+ if (fdGetIo(fd) == gzdio) {
+ errstr = fd->errcookie;
+ } else
+#ifdef HAVE_BZLIB_H
+ if (fdGetIo(fd) == bzdio) {
+ errstr = fd->errcookie;
+ } else
+#endif /* HAVE_BZLIB_H */
+#ifdef HAVE_LZMA_H
+ if (fdGetIo(fd) == xzdio || fdGetIo(fd) == lzdio) {
+ errstr = fd->errcookie;
+ } else
+#endif /* HAVE_LZMA_H */
+ {
+ errstr = (fd->syserrno ? strerror(fd->syserrno) : "");
+ }
+
+ return errstr;
+}
+
+/* =============================================================== */
+/* Support for LZMA library.
+ */
+
+#ifdef HAVE_LZMA_H
+
+#include <sys/types.h>
+#include <inttypes.h>
+#include <lzma.h>
+
+#define kBufferSize (1 << 15)
+
+typedef struct lzfile {
+ /* IO buffer */
+ uint8_t buf[kBufferSize];
+
+ lzma_stream strm;
+
+ FILE *file;
+
+ int encoding;
+ int eof;
+
+} LZFILE;
+
+static LZFILE *lzopen_internal(const char *path, const char *mode, int fd, int xz)
+{
+ int level = 7; /* Use XZ's default compression level if unspecified */
+ int encoding = 0;
+ FILE *fp;
+ LZFILE *lzfile;
+ lzma_ret ret;
+ lzma_stream init_strm = LZMA_STREAM_INIT;
+
+ for (; *mode; mode++) {
+ if (*mode == 'w')
+ encoding = 1;
+ else if (*mode == 'r')
+ encoding = 0;
+ else if (*mode >= '1' && *mode <= '9')
+ level = *mode - '0';
+ }
+ if (fd != -1)
+ fp = fdopen(fd, encoding ? "w" : "r");
+ else
+ fp = fopen(path, encoding ? "w" : "r");
+ if (!fp)
+ return 0;
+ lzfile = calloc(1, sizeof(*lzfile));
+ if (!lzfile) {
+ fclose(fp);
+ return 0;
+ }
+
+ lzfile->file = fp;
+ lzfile->encoding = encoding;
+ lzfile->eof = 0;
+ lzfile->strm = init_strm;
+ if (encoding) {
+ if (xz) {
+ ret = lzma_easy_encoder(&lzfile->strm, level, LZMA_CHECK_SHA256);
+ } else {
+ lzma_options_lzma options;
+ lzma_lzma_preset(&options, level);
+ ret = lzma_alone_encoder(&lzfile->strm, &options);
+ }
+ } else { /* lzma_easy_decoder_memusage(level) is not ready yet, use hardcoded limit for now */
+ ret = lzma_auto_decoder(&lzfile->strm, 100<<20, 0);
+ }
+ if (ret != LZMA_OK) {
+ fclose(fp);
+ free(lzfile);
+ return 0;
+ }
+ return lzfile;
+}
+
+static LZFILE *xzopen(const char *path, const char *mode)
+{
+ return lzopen_internal(path, mode, -1, 1);
+}
+
+static LZFILE *xzdopen(int fd, const char *mode)
+{
+ if (fd < 0)
+ return 0;
+ return lzopen_internal(0, mode, fd, 1);
+}
+
+static LZFILE *lzopen(const char *path, const char *mode)
+{
+ return lzopen_internal(path, mode, -1, 0);
+}
+
+static LZFILE *lzdopen(int fd, const char *mode)
+{
+ if (fd < 0)
+ return 0;
+ return lzopen_internal(0, mode, fd, 0);
+}
+
+static int lzflush(LZFILE *lzfile)
+{
+ return fflush(lzfile->file);
+}
+
+static int lzclose(LZFILE *lzfile)
+{
+ lzma_ret ret;
+ size_t n;
+ int rc;
+
+ if (!lzfile)
+ return -1;
+ if (lzfile->encoding) {
+ for (;;) {
+ lzfile->strm.avail_out = kBufferSize;
+ lzfile->strm.next_out = lzfile->buf;
+ ret = lzma_code(&lzfile->strm, LZMA_FINISH);
+ if (ret != LZMA_OK && ret != LZMA_STREAM_END)
+ return -1;
+ n = kBufferSize - lzfile->strm.avail_out;
+ if (n && fwrite(lzfile->buf, 1, n, lzfile->file) != n)
+ return -1;
+ if (ret == LZMA_STREAM_END)
+ break;
+ }
+ }
+ lzma_end(&lzfile->strm);
+ rc = fclose(lzfile->file);
+ free(lzfile);
+ return rc;
+}
+
+static ssize_t lzread(LZFILE *lzfile, void *buf, size_t len)
+{
+ lzma_ret ret;
+ int eof = 0;
+
+ if (!lzfile || lzfile->encoding)
+ return -1;
+ if (lzfile->eof)
+ return 0;
+ lzfile->strm.next_out = buf;
+ lzfile->strm.avail_out = len;
+ for (;;) {
+ if (!lzfile->strm.avail_in) {
+ lzfile->strm.next_in = lzfile->buf;
+ lzfile->strm.avail_in = fread(lzfile->buf, 1, kBufferSize, lzfile->file);
+ if (!lzfile->strm.avail_in)
+ eof = 1;
+ }
+ ret = lzma_code(&lzfile->strm, LZMA_RUN);
+ if (ret == LZMA_STREAM_END) {
+ lzfile->eof = 1;
+ return len - lzfile->strm.avail_out;
+ }
+ if (ret != LZMA_OK)
+ return -1;
+ if (!lzfile->strm.avail_out)
+ return len;
+ if (eof)
+ return -1;
+ }
+}
+
+static ssize_t lzwrite(LZFILE *lzfile, void *buf, size_t len)
+{
+ lzma_ret ret;
+ size_t n;
+ if (!lzfile || !lzfile->encoding)
+ return -1;
+ if (!len)
+ return 0;
+ lzfile->strm.next_in = buf;
+ lzfile->strm.avail_in = len;
+ for (;;) {
+ lzfile->strm.next_out = lzfile->buf;
+ lzfile->strm.avail_out = kBufferSize;
+ ret = lzma_code(&lzfile->strm, LZMA_RUN);
+ if (ret != LZMA_OK)
+ return -1;
+ n = kBufferSize - lzfile->strm.avail_out;
+ if (n && fwrite(lzfile->buf, 1, n, lzfile->file) != n)
+ return -1;
+ if (!lzfile->strm.avail_in)
+ return len;
+ }
+}
+
+/* =============================================================== */
+
+static void * lzdFileno(FD_t fd)
+{
+ void * rc = NULL;
+
+ if (fd == NULL)
+ return NULL;
+
+ for (int i = fd->nfps; i >= 0; i--) {
+ FDSTACK_t * fps = &fd->fps[i];
+ if (fps->io != xzdio && fps->io != lzdio)
+ continue;
+ rc = fps->fp;
+ break;
+ }
+ return rc;
+}
+
+static FD_t xzdOpen(const char * path, const char * mode)
+{
+ FD_t fd;
+ LZFILE *lzfile;
+ if ((lzfile = xzopen(path, mode)) == NULL)
+ return NULL;
+ fd = fdNew();
+ fdPop(fd); fdPush(fd, xzdio, lzfile, -1);
+ return fdLink(fd);
+}
+
+static FD_t xzdFdopen(void * cookie, const char * fmode)
+{
+ FD_t fd = c2f(cookie);
+ int fdno;
+ LZFILE *lzfile;
+
+ if (fd == NULL || fmode == NULL) return NULL;
+ fdno = fdFileno(fd);
+ fdSetFdno(fd, -1); /* XXX skip the fdio close */
+ if (fdno < 0) return NULL;
+ lzfile = xzdopen(fdno, fmode);
+ if (lzfile == NULL) return NULL;
+ fdPush(fd, xzdio, lzfile, fdno);
+ return fdLink(fd);
+}
+
+static FD_t lzdOpen(const char * path, const char * mode)
+{
+ FD_t fd;
+ LZFILE *lzfile;
+ if ((lzfile = lzopen(path, mode)) == NULL)
+ return NULL;
+ fd = fdNew();
+ fdPop(fd); fdPush(fd, xzdio, lzfile, -1);
+ return fdLink(fd);
+}
+
+static FD_t lzdFdopen(void * cookie, const char * fmode)
+{
+ FD_t fd = c2f(cookie);
+ int fdno;
+ LZFILE *lzfile;
+
+ if (fd == NULL || fmode == NULL) return NULL;
+ fdno = fdFileno(fd);
+ fdSetFdno(fd, -1); /* XXX skip the fdio close */
+ if (fdno < 0) return NULL;
+ lzfile = lzdopen(fdno, fmode);
+ if (lzfile == NULL) return NULL;
+ fdPush(fd, xzdio, lzfile, fdno);
+ return fdLink(fd);
+}
+
+static int lzdFlush(FD_t fd)
+{
+ return lzflush(lzdFileno(fd));
+}
+
+/* =============================================================== */
+static ssize_t lzdRead(void * cookie, void * buf, size_t count)
+{
+ FD_t fd = c2f(cookie);
+ LZFILE *lzfile;
+ ssize_t rc = 0;
+
+ if (fd == NULL || fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
+ lzfile = lzdFileno(fd);
+ fdstat_enter(fd, FDSTAT_READ);
+ if (lzfile)
+ rc = lzread(lzfile, buf, count);
+ if (rc == -1) {
+ fd->errcookie = "Lzma: decoding error";
+ } else if (rc >= 0) {
+ fdstat_exit(fd, FDSTAT_READ, rc);
+ if (fd->digests && rc > 0) fdUpdateDigests(fd, buf, rc);
+ }
+ return rc;
+}
+
+static ssize_t lzdWrite(void * cookie, const void * buf, size_t count)
+{
+ FD_t fd = c2f(cookie);
+ LZFILE *lzfile;
+ ssize_t rc = 0;
+
+ if (fd == NULL || fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
+
+ if (fd->digests && count > 0) fdUpdateDigests(fd, buf, count);
+
+ lzfile = lzdFileno(fd);
+
+ fdstat_enter(fd, FDSTAT_WRITE);
+ rc = lzwrite(lzfile, (void *)buf, count);
+ if (rc < 0) {
+ fd->errcookie = "Lzma: encoding error";
+ } else if (rc > 0) {
+ fdstat_exit(fd, FDSTAT_WRITE, rc);
+ }
+ return rc;
+}
+
+static int lzdClose(void * cookie)
+{
+ FD_t fd = c2f(cookie);
+ LZFILE *lzfile;
+ int rc;
+
+ lzfile = lzdFileno(fd);
+
+ if (lzfile == NULL) return -2;
+ fdstat_enter(fd, FDSTAT_CLOSE);
+ rc = lzclose(lzfile);
+
+ /* XXX TODO: preserve fd if errors */
+
+ if (fd) {
+ if (rc == -1) {
+ fd->errcookie = strerror(ferror(lzfile->file));
+ } else if (rc >= 0) {
+ fdstat_exit(fd, FDSTAT_CLOSE, rc);
+ }
+ }
+
+DBGIO(fd, (stderr, "==>\tlzdClose(%p) rc %lx %s\n", cookie, (unsigned long)rc, fdbg(fd)));
+
+ if (_rpmio_debug || rpmIsDebug()) fdstat_print(fd, "XZDIO", stderr);
+ if (rc == 0)
+ fd = fdFree(fd);
+ return rc;
+}
+
+static struct FDIO_s xzdio_s = {
+ lzdRead, lzdWrite, fdSeekNot, lzdClose, NULL, NULL, NULL, fdFileno,
+ NULL, xzdOpen, lzdFileno, lzdFlush
+};
+static const FDIO_t xzdio = &xzdio_s;
+
+static struct FDIO_s lzdio_s = {
+ lzdRead, lzdWrite, fdSeekNot, lzdClose, NULL, NULL, NULL, fdFileno,
+ NULL, lzdOpen, lzdFileno, lzdFlush
+};
+static const FDIO_t lzdio = &lzdio_s;
+
+#endif /* HAVE_LZMA_H */
+
+/* =============================================================== */
+
+const char *Fstrerror(FD_t fd)
+{
+ if (fd == NULL)
+ return (errno ? strerror(errno) : "");
+ return getFdErrstr(fd);
+}
+
+#define FDIOVEC(_fd, _vec) \
+ ((fdGetIo(_fd) && fdGetIo(_fd)->_vec) ? fdGetIo(_fd)->_vec : NULL)
+
+ssize_t Fread(void *buf, size_t size, size_t nmemb, FD_t fd) {
+ fdio_read_function_t _read;
+ int rc;
+
+ if (fd == NULL)
+ return 0;
+
+ if (fdGetIo(fd) == fpio) {
+ rc = fread(buf, size, nmemb, fdGetFILE(fd));
+ return rc;
+ }
+
+ _read = FDIOVEC(fd, read);
+
+ rc = (_read ? (*_read) (fd, buf, size * nmemb) : -2);
+ return rc;
+}
+
+ssize_t Fwrite(const void *buf, size_t size, size_t nmemb, FD_t fd)
+{
+ fdio_write_function_t _write;
+ int rc;
+
+ if (fd == NULL)
+ return 0;
+
+ if (fdGetIo(fd) == fpio) {
+ rc = fwrite(buf, size, nmemb, fdGetFILE(fd));
+ return rc;
+ }
+
+ _write = FDIOVEC(fd, write);
+
+ rc = (_write ? _write(fd, buf, size * nmemb) : -2);
+ return rc;
+}
+
+int Fseek(FD_t fd, _libio_off_t offset, int whence) {
+ fdio_seek_function_t _seek;
+#ifdef USE_COOKIE_SEEK_POINTER
+ _IO_off64_t o64 = offset;
+ _libio_pos_t pos = &o64;
+#else
+ _libio_pos_t pos = offset;
+#endif
+
+ long int rc;
+
+ if (fd == NULL)
+ return -1;
+
+ if (fdGetIo(fd) == fpio) {
+ FILE *fp;
+
+ fp = fdGetFILE(fd);
+ rc = fseek(fp, offset, whence);
+ return rc;
+ }
+
+ _seek = FDIOVEC(fd, seek);
+
+ rc = (_seek ? _seek(fd, pos, whence) : -2);
+ return rc;
+}
+
+int Fclose(FD_t fd)
+{
+ int rc = 0, ec = 0;
+
+ if (fd == NULL)
+ return -1;
+
+ fd = fdLink(fd);
+ while (fd->nfps >= 0) {
+ FDSTACK_t * fps = &fd->fps[fd->nfps];
+
+ if (fps->io == fpio) {
+ FILE *fp;
+ int fpno;
+
+ fp = fdGetFILE(fd);
+ fpno = fileno(fp);
+ if (fp)
+ rc = fclose(fp);
+ if (fpno == -1) {
+ fd = fdFree(fd);
+ fdPop(fd);
+ }
+ } else {
+ fdio_close_function_t _close = FDIOVEC(fd, close);
+ rc = _close ? _close(fd) : -2;
+ }
+ if (fd->nfps == 0)
+ break;
+ if (ec == 0 && rc)
+ ec = rc;
+ fdPop(fd);
+ }
+ fd = fdFree(fd);
+ return ec;
+}
+
+/**
+ * Convert stdio fmode to open(2) mode, filtering out zlib/bzlib flags.
+ * returns stdio[0] = NUL on error.
+ *
+ * - gzopen: [0-9] is compression level
+ * - gzopen: 'f' is filtered (Z_FILTERED)
+ * - gzopen: 'h' is Huffman encoding (Z_HUFFMAN_ONLY)
+ * - bzopen: [1-9] is block size (modulo 100K)
+ * - bzopen: 's' is smallmode
+ * - HACK: '.' terminates, rest is type of I/O
+ */
+static void cvtfmode (const char *m,
+ char *stdio, size_t nstdio,
+ char *other, size_t nother,
+ const char **end, int * f)
+{
+ int flags = 0;
+ char c;
+
+ switch (*m) {
+ case 'a':
+ flags |= O_WRONLY | O_CREAT | O_APPEND;
+ if (--nstdio > 0) *stdio++ = *m;
+ break;
+ case 'w':
+ flags |= O_WRONLY | O_CREAT | O_TRUNC;
+ if (--nstdio > 0) *stdio++ = *m;
+ break;
+ case 'r':
+ flags |= O_RDONLY;
+ if (--nstdio > 0) *stdio++ = *m;
+ break;
+ default:
+ *stdio = '\0';
+ return;
+ break;
+ }
+ m++;
+
+ while ((c = *m++) != '\0') {
+ switch (c) {
+ case '.':
+ break;
+ case '+':
+ flags &= ~(O_RDONLY|O_WRONLY);
+ flags |= O_RDWR;
+ if (--nstdio > 0) *stdio++ = c;
+ continue;
+ break;
+ case 'b':
+ if (--nstdio > 0) *stdio++ = c;
+ continue;
+ break;
+ case 'x':
+ flags |= O_EXCL;
+ if (--nstdio > 0) *stdio++ = c;
+ continue;
+ break;
+ default:
+ if (--nother > 0) *other++ = c;
+ continue;
+ break;
+ }
+ break;
+ }
+
+ *stdio = *other = '\0';
+ if (end != NULL)
+ *end = (*m != '\0' ? m : NULL);
+ if (f != NULL)
+ *f = flags;
+}
+
+#if _USE_LIBIO
+#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ == 0
+/* XXX retrofit glibc-2.1.x typedef on glibc-2.0.x systems */
+typedef _IO_cookie_io_functions_t cookie_io_functions_t;
+#endif
+#endif
+
+FD_t Fdopen(FD_t ofd, const char *fmode)
+{
+ char stdio[20], other[20], zstdio[20];
+ const char *end = NULL;
+ FDIO_t iof = NULL;
+ FD_t fd = ofd;
+
+if (_rpmio_debug)
+fprintf(stderr, "*** Fdopen(%p,%s) %s\n", fd, fmode, fdbg(fd));
+
+ if (fd == NULL || fmode == NULL)
+ return NULL;
+
+ cvtfmode(fmode, stdio, sizeof(stdio), other, sizeof(other), &end, NULL);
+ if (stdio[0] == '\0')
+ return NULL;
+ zstdio[0] = '\0';
+ strncat(zstdio, stdio, sizeof(zstdio) - strlen(zstdio));
+ strncat(zstdio, other, sizeof(zstdio) - strlen(zstdio));
+
+ if (end == NULL && other[0] == '\0')
+ return fd;
+
+ if (end && *end) {
+ if (rstreq(end, "fdio")) {
+ iof = fdio;
+ } else if (rstreq(end, "gzdio") || rstreq(end, "gzip")) {
+ iof = gzdio;
+ fd = gzdFdopen(fd, zstdio);
+#if HAVE_BZLIB_H
+ } else if (rstreq(end, "bzdio") || rstreq(end, "bzip2")) {
+ iof = bzdio;
+ fd = bzdFdopen(fd, zstdio);
+#endif
+#if HAVE_LZMA_H
+ } else if (rstreq(end, "xzdio") || rstreq(end, "xz")) {
+ iof = xzdio;
+ fd = xzdFdopen(fd, zstdio);
+ } else if (rstreq(end, "lzdio") || rstreq(end, "lzma")) {
+ iof = lzdio;
+ fd = lzdFdopen(fd, zstdio);
+#endif
+ } else if (rstreq(end, "ufdio")) {
+ iof = ufdio;
+ } else if (rstreq(end, "fpio")) {
+ iof = fpio;
+ if (noLibio) {
+ int fdno = Fileno(fd);
+ FILE * fp = fdopen(fdno, stdio);
+if (_rpmio_debug)
+fprintf(stderr, "*** Fdopen fpio fp %p\n", (void *)fp);
+ if (fp == NULL)
+ return NULL;
+ /* XXX gzdio/bzdio use fp for private data */
+ if (fdGetFp(fd) == NULL)
+ fdSetFp(fd, fp);
+ fdPush(fd, fpio, fp, fdno); /* Push fpio onto stack */
+ }
+ }
+ } else if (other[0] != '\0') {
+ for (end = other; *end && strchr("0123456789fh", *end); end++)
+ {};
+ if (*end == '\0') {
+ iof = gzdio;
+ fd = gzdFdopen(fd, zstdio);
+ }
+ }
+ if (iof == NULL)
+ return fd;
+
+ if (!noLibio) {
+ FILE * fp = NULL;
+
+#if _USE_LIBIO
+ { cookie_io_functions_t ciof;
+ ciof.read = (cookie_read_function_t *) iof->read;
+ ciof.write = (cookie_write_function_t *) iof->write;
+ ciof.seek = iof->seek;
+ ciof.close = iof->close;
+ fp = fopencookie(fd, stdio, ciof);
+DBGIO(fd, (stderr, "==> fopencookie(%p,\"%s\",*%p) returns fp %p\n", fd, stdio, iof, fp));
+ }
+#endif
+
+ if (fp) {
+ /* XXX gzdio/bzdio use fp for private data */
+ if (fdGetFp(fd) == NULL)
+ fdSetFp(fd, fp);
+ fdPush(fd, fpio, fp, fileno(fp)); /* Push fpio onto stack */
+ fd = fdLink(fd);
+ }
+ }
+
+DBGIO(fd, (stderr, "==> Fdopen(%p,\"%s\") returns fd %p %s\n", ofd, fmode, (fd ? fd : NULL), fdbg(fd)));
+ return fd;
+}
+
+FD_t Fopen(const char *path, const char *fmode)
+{
+ char stdio[20], other[20];
+ const char *end = NULL;
+ mode_t perms = 0666;
+ int flags = 0;
+ FD_t fd;
+
+ if (path == NULL || fmode == NULL)
+ return NULL;
+
+ stdio[0] = '\0';
+ cvtfmode(fmode, stdio, sizeof(stdio), other, sizeof(other), &end, &flags);
+ if (stdio[0] == '\0')
+ return NULL;
+
+ if (end == NULL || rstreq(end, "fdio")) {
+if (_rpmio_debug)
+fprintf(stderr, "*** Fopen fdio path %s fmode %s\n", path, fmode);
+ fd = fdOpen(path, flags, perms);
+ if (fdFileno(fd) < 0) {
+ if (fd) (void) fdClose(fd);
+ return NULL;
+ }
+ } else {
+ /* XXX gzdio and bzdio here too */
+
+ switch (urlIsURL(path)) {
+ case URL_IS_HTTPS:
+ case URL_IS_HTTP:
+ case URL_IS_HKP:
+ case URL_IS_PATH:
+ case URL_IS_DASH:
+ case URL_IS_FTP:
+ case URL_IS_UNKNOWN:
+if (_rpmio_debug)
+fprintf(stderr, "*** Fopen ufdio path %s fmode %s\n", path, fmode);
+ fd = ufdOpen(path, flags, perms);
+ if (fd == NULL || !(fdFileno(fd) >= 0))
+ return fd;
+ break;
+ default:
+if (_rpmio_debug)
+fprintf(stderr, "*** Fopen WTFO path %s fmode %s\n", path, fmode);
+ return NULL;
+ break;
+ }
+
+ }
+
+ if (fd)
+ fd = Fdopen(fd, fmode);
+ return fd;
+}
+
+int Fflush(FD_t fd)
+{
+ void * vh;
+ if (fd == NULL) return -1;
+ if (fdGetIo(fd) == fpio)
+ return fflush(fdGetFILE(fd));
+
+ vh = fdGetFp(fd);
+ if (vh && fdGetIo(fd) == gzdio)
+ return gzdFlush(vh);
+#if HAVE_BZLIB_H
+ if (vh && fdGetIo(fd) == bzdio)
+ return bzdFlush(vh);
+#endif
+#if HAVE_LZMA_H
+ if (vh && (fdGetIo(fd) == xzdio || fdGetIo(fd) == lzdio))
+ return lzdFlush(vh);
+#endif
+/* FIXME: If we get here, something went wrong above */
+ return 0;
+}
+
+off_t Ftell(FD_t fd)
+{
+ FDIO_t iot;
+ off_t pos = -2; /* assume not implemented */
+
+ if (fd == NULL) return -1;
+ iot = fdGetIo(fd);
+ /* this wont work correctly for compressed types */
+ if (iot == fpio || iot == fdio || iot == ufdio) {
+ pos = lseek(Fileno(fd), 0, SEEK_CUR);
+ }
+
+ return pos;
+}
+
+int Ferror(FD_t fd)
+{
+ int i, rc = 0;
+
+ if (fd == NULL) return -1;
+ for (i = fd->nfps; rc == 0 && i >= 0; i--) {
+ FDSTACK_t * fps = &fd->fps[i];
+ int ec;
+
+ if (fps->io == fpio) {
+ ec = ferror(fdGetFILE(fd));
+ } else if (fps->io == gzdio) {
+ ec = (fd->syserrno || fd->errcookie != NULL) ? -1 : 0;
+ i--; /* XXX fdio under gzdio always has fdno == -1 */
+#if HAVE_BZLIB_H
+ } else if (fps->io == bzdio) {
+ ec = (fd->syserrno || fd->errcookie != NULL) ? -1 : 0;
+ i--; /* XXX fdio under bzdio always has fdno == -1 */
+#endif
+#if HAVE_LZMA_H
+ } else if (fps->io == xzdio || fps->io == lzdio) {
+ ec = (fd->syserrno || fd->errcookie != NULL) ? -1 : 0;
+ i--; /* XXX fdio under xzdio/lzdio always has fdno == -1 */
+#endif
+ } else {
+ /* XXX need to check ufdio/gzdio/bzdio/fdio errors correctly. */
+ ec = (fdFileno(fd) < 0 ? -1 : 0);
+ }
+
+ if (rc == 0 && ec)
+ rc = ec;
+ }
+DBGIO(fd, (stderr, "==> Ferror(%p) rc %d %s\n", fd, rc, fdbg(fd)));
+ return rc;
+}
+
+int Fileno(FD_t fd)
+{
+ int i, rc = -1;
+
+ if (fd == NULL) return -1;
+ for (i = fd->nfps ; rc == -1 && i >= 0; i--) {
+ rc = fd->fps[i].fdno;
+ }
+
+DBGIO(fd, (stderr, "==> Fileno(%p) rc %d %s\n", (fd ? fd : NULL), rc, fdbg(fd)));
+ return rc;
+}
+
+/* XXX this is naive */
+int Fcntl(FD_t fd, int op, void *lip)
+{
+ return fcntl(Fileno(fd), op, lip);
+}
+
+rpmop fdOp(FD_t fd, fdOpX opx)
+{
+ rpmop op = NULL;
+
+ if (fd != NULL && fd->stats != NULL && opx >= 0 && opx < FDSTAT_MAX)
+ op = fd->stats->ops + opx;
+ return op;
+}
+
+int rpmioSlurp(const char * fn, uint8_t ** bp, ssize_t * blenp)
+{
+ static const ssize_t blenmax = (32 * BUFSIZ);
+ ssize_t blen = 0;
+ uint8_t * b = NULL;
+ ssize_t size;
+ FD_t fd;
+ int rc = 0;
+
+ fd = Fopen(fn, "r.ufdio");
+ if (fd == NULL || Ferror(fd)) {
+ rc = 2;
+ goto exit;
+ }
+
+ size = fdSize(fd);
+ blen = (size >= 0 ? size : blenmax);
+ if (blen) {
+ int nb;
+ b = xmalloc(blen+1);
+ b[0] = '\0';
+ nb = Fread(b, sizeof(*b), blen, fd);
+ if (Ferror(fd) || (size > 0 && nb != blen)) {
+ rc = 1;
+ goto exit;
+ }
+ if (blen == blenmax && nb < blen) {
+ blen = nb;
+ b = xrealloc(b, blen+1);
+ }
+ b[blen] = '\0';
+ }
+
+exit:
+ if (fd) (void) Fclose(fd);
+
+ if (rc) {
+ if (b) free(b);
+ b = NULL;
+ blen = 0;
+ }
+
+ if (bp) *bp = b;
+ else if (b) free(b);
+
+ if (blenp) *blenp = blen;
+
+ return rc;
+}
+
+static const struct FDIO_s fpio_s = {
+ fdRead, fdWrite, fdSeek, fdClose, fdLink, fdFree, fdNew, fdFileno,
+ ufdOpen, NULL, fdGetFp, NULL
+};
+static const FDIO_t fpio = &fpio_s ;
+
+void fdInitDigest(FD_t fd, int hashalgo, rpmDigestFlags flags)
+{
+ if (fd->digests == NULL) {
+ fd->digests = rpmDigestBundleNew();
+ }
+ fdstat_enter(fd, FDSTAT_DIGEST);
+ rpmDigestBundleAdd(fd->digests, hashalgo, flags);
+ fdstat_exit(fd, FDSTAT_DIGEST, (ssize_t) 0);
+}
+
+static void fdUpdateDigests(FD_t fd, const void * buf, size_t buflen)
+{
+ if (fd && fd->digests) {
+ fdstat_enter(fd, FDSTAT_DIGEST);
+ rpmDigestBundleUpdate(fd->digests, buf, buflen);
+ fdstat_exit(fd, FDSTAT_DIGEST, (ssize_t) buflen);
+ }
+}
+
+void fdFiniDigest(FD_t fd, int hashalgo,
+ void ** datap, size_t * lenp, int asAscii)
+{
+ if (fd && fd->digests) {
+ fdstat_enter(fd, FDSTAT_DIGEST);
+ rpmDigestBundleFinal(fd->digests, hashalgo, datap, lenp, asAscii);
+ fdstat_exit(fd, FDSTAT_DIGEST, (ssize_t) 0);
+ }
+}
+
+
diff --git a/rpmio/rpmio.h b/rpmio/rpmio.h
new file mode 100644
index 0000000..93072e0
--- /dev/null
+++ b/rpmio/rpmio.h
@@ -0,0 +1,169 @@
+#ifndef H_RPMIO
+#define H_RPMIO
+
+/** \ingroup rpmio
+ * \file rpmio/rpmio.h
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <rpm/rpmtypes.h>
+#include <rpm/rpmsw.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** \ingroup rpmio
+ * Hide libio API lossage.
+ * The libio interface changed after glibc-2.1.3 to pass the seek offset
+ * argument as a pointer rather than as an off_t. The snarl below defines
+ * typedefs to isolate the lossage.
+ */
+#if defined(__GLIBC__) && \
+ (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2))
+#define USE_COOKIE_SEEK_POINTER 1
+typedef _IO_off64_t _libio_off_t;
+typedef _libio_off_t * _libio_pos_t;
+#else
+typedef off_t _libio_off_t;
+typedef off_t _libio_pos_t;
+#endif
+
+/** \ingroup rpmio
+ */
+typedef const struct FDIO_s * FDIO_t;
+
+
+/** \ingroup rpmio
+ * \name RPMIO Interface.
+ */
+
+/** \ingroup rpmio
+ * strerror(3) clone.
+ */
+const char * Fstrerror(FD_t fd);
+
+/** \ingroup rpmio
+ * fread(3) clone.
+ */
+ssize_t Fread(void * buf, size_t size, size_t nmemb, FD_t fd);
+
+/** \ingroup rpmio
+ * fwrite(3) clone.
+ */
+ssize_t Fwrite(const void * buf, size_t size, size_t nmemb, FD_t fd);
+
+/** \ingroup rpmio
+ * fseek(3) clone.
+ */
+int Fseek(FD_t fd, _libio_off_t offset, int whence);
+
+/** \ingroup rpmio
+ * ftell(3) clone.
+ */
+off_t Ftell(FD_t fd);
+
+/** \ingroup rpmio
+ * fclose(3) clone.
+ */
+int Fclose( FD_t fd);
+
+/** \ingroup rpmio
+ */
+FD_t Fdopen(FD_t ofd, const char * fmode);
+
+/** \ingroup rpmio
+ * fopen(3) clone.
+ */
+FD_t Fopen(const char * path,
+ const char * fmode);
+
+
+/** \ingroup rpmio
+ * fflush(3) clone.
+ */
+int Fflush(FD_t fd);
+
+/** \ingroup rpmio
+ * ferror(3) clone.
+ */
+int Ferror(FD_t fd);
+
+/** \ingroup rpmio
+ * fileno(3) clone.
+ */
+int Fileno(FD_t fd);
+
+/** \ingroup rpmio
+ * fcntl(2) clone.
+ */
+int Fcntl(FD_t fd, int op, void *lip);
+
+/** \ingroup rpmio
+ * \name RPMIO Utilities.
+ */
+
+/** \ingroup rpmio
+ */
+off_t fdSize(FD_t fd);
+
+/** \ingroup rpmio
+ */
+FD_t fdDup(int fdno);
+
+/** \ingroup rpmio
+ * Get associated FILE stream from fd (if any)
+ */
+FILE * fdGetFILE(FD_t fd);
+
+/** \ingroup rpmio
+ */
+FD_t fdLink(void * cookie);
+
+/** \ingroup rpmio
+ */
+FD_t fdFree(FD_t fd);
+
+/** \ingroup rpmio
+ */
+FD_t fdNew (void);
+
+/**
+ */
+int ufdCopy(FD_t sfd, FD_t tfd);
+
+/**
+ * XXX the name is misleading, this is a legacy wrapper that ensures
+ * only S_ISREG() files are read, nothing to do with timed...
+ * TODO: get this out of the API
+ */
+ssize_t timedRead(FD_t fd, void * bufptr, size_t length);
+
+/** \ingroup rpmio
+ * Identify per-desciptor I/O operation statistics.
+ */
+typedef enum fdOpX_e {
+ FDSTAT_READ = 0, /*!< Read statistics index. */
+ FDSTAT_WRITE = 1, /*!< Write statistics index. */
+ FDSTAT_SEEK = 2, /*!< Seek statistics index. */
+ FDSTAT_CLOSE = 3, /*!< Close statistics index */
+ FDSTAT_DIGEST = 4, /*!< Digest statistics index. */
+ FDSTAT_MAX = 5
+} fdOpX;
+
+/** \ingroup rpmio
+ *
+ */
+rpmop fdOp(FD_t fd, fdOpX opx);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* H_RPMIO */
diff --git a/rpmio/rpmio_internal.h b/rpmio/rpmio_internal.h
new file mode 100644
index 0000000..8c9f1a8
--- /dev/null
+++ b/rpmio/rpmio_internal.h
@@ -0,0 +1,44 @@
+#ifndef H_RPMIO_INTERNAL
+#define H_RPMIO_INTERNAL
+
+/** \ingroup rpmio
+ * \file rpmio/rpmio_internal.h
+ */
+
+#include <rpm/rpmio.h>
+#include <rpm/rpmpgp.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void fdSetBundle(FD_t fd, rpmDigestBundle bundle);
+rpmDigestBundle fdGetBundle(FD_t fd);
+
+/** \ingroup rpmio
+ * Attach digest to fd.
+ */
+void fdInitDigest(FD_t fd, int hashalgo, rpmDigestFlags flags);
+
+/** \ingroup rpmio
+ */
+void fdFiniDigest(FD_t fd, int hashalgo,
+ void ** datap,
+ size_t * lenp,
+ int asAscii);
+
+/**
+ * Read an entire file into a buffer.
+ * @param fn file name to read
+ * @retval *bp (malloc'd) buffer address
+ * @retval *blenp (malloc'd) buffer length
+ * @return 0 on success
+ */
+int rpmioSlurp(const char * fn,
+ uint8_t ** bp, ssize_t * blenp);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* H_RPMIO_INTERNAL */
diff --git a/rpmio/rpmkeyring.c b/rpmio/rpmkeyring.c
new file mode 100644
index 0000000..784bc3a
--- /dev/null
+++ b/rpmio/rpmkeyring.c
@@ -0,0 +1,231 @@
+#include "system.h"
+
+#include <rpm/rpmstring.h>
+#include <rpm/rpmpgp.h>
+#include <rpm/rpmfileutil.h>
+#include <rpm/rpmlog.h>
+#include <rpm/rpmkeyring.h>
+
+#include "rpmio/base64.h"
+#include "rpmio/digest.h"
+
+#include "debug.h"
+
+struct rpmPubkey_s {
+ uint8_t *pkt;
+ size_t pktlen;
+ pgpKeyID_t keyid;
+ int nrefs;
+};
+
+struct rpmKeyring_s {
+ struct rpmPubkey_s **keys;
+ size_t numkeys;
+ int nrefs;
+};
+
+static rpmPubkey rpmPubkeyUnlink(rpmPubkey key);
+static rpmKeyring rpmKeyringUnlink(rpmKeyring keyring);
+
+static int keyidcmp(const void *k1, const void *k2)
+{
+ const struct rpmPubkey_s *key1 = *(const struct rpmPubkey_s **) k1;
+ const struct rpmPubkey_s *key2 = *(const struct rpmPubkey_s **) k2;
+
+ return memcmp(key1->keyid, key2->keyid, sizeof(key1->keyid));
+}
+
+rpmKeyring rpmKeyringNew(void)
+{
+ rpmKeyring keyring = xcalloc(1, sizeof(*keyring));
+ keyring->keys = NULL;
+ keyring->numkeys = 0;
+ keyring->nrefs = 0;
+ return rpmKeyringLink(keyring);
+}
+
+rpmKeyring rpmKeyringFree(rpmKeyring keyring)
+{
+ if (keyring == NULL) {
+ return NULL;
+ }
+
+ if (keyring->nrefs > 1) {
+ return rpmKeyringUnlink(keyring);
+ }
+
+ if (keyring->keys) {
+ for (int i = 0; i < keyring->numkeys; i++) {
+ keyring->keys[i] = rpmPubkeyFree(keyring->keys[i]);
+ }
+ free(keyring->keys);
+ }
+ free(keyring);
+ return NULL;
+}
+
+static rpmPubkey rpmKeyringFindKeyid(rpmKeyring keyring, rpmPubkey key)
+{
+ rpmPubkey *found = NULL;
+ found = bsearch(&key, keyring->keys, keyring->numkeys, sizeof(*keyring->keys), keyidcmp);
+ return found ? *found : NULL;
+}
+
+int rpmKeyringAddKey(rpmKeyring keyring, rpmPubkey key)
+{
+ if (keyring == NULL || key == NULL)
+ return -1;
+
+ /* check if we already have this key */
+ if (rpmKeyringFindKeyid(keyring, key)) {
+ return 1;
+ }
+
+ keyring->keys = xrealloc(keyring->keys, (keyring->numkeys + 1) * sizeof(rpmPubkey));
+ keyring->keys[keyring->numkeys] = rpmPubkeyLink(key);
+ keyring->numkeys++;
+ qsort(keyring->keys, keyring->numkeys, sizeof(*keyring->keys), keyidcmp);
+
+ return 0;
+}
+
+rpmKeyring rpmKeyringLink(rpmKeyring keyring)
+{
+ if (keyring) {
+ keyring->nrefs++;
+ }
+ return keyring;
+}
+
+static rpmKeyring rpmKeyringUnlink(rpmKeyring keyring)
+{
+ if (keyring) {
+ keyring->nrefs--;
+ }
+ return NULL;
+}
+
+rpmPubkey rpmPubkeyRead(const char *filename)
+{
+ uint8_t *pkt = NULL;
+ size_t pktlen;
+ rpmPubkey key = NULL;
+
+ if (pgpReadPkts(filename, &pkt, &pktlen) <= 0) {
+ goto exit;
+ }
+ key = rpmPubkeyNew(pkt, pktlen);
+ free(pkt);
+
+exit:
+ return key;
+}
+
+rpmPubkey rpmPubkeyNew(const uint8_t *pkt, size_t pktlen)
+{
+ rpmPubkey key = NULL;
+
+ if (pkt == NULL || pktlen == 0)
+ goto exit;
+
+ key = xcalloc(1, sizeof(*key));
+ pgpPubkeyFingerprint(pkt, pktlen, key->keyid);
+ key->pkt = xmalloc(pktlen);
+ key->pktlen = pktlen;
+ key->nrefs = 0;
+ memcpy(key->pkt, pkt, pktlen);
+
+exit:
+ return rpmPubkeyLink(key);
+}
+
+rpmPubkey rpmPubkeyFree(rpmPubkey key)
+{
+ if (key == NULL)
+ return NULL;
+
+ if (key->nrefs > 1)
+ return rpmPubkeyUnlink(key);
+
+ free(key->pkt);
+ free(key);
+ return NULL;
+}
+
+rpmPubkey rpmPubkeyLink(rpmPubkey key)
+{
+ if (key) {
+ key->nrefs++;
+ }
+ return key;
+}
+
+static rpmPubkey rpmPubkeyUnlink(rpmPubkey key)
+{
+ if (key) {
+ key->nrefs--;
+ }
+ return NULL;
+}
+
+pgpDig rpmPubkeyDig(rpmPubkey key)
+{
+ pgpDig dig = NULL;
+ static unsigned char zeros[] =
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ int rc;
+ if (key == NULL)
+ return NULL;
+
+ dig = pgpNewDig();
+ rc = pgpPrtPkts(key->pkt, key->pktlen, dig, 0);
+ if (rc == 0) {
+ pgpDigParams pubp = &dig->pubkey;
+ if (!memcmp(pubp->signid, zeros, sizeof(pubp->signid)) ||
+ !memcmp(pubp->time, zeros, sizeof(pubp->time)) ||
+ pubp->userid == NULL) {
+ rc = -1;
+ }
+ }
+
+ if (rc)
+ dig = pgpFreeDig(dig);
+
+ return dig;
+}
+
+char * rpmPubkeyBase64(rpmPubkey key)
+{
+ char *enc = NULL;
+
+ if (key) {
+ enc = b64encode(key->pkt, key->pktlen, -1);
+ }
+ return enc;
+}
+
+rpmRC rpmKeyringLookup(rpmKeyring keyring, pgpDig sig)
+{
+ rpmRC res = RPMRC_NOKEY;
+
+ if (keyring && sig) {
+ pgpDigParams sigp = &sig->signature;
+ pgpDigParams pubp = &sig->pubkey;
+ struct rpmPubkey_s needle, *key;
+ needle.pkt = NULL;
+ needle.pktlen = 0;
+ memcpy(needle.keyid, sigp->signid, sizeof(needle.keyid));
+
+ if ((key = rpmKeyringFindKeyid(keyring, &needle))) {
+ /* Retrieve parameters from pubkey packet(s) */
+ int pktrc = pgpPrtPkts(key->pkt, key->pktlen, sig, 0);
+ /* Do the parameters match the signature? */
+ if (pktrc == 0 && sigp->pubkey_algo == pubp->pubkey_algo &&
+ memcmp(sigp->signid, pubp->signid, sizeof(sigp->signid)) == 0) {
+ res = RPMRC_OK;
+ }
+ }
+ }
+
+ return res;
+}
diff --git a/rpmio/rpmkeyring.h b/rpmio/rpmkeyring.h
new file mode 100644
index 0000000..8b4378b
--- /dev/null
+++ b/rpmio/rpmkeyring.h
@@ -0,0 +1,96 @@
+#ifndef _RPMKEYRING_H
+#define _RPMKEYRING_H
+
+/** \ingroup rpmkeyring
+ * \file rpmio/rpmkeyring.h
+ */
+
+#include <rpm/rpmtypes.h>
+#include <rpm/rpmpgp.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** \ingroup rpmkeyring
+ * Create a new, empty keyring
+ * @return new keyring handle
+ */
+rpmKeyring rpmKeyringNew(void);
+
+/** \ingroup rpmkeyring
+ * Free keyring and the keys within it
+ * @return NULL always
+ */
+rpmKeyring rpmKeyringFree(rpmKeyring keyring);
+
+/** \ingroup rpmkeyring
+ * Add a public key to keyring.
+ * @param keyring keyring handle
+ * @param key pubkey handle
+ * @return 0 on success, -1 on error, 1 if key already present
+ */
+int rpmKeyringAddKey(rpmKeyring keyring, rpmPubkey key);
+
+/** \ingroup rpmkeyring
+ * Perform keyring lookup for a key matching a signature
+ * @param keyring keyring handle
+ * @param sig OpenPGP packet container of signature
+ * @return RPMRC_OK if found, RPMRC_NOKEY otherwise
+ */
+rpmRC rpmKeyringLookup(rpmKeyring keyring, pgpDig sig);
+
+/** \ingroup rpmkeyring
+ * Reference a keyring.
+ * @param keyring keyring handle
+ * @return new keyring reference
+ */
+rpmKeyring rpmKeyringLink(rpmKeyring keyring);
+
+/** \ingroup rpmkeyring
+ * Create a new rpmPubkey from OpenPGP packet
+ * @param pkt OpenPGP packet data
+ * @param pktlen Data length
+ * @return new pubkey handle
+ */
+rpmPubkey rpmPubkeyNew(const uint8_t *pkt, size_t pktlen);
+
+/** \ingroup rpmkeyring
+ * Create a new rpmPubkey from ASCII-armored pubkey file
+ * @param filename Path to pubkey file
+ * @return new pubkey handle
+ */
+rpmPubkey rpmPubkeyRead(const char *filename);
+
+/** \ingroup rpmkeyring
+ * Free a pubkey.
+ * @param key Pubkey to free
+ * @return NULL always
+ */
+rpmPubkey rpmPubkeyFree(rpmPubkey key);
+
+/** \ingroup rpmkeyring
+ * Reference a pubkey.
+ * @param key Pubkey
+ * @return new pubkey reference
+ */
+rpmPubkey rpmPubkeyLink(rpmPubkey key);
+
+/** \ingroup rpmkeyring
+ * Parse OpenPGP pubkey parameters.
+ * @param key Pubkey
+ * @return parsed output of pubkey packet parameters
+ */
+pgpDig rpmPubkeyDig(rpmPubkey key);
+
+/** \ingroup rpmkeyring
+ * Return base64 encoding of pubkey
+ * @param key Pubkey
+ * @return base64 encoded pubkey (malloced), NULL on error
+ */
+char * rpmPubkeyBase64(rpmPubkey key);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _RPMKEYDB_H */
diff --git a/rpmio/rpmlog.c b/rpmio/rpmlog.c
new file mode 100644
index 0000000..8023d5c
--- /dev/null
+++ b/rpmio/rpmlog.c
@@ -0,0 +1,230 @@
+/** \ingroup rpmio
+ * \file rpmio/rpmlog.c
+ */
+
+#include "system.h"
+#include <stdarg.h>
+#include <stdlib.h>
+#include <rpm/rpmlog.h>
+#include "debug.h"
+
+static int nrecs = 0;
+static rpmlogRec recs = NULL;
+
+struct rpmlogRec_s {
+ int code; /* unused */
+ rpmlogLvl pri; /* priority */
+ char * message; /* log message string */
+};
+
+int rpmlogGetNrecs(void)
+{
+ return nrecs;
+}
+
+int rpmlogCode(void)
+{
+ if (recs != NULL && nrecs > 0)
+ return recs[nrecs-1].code;
+ return -1;
+}
+
+
+const char * rpmlogMessage(void)
+{
+ if (recs != NULL && nrecs > 0)
+ return recs[nrecs-1].message;
+ return _("(no error)");
+}
+
+const char * rpmlogRecMessage(rpmlogRec rec)
+{
+ assert(rec != NULL);
+ return (rec->message);
+}
+
+rpmlogLvl rpmlogRecPriority(rpmlogRec rec)
+{
+ assert(rec != NULL);
+ return (rec->pri);
+}
+
+void rpmlogPrint(FILE *f)
+{
+ int i;
+
+ if (f == NULL)
+ f = stderr;
+
+ if (recs)
+ for (i = 0; i < nrecs; i++) {
+ rpmlogRec rec = recs + i;
+ if (rec->message && *rec->message)
+ fprintf(f, " %s", rec->message);
+ }
+}
+
+void rpmlogClose (void)
+{
+ int i;
+
+ if (recs)
+ for (i = 0; i < nrecs; i++) {
+ rpmlogRec rec = recs + i;
+ rec->message = _free(rec->message);
+ }
+ recs = _free(recs);
+ nrecs = 0;
+}
+
+void rpmlogOpen (const char *ident, int option,
+ int facility)
+{
+}
+
+static unsigned rpmlogMask = RPMLOG_UPTO( RPMLOG_NOTICE );
+
+#ifdef NOTYET
+static unsigned rpmlogFacility = RPMLOG_USER;
+#endif
+
+int rpmlogSetMask (int mask)
+{
+ int omask = rpmlogMask;
+ if (mask)
+ rpmlogMask = mask;
+ return omask;
+}
+
+static rpmlogCallback _rpmlogCallback = NULL;
+static rpmlogCallbackData _rpmlogCallbackData = NULL;
+
+rpmlogCallback rpmlogSetCallback(rpmlogCallback cb, rpmlogCallbackData data)
+{
+ rpmlogCallback ocb = _rpmlogCallback;
+ _rpmlogCallback = cb;
+ _rpmlogCallbackData = data;
+ return ocb;
+}
+
+static FILE * _stdlog = NULL;
+
+static int rpmlogDefault(rpmlogRec rec)
+{
+ FILE *msgout = (_stdlog ? _stdlog : stderr);
+
+ switch (rec->pri) {
+ case RPMLOG_INFO:
+ case RPMLOG_NOTICE:
+ msgout = (_stdlog ? _stdlog : stdout);
+ break;
+ case RPMLOG_EMERG:
+ case RPMLOG_ALERT:
+ case RPMLOG_CRIT:
+ case RPMLOG_ERR:
+ case RPMLOG_WARNING:
+ case RPMLOG_DEBUG:
+ default:
+ break;
+ }
+
+ (void) fputs(rpmlogLevelPrefix(rec->pri), msgout);
+
+ (void) fputs(rec->message, msgout);
+ (void) fflush(msgout);
+
+ return (rec->pri <= RPMLOG_CRIT ? RPMLOG_EXIT : 0);
+}
+
+
+FILE * rpmlogSetFile(FILE * fp)
+{
+ FILE * ofp = _stdlog;
+ _stdlog = fp;
+ return ofp;
+}
+
+static const char * const rpmlogMsgPrefix[] = {
+ N_("fatal error: "),/*!< RPMLOG_EMERG */
+ N_("fatal error: "),/*!< RPMLOG_ALERT */
+ N_("fatal error: "),/*!< RPMLOG_CRIT */
+ N_("error: "), /*!< RPMLOG_ERR */
+ N_("warning: "), /*!< RPMLOG_WARNING */
+ "", /*!< RPMLOG_NOTICE */
+ "", /*!< RPMLOG_INFO */
+ "D: ", /*!< RPMLOG_DEBUG */
+};
+
+const char * rpmlogLevelPrefix(rpmlogLvl pri)
+{
+ const char * prefix = "";
+ if (rpmlogMsgPrefix[pri] && *rpmlogMsgPrefix[pri])
+ prefix = _(rpmlogMsgPrefix[pri]);
+ return prefix;
+}
+
+/* FIX: rpmlogMsgPrefix[] dependent, not unqualified */
+/* FIX: rpmlogMsgPrefix[] may be NULL */
+static void dolog (struct rpmlogRec_s *rec)
+{
+ int cbrc = RPMLOG_DEFAULT;
+ int needexit = 0;
+
+ /* Save copy of all messages at warning (or below == "more important"). */
+ if (rec->pri <= RPMLOG_WARNING) {
+ recs = xrealloc(recs, (nrecs+2) * sizeof(*recs));
+ recs[nrecs].code = rec->code;
+ recs[nrecs].pri = rec->pri;
+ recs[nrecs].message = xstrdup(rec->message);
+ recs[nrecs+1].code = 0;
+ recs[nrecs+1].message = NULL;
+ ++nrecs;
+ }
+
+ if (_rpmlogCallback) {
+ cbrc = _rpmlogCallback(rec, _rpmlogCallbackData);
+ needexit += cbrc & RPMLOG_EXIT;
+ }
+
+ if (cbrc & RPMLOG_DEFAULT) {
+ cbrc = rpmlogDefault(rec);
+ needexit += cbrc & RPMLOG_EXIT;
+ }
+
+ if (needexit)
+ exit(EXIT_FAILURE);
+}
+
+void rpmlog (int code, const char *fmt, ...)
+{
+ unsigned pri = RPMLOG_PRI(code);
+ unsigned mask = RPMLOG_MASK(pri);
+ va_list ap;
+ int n;
+
+ if ((mask & rpmlogMask) == 0)
+ return;
+
+ va_start(ap, fmt);
+ n = vsnprintf(NULL, 0, fmt, ap);
+ va_end(ap);
+
+ if (n >= -1) {
+ struct rpmlogRec_s rec;
+ size_t nb = n + 1;
+ char *msg = xmalloc(nb);
+
+ va_start(ap, fmt);
+ n = vsnprintf(msg, nb, fmt, ap);
+ va_end(ap);
+
+ rec.code = code;
+ rec.pri = pri;
+ rec.message = msg;
+
+ dolog(&rec);
+
+ free(msg);
+ }
+}
+
diff --git a/rpmio/rpmlog.h b/rpmio/rpmlog.h
new file mode 100644
index 0000000..0766086
--- /dev/null
+++ b/rpmio/rpmlog.h
@@ -0,0 +1,281 @@
+#ifndef H_RPMLOG
+#define H_RPMLOG 1
+
+/** \ingroup rpmio
+ * \file rpmio/rpmlog.h
+ * Yet Another syslog(3) API clone.
+ * Used to unify rpmError() and rpmMessage() interfaces in rpm.
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#include <rpm/rpmutil.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** \ingroup rpmlog
+ * RPM Log levels.
+ * priorities/facilities are encoded into a single 32-bit quantity, where the
+ * bottom 3 bits are the priority (0-7) and the top 28 bits are the facility
+ * (0-big number). Both the priorities and the facilities map roughly
+ * one-to-one to strings in the syslogd(8) source code. This mapping is
+ * included in this file.
+ *
+ * priorities (these are ordered)
+ */
+typedef enum rpmlogLvl_e {
+ RPMLOG_EMERG = 0, /*!< system is unusable */
+ RPMLOG_ALERT = 1, /*!< action must be taken immediately */
+ RPMLOG_CRIT = 2, /*!< critical conditions */
+ RPMLOG_ERR = 3, /*!< error conditions */
+ RPMLOG_WARNING = 4, /*!< warning conditions */
+ RPMLOG_NOTICE = 5, /*!< normal but significant condition */
+ RPMLOG_INFO = 6, /*!< informational */
+ RPMLOG_DEBUG = 7 /*!< debug-level messages */
+} rpmlogLvl;
+
+#define RPMLOG_PRIMASK 0x07 /* mask to extract priority part (internal) */
+ /* extract priority */
+#define RPMLOG_PRI(p) ((p) & RPMLOG_PRIMASK)
+#define RPMLOG_MAKEPRI(fac, pri) ((((unsigned)(fac)) << 3) | (pri))
+
+#ifdef RPMLOG_NAMES
+#define _RPMLOG_NOPRI 0x10 /* the "no priority" priority */
+ /* mark "facility" */
+#define _RPMLOG_MARK RPMLOG_MAKEPRI(RPMLOG_NFACILITIES, 0)
+typedef struct _rpmcode {
+ const char *c_name;
+ int c_val;
+} RPMCODE;
+
+RPMCODE rpmprioritynames[] =
+ {
+ { "alert", RPMLOG_ALERT },
+ { "crit", RPMLOG_CRIT },
+ { "debug", RPMLOG_DEBUG },
+ { "emerg", RPMLOG_EMERG },
+ { "err", RPMLOG_ERR },
+ { "error", RPMLOG_ERR }, /* DEPRECATED */
+ { "info", RPMLOG_INFO },
+ { "none", _RPMLOG_NOPRI }, /* INTERNAL */
+ { "notice", RPMLOG_NOTICE },
+ { "panic", RPMLOG_EMERG }, /* DEPRECATED */
+ { "warn", RPMLOG_WARNING }, /* DEPRECATED */
+ { "warning",RPMLOG_WARNING },
+ { NULL, -1 }
+ };
+#endif
+
+/** \ingroup rpmlog
+ * facility codes
+ */
+typedef enum rpmlogFac_e {
+ RPMLOG_KERN = (0<<3), /*!< kernel messages */
+ RPMLOG_USER = (1<<3), /*!< random user-level messages */
+ RPMLOG_MAIL = (2<<3), /*!< mail system */
+ RPMLOG_DAEMON = (3<<3), /*!< system daemons */
+ RPMLOG_AUTH = (4<<3), /*!< security/authorization messages */
+ RPMLOG_SYSLOG = (5<<3), /*!< messages generated internally by syslogd */
+ RPMLOG_LPR = (6<<3), /*!< line printer subsystem */
+ RPMLOG_NEWS = (7<<3), /*!< network news subsystem */
+ RPMLOG_UUCP = (8<<3), /*!< UUCP subsystem */
+ RPMLOG_CRON = (9<<3), /*!< clock daemon */
+ RPMLOG_AUTHPRIV = (10<<3), /*!< security/authorization messages (private) */
+ RPMLOG_FTP = (11<<3), /*!< ftp daemon */
+
+ /* other codes through 15 reserved for system use */
+ RPMLOG_LOCAL0 = (16<<3), /*!< reserved for local use */
+ RPMLOG_LOCAL1 = (17<<3), /*!< reserved for local use */
+ RPMLOG_LOCAL2 = (18<<3), /*!< reserved for local use */
+ RPMLOG_LOCAL3 = (19<<3), /*!< reserved for local use */
+ RPMLOG_LOCAL4 = (20<<3), /*!< reserved for local use */
+ RPMLOG_LOCAL5 = (21<<3), /*!< reserved for local use */
+ RPMLOG_LOCAL6 = (22<<3), /*!< reserved for local use */
+ RPMLOG_LOCAL7 = (23<<3), /*!< reserved for local use */
+
+#define RPMLOG_NFACILITIES 24 /*!< current number of facilities */
+ RPMLOG_ERRMSG = (((unsigned)(RPMLOG_NFACILITIES+0))<<3)
+} rpmlogFac;
+
+#define RPMLOG_FACMASK 0x03f8 /*!< mask to extract facility part */
+#define RPMLOG_FAC(p) (((p) & RPMLOG_FACMASK) >> 3)
+
+
+#ifdef RPMLOG_NAMES
+RPMCODE facilitynames[] =
+ {
+ { "auth", RPMLOG_AUTH },
+ { "authpriv",RPMLOG_AUTHPRIV },
+ { "cron", RPMLOG_CRON },
+ { "daemon", RPMLOG_DAEMON },
+ { "ftp", RPMLOG_FTP },
+ { "kern", RPMLOG_KERN },
+ { "lpr", RPMLOG_LPR },
+ { "mail", RPMLOG_MAIL },
+ { "mark", _RPMLOG_MARK }, /* INTERNAL */
+ { "news", RPMLOG_NEWS },
+ { "security",RPMLOG_AUTH }, /* DEPRECATED */
+ { "syslog", RPMLOG_SYSLOG },
+ { "user", RPMLOG_USER },
+ { "uucp", RPMLOG_UUCP },
+ { "local0", RPMLOG_LOCAL0 },
+ { "local1", RPMLOG_LOCAL1 },
+ { "local2", RPMLOG_LOCAL2 },
+ { "local3", RPMLOG_LOCAL3 },
+ { "local4", RPMLOG_LOCAL4 },
+ { "local5", RPMLOG_LOCAL5 },
+ { "local6", RPMLOG_LOCAL6 },
+ { "local7", RPMLOG_LOCAL7 },
+ { NULL, -1 }
+ };
+#endif
+
+/*
+ * arguments to setlogmask.
+ */
+#define RPMLOG_MASK(pri) (1 << ((unsigned)(pri))) /*!< mask for one priority */
+#define RPMLOG_UPTO(pri) ((1 << (((unsigned)(pri))+1)) - 1) /*!< all priorities through pri */
+
+/*
+ * Option flags for openlog.
+ *
+ * RPMLOG_ODELAY no longer does anything.
+ * RPMLOG_NDELAY is the inverse of what it used to be.
+ */
+#define RPMLOG_PID 0x01 /*!< log the pid with each message */
+#define RPMLOG_CONS 0x02 /*!< log on the console if errors in sending */
+#define RPMLOG_ODELAY 0x04 /*!< delay open until first syslog() (default) */
+#define RPMLOG_NDELAY 0x08 /*!< don't delay open */
+#define RPMLOG_NOWAIT 0x10 /*!< don't wait for console forks: DEPRECATED */
+#define RPMLOG_PERROR 0x20 /*!< log to stderr as well */
+
+/* \ingroup rpmlog
+ * Option flags for callback return value.
+ */
+#define RPMLOG_DEFAULT 0x01 /*!< perform default logging */
+#define RPMLOG_EXIT 0x02 /*!< exit after logging */
+
+/** \ingroup rpmlog
+ */
+typedef struct rpmlogRec_s * rpmlogRec;
+
+/** \ingroup rpmlog
+ * Retrieve log message string from rpmlog record
+ * @param rec rpmlog record
+ * @return log message
+ */
+const char * rpmlogRecMessage(rpmlogRec rec);
+
+/** \ingroup rpmlog
+ * Retrieve log priority from rpmlog record
+ * @param rec rpmlog record
+ * @return log priority
+ */
+rpmlogLvl rpmlogRecPriority(rpmlogRec rec);
+
+typedef void * rpmlogCallbackData;
+
+/** \ingroup rpmlog
+ * @param rec rpmlog record
+ * @param data private callback data
+ * @return flags to define further behavior:
+ * RPMLOG_DEFAULT to perform default logging,
+ * RPMLOG_EXIT to exit after processing,
+ * 0 to return after callback
+ */
+typedef int (*rpmlogCallback) (rpmlogRec rec, rpmlogCallbackData data);
+
+/** \ingroup rpmlog
+ * Return number of rpmError() ressages.
+ * @return number of messages
+ */
+int rpmlogGetNrecs(void) ;
+
+/** \ingroup rpmlog
+ * Print all rpmError() messages.
+ * @param f file handle (NULL uses stderr)
+ */
+void rpmlogPrint(FILE *f);
+
+/** \ingroup rpmlog
+ * Close desriptor used to write to system logger.
+ * @todo Implement.
+ */
+void rpmlogClose (void);
+
+/** \ingroup rpmlog
+ * Open connection to system logger.
+ * @todo Implement.
+ */
+void rpmlogOpen (const char * ident, int option, int facility);
+
+/** \ingroup rpmlog
+ * Set the log mask level.
+ * @param mask log mask (0 is no operation)
+ * @return previous log mask
+ */
+int rpmlogSetMask (int mask);
+
+/** \ingroup rpmlog
+ * Generate a log message using FMT string and option arguments.
+ */
+void rpmlog (int code, const char *fmt, ...) RPM_GNUC_PRINTF(2, 3);
+
+/** \ingroup rpmlog
+ * Return text of last rpmError() message.
+ * @return text of last message
+ */
+const char * rpmlogMessage(void);
+
+/** \ingroup rpmlog
+ * Return error code from last rpmError() message.
+ * @deprecated Perl-RPM needs, what's really needed is predictable, non-i18n
+ * encumbered, error text that can be retrieved through rpmlogMessage()
+ * and parsed IMHO.
+ * @return code from last message
+ */
+int rpmlogCode(void);
+
+/** \ingroup rpmlog
+ * Return translated prefix string (if any) given log level.
+ * @param pri log priority
+ * @return message prefix (or "" for none)
+ */
+const char * rpmlogLevelPrefix(rpmlogLvl pri);
+
+/** \ingroup rpmlog
+ * Set rpmlog callback function.
+ * @param cb rpmlog callback function
+ * @param data callback private (user) data
+ * @return previous rpmlog callback function
+ */
+rpmlogCallback rpmlogSetCallback(rpmlogCallback cb, rpmlogCallbackData data);
+
+/** \ingroup rpmlog
+ * Set rpmlog file handle.
+ * @param fp rpmlog file handle (NULL uses stdout/stderr)
+ * @return previous rpmlog file handle
+ */
+FILE * rpmlogSetFile(FILE * fp);
+
+#define rpmSetVerbosity(_lvl) \
+ ((void)rpmlogSetMask( RPMLOG_UPTO( RPMLOG_PRI(_lvl))))
+#define rpmIncreaseVerbosity() \
+ ((void)rpmlogSetMask(((((unsigned)(rpmlogSetMask(0) & 0xff)) << 1) | 1)))
+#define rpmDecreaseVerbosity() \
+ ((void)rpmlogSetMask((((int)(rpmlogSetMask(0) & 0xff)) >> 1)))
+#define rpmIsNormal() \
+ (rpmlogSetMask(0) >= RPMLOG_MASK( RPMLOG_NOTICE ))
+#define rpmIsVerbose() \
+ (rpmlogSetMask(0) >= RPMLOG_MASK( RPMLOG_INFO ))
+#define rpmIsDebug() \
+ (rpmlogSetMask(0) >= RPMLOG_MASK( RPMLOG_DEBUG ))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* H_RPMLOG */
diff --git a/rpmio/rpmlua.c b/rpmio/rpmlua.c
new file mode 100644
index 0000000..fa8eed3
--- /dev/null
+++ b/rpmio/rpmlua.c
@@ -0,0 +1,844 @@
+#include "system.h"
+
+#ifdef WITH_LUA
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+#include <lposix.h>
+#include <lrexlib.h>
+
+#include <unistd.h>
+#include <assert.h>
+
+#include <rpm/rpmio.h>
+#include <rpm/rpmmacro.h>
+#include <rpm/rpmlog.h>
+#include <rpm/rpmurl.h>
+#include <rpm/rpmfileutil.h>
+#include "rpmio/rpmhook.h"
+#include "rpmio/base64.h"
+
+#define _RPMLUA_INTERNAL
+#include "rpmio/rpmlua.h"
+
+#include "debug.h"
+
+#define INITSTATE(_lua, lua) \
+ rpmlua lua = _lua ? _lua : \
+ (globalLuaState ? globalLuaState : \
+ \
+ (globalLuaState = rpmluaNew()) \
+ \
+ )
+
+struct rpmluapb_s {
+ size_t alloced;
+ size_t used;
+ char *buf;
+ rpmluapb next;
+};
+
+static rpmlua globalLuaState = NULL;
+
+static int luaopen_rpm(lua_State *L);
+static int rpm_print(lua_State *L);
+
+rpmlua rpmluaGetGlobalState(void)
+{
+ INITSTATE(NULL, lua);
+ return lua;
+}
+
+rpmlua rpmluaNew()
+{
+ rpmlua lua = (rpmlua) xcalloc(1, sizeof(*lua));
+ struct stat st;
+ const luaL_reg *lib;
+ char *initlua = rpmGenPath(rpmConfigDir(), "init.lua", NULL);
+
+ static const luaL_reg extlibs[] = {
+ {"posix", luaopen_posix},
+ {"rex", luaopen_rex},
+ {"rpm", luaopen_rpm},
+ {"os", luaopen_rpm_os},
+ {NULL, NULL},
+ };
+
+ lua_State *L = lua_open();
+ luaL_openlibs(L);
+ lua->L = L;
+
+ for (lib = extlibs; lib->name; lib++) {
+ lua_pushcfunction(L, lib->func);
+ lua_pushstring(L, lib->name);
+ lua_call(L, 1, 0);
+ lua_settop(L, 0);
+ }
+ lua_pushliteral(L, "LUA_PATH");
+ lua_pushfstring(L, "%s/%s", rpmConfigDir(), "/lua/?.lua");
+ lua_rawset(L, LUA_GLOBALSINDEX);
+ lua_pushliteral(L, "print");
+ lua_pushcfunction(L, rpm_print);
+ lua_rawset(L, LUA_GLOBALSINDEX);
+ rpmluaSetData(lua, "lua", lua);
+ if (stat(initlua, &st) != -1)
+ (void)rpmluaRunScriptFile(lua, initlua);
+ free(initlua);
+ return lua;
+}
+
+rpmlua rpmluaFree(rpmlua lua)
+{
+ if (lua) {
+ if (lua->L) lua_close(lua->L);
+ free(lua->printbuf);
+ free(lua);
+ if (lua == globalLuaState) globalLuaState = NULL;
+ }
+ return NULL;
+}
+
+void rpmluaSetData(rpmlua _lua, const char *key, const void *data)
+{
+ INITSTATE(_lua, lua);
+ lua_State *L = lua->L;
+ lua_pushliteral(L, "rpm_");
+ lua_pushstring(L, key);
+ lua_concat(L, 2);
+ if (data == NULL)
+ lua_pushnil(L);
+ else
+ lua_pushlightuserdata(L, (void *)data);
+ lua_rawset(L, LUA_REGISTRYINDEX);
+}
+
+static void *getdata(lua_State *L, const char *key)
+{
+ void *ret = NULL;
+ lua_pushliteral(L, "rpm_");
+ lua_pushstring(L, key);
+ lua_concat(L, 2);
+ lua_rawget(L, LUA_REGISTRYINDEX);
+ if (lua_islightuserdata(L, -1))
+ ret = lua_touserdata(L, -1);
+ lua_pop(L, 1);
+ return ret;
+}
+
+void *rpmluaGetData(rpmlua _lua, const char *key)
+{
+ INITSTATE(_lua, lua);
+ return getdata(lua->L, key);
+}
+
+void rpmluaPushPrintBuffer(rpmlua _lua)
+{
+ INITSTATE(_lua, lua);
+ rpmluapb prbuf = xcalloc(1, sizeof(*prbuf));
+ prbuf->buf = NULL;
+ prbuf->alloced = 0;
+ prbuf->used = 0;
+ prbuf->next = lua->printbuf;
+
+ lua->printbuf = prbuf;
+}
+
+char *rpmluaPopPrintBuffer(rpmlua _lua)
+{
+ INITSTATE(_lua, lua);
+ rpmluapb prbuf = lua->printbuf;
+ char *ret = NULL;
+
+ if (prbuf) {
+ ret = prbuf->buf;
+ lua->printbuf = prbuf->next;
+ free(prbuf);
+ }
+
+ return ret;
+}
+
+static int pushvar(lua_State *L, rpmluavType type, void *value)
+{
+ int ret = 0;
+ switch (type) {
+ case RPMLUAV_NIL:
+ lua_pushnil(L);
+ break;
+ case RPMLUAV_STRING:
+ lua_pushstring(L, *((char **)value));
+ break;
+ case RPMLUAV_NUMBER:
+ lua_pushnumber(L, *((double *)value));
+ break;
+ default:
+ ret = -1;
+ break;
+ }
+ return ret;
+}
+
+void rpmluaSetVar(rpmlua _lua, rpmluav var)
+{
+ INITSTATE(_lua, lua);
+ lua_State *L = lua->L;
+ if (var->listmode && lua->pushsize > 0) {
+ if (var->keyType != RPMLUAV_NUMBER || var->key.num == (double)0) {
+ var->keyType = RPMLUAV_NUMBER;
+ var->key.num = (double) luaL_getn(L, -1);
+ }
+ var->key.num++;
+ }
+ if (!var->listmode || lua->pushsize > 0) {
+ if (lua->pushsize == 0)
+ lua_pushvalue(L, LUA_GLOBALSINDEX);
+ if (pushvar(L, var->keyType, &var->key) != -1) {
+ if (pushvar(L, var->valueType, &var->value) != -1)
+ lua_rawset(L, -3);
+ else
+ lua_pop(L, 1);
+ }
+ if (lua->pushsize == 0)
+ lua_pop(L, 1);
+ }
+}
+
+static void popvar(lua_State *L, rpmluavType *type, void *value)
+{
+ switch (lua_type(L, -1)) {
+ case LUA_TSTRING:
+ *type = RPMLUAV_STRING;
+ *((const char **)value) = lua_tostring(L, -1);
+ break;
+ case LUA_TNUMBER:
+ *type = RPMLUAV_NUMBER;
+ *((double *)value) = lua_tonumber(L, -1);
+ break;
+ default:
+ *type = RPMLUAV_NIL;
+ *((void **)value) = NULL;
+ break;
+ }
+ lua_pop(L, 1);
+}
+
+void rpmluaGetVar(rpmlua _lua, rpmluav var)
+{
+ INITSTATE(_lua, lua);
+ lua_State *L = lua->L;
+ if (!var->listmode) {
+ if (lua->pushsize == 0)
+ lua_pushvalue(L, LUA_GLOBALSINDEX);
+ if (pushvar(L, var->keyType, &var->key) != -1) {
+ lua_rawget(L, -2);
+ popvar(L, &var->valueType, &var->value);
+ }
+ if (lua->pushsize == 0)
+ lua_pop(L, 1);
+ } else if (lua->pushsize > 0) {
+ (void) pushvar(L, var->keyType, &var->key);
+ if (lua_next(L, -2) != 0)
+ popvar(L, &var->valueType, &var->value);
+ }
+}
+
+#define FINDKEY_RETURN 0
+#define FINDKEY_CREATE 1
+#define FINDKEY_REMOVE 2
+static int findkey(lua_State *L, int oper, const char *key, va_list va)
+{
+ char *buf;
+ const char *s, *e;
+ int ret = 0;
+ int blen;
+
+ blen = vsnprintf(NULL, 0, key, va);
+ if (blen <= 0) {
+ return -1;
+ }
+
+ buf = xmalloc(blen + 1);
+ vsnprintf(buf, blen + 1, key, va);
+
+ s = e = buf;
+ lua_pushvalue(L, LUA_GLOBALSINDEX);
+ for (;;) {
+ if (*e == '\0' || *e == '.') {
+ if (e != s) {
+ lua_pushlstring(L, s, e-s);
+ switch (oper) {
+ case FINDKEY_REMOVE:
+ if (*e == '\0') {
+ lua_pushnil(L);
+ lua_rawset(L, -3);
+ lua_pop(L, 1);
+ break;
+ }
+ case FINDKEY_RETURN:
+ lua_rawget(L, -2);
+ lua_remove(L, -2);
+ break;
+ case FINDKEY_CREATE:
+ lua_rawget(L, -2);
+ if (!lua_istable(L, -1)) {
+ lua_pop(L, 1);
+ lua_newtable(L);
+ lua_pushlstring(L, s, e-s);
+ lua_pushvalue(L, -2);
+ lua_rawset(L, -4);
+ }
+ lua_remove(L, -2);
+ break;
+ }
+ }
+ if (*e == '\0')
+ break;
+ if (!lua_istable(L, -1)) {
+ lua_pop(L, 1);
+ ret = -1;
+ break;
+ }
+ s = e+1;
+ }
+ e++;
+ }
+ free(buf);
+
+ return ret;
+}
+
+void rpmluaDelVar(rpmlua _lua, const char *key, ...)
+{
+ INITSTATE(_lua, lua);
+ va_list va;
+ va_start(va, key);
+ (void) findkey(lua->L, FINDKEY_REMOVE, key, va);
+ va_end(va);
+}
+
+int rpmluaVarExists(rpmlua _lua, const char *key, ...)
+{
+ INITSTATE(_lua, lua);
+ lua_State *L = lua->L;
+ int ret = 0;
+ va_list va;
+ va_start(va, key);
+ if (findkey(L, FINDKEY_RETURN, key, va) == 0) {
+ if (!lua_isnil(L, -1))
+ ret = 1;
+ lua_pop(L, 1);
+ }
+ va_end(va);
+ return ret;
+}
+
+void rpmluaPushTable(rpmlua _lua, const char *key, ...)
+{
+ INITSTATE(_lua, lua);
+ va_list va;
+ va_start(va, key);
+ (void) findkey(lua->L, FINDKEY_CREATE, key, va);
+ lua->pushsize++;
+ va_end(va);
+}
+
+void rpmluaPop(rpmlua _lua)
+{
+ INITSTATE(_lua, lua);
+ assert(lua->pushsize > 0);
+ lua->pushsize--;
+ lua_pop(lua->L, 1);
+}
+
+rpmluav rpmluavNew(void)
+{
+ rpmluav var = (rpmluav) xcalloc(1, sizeof(*var));
+ return var;
+}
+
+rpmluav rpmluavFree(rpmluav var)
+{
+ free(var);
+ return NULL;
+}
+
+void rpmluavSetListMode(rpmluav var, int flag)
+{
+ var->listmode = flag;
+ var->keyType = RPMLUAV_NIL;
+}
+
+void rpmluavSetKey(rpmluav var, rpmluavType type, const void *value)
+{
+ var->keyType = type;
+ switch (type) {
+ case RPMLUAV_NUMBER:
+ var->key.num = *((double *)value);
+ break;
+ case RPMLUAV_STRING:
+ var->key.str = (char *)value;
+ break;
+ default:
+ break;
+ }
+}
+
+void rpmluavSetValue(rpmluav var, rpmluavType type, const void *value)
+{
+ var->valueType = type;
+ switch (type) {
+ case RPMLUAV_NUMBER:
+ var->value.num = *((const double *)value);
+ break;
+ case RPMLUAV_STRING:
+ var->value.str = (const char *)value;
+ break;
+ default:
+ break;
+ }
+}
+
+void rpmluavGetKey(rpmluav var, rpmluavType *type, void **value)
+{
+ *type = var->keyType;
+ switch (var->keyType) {
+ case RPMLUAV_NUMBER:
+ *((double **)value) = &var->key.num;
+ break;
+ case RPMLUAV_STRING:
+ *((const char **)value) = var->key.str;
+ break;
+ default:
+ break;
+ }
+}
+
+void rpmluavGetValue(rpmluav var, rpmluavType *type, void **value)
+{
+ *type = var->valueType;
+ switch (var->valueType) {
+ case RPMLUAV_NUMBER:
+ *((double **)value) = &var->value.num;
+ break;
+ case RPMLUAV_STRING:
+ *((const char **)value) = var->value.str;
+ break;
+ default:
+ break;
+ }
+}
+
+void rpmluavSetKeyNum(rpmluav var, double value)
+{
+ rpmluavSetKey(var, RPMLUAV_NUMBER, &value);
+}
+
+void rpmluavSetValueNum(rpmluav var, double value)
+{
+ rpmluavSetValue(var, RPMLUAV_NUMBER, &value);
+}
+
+double rpmluavGetKeyNum(rpmluav var)
+{
+ rpmluavType type;
+ void *value;
+ rpmluavGetKey(var, &type, &value);
+ if (type == RPMLUAV_NUMBER)
+ return *((double *)value);
+ return (double) 0;
+}
+
+double rpmluavGetValueNum(rpmluav var)
+{
+ rpmluavType type;
+ void *value;
+ rpmluavGetValue(var, &type, &value);
+ if (type == RPMLUAV_NUMBER)
+ return *((double *)value);
+ return (double) 0;
+}
+
+int rpmluavKeyIsNum(rpmluav var)
+{
+ return (var->keyType == RPMLUAV_NUMBER) ? 1 : 0;
+}
+
+int rpmluavValueIsNum(rpmluav var)
+{
+ return (var->valueType == RPMLUAV_NUMBER) ? 1 : 0;
+}
+
+int rpmluaCheckScript(rpmlua _lua, const char *script, const char *name)
+{
+ INITSTATE(_lua, lua);
+ lua_State *L = lua->L;
+ int ret = 0;
+ if (name == NULL)
+ name = "<lua>";
+ if (luaL_loadbuffer(L, script, strlen(script), name) != 0) {
+ rpmlog(RPMLOG_ERR,
+ _("invalid syntax in lua scriptlet: %s\n"),
+ lua_tostring(L, -1));
+ ret = -1;
+ }
+ lua_pop(L, 1); /* Error or chunk. */
+ return ret;
+}
+
+int rpmluaRunScript(rpmlua _lua, const char *script, const char *name)
+{
+ INITSTATE(_lua, lua);
+ lua_State *L = lua->L;
+ int ret = 0;
+ if (name == NULL)
+ name = "<lua>";
+ if (luaL_loadbuffer(L, script, strlen(script), name) != 0) {
+ rpmlog(RPMLOG_ERR, _("invalid syntax in lua script: %s\n"),
+ lua_tostring(L, -1));
+ lua_pop(L, 1);
+ ret = -1;
+ } else if (lua_pcall(L, 0, 0, 0) != 0) {
+ rpmlog(RPMLOG_ERR, _("lua script failed: %s\n"),
+ lua_tostring(L, -1));
+ lua_pop(L, 1);
+ ret = -1;
+ }
+ return ret;
+}
+
+int rpmluaRunScriptFile(rpmlua _lua, const char *filename)
+{
+ INITSTATE(_lua, lua);
+ lua_State *L = lua->L;
+ int ret = 0;
+ if (luaL_loadfile(L, filename) != 0) {
+ rpmlog(RPMLOG_ERR, _("invalid syntax in lua file: %s\n"),
+ lua_tostring(L, -1));
+ lua_pop(L, 1);
+ ret = -1;
+ } else if (lua_pcall(L, 0, 0, 0) != 0) {
+ rpmlog(RPMLOG_ERR, _("lua script failed: %s\n"),
+ lua_tostring(L, -1));
+ lua_pop(L, 1);
+ ret = -1;
+ }
+ return ret;
+}
+
+/* From lua.c */
+static int rpmluaReadline(lua_State *L, const char *prompt)
+{
+ static char buffer[1024];
+ if (prompt) {
+ (void) fputs(prompt, stdout);
+ (void) fflush(stdout);
+ }
+ if (fgets(buffer, sizeof(buffer), stdin) == NULL) {
+ return 0; /* read fails */
+ } else {
+ lua_pushstring(L, buffer);
+ return 1;
+ }
+}
+
+/* Based on lua.c */
+static void _rpmluaInteractive(lua_State *L)
+{
+ (void) fputs("\n", stdout);
+ printf("RPM Interactive %s Interpreter\n", LUA_VERSION);
+ for (;;) {
+ int rc = 0;
+
+ if (rpmluaReadline(L, "> ") == 0)
+ break;
+ if (lua_tostring(L, -1)[0] == '=') {
+ (void) lua_pushfstring(L, "print(%s)", lua_tostring(L, -1)+1);
+ lua_remove(L, -2);
+ }
+ for (;;) {
+ rc = luaL_loadbuffer(L, lua_tostring(L, -1),
+ lua_strlen(L, -1), "<lua>");
+ if (rc == LUA_ERRSYNTAX &&
+ strstr(lua_tostring(L, -1), "near `<eof>'") != NULL) {
+ if (rpmluaReadline(L, ">> ") == 0)
+ break;
+ lua_remove(L, -2); /* Remove error */
+ lua_concat(L, 2);
+ continue;
+ }
+ break;
+ }
+ if (rc == 0)
+ rc = lua_pcall(L, 0, 0, 0);
+ if (rc != 0) {
+ fprintf(stderr, "%s\n", lua_tostring(L, -1));
+ lua_pop(L, 1);
+ }
+ lua_pop(L, 1); /* Remove line */
+ }
+ (void) fputs("\n", stdout);
+}
+
+void rpmluaInteractive(rpmlua _lua)
+{
+ INITSTATE(_lua, lua);
+ _rpmluaInteractive(lua->L);
+}
+
+/* ------------------------------------------------------------------ */
+/* Lua API */
+
+static int rpm_b64encode(lua_State *L)
+{
+ const char *str = luaL_checkstring(L, 1);
+ size_t len = lua_strlen(L, 1);
+ int linelen = -1;
+ if (lua_gettop(L) == 2)
+ linelen = luaL_checkinteger(L, 2);
+ if (str && len) {
+ char *data = b64encode(str, len, linelen);
+ lua_pushstring(L, data);
+ free(data);
+ }
+ return 1;
+}
+
+static int rpm_b64decode(lua_State *L)
+{
+ const char *str = luaL_checkstring(L, 1);
+ if (str) {
+ void *data = NULL;
+ size_t len = 0;
+ if (b64decode(str, &data, &len) == 0) {
+ lua_pushlstring(L, data, len);
+ } else {
+ lua_pushnil(L);
+ }
+ free(data);
+ }
+ return 1;
+}
+
+static int rpm_expand(lua_State *L)
+{
+ const char *str = luaL_checkstring(L, 1);
+ char *val = rpmExpand(str, NULL);
+ lua_pushstring(L, val);
+ free(val);
+ return 1;
+}
+
+static int rpm_define(lua_State *L)
+{
+ const char *str = luaL_checkstring(L, 1);
+ (void) rpmDefineMacro(NULL, str, 0);
+ return 0;
+}
+
+static int rpm_interactive(lua_State *L)
+{
+ _rpmluaInteractive(L);
+ return 0;
+}
+
+typedef struct rpmluaHookData_s {
+ lua_State *L;
+ int funcRef;
+ int dataRef;
+} * rpmluaHookData;
+
+static int rpmluaHookWrapper(rpmhookArgs args, void *data)
+{
+ rpmluaHookData hookdata = (rpmluaHookData)data;
+ lua_State *L = hookdata->L;
+ int ret = 0;
+ int i;
+ lua_rawgeti(L, LUA_REGISTRYINDEX, hookdata->funcRef);
+ lua_newtable(L);
+ for (i = 0; i != args->argc; i++) {
+ switch (args->argt[i]) {
+ case 's':
+ lua_pushstring(L, args->argv[i].s);
+ lua_rawseti(L, -2, i+1);
+ break;
+ case 'i':
+ lua_pushnumber(L, (lua_Number)args->argv[i].i);
+ lua_rawseti(L, -2, i+1);
+ break;
+ case 'f':
+ lua_pushnumber(L, (lua_Number)args->argv[i].f);
+ lua_rawseti(L, -2, i+1);
+ break;
+ case 'p':
+ lua_pushlightuserdata(L, args->argv[i].p);
+ lua_rawseti(L, -2, i+1);
+ break;
+ default:
+ (void) luaL_error(L, "unsupported type '%c' as "
+ "a hook argument\n", args->argt[i]);
+ break;
+ }
+ }
+ if (lua_pcall(L, 1, 1, 0) != 0) {
+ rpmlog(RPMLOG_ERR, _("lua hook failed: %s\n"),
+ lua_tostring(L, -1));
+ lua_pop(L, 1);
+ } else {
+ if (lua_isnumber(L, -1))
+ ret = (int)lua_tonumber(L, -1);
+ lua_pop(L, 1);
+ }
+ return ret;
+}
+
+static int rpm_register(lua_State *L)
+{
+ if (!lua_isstring(L, 1)) {
+ (void) luaL_argerror(L, 1, "hook name expected");
+ } else if (!lua_isfunction(L, 2)) {
+ (void) luaL_argerror(L, 2, "function expected");
+ } else {
+ rpmluaHookData hookdata =
+ lua_newuserdata(L, sizeof(struct rpmluaHookData_s));
+ lua_pushvalue(L, -1);
+ hookdata->dataRef = luaL_ref(L, LUA_REGISTRYINDEX);
+ lua_pushvalue(L, 2);
+ hookdata->funcRef = luaL_ref(L, LUA_REGISTRYINDEX);
+ hookdata->L = L;
+ rpmhookRegister(lua_tostring(L, 1), rpmluaHookWrapper, hookdata);
+ return 1;
+ }
+ return 0;
+}
+
+static int rpm_unregister(lua_State *L)
+{
+ if (!lua_isstring(L, 1)) {
+ (void) luaL_argerror(L, 1, "hook name expected");
+ } else if (!lua_isuserdata(L, 2)) {
+ (void) luaL_argerror(L, 2, "hook information expected");
+ } else {
+ rpmluaHookData hookdata = (rpmluaHookData)lua_touserdata(L, 2);
+ luaL_unref(L, LUA_REGISTRYINDEX, hookdata->funcRef);
+ luaL_unref(L, LUA_REGISTRYINDEX, hookdata->dataRef);
+ rpmhookUnregister(lua_tostring(L, 1), rpmluaHookWrapper, hookdata);
+ }
+ return 0;
+}
+
+static int rpm_call(lua_State *L)
+{
+ if (!lua_isstring(L, 1)) {
+ (void) luaL_argerror(L, 1, "hook name expected");
+ } else {
+ rpmhookArgs args = rpmhookArgsNew(lua_gettop(L)-1);
+ const char *name = lua_tostring(L, 1);
+ char *argt = (char *)xmalloc(args->argc+1);
+ int i;
+ for (i = 0; i != args->argc; i++) {
+ switch (lua_type(L, i+1)) {
+ case LUA_TNIL:
+ argt[i] = 'p';
+ args->argv[i].p = NULL;
+ break;
+ case LUA_TNUMBER: {
+ float f = (float)lua_tonumber(L, i+1);
+ if (f == (int)f) {
+ argt[i] = 'i';
+ args->argv[i].i = (int)f;
+ } else {
+ argt[i] = 'f';
+ args->argv[i].f = f;
+ }
+ } break;
+ case LUA_TSTRING:
+ argt[i] = 's';
+ args->argv[i].s = lua_tostring(L, i+1);
+ break;
+ case LUA_TUSERDATA:
+ case LUA_TLIGHTUSERDATA:
+ argt[i] = 'p';
+ args->argv[i].p = lua_touserdata(L, i+1);
+ break;
+ default:
+ (void) luaL_error(L, "unsupported Lua type passed to hook");
+ argt[i] = 'p';
+ args->argv[i].p = NULL;
+ break;
+ }
+ }
+ args->argt = argt;
+ rpmhookCallArgs(name, args);
+ free(argt);
+ (void) rpmhookArgsFree(args);
+ }
+ return 0;
+}
+
+/* Based on luaB_print. */
+static int rpm_print (lua_State *L)
+{
+ rpmlua lua = (rpmlua)getdata(L, "lua");
+ int n = lua_gettop(L); /* number of arguments */
+ int i;
+ if (!lua) return 0;
+ lua_getglobal(L, "tostring");
+ for (i = 1; i <= n; i++) {
+ const char *s;
+ lua_pushvalue(L, -1); /* function to be called */
+ lua_pushvalue(L, i); /* value to print */
+ lua_call(L, 1, 1);
+ s = lua_tostring(L, -1); /* get result */
+ if (s == NULL)
+ return luaL_error(L, "`tostring' must return a string to `print'");
+ if (lua->printbuf) {
+ rpmluapb prbuf = lua->printbuf;
+ int sl = lua_strlen(L, -1);
+ if (prbuf->used+sl+1 > prbuf->alloced) {
+ prbuf->alloced += sl+512;
+ prbuf->buf = xrealloc(prbuf->buf, prbuf->alloced);
+ }
+ if (i > 1)
+ prbuf->buf[prbuf->used++] = '\t';
+ memcpy(prbuf->buf+prbuf->used, s, sl+1);
+ prbuf->used += sl;
+ } else {
+ if (i > 1)
+ (void) fputs("\t", stdout);
+ (void) fputs(s, stdout);
+ }
+ lua_pop(L, 1); /* pop result */
+ }
+ if (!lua->printbuf) {
+ (void) fputs("\n", stdout);
+ } else {
+ rpmluapb prbuf = lua->printbuf;
+ if (prbuf->used+1 > prbuf->alloced) {
+ prbuf->alloced += 512;
+ prbuf->buf = xrealloc(prbuf->buf, prbuf->alloced);
+ }
+ prbuf->buf[prbuf->used] = '\0';
+ }
+ return 0;
+}
+
+static const luaL_reg rpmlib[] = {
+ {"b64encode", rpm_b64encode},
+ {"b64decode", rpm_b64decode},
+ {"expand", rpm_expand},
+ {"define", rpm_define},
+ {"register", rpm_register},
+ {"unregister", rpm_unregister},
+ {"call", rpm_call},
+ {"interactive", rpm_interactive},
+ {NULL, NULL}
+};
+
+static int luaopen_rpm(lua_State *L)
+{
+ lua_pushvalue(L, LUA_GLOBALSINDEX);
+ luaL_openlib(L, "rpm", rpmlib, 0);
+ return 0;
+}
+#endif /* WITH_LUA */
+
diff --git a/rpmio/rpmlua.h b/rpmio/rpmlua.h
new file mode 100644
index 0000000..7298ed5
--- /dev/null
+++ b/rpmio/rpmlua.h
@@ -0,0 +1,92 @@
+#ifndef RPMLUA_H
+#define RPMLUA_H
+
+typedef enum rpmluavType_e {
+ RPMLUAV_NIL = 0,
+ RPMLUAV_STRING = 1,
+ RPMLUAV_NUMBER = 2
+} rpmluavType;
+
+#if defined(_RPMLUA_INTERNAL)
+
+#include <stdarg.h>
+#include <lua.h>
+
+typedef struct rpmluapb_s * rpmluapb;
+
+struct rpmlua_s {
+ lua_State *L;
+ size_t pushsize;
+ rpmluapb printbuf;
+};
+
+struct rpmluav_s {
+ rpmluavType keyType;
+ rpmluavType valueType;
+ union {
+ const char *str;
+ const void *ptr;
+ double num;
+ } key;
+ union {
+ const char *str;
+ const void *ptr;
+ double num;
+ } value;
+ int listmode;
+};
+
+#endif /* _RPMLUA_INTERNAL */
+
+typedef struct rpmlua_s * rpmlua;
+typedef struct rpmluav_s * rpmluav;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+rpmlua rpmluaNew(void);
+rpmlua rpmluaFree(rpmlua lua);
+rpmlua rpmluaGetGlobalState(void);
+
+int rpmluaCheckScript(rpmlua lua, const char *script,
+ const char *name);
+int rpmluaRunScript(rpmlua lua, const char *script,
+ const char *name);
+int rpmluaRunScriptFile(rpmlua lua, const char *filename);
+void rpmluaInteractive(rpmlua lua);
+
+void *rpmluaGetData(rpmlua lua, const char *key);
+void rpmluaSetData(rpmlua lua, const char *key, const void *data);
+
+char *rpmluaPopPrintBuffer(rpmlua lua);
+void rpmluaPushPrintBuffer(rpmlua lua);
+
+void rpmluaGetVar(rpmlua lua, rpmluav var);
+void rpmluaSetVar(rpmlua lua, rpmluav var);
+void rpmluaDelVar(rpmlua lua, const char *key, ...);
+int rpmluaVarExists(rpmlua lua, const char *key, ...);
+void rpmluaPushTable(rpmlua lua, const char *key, ...);
+void rpmluaPop(rpmlua lua);
+
+rpmluav rpmluavNew(void);
+rpmluav rpmluavFree(rpmluav var);
+void rpmluavSetListMode(rpmluav var, int flag);
+void rpmluavSetKey(rpmluav var, rpmluavType type, const void *value);
+void rpmluavSetValue(rpmluav var, rpmluavType type, const void *value);
+void rpmluavGetKey(rpmluav var, rpmluavType *type, void **value);
+void rpmluavGetValue(rpmluav var, rpmluavType *type, void **value);
+
+/* Optional helpers for numbers. */
+void rpmluavSetKeyNum(rpmluav var, double value);
+void rpmluavSetValueNum(rpmluav var, double value);
+double rpmluavGetKeyNum(rpmluav var);
+double rpmluavGetValueNum(rpmluav var);
+int rpmluavKeyIsNum(rpmluav var);
+int rpmluavValueIsNum(rpmluav var);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* RPMLUA_H */
diff --git a/rpmio/rpmmacro.h b/rpmio/rpmmacro.h
new file mode 100644
index 0000000..765c78c
--- /dev/null
+++ b/rpmio/rpmmacro.h
@@ -0,0 +1,156 @@
+#ifndef _H_MACRO_
+#define _H_MACRO_
+
+/** \ingroup rpmio
+ * \file rpmio/rpmmacro.h
+ */
+
+#include <stdio.h>
+#include <stddef.h>
+
+#include <rpm/rpmutil.h>
+#include <rpm/rpmfileutil.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct rpmMacroEntry_s * rpmMacroEntry;
+
+typedef struct rpmMacroContext_s * rpmMacroContext;
+
+extern rpmMacroContext rpmGlobalMacroContext;
+
+extern rpmMacroContext rpmCLIMacroContext;
+
+/** \ingroup rpmrc
+ * List of macro files to read when configuring rpm.
+ * This is a colon separated list of files. URI's are permitted as well,
+ * identified by the token '://', so file paths must not begin with '//'.
+ */
+extern const char * macrofiles;
+
+/**
+ * Markers for sources of macros added throughout rpm.
+ */
+#define RMIL_DEFAULT -15
+#define RMIL_MACROFILES -13
+#define RMIL_RPMRC -11
+
+#define RMIL_CMDLINE -7
+#define RMIL_TARBALL -5
+#define RMIL_SPEC -3
+#define RMIL_OLDSPEC -1
+#define RMIL_GLOBAL 0
+
+/** \ingroup rpmmacro
+ * Print macros to file stream.
+ * @param mc macro context (NULL uses global context).
+ * @param fp file stream (NULL uses stderr).
+ */
+void rpmDumpMacroTable (rpmMacroContext mc,
+ FILE * fp);
+
+/** \ingroup rpmmacro
+ * Expand macro into buffer.
+ * @deprecated Use rpmExpand().
+ * @todo Eliminate from API.
+ * @param spec cookie (unused)
+ * @param mc macro context (NULL uses global context).
+ * @retval sbuf input macro to expand, output expansion
+ * @param slen size of buffer
+ * @return 0 on success
+ */
+int expandMacros (void * spec, rpmMacroContext mc,
+ char * sbuf,
+ size_t slen);
+
+/** \ingroup rpmmacro
+ * Add macro to context.
+ * @deprecated Use rpmDefineMacro().
+ * @param mc macro context (NULL uses global context).
+ * @param n macro name
+ * @param o macro paramaters
+ * @param b macro body
+ * @param level macro recursion level (0 is entry API)
+ */
+void addMacro (rpmMacroContext mc, const char * n,
+ const char * o,
+ const char * b, int level);
+
+/** \ingroup rpmmacro
+ * Delete macro from context.
+ * @param mc macro context (NULL uses global context).
+ * @param n macro name
+ */
+void delMacro (rpmMacroContext mc, const char * n);
+
+/** \ingroup rpmmacro
+ * Define macro in context.
+ * @param mc macro context (NULL uses global context).
+ * @param macro macro name, options, body
+ * @param level macro recursion level (0 is entry API)
+ * @return 0 on success (always)
+ */
+int rpmDefineMacro (rpmMacroContext mc, const char * macro,
+ int level);
+
+/** \ingroup rpmmacro
+ * Load macros from specific context into global context.
+ * @param mc macro context (NULL does nothing).
+ * @param level macro recursion level (0 is entry API)
+ */
+void rpmLoadMacros (rpmMacroContext mc, int level);
+
+/** \ingroup rpmmacro
+ * Load macro context from a macro file.
+ * @param mc (unused)
+ * @param fn macro file name
+ */
+int rpmLoadMacroFile(rpmMacroContext mc, const char * fn);
+
+/** \ingroup rpmmacro
+ * Initialize macro context from set of macrofile(s).
+ * @param mc macro context
+ * @param macrofiles colon separated list of macro files (NULL does nothing)
+ */
+void rpmInitMacros (rpmMacroContext mc, const char * macrofiles);
+
+/** \ingroup rpmmacro
+ * Destroy macro context.
+ * @param mc macro context (NULL uses global context).
+ */
+void rpmFreeMacros (rpmMacroContext mc);
+
+/** \ingroup rpmmacro
+ * Return (malloc'ed) concatenated macro expansion(s).
+ * @param arg macro(s) to expand (NULL terminates list)
+ * @return macro expansion (malloc'ed)
+ */
+char * rpmExpand (const char * arg, ...) RPM_GNUC_NULL_TERMINATED;
+
+/** \ingroup rpmmacro
+ * Return macro expansion as a numeric value.
+ * Boolean values ('Y' or 'y' returns 1, 'N' or 'n' returns 0)
+ * are permitted as well. An undefined macro returns 0.
+ * @param arg macro to expand
+ * @return numeric value
+ */
+int rpmExpandNumeric (const char * arg);
+
+/** \ingroup rpmmacro
+ * Return rpm configuration base directory.
+ * If RPM_CONFIGDIR environment variable is set, it's value will be used.
+ * Otherwise the configuration directory is the one set at build time,
+ * typically /usr/lib/rpm. The value of rpmConfigDir() is determined
+ * on first call to this function and is guaranteed to remain the same
+ * on subsequent calls.
+ * @return rpm configuration directory name
+ */
+const char *rpmConfigDir(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _H_ MACRO_ */
diff --git a/rpmio/rpmmalloc.c b/rpmio/rpmmalloc.c
new file mode 100644
index 0000000..c38ab96
--- /dev/null
+++ b/rpmio/rpmmalloc.c
@@ -0,0 +1,85 @@
+/** \ingroup rpmio
+ * \file rpmio/rpmmalloc.c
+ */
+
+#include "system.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "debug.h"
+
+static rpmMemFailFunc failfunc = NULL;
+static void *failfunc_data = NULL;
+
+/*
+ * Give memfail callback a chance to try to give us memory or perform
+ * it's own cleanup. If we dont get memory we die anyway as rpm doesn't
+ * check for NULL returns from allocations.
+ */
+static void *vmefail(size_t size)
+{
+ void *val = failfunc ? (*failfunc)(size, failfunc_data) : NULL;
+ if (val == NULL) {
+ fprintf(stderr, _("memory alloc (%u bytes) returned NULL.\n"),
+ (unsigned)size);
+ exit(EXIT_FAILURE);
+ }
+ return val;
+}
+
+rpmMemFailFunc rpmSetMemFail(rpmMemFailFunc func, void *data)
+{
+ rpmMemFailFunc ofunc = failfunc;
+ failfunc = func;
+ failfunc_data = data;
+ return ofunc;
+}
+
+void * rmalloc (size_t size)
+{
+ register void *value;
+ if (size == 0) size++;
+ value = malloc (size);
+ if (value == NULL)
+ value = vmefail(size);
+ return value;
+}
+
+void * rcalloc (size_t nmemb, size_t size)
+{
+ register void *value;
+ if (size == 0) size++;
+ if (nmemb == 0) nmemb++;
+ value = calloc (nmemb, size);
+ if (value == NULL)
+ value = vmefail(size);
+ return value;
+}
+
+void * rrealloc (void *ptr, size_t size)
+{
+ register void *value;
+ if (size == 0) size++;
+ value = realloc (ptr, size);
+ if (value == NULL)
+ value = vmefail(size);
+ return value;
+}
+
+char * rstrdup (const char *str)
+{
+ size_t size = strlen(str) + 1;
+ char *newstr = (char *) malloc (size);
+ if (newstr == NULL)
+ newstr = (char *) vmefail(size);
+ strcpy (newstr, str);
+ return newstr;
+}
+
+void * rfree (void *ptr)
+{
+ free(ptr);
+ return NULL;
+}
diff --git a/rpmio/rpmpgp.c b/rpmio/rpmpgp.c
new file mode 100644
index 0000000..04472de
--- /dev/null
+++ b/rpmio/rpmpgp.c
@@ -0,0 +1,1669 @@
+/** \ingroup rpmio signature
+ * \file rpmio/rpmpgp.c
+ * Routines to handle RFC-2440 detached signatures.
+ */
+
+#include "system.h"
+
+#include <pthread.h>
+
+#include <rpm/rpmstring.h>
+#include <rpm/rpmlog.h>
+
+#include "rpmio/digest.h"
+#include "rpmio/rpmio_internal.h" /* XXX rpmioSlurp */
+
+#include "debug.h"
+
+
+static int _debug = 0;
+
+static int _print = 0;
+
+static int _crypto_initialized = 0;
+static int _new_process = 1;
+
+typedef const struct pgpValTbl_s {
+ int val;
+ char const * const str;
+} * pgpValTbl;
+
+static struct pgpValTbl_s const pgpSigTypeTbl[] = {
+ { PGPSIGTYPE_BINARY, "Binary document signature" },
+ { PGPSIGTYPE_TEXT, "Text document signature" },
+ { PGPSIGTYPE_STANDALONE, "Standalone signature" },
+ { PGPSIGTYPE_GENERIC_CERT, "Generic certification of a User ID and Public Key" },
+ { PGPSIGTYPE_PERSONA_CERT, "Persona certification of a User ID and Public Key" },
+ { PGPSIGTYPE_CASUAL_CERT, "Casual certification of a User ID and Public Key" },
+ { PGPSIGTYPE_POSITIVE_CERT, "Positive certification of a User ID and Public Key" },
+ { PGPSIGTYPE_SUBKEY_BINDING,"Subkey Binding Signature" },
+ { PGPSIGTYPE_SIGNED_KEY, "Signature directly on a key" },
+ { PGPSIGTYPE_KEY_REVOKE, "Key revocation signature" },
+ { PGPSIGTYPE_SUBKEY_REVOKE, "Subkey revocation signature" },
+ { PGPSIGTYPE_CERT_REVOKE, "Certification revocation signature" },
+ { PGPSIGTYPE_TIMESTAMP, "Timestamp signature" },
+ { -1, "Unknown signature type" },
+};
+
+static struct pgpValTbl_s const pgpPubkeyTbl[] = {
+ { PGPPUBKEYALGO_RSA, "RSA" },
+ { PGPPUBKEYALGO_RSA_ENCRYPT,"RSA(Encrypt-Only)" },
+ { PGPPUBKEYALGO_RSA_SIGN, "RSA(Sign-Only)" },
+ { PGPPUBKEYALGO_ELGAMAL_ENCRYPT,"Elgamal(Encrypt-Only)" },
+ { PGPPUBKEYALGO_DSA, "DSA" },
+ { PGPPUBKEYALGO_EC, "Elliptic Curve" },
+ { PGPPUBKEYALGO_ECDSA, "ECDSA" },
+ { PGPPUBKEYALGO_ELGAMAL, "Elgamal" },
+ { PGPPUBKEYALGO_DH, "Diffie-Hellman (X9.42)" },
+ { -1, "Unknown public key algorithm" },
+};
+
+static struct pgpValTbl_s const pgpSymkeyTbl[] = {
+ { PGPSYMKEYALGO_PLAINTEXT, "Plaintext" },
+ { PGPSYMKEYALGO_IDEA, "IDEA" },
+ { PGPSYMKEYALGO_TRIPLE_DES, "3DES" },
+ { PGPSYMKEYALGO_CAST5, "CAST5" },
+ { PGPSYMKEYALGO_BLOWFISH, "BLOWFISH" },
+ { PGPSYMKEYALGO_SAFER, "SAFER" },
+ { PGPSYMKEYALGO_DES_SK, "DES/SK" },
+ { PGPSYMKEYALGO_AES_128, "AES(128-bit key)" },
+ { PGPSYMKEYALGO_AES_192, "AES(192-bit key)" },
+ { PGPSYMKEYALGO_AES_256, "AES(256-bit key)" },
+ { PGPSYMKEYALGO_TWOFISH, "TWOFISH(256-bit key)" },
+ { PGPSYMKEYALGO_NOENCRYPT, "no encryption" },
+ { -1, "Unknown symmetric key algorithm" },
+};
+
+static struct pgpValTbl_s const pgpCompressionTbl[] = {
+ { PGPCOMPRESSALGO_NONE, "Uncompressed" },
+ { PGPCOMPRESSALGO_ZIP, "ZIP" },
+ { PGPCOMPRESSALGO_ZLIB, "ZLIB" },
+ { PGPCOMPRESSALGO_BZIP2, "BZIP2" },
+ { -1, "Unknown compression algorithm" },
+};
+
+static struct pgpValTbl_s const pgpHashTbl[] = {
+ { PGPHASHALGO_MD5, "MD5" },
+ { PGPHASHALGO_SHA1, "SHA1" },
+ { PGPHASHALGO_RIPEMD160, "RIPEMD160" },
+ { PGPHASHALGO_MD2, "MD2" },
+ { PGPHASHALGO_TIGER192, "TIGER192" },
+ { PGPHASHALGO_HAVAL_5_160, "HAVAL-5-160" },
+ { PGPHASHALGO_SHA256, "SHA256" },
+ { PGPHASHALGO_SHA384, "SHA384" },
+ { PGPHASHALGO_SHA512, "SHA512" },
+ { PGPHASHALGO_SHA224, "SHA224" },
+ { -1, "Unknown hash algorithm" },
+};
+
+static struct pgpValTbl_s const pgpKeyServerPrefsTbl[] = {
+ { 0x80, "No-modify" },
+ { -1, "Unknown key server preference" },
+};
+
+static struct pgpValTbl_s const pgpSubTypeTbl[] = {
+ { PGPSUBTYPE_SIG_CREATE_TIME,"signature creation time" },
+ { PGPSUBTYPE_SIG_EXPIRE_TIME,"signature expiration time" },
+ { PGPSUBTYPE_EXPORTABLE_CERT,"exportable certification" },
+ { PGPSUBTYPE_TRUST_SIG, "trust signature" },
+ { PGPSUBTYPE_REGEX, "regular expression" },
+ { PGPSUBTYPE_REVOCABLE, "revocable" },
+ { PGPSUBTYPE_KEY_EXPIRE_TIME,"key expiration time" },
+ { PGPSUBTYPE_ARR, "additional recipient request" },
+ { PGPSUBTYPE_PREFER_SYMKEY, "preferred symmetric algorithms" },
+ { PGPSUBTYPE_REVOKE_KEY, "revocation key" },
+ { PGPSUBTYPE_ISSUER_KEYID, "issuer key ID" },
+ { PGPSUBTYPE_NOTATION, "notation data" },
+ { PGPSUBTYPE_PREFER_HASH, "preferred hash algorithms" },
+ { PGPSUBTYPE_PREFER_COMPRESS,"preferred compression algorithms" },
+ { PGPSUBTYPE_KEYSERVER_PREFERS,"key server preferences" },
+ { PGPSUBTYPE_PREFER_KEYSERVER,"preferred key server" },
+ { PGPSUBTYPE_PRIMARY_USERID,"primary user id" },
+ { PGPSUBTYPE_POLICY_URL, "policy URL" },
+ { PGPSUBTYPE_KEY_FLAGS, "key flags" },
+ { PGPSUBTYPE_SIGNER_USERID, "signer's user id" },
+ { PGPSUBTYPE_REVOKE_REASON, "reason for revocation" },
+ { PGPSUBTYPE_FEATURES, "features" },
+ { PGPSUBTYPE_EMBEDDED_SIG, "embedded signature" },
+
+ { PGPSUBTYPE_INTERNAL_100, "internal subpkt type 100" },
+ { PGPSUBTYPE_INTERNAL_101, "internal subpkt type 101" },
+ { PGPSUBTYPE_INTERNAL_102, "internal subpkt type 102" },
+ { PGPSUBTYPE_INTERNAL_103, "internal subpkt type 103" },
+ { PGPSUBTYPE_INTERNAL_104, "internal subpkt type 104" },
+ { PGPSUBTYPE_INTERNAL_105, "internal subpkt type 105" },
+ { PGPSUBTYPE_INTERNAL_106, "internal subpkt type 106" },
+ { PGPSUBTYPE_INTERNAL_107, "internal subpkt type 107" },
+ { PGPSUBTYPE_INTERNAL_108, "internal subpkt type 108" },
+ { PGPSUBTYPE_INTERNAL_109, "internal subpkt type 109" },
+ { PGPSUBTYPE_INTERNAL_110, "internal subpkt type 110" },
+ { -1, "Unknown signature subkey type" },
+};
+
+static struct pgpValTbl_s const pgpTagTbl[] = {
+ { PGPTAG_PUBLIC_SESSION_KEY,"Public-Key Encrypted Session Key" },
+ { PGPTAG_SIGNATURE, "Signature" },
+ { PGPTAG_SYMMETRIC_SESSION_KEY,"Symmetric-Key Encrypted Session Key" },
+ { PGPTAG_ONEPASS_SIGNATURE, "One-Pass Signature" },
+ { PGPTAG_SECRET_KEY, "Secret Key" },
+ { PGPTAG_PUBLIC_KEY, "Public Key" },
+ { PGPTAG_SECRET_SUBKEY, "Secret Subkey" },
+ { PGPTAG_COMPRESSED_DATA, "Compressed Data" },
+ { PGPTAG_SYMMETRIC_DATA, "Symmetrically Encrypted Data" },
+ { PGPTAG_MARKER, "Marker" },
+ { PGPTAG_LITERAL_DATA, "Literal Data" },
+ { PGPTAG_TRUST, "Trust" },
+ { PGPTAG_USER_ID, "User ID" },
+ { PGPTAG_PUBLIC_SUBKEY, "Public Subkey" },
+ { PGPTAG_COMMENT_OLD, "Comment (from OpenPGP draft)" },
+ { PGPTAG_PHOTOID, "PGP's photo ID" },
+ { PGPTAG_ENCRYPTED_MDC, "Integrity protected encrypted data" },
+ { PGPTAG_MDC, "Manipulaion detection code packet" },
+ { PGPTAG_PRIVATE_60, "Private #60" },
+ { PGPTAG_COMMENT, "Comment" },
+ { PGPTAG_PRIVATE_62, "Private #62" },
+ { PGPTAG_CONTROL, "Control (GPG)" },
+ { -1, "Unknown packet tag" },
+};
+
+static struct pgpValTbl_s const pgpArmorTbl[] = {
+ { PGPARMOR_MESSAGE, "MESSAGE" },
+ { PGPARMOR_PUBKEY, "PUBLIC KEY BLOCK" },
+ { PGPARMOR_SIGNATURE, "SIGNATURE" },
+ { PGPARMOR_SIGNED_MESSAGE, "SIGNED MESSAGE" },
+ { PGPARMOR_FILE, "ARMORED FILE" },
+ { PGPARMOR_PRIVKEY, "PRIVATE KEY BLOCK" },
+ { PGPARMOR_SECKEY, "SECRET KEY BLOCK" },
+ { -1, "Unknown armor block" }
+};
+
+static struct pgpValTbl_s const pgpArmorKeyTbl[] = {
+ { PGPARMORKEY_VERSION, "Version: " },
+ { PGPARMORKEY_COMMENT, "Comment: " },
+ { PGPARMORKEY_MESSAGEID, "MessageID: " },
+ { PGPARMORKEY_HASH, "Hash: " },
+ { PGPARMORKEY_CHARSET, "Charset: " },
+ { -1, "Unknown armor key" }
+};
+
+static void pgpPrtNL(void)
+{
+ if (!_print) return;
+ fprintf(stderr, "\n");
+}
+
+static void pgpPrtInt(const char *pre, int i)
+{
+ if (!_print) return;
+ if (pre && *pre)
+ fprintf(stderr, "%s", pre);
+ fprintf(stderr, " %d", i);
+}
+
+static void pgpPrtStr(const char *pre, const char *s)
+{
+ if (!_print) return;
+ if (pre && *pre)
+ fprintf(stderr, "%s", pre);
+ fprintf(stderr, " %s", s);
+}
+
+static const char * pgpValStr(pgpValTbl vs, uint8_t val)
+{
+ do {
+ if (vs->val == val)
+ break;
+ } while ((++vs)->val != -1);
+ return vs->str;
+}
+
+static pgpValTbl pgpValTable(pgpValType type)
+{
+ switch (type) {
+ case PGPVAL_TAG: return pgpTagTbl;
+ case PGPVAL_ARMORBLOCK: return pgpArmorTbl;
+ case PGPVAL_ARMORKEY: return pgpArmorKeyTbl;
+ case PGPVAL_SIGTYPE: return pgpSigTypeTbl;
+ case PGPVAL_SUBTYPE: return pgpSubTypeTbl;
+ case PGPVAL_PUBKEYALGO: return pgpPubkeyTbl;
+ case PGPVAL_SYMKEYALGO: return pgpSymkeyTbl;
+ case PGPVAL_COMPRESSALGO: return pgpCompressionTbl;
+ case PGPVAL_HASHALGO: return pgpHashTbl;
+ case PGPVAL_SERVERPREFS: return pgpKeyServerPrefsTbl;
+ default:
+ break;
+ }
+ return NULL;
+}
+
+const char * pgpValString(pgpValType type, uint8_t val)
+{
+ pgpValTbl tbl = pgpValTable(type);
+ return (tbl != NULL) ? pgpValStr(tbl, val) : NULL;
+}
+
+static void pgpPrtHex(const char *pre, const uint8_t *p, size_t plen)
+{
+ char *hex = NULL;
+ if (!_print) return;
+ if (pre && *pre)
+ fprintf(stderr, "%s", pre);
+ hex = pgpHexStr(p, plen);
+ fprintf(stderr, " %s", hex);
+ free(hex);
+}
+
+static void pgpPrtVal(const char * pre, pgpValTbl vs, uint8_t val)
+{
+ if (!_print) return;
+ if (pre && *pre)
+ fprintf(stderr, "%s", pre);
+ fprintf(stderr, "%s(%u)", pgpValStr(vs, val), (unsigned)val);
+}
+
+/** \ingroup rpmpgp
+ * Return no. of bits in a multiprecision integer.
+ * @param p pointer to multiprecision integer
+ * @return no. of bits
+ */
+static
+unsigned int pgpMpiBits(const uint8_t *p)
+{
+ return ((p[0] << 8) | p[1]);
+}
+
+/** \ingroup rpmpgp
+ * Return no. of bytes in a multiprecision integer.
+ * @param p pointer to multiprecision integer
+ * @return no. of bytes
+ */
+static
+size_t pgpMpiLen(const uint8_t *p)
+{
+ return (2 + ((pgpMpiBits(p)+7)>>3));
+}
+
+/** \ingroup rpmpgp
+ * Return hex formatted representation of a multiprecision integer.
+ * @param p bytes
+ * @return hex formatted string (malloc'ed)
+ */
+static inline
+char * pgpMpiStr(const uint8_t *p)
+{
+ char *str = NULL;
+ char *hex = pgpHexStr(p+2, pgpMpiLen(p)-2);
+ rasprintf(&str, "[%4u]: %s", pgpGrab(p, (size_t) 2), hex);
+ free(hex);
+ return str;
+}
+
+/** \ingroup rpmpgp
+ * Return value of an OpenPGP string.
+ * @param vs table of (string,value) pairs
+ * @param s string token to lookup
+ * @param se end-of-string address
+ * @return byte value
+ */
+static inline
+int pgpValTok(pgpValTbl vs, const char * s, const char * se)
+{
+ do {
+ size_t vlen = strlen(vs->str);
+ if (vlen <= (se-s) && rstreqn(s, vs->str, vlen))
+ break;
+ } while ((++vs)->val != -1);
+ return vs->val;
+}
+/**
+ * @return 0 on success
+ */
+static int pgpMpiSet(const char * pre, unsigned int lbits,
+ uint8_t *dest, const uint8_t * p, const uint8_t * pend)
+{
+ unsigned int mbits = pgpMpiBits(p);
+ unsigned int nbits;
+ size_t nbytes;
+ uint8_t *t = dest;
+ unsigned int ix;
+
+ if ((p + ((mbits+7) >> 3)) > pend)
+ return 1;
+
+ if (mbits > lbits)
+ return 1;
+
+ nbits = (lbits > mbits ? lbits : mbits);
+ nbytes = ((nbits + 7) >> 3);
+ ix = (nbits - mbits) >> 3;
+
+if (_debug)
+fprintf(stderr, "*** mbits %u nbits %u nbytes %zu ix %u\n", mbits, nbits, nbytes, ix);
+ if (ix > 0) memset(t, '\0', ix);
+ memcpy(t+ix, p+2, nbytes-ix);
+if (_debug)
+fprintf(stderr, "*** %s %s\n", pre, pgpHexStr(dest, nbytes));
+
+ return 0;
+}
+
+/**
+ * @return NULL on error
+ */
+static SECItem *pgpMpiItem(PRArenaPool *arena, SECItem *item, const uint8_t *p)
+{
+ size_t nbytes = pgpMpiLen(p)-2;
+
+ if (item == NULL) {
+ if ((item=SECITEM_AllocItem(arena, item, nbytes)) == NULL)
+ return item;
+ } else {
+ if (arena != NULL)
+ item->data = PORT_ArenaGrow(arena, item->data, item->len, nbytes);
+ else
+ item->data = PORT_Realloc(item->data, nbytes);
+
+ if (item->data == NULL) {
+ if (arena == NULL)
+ SECITEM_FreeItem(item, PR_TRUE);
+ return NULL;
+ }
+ }
+
+ memcpy(item->data, p+2, nbytes);
+ item->len = nbytes;
+ return item;
+}
+/*@=boundswrite@*/
+
+static SECKEYPublicKey *pgpNewPublicKey(KeyType type)
+{
+ PRArenaPool *arena;
+ SECKEYPublicKey *key;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL)
+ return NULL;
+
+ key = PORT_ArenaZAlloc(arena, sizeof(SECKEYPublicKey));
+
+ if (key == NULL) {
+ PORT_FreeArena(arena, PR_FALSE);
+ return NULL;
+ }
+
+ key->keyType = type;
+ key->pkcs11ID = CK_INVALID_HANDLE;
+ key->pkcs11Slot = NULL;
+ key->arena = arena;
+ return key;
+}
+
+/** \ingroup rpmpgp
+ * Is buffer at beginning of an OpenPGP packet?
+ * @param p buffer
+ * @return 1 if an OpenPGP packet, 0 otherwise
+ */
+static inline
+int pgpIsPkt(const uint8_t * p)
+{
+ unsigned int val = *p++;
+ pgpTag tag;
+ int rc;
+
+ /* XXX can't deal with these. */
+ if (!(val & 0x80))
+ return 0;
+
+ if (val & 0x40)
+ tag = (pgpTag)(val & 0x3f);
+ else
+ tag = (pgpTag)((val >> 2) & 0xf);
+
+ switch (tag) {
+ case PGPTAG_MARKER:
+ case PGPTAG_SYMMETRIC_SESSION_KEY:
+ case PGPTAG_ONEPASS_SIGNATURE:
+ case PGPTAG_PUBLIC_KEY:
+ case PGPTAG_SECRET_KEY:
+ case PGPTAG_PUBLIC_SESSION_KEY:
+ case PGPTAG_SIGNATURE:
+ case PGPTAG_COMMENT:
+ case PGPTAG_COMMENT_OLD:
+ case PGPTAG_LITERAL_DATA:
+ case PGPTAG_COMPRESSED_DATA:
+ case PGPTAG_SYMMETRIC_DATA:
+ rc = 1;
+ break;
+ case PGPTAG_PUBLIC_SUBKEY:
+ case PGPTAG_SECRET_SUBKEY:
+ case PGPTAG_USER_ID:
+ case PGPTAG_RESERVED:
+ case PGPTAG_TRUST:
+ case PGPTAG_PHOTOID:
+ case PGPTAG_ENCRYPTED_MDC:
+ case PGPTAG_MDC:
+ case PGPTAG_PRIVATE_60:
+ case PGPTAG_PRIVATE_62:
+ case PGPTAG_CONTROL:
+ default:
+ rc = 0;
+ break;
+ }
+
+ return rc;
+}
+
+#define CRC24_INIT 0xb704ce
+#define CRC24_POLY 0x1864cfb
+
+/** \ingroup rpmpgp
+ * Return CRC of a buffer.
+ * @param octets bytes
+ * @param len no. of bytes
+ * @return crc of buffer
+ */
+static inline
+unsigned int pgpCRC(const uint8_t *octets, size_t len)
+{
+ unsigned int crc = CRC24_INIT;
+ size_t i;
+
+ while (len--) {
+ crc ^= (*octets++) << 16;
+ for (i = 0; i < 8; i++) {
+ crc <<= 1;
+ if (crc & 0x1000000)
+ crc ^= CRC24_POLY;
+ }
+ }
+ return crc & 0xffffff;
+}
+
+static int pgpPrtSubType(const uint8_t *h, size_t hlen, pgpSigType sigtype,
+ pgpDigParams _digp)
+{
+ const uint8_t *p = h;
+ size_t plen, i;
+
+ while (hlen > 0) {
+ i = pgpLen(p, &plen);
+ p += i;
+ hlen -= i;
+
+ pgpPrtVal(" ", pgpSubTypeTbl, (p[0]&(~PGPSUBTYPE_CRITICAL)));
+ if (p[0] & PGPSUBTYPE_CRITICAL)
+ if (_print)
+ fprintf(stderr, " *CRITICAL*");
+ switch (*p) {
+ case PGPSUBTYPE_PREFER_SYMKEY: /* preferred symmetric algorithms */
+ for (i = 1; i < plen; i++)
+ pgpPrtVal(" ", pgpSymkeyTbl, p[i]);
+ break;
+ case PGPSUBTYPE_PREFER_HASH: /* preferred hash algorithms */
+ for (i = 1; i < plen; i++)
+ pgpPrtVal(" ", pgpHashTbl, p[i]);
+ break;
+ case PGPSUBTYPE_PREFER_COMPRESS:/* preferred compression algorithms */
+ for (i = 1; i < plen; i++)
+ pgpPrtVal(" ", pgpCompressionTbl, p[i]);
+ break;
+ case PGPSUBTYPE_KEYSERVER_PREFERS:/* key server preferences */
+ for (i = 1; i < plen; i++)
+ pgpPrtVal(" ", pgpKeyServerPrefsTbl, p[i]);
+ break;
+ case PGPSUBTYPE_SIG_CREATE_TIME:
+ if (_digp && !(_digp->saved & PGPDIG_SAVED_TIME) &&
+ (sigtype == PGPSIGTYPE_POSITIVE_CERT || sigtype == PGPSIGTYPE_BINARY || sigtype == PGPSIGTYPE_TEXT || sigtype == PGPSIGTYPE_STANDALONE))
+ {
+ _digp->saved |= PGPDIG_SAVED_TIME;
+ memcpy(_digp->time, p+1, sizeof(_digp->time));
+ }
+ case PGPSUBTYPE_SIG_EXPIRE_TIME:
+ case PGPSUBTYPE_KEY_EXPIRE_TIME:
+ if ((plen - 1) == 4) {
+ time_t t = pgpGrab(p+1, plen-1);
+ if (_print)
+ fprintf(stderr, " %-24.24s(0x%08x)", ctime(&t), (unsigned)t);
+ } else
+ pgpPrtHex("", p+1, plen-1);
+ break;
+
+ case PGPSUBTYPE_ISSUER_KEYID: /* issuer key ID */
+ if (_digp && !(_digp->saved & PGPDIG_SAVED_ID) &&
+ (sigtype == PGPSIGTYPE_POSITIVE_CERT || sigtype == PGPSIGTYPE_BINARY || sigtype == PGPSIGTYPE_TEXT || sigtype == PGPSIGTYPE_STANDALONE))
+ {
+ _digp->saved |= PGPDIG_SAVED_ID;
+ memcpy(_digp->signid, p+1, sizeof(_digp->signid));
+ }
+ case PGPSUBTYPE_EXPORTABLE_CERT:
+ case PGPSUBTYPE_TRUST_SIG:
+ case PGPSUBTYPE_REGEX:
+ case PGPSUBTYPE_REVOCABLE:
+ case PGPSUBTYPE_ARR:
+ case PGPSUBTYPE_REVOKE_KEY:
+ case PGPSUBTYPE_NOTATION:
+ case PGPSUBTYPE_PREFER_KEYSERVER:
+ case PGPSUBTYPE_PRIMARY_USERID:
+ case PGPSUBTYPE_POLICY_URL:
+ case PGPSUBTYPE_KEY_FLAGS:
+ case PGPSUBTYPE_SIGNER_USERID:
+ case PGPSUBTYPE_REVOKE_REASON:
+ case PGPSUBTYPE_FEATURES:
+ case PGPSUBTYPE_EMBEDDED_SIG:
+ case PGPSUBTYPE_INTERNAL_100:
+ case PGPSUBTYPE_INTERNAL_101:
+ case PGPSUBTYPE_INTERNAL_102:
+ case PGPSUBTYPE_INTERNAL_103:
+ case PGPSUBTYPE_INTERNAL_104:
+ case PGPSUBTYPE_INTERNAL_105:
+ case PGPSUBTYPE_INTERNAL_106:
+ case PGPSUBTYPE_INTERNAL_107:
+ case PGPSUBTYPE_INTERNAL_108:
+ case PGPSUBTYPE_INTERNAL_109:
+ case PGPSUBTYPE_INTERNAL_110:
+ default:
+ pgpPrtHex("", p+1, plen-1);
+ break;
+ }
+ pgpPrtNL();
+ p += plen;
+ hlen -= plen;
+ }
+ return 0;
+}
+
+static const char * const pgpSigRSA[] = {
+ " m**d =",
+ NULL,
+};
+
+static const char * const pgpSigDSA[] = {
+ " r =",
+ " s =",
+ NULL,
+};
+
+#ifndef DSA_SUBPRIME_LEN
+#define DSA_SUBPRIME_LEN 20
+#endif
+
+static int pgpPrtSigParams(pgpTag tag, uint8_t pubkey_algo, uint8_t sigtype,
+ const uint8_t *p, const uint8_t *h, size_t hlen, pgpDig _dig)
+{
+ const uint8_t * pend = h + hlen;
+ size_t i;
+ SECItem dsaraw;
+ unsigned char dsabuf[2*DSA_SUBPRIME_LEN];
+ char *mpi;
+
+ dsaraw.type = 0;
+ dsaraw.data = dsabuf;
+ dsaraw.len = sizeof(dsabuf);
+
+ for (i = 0; p < pend; i++, p += pgpMpiLen(p)) {
+ if (pubkey_algo == PGPPUBKEYALGO_RSA) {
+ if (i >= 1) break;
+ if (_dig &&
+ (sigtype == PGPSIGTYPE_BINARY || sigtype == PGPSIGTYPE_TEXT))
+ {
+ switch (i) {
+ case 0: /* m**d */
+ _dig->sigdata = pgpMpiItem(NULL, _dig->sigdata, p);
+ if (_dig->sigdata == NULL)
+ return 1;
+ break;
+ default:
+ break;
+ }
+ }
+ pgpPrtStr("", pgpSigRSA[i]);
+ } else if (pubkey_algo == PGPPUBKEYALGO_DSA) {
+ if (i >= 2) break;
+ if (_dig &&
+ (sigtype == PGPSIGTYPE_BINARY || sigtype == PGPSIGTYPE_TEXT))
+ {
+ int xx;
+ xx = 0;
+ switch (i) {
+ case 0:
+ memset(dsaraw.data, '\0', 2*DSA_SUBPRIME_LEN);
+ /* r */
+ xx = pgpMpiSet(pgpSigDSA[i], DSA_SUBPRIME_LEN*8, dsaraw.data, p, pend);
+ break;
+ case 1: /* s */
+ xx = pgpMpiSet(pgpSigDSA[i], DSA_SUBPRIME_LEN*8, dsaraw.data + DSA_SUBPRIME_LEN, p, pend);
+ if (_dig->sigdata != NULL)
+ SECITEM_FreeItem(_dig->sigdata, PR_FALSE);
+ else if ((_dig->sigdata=SECITEM_AllocItem(NULL, NULL, 0)) == NULL) {
+ xx = 1;
+ break;
+ }
+ if (DSAU_EncodeDerSig(_dig->sigdata, &dsaraw) != SECSuccess)
+ xx = 1;
+ break;
+ default:
+ xx = 1;
+ break;
+ }
+ if (xx) return xx;
+ }
+ pgpPrtStr("", pgpSigDSA[i]);
+ } else {
+ if (_print)
+ fprintf(stderr, "%7zd", i);
+ }
+ mpi = pgpMpiStr(p);
+ pgpPrtStr("", mpi);
+ free(mpi);
+ pgpPrtNL();
+ }
+
+ return 0;
+}
+
+static int pgpPrtSig(pgpTag tag, const uint8_t *h, size_t hlen,
+ pgpDig _dig, pgpDigParams _digp)
+{
+ uint8_t version = h[0];
+ uint8_t * p;
+ size_t plen;
+ int rc;
+
+ switch (version) {
+ case 3:
+ { pgpPktSigV3 v = (pgpPktSigV3)h;
+ time_t t;
+
+ if (v->hashlen != 5)
+ return 1;
+
+ pgpPrtVal("V3 ", pgpTagTbl, tag);
+ pgpPrtVal(" ", pgpPubkeyTbl, v->pubkey_algo);
+ pgpPrtVal(" ", pgpHashTbl, v->hash_algo);
+ pgpPrtVal(" ", pgpSigTypeTbl, v->sigtype);
+ pgpPrtNL();
+ t = pgpGrab(v->time, sizeof(v->time));
+ if (_print)
+ fprintf(stderr, " %-24.24s(0x%08x)", ctime(&t), (unsigned)t);
+ pgpPrtNL();
+ pgpPrtHex(" signer keyid", v->signid, sizeof(v->signid));
+ plen = pgpGrab(v->signhash16, sizeof(v->signhash16));
+ pgpPrtHex(" signhash16", v->signhash16, sizeof(v->signhash16));
+ pgpPrtNL();
+
+ if (_digp && _digp->pubkey_algo == 0) {
+ _digp->version = v->version;
+ _digp->hashlen = v->hashlen;
+ _digp->sigtype = v->sigtype;
+ _digp->hash = memcpy(xmalloc(v->hashlen), &v->sigtype, v->hashlen);
+ memcpy(_digp->time, v->time, sizeof(_digp->time));
+ memcpy(_digp->signid, v->signid, sizeof(_digp->signid));
+ _digp->pubkey_algo = v->pubkey_algo;
+ _digp->hash_algo = v->hash_algo;
+ memcpy(_digp->signhash16, v->signhash16, sizeof(_digp->signhash16));
+ }
+
+ p = ((uint8_t *)v) + sizeof(*v);
+ rc = pgpPrtSigParams(tag, v->pubkey_algo, v->sigtype, p, h, hlen, _dig);
+ } break;
+ case 4:
+ { pgpPktSigV4 v = (pgpPktSigV4)h;
+
+ pgpPrtVal("V4 ", pgpTagTbl, tag);
+ pgpPrtVal(" ", pgpPubkeyTbl, v->pubkey_algo);
+ pgpPrtVal(" ", pgpHashTbl, v->hash_algo);
+ pgpPrtVal(" ", pgpSigTypeTbl, v->sigtype);
+ pgpPrtNL();
+
+ p = &v->hashlen[0];
+ plen = pgpGrab(v->hashlen, sizeof(v->hashlen));
+ p += sizeof(v->hashlen);
+
+ if ((p + plen) > (h + hlen))
+ return 1;
+
+if (_debug && _print)
+fprintf(stderr, " hash[%zu] -- %s\n", plen, pgpHexStr(p, plen));
+ if (_digp && _digp->pubkey_algo == 0) {
+ _digp->hashlen = sizeof(*v) + plen;
+ _digp->hash = memcpy(xmalloc(_digp->hashlen), v, _digp->hashlen);
+ }
+ (void) pgpPrtSubType(p, plen, v->sigtype, _digp);
+ p += plen;
+
+ plen = pgpGrab(p,2);
+ p += 2;
+
+ if ((p + plen) > (h + hlen))
+ return 1;
+
+if (_debug && _print)
+fprintf(stderr, " unhash[%zu] -- %s\n", plen, pgpHexStr(p, plen));
+ (void) pgpPrtSubType(p, plen, v->sigtype, _digp);
+ p += plen;
+
+ plen = pgpGrab(p,2);
+ pgpPrtHex(" signhash16", p, 2);
+ pgpPrtNL();
+
+ if (_digp && _digp->pubkey_algo == 0) {
+ _digp->version = v->version;
+ _digp->sigtype = v->sigtype;
+ _digp->pubkey_algo = v->pubkey_algo;
+ _digp->hash_algo = v->hash_algo;
+ memcpy(_digp->signhash16, p, sizeof(_digp->signhash16));
+ }
+
+ p += 2;
+ if (p > (h + hlen))
+ return 1;
+
+ rc = pgpPrtSigParams(tag, v->pubkey_algo, v->sigtype, p, h, hlen, _dig);
+ } break;
+ default:
+ rc = 1;
+ break;
+ }
+ return rc;
+}
+
+static const char * const pgpPublicRSA[] = {
+ " n =",
+ " e =",
+ NULL,
+};
+
+#ifdef NOTYET
+static const char * const pgpSecretRSA[] = {
+ " d =",
+ " p =",
+ " q =",
+ " u =",
+ NULL,
+};
+#endif
+
+static const char * const pgpPublicDSA[] = {
+ " p =",
+ " q =",
+ " g =",
+ " y =",
+ NULL,
+};
+
+#ifdef NOTYET
+static const char * const pgpSecretDSA[] = {
+ " x =",
+ NULL,
+};
+#endif
+
+static const char * const pgpPublicELGAMAL[] = {
+ " p =",
+ " g =",
+ " y =",
+ NULL,
+};
+
+#ifdef NOTYET
+static const char * const pgpSecretELGAMAL[] = {
+ " x =",
+ NULL,
+};
+#endif
+
+char * pgpHexStr(const uint8_t *p, size_t plen)
+{
+ char *t, *str;
+ str = t = xmalloc(plen * 2 + 1);
+ static char const hex[] = "0123456789abcdef";
+ while (plen-- > 0) {
+ size_t i;
+ i = *p++;
+ *t++ = hex[ (i >> 4) & 0xf ];
+ *t++ = hex[ (i ) & 0xf ];
+ }
+ *t = '\0';
+ return str;
+}
+
+static const uint8_t * pgpPrtPubkeyParams(uint8_t pubkey_algo,
+ const uint8_t *p, const uint8_t *h, size_t hlen,
+ pgpDig _dig)
+{
+ size_t i;
+
+ /* XXX we can't handle more than one key in a packet, error out */
+ if (_dig && _dig->keydata)
+ return NULL;
+
+ for (i = 0; p < &h[hlen]; i++, p += pgpMpiLen(p)) {
+ char * mpi;
+ if (pubkey_algo == PGPPUBKEYALGO_RSA) {
+ if (i >= 2) break;
+ if (_dig) {
+ if (_dig->keydata == NULL) {
+ _dig->keydata = pgpNewPublicKey(rsaKey);
+ if (_dig->keydata == NULL)
+ return NULL;
+ }
+ switch (i) {
+ case 0: /* n */
+ pgpMpiItem(_dig->keydata->arena, &_dig->keydata->u.rsa.modulus, p);
+ break;
+ case 1: /* e */
+ pgpMpiItem(_dig->keydata->arena, &_dig->keydata->u.rsa.publicExponent, p);
+ break;
+ default:
+ break;
+ }
+ }
+ pgpPrtStr("", pgpPublicRSA[i]);
+ } else if (pubkey_algo == PGPPUBKEYALGO_DSA) {
+ if (i >= 4) break;
+ if (_dig) {
+ if (_dig->keydata == NULL) {
+ _dig->keydata = pgpNewPublicKey(dsaKey);
+ if (_dig->keydata == NULL)
+ return NULL;
+ }
+ switch (i) {
+ case 0: /* p */
+ pgpMpiItem(_dig->keydata->arena, &_dig->keydata->u.dsa.params.prime, p);
+ break;
+ case 1: /* q */
+ pgpMpiItem(_dig->keydata->arena, &_dig->keydata->u.dsa.params.subPrime, p);
+ break;
+ case 2: /* g */
+ pgpMpiItem(_dig->keydata->arena, &_dig->keydata->u.dsa.params.base, p);
+ break;
+ case 3: /* y */
+ pgpMpiItem(_dig->keydata->arena, &_dig->keydata->u.dsa.publicValue, p);
+ break;
+ default:
+ break;
+ }
+ }
+ pgpPrtStr("", pgpPublicDSA[i]);
+ } else if (pubkey_algo == PGPPUBKEYALGO_ELGAMAL_ENCRYPT) {
+ if (i >= 3) break;
+ pgpPrtStr("", pgpPublicELGAMAL[i]);
+ } else {
+ if (_print)
+ fprintf(stderr, "%7zd", i);
+ }
+ mpi = pgpMpiStr(p);
+ pgpPrtStr("", mpi);
+ free(mpi);
+ pgpPrtNL();
+ }
+
+ return p;
+}
+
+static const uint8_t * pgpPrtSeckeyParams(uint8_t pubkey_algo,
+ const uint8_t *p, const uint8_t *h, size_t hlen)
+{
+ size_t i;
+
+ switch (*p) {
+ case 0:
+ pgpPrtVal(" ", pgpSymkeyTbl, *p);
+ break;
+ case 255:
+ p++;
+ pgpPrtVal(" ", pgpSymkeyTbl, *p);
+ switch (p[1]) {
+ case 0x00:
+ pgpPrtVal(" simple ", pgpHashTbl, p[2]);
+ p += 2;
+ break;
+ case 0x01:
+ pgpPrtVal(" salted ", pgpHashTbl, p[2]);
+ pgpPrtHex("", p+3, 8);
+ p += 10;
+ break;
+ case 0x03:
+ pgpPrtVal(" iterated/salted ", pgpHashTbl, p[2]);
+ /* FIX: unsigned cast */
+ i = (16 + (p[11] & 0xf)) << ((p[11] >> 4) + 6);
+ pgpPrtHex("", p+3, 8);
+ pgpPrtInt(" iter", i);
+ p += 11;
+ break;
+ }
+ break;
+ default:
+ pgpPrtVal(" ", pgpSymkeyTbl, *p);
+ pgpPrtHex(" IV", p+1, 8);
+ p += 8;
+ break;
+ }
+ pgpPrtNL();
+
+ p++;
+
+#ifdef NOTYET /* XXX encrypted MPI's need to be handled. */
+ for (i = 0; p < &h[hlen]; i++, p += pgpMpiLen(p)) {
+ char *mpi;
+ if (pubkey_algo == PGPPUBKEYALGO_RSA) {
+ if (pgpSecretRSA[i] == NULL) break;
+ pgpPrtStr("", pgpSecretRSA[i]);
+ } else if (pubkey_algo == PGPPUBKEYALGO_DSA) {
+ if (pgpSecretDSA[i] == NULL) break;
+ pgpPrtStr("", pgpSecretDSA[i]);
+ } else if (pubkey_algo == PGPPUBKEYALGO_ELGAMAL_ENCRYPT) {
+ if (pgpSecretELGAMAL[i] == NULL) break;
+ pgpPrtStr("", pgpSecretELGAMAL[i]);
+ } else {
+ if (_print)
+ fprintf(stderr, "%7d", i);
+ }
+ mpi = pgpMpiStr(p);
+ pgpPrtStr("", mpi);
+ free(mpi);
+ pgpPrtNL();
+ }
+#else
+ pgpPrtHex(" secret", p, (hlen - (p - h) - 2));
+ pgpPrtNL();
+ p += (hlen - (p - h) - 2);
+#endif
+ pgpPrtHex(" checksum", p, 2);
+ pgpPrtNL();
+
+ return p;
+}
+
+static int pgpPrtKey(pgpTag tag, const uint8_t *h, size_t hlen,
+ pgpDig _dig, pgpDigParams _digp)
+{
+ uint8_t version = *h;
+ const uint8_t * p;
+ size_t plen;
+ time_t t;
+ int rc;
+
+ switch (version) {
+ case 3:
+ { pgpPktKeyV3 v = (pgpPktKeyV3)h;
+ pgpPrtVal("V3 ", pgpTagTbl, tag);
+ pgpPrtVal(" ", pgpPubkeyTbl, v->pubkey_algo);
+ t = pgpGrab(v->time, sizeof(v->time));
+ if (_print)
+ fprintf(stderr, " %-24.24s(0x%08x)", ctime(&t), (unsigned)t);
+ plen = pgpGrab(v->valid, sizeof(v->valid));
+ if (plen != 0)
+ fprintf(stderr, " valid %zu days", plen);
+ pgpPrtNL();
+
+ if (_digp && _digp->tag == tag) {
+ _digp->version = v->version;
+ memcpy(_digp->time, v->time, sizeof(_digp->time));
+ _digp->pubkey_algo = v->pubkey_algo;
+ }
+
+ p = ((uint8_t *)v) + sizeof(*v);
+ p = pgpPrtPubkeyParams(v->pubkey_algo, p, h, hlen, _dig);
+ rc = (p == NULL);
+ } break;
+ case 4:
+ { pgpPktKeyV4 v = (pgpPktKeyV4)h;
+ pgpPrtVal("V4 ", pgpTagTbl, tag);
+ pgpPrtVal(" ", pgpPubkeyTbl, v->pubkey_algo);
+ t = pgpGrab(v->time, sizeof(v->time));
+ if (_print)
+ fprintf(stderr, " %-24.24s(0x%08x)", ctime(&t), (unsigned)t);
+ pgpPrtNL();
+
+ if (_digp && _digp->tag == tag) {
+ _digp->version = v->version;
+ memcpy(_digp->time, v->time, sizeof(_digp->time));
+ _digp->pubkey_algo = v->pubkey_algo;
+ }
+
+ p = ((uint8_t *)v) + sizeof(*v);
+ p = pgpPrtPubkeyParams(v->pubkey_algo, p, h, hlen, _dig);
+ if (!(tag == PGPTAG_PUBLIC_KEY || tag == PGPTAG_PUBLIC_SUBKEY))
+ p = pgpPrtSeckeyParams(v->pubkey_algo, p, h, hlen);
+ rc = (p == NULL);
+ } break;
+ default:
+ rc = 1;
+ break;
+ }
+ return rc;
+}
+
+static int pgpPrtUserID(pgpTag tag, const uint8_t *h, size_t hlen,
+ pgpDigParams _digp)
+{
+ pgpPrtVal("", pgpTagTbl, tag);
+ if (_print)
+ fprintf(stderr, " \"%.*s\"", (int)hlen, (const char *)h);
+ pgpPrtNL();
+ if (_digp) {
+ free(_digp->userid);
+ _digp->userid = memcpy(xmalloc(hlen+1), h, hlen);
+ _digp->userid[hlen] = '\0';
+ }
+ return 0;
+}
+
+static int pgpPrtComment(pgpTag tag, const uint8_t *h, size_t hlen)
+{
+ size_t i = hlen;
+
+ pgpPrtVal("", pgpTagTbl, tag);
+ if (_print)
+ fprintf(stderr, " ");
+ while (i > 0) {
+ size_t j;
+ if (*h >= ' ' && *h <= 'z') {
+ if (_print)
+ fprintf(stderr, "%s", (const char *)h);
+ j = strlen((const char*)h);
+ while (h[j] == '\0')
+ j++;
+ } else {
+ pgpPrtHex("", h, i);
+ j = i;
+ }
+ i -= j;
+ h += j;
+ }
+ pgpPrtNL();
+ return 0;
+}
+
+int pgpPubkeyFingerprint(const uint8_t * pkt, size_t pktlen, pgpKeyID_t keyid)
+{
+ unsigned int val = *pkt;
+ size_t plen, hlen;
+ pgpTag tag;
+ const uint8_t *se, *h;
+ DIGEST_CTX ctx;
+ int rc = -1; /* assume failure. */
+
+ if (!(val & 0x80))
+ return rc;
+
+ if (val & 0x40) {
+ tag = (val & 0x3f);
+ plen = pgpLen(pkt+1, &hlen);
+ } else {
+ tag = (val >> 2) & 0xf;
+ plen = (1 << (val & 0x3));
+ hlen = pgpGrab(pkt+1, plen);
+ }
+ if (pktlen > 0 && 1 + plen + hlen > pktlen)
+ return rc;
+
+ h = pkt + 1 + plen;
+
+ switch (h[0]) {
+ case 3:
+ { pgpPktKeyV3 v = (pgpPktKeyV3) (h);
+ se = (uint8_t *)(v + 1);
+ switch (v->pubkey_algo) {
+ case PGPPUBKEYALGO_RSA:
+ se += pgpMpiLen(se);
+ memmove(keyid, (se-8), 8);
+ rc = 0;
+ break;
+ default: /* TODO: md5 of mpi bodies (i.e. no length) */
+ break;
+ }
+ } break;
+ case 4:
+ { pgpPktKeyV4 v = (pgpPktKeyV4) (h);
+ uint8_t * d = NULL;
+ uint8_t in[3];
+ size_t dlen;
+ int i;
+
+ se = (uint8_t *)(v + 1);
+ switch (v->pubkey_algo) {
+ case PGPPUBKEYALGO_RSA:
+ for (i = 0; i < 2; i++)
+ se += pgpMpiLen(se);
+ break;
+ case PGPPUBKEYALGO_DSA:
+ for (i = 0; i < 4; i++)
+ se += pgpMpiLen(se);
+ break;
+ }
+
+ ctx = rpmDigestInit(PGPHASHALGO_SHA1, RPMDIGEST_NONE);
+ i = se - h;
+ in[0] = 0x99;
+ in[1] = i >> 8;
+ in[2] = i;
+ (void) rpmDigestUpdate(ctx, in, 3);
+ (void) rpmDigestUpdate(ctx, h, i);
+ (void) rpmDigestFinal(ctx, (void **)&d, &dlen, 0);
+
+ if (d) {
+ memmove(keyid, (d + (dlen-8)), 8);
+ free(d);
+ rc = 0;
+ }
+
+ } break;
+ }
+ return rc;
+}
+
+int pgpExtractPubkeyFingerprint(const char * b64pkt, pgpKeyID_t keyid)
+{
+ uint8_t * pkt;
+ size_t pktlen;
+
+ if (b64decode(b64pkt, (void **)&pkt, &pktlen))
+ return -1; /* on error */
+ (void) pgpPubkeyFingerprint(pkt, pktlen, keyid);
+ pkt = _free(pkt);
+ return sizeof(keyid); /* no. of bytes of pubkey signid */
+}
+
+static int pgpPrtPkt(const uint8_t *pkt, size_t pleft,
+ pgpDig _dig, pgpDigParams _digp)
+{
+ unsigned int val = *pkt;
+ size_t pktlen;
+ pgpTag tag;
+ size_t plen;
+ const uint8_t *h;
+ size_t hlen = 0;
+ int rc = 0;
+
+ /* XXX can't deal with these. */
+ if (!(val & 0x80))
+ return -1;
+
+ if (val & 0x40) {
+ tag = (val & 0x3f);
+ plen = pgpLen(pkt+1, &hlen);
+ } else {
+ tag = (val >> 2) & 0xf;
+ plen = (1 << (val & 0x3));
+ hlen = pgpGrab(pkt+1, plen);
+ }
+
+ pktlen = 1 + plen + hlen;
+ if (pktlen > pleft)
+ return -1;
+
+ h = pkt + 1 + plen;
+ switch (tag) {
+ case PGPTAG_SIGNATURE:
+ rc = pgpPrtSig(tag, h, hlen, _dig, _digp);
+ break;
+ case PGPTAG_PUBLIC_KEY:
+ /* Get the public key fingerprint. */
+ if (_digp) {
+ if (!pgpPubkeyFingerprint(pkt, pktlen, _digp->signid))
+ _digp->saved |= PGPDIG_SAVED_ID;
+ else
+ memset(_digp->signid, 0, sizeof(_digp->signid));
+ }
+ rc = pgpPrtKey(tag, h, hlen, _dig, _digp);
+ break;
+ case PGPTAG_USER_ID:
+ rc = pgpPrtUserID(tag, h, hlen, _digp);
+ break;
+ case PGPTAG_COMMENT:
+ case PGPTAG_COMMENT_OLD:
+ rc = pgpPrtComment(tag, h, hlen);
+ break;
+
+ case PGPTAG_PUBLIC_SUBKEY:
+ case PGPTAG_SECRET_KEY:
+ case PGPTAG_SECRET_SUBKEY:
+ case PGPTAG_RESERVED:
+ case PGPTAG_PUBLIC_SESSION_KEY:
+ case PGPTAG_SYMMETRIC_SESSION_KEY:
+ case PGPTAG_COMPRESSED_DATA:
+ case PGPTAG_SYMMETRIC_DATA:
+ case PGPTAG_MARKER:
+ case PGPTAG_LITERAL_DATA:
+ case PGPTAG_TRUST:
+ case PGPTAG_PHOTOID:
+ case PGPTAG_ENCRYPTED_MDC:
+ case PGPTAG_MDC:
+ case PGPTAG_PRIVATE_60:
+ case PGPTAG_PRIVATE_62:
+ case PGPTAG_CONTROL:
+ default:
+ pgpPrtVal("", pgpTagTbl, tag);
+ pgpPrtHex("", h, hlen);
+ pgpPrtNL();
+ break;
+ }
+
+ return (rc ? -1 : pktlen);
+}
+
+pgpDig pgpNewDig(void)
+{
+ pgpDig dig = xcalloc(1, sizeof(*dig));
+
+ return dig;
+}
+
+void pgpCleanDig(pgpDig dig)
+{
+ if (dig != NULL) {
+ int i;
+ dig->signature.userid = _free(dig->signature.userid);
+ dig->pubkey.userid = _free(dig->pubkey.userid);
+ dig->signature.hash = _free(dig->signature.hash);
+ dig->pubkey.hash = _free(dig->pubkey.hash);
+ /* FIX: double indirection */
+ for (i = 0; i < 4; i++) {
+ dig->signature.params[i] = _free(dig->signature.params[i]);
+ dig->pubkey.params[i] = _free(dig->pubkey.params[i]);
+ }
+
+ memset(&dig->signature, 0, sizeof(dig->signature));
+ memset(&dig->pubkey, 0, sizeof(dig->pubkey));
+
+ if (dig->keydata != NULL) {
+ SECKEY_DestroyPublicKey(dig->keydata);
+ dig->keydata = NULL;
+ }
+
+ if (dig->sigdata != NULL) {
+ SECITEM_ZfreeItem(dig->sigdata, PR_TRUE);
+ dig->sigdata = NULL;
+ }
+ }
+ return;
+}
+
+pgpDig pgpFreeDig(pgpDig dig)
+{
+ if (dig != NULL) {
+
+ /* DUmp the signature/pubkey data. */
+ pgpCleanDig(dig);
+ dig = _free(dig);
+ }
+ return dig;
+}
+
+int pgpPrtPkts(const uint8_t * pkts, size_t pktlen, pgpDig dig, int printing)
+{
+ unsigned int val = *pkts;
+ const uint8_t *p;
+ size_t pleft;
+ int len;
+ pgpDigParams _digp = NULL;
+
+ _print = printing;
+ if (dig != NULL && (val & 0x80)) {
+ pgpTag tag = (val & 0x40) ? (val & 0x3f) : ((val >> 2) & 0xf);
+ _digp = (tag == PGPTAG_SIGNATURE) ? &dig->signature : &dig->pubkey;
+ _digp->tag = tag;
+ } else
+ _digp = NULL;
+
+ for (p = pkts, pleft = pktlen; p < (pkts + pktlen); p += len, pleft -= len) {
+ len = pgpPrtPkt(p, pleft, dig, _digp);
+ if (len <= 0)
+ return len;
+ if (len > pleft) /* XXX shouldn't happen */
+ break;
+ }
+ return 0;
+}
+
+static SECOidTag getSigAlg(pgpDigParams sigp)
+{
+ SECOidTag sigalg = SEC_OID_UNKNOWN;
+ if (sigp->pubkey_algo == PGPPUBKEYALGO_DSA) {
+ /* assume SHA1 for now, NSS doesn't have SECOID's for other types */
+ sigalg = SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST;
+ } else if (sigp->pubkey_algo == PGPPUBKEYALGO_RSA) {
+ switch (sigp->hash_algo) {
+ case PGPHASHALGO_MD5:
+ sigalg = SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION;
+ break;
+ case PGPHASHALGO_MD2:
+ sigalg = SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION;
+ break;
+ case PGPHASHALGO_SHA1:
+ sigalg = SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION;
+ break;
+ case PGPHASHALGO_SHA256:
+ sigalg = SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION;
+ break;
+ case PGPHASHALGO_SHA384:
+ sigalg = SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION;
+ break;
+ case PGPHASHALGO_SHA512:
+ sigalg = SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION;
+ break;
+ default:
+ break;
+ }
+ }
+ return sigalg;
+}
+
+char *pgpIdentItem(pgpDigParams digp)
+{
+ char *id = NULL;
+ if (digp) {
+
+ char *signid = pgpHexStr(digp->signid+4, sizeof(digp->signid)-4);
+ rasprintf(&id, _("V%d %s/%s %s, key ID %s"),
+ digp->version,
+ pgpValStr(pgpPubkeyTbl, digp->pubkey_algo),
+ pgpValStr(pgpHashTbl, digp->hash_algo),
+ pgpValStr(pgpTagTbl, digp->tag),
+ signid);
+ free(signid);
+ } else {
+ id = xstrdup(_("(none)"));
+ }
+ return id;
+}
+
+rpmRC pgpVerifySig(pgpDig dig, DIGEST_CTX hashctx)
+{
+ DIGEST_CTX ctx = rpmDigestDup(hashctx);
+ uint8_t *hash = NULL;
+ size_t hashlen = 0;
+ rpmRC res = RPMRC_FAIL; /* assume failure */
+ pgpDigParams sigp = dig ? &dig->signature : NULL;
+
+ if (sigp == NULL || ctx == NULL)
+ goto exit;
+
+ if (sigp->hash != NULL)
+ rpmDigestUpdate(ctx, sigp->hash, sigp->hashlen);
+
+ if (sigp->version == 4) {
+ /* V4 trailer is six octets long (rfc4880) */
+ uint8_t trailer[6];
+ uint32_t nb = sigp->hashlen;
+ nb = htonl(nb);
+ trailer[0] = sigp->version;
+ trailer[1] = 0xff;
+ memcpy(trailer+2, &nb, 4);
+ rpmDigestUpdate(ctx, trailer, sizeof(trailer));
+ }
+
+ rpmDigestFinal(ctx, (void **)&hash, &hashlen, 0);
+
+ /* Compare leading 16 bits of digest for quick check. */
+ if (hash && memcmp(hash, sigp->signhash16, 2) != 0)
+ goto exit;
+
+ /*
+ * If we have a key, verify the signature for real. Otherwise we've
+ * done all we can, return NOKEY to indicate "looks okay but dunno."
+ */
+ if (dig->keydata == NULL) {
+ res = RPMRC_NOKEY;
+ } else {
+ SECItem digest = { .type = siBuffer, .data = hash, .len = hashlen };
+ SECItem *sig = dig->sigdata;
+
+ /* Zero-pad RSA signature to expected size if necessary */
+ if (sigp->pubkey_algo == PGPPUBKEYALGO_RSA) {
+ size_t siglen = SECKEY_SignatureLen(dig->keydata);
+ if (siglen > sig->len) {
+ size_t pad = siglen - sig->len;
+ if ((sig = SECITEM_AllocItem(NULL, NULL, siglen)) == NULL) {
+ goto exit;
+ }
+ memset(sig->data, 0, pad);
+ memcpy(sig->data+pad, dig->sigdata->data, dig->sigdata->len);
+ }
+ }
+
+ /* XXX VFY_VerifyDigest() is deprecated in NSS 3.12 */
+ if (VFY_VerifyDigest(&digest, dig->keydata, sig,
+ getSigAlg(sigp), NULL) == SECSuccess) {
+ res = RPMRC_OK;
+ }
+
+ if (sig != dig->sigdata) {
+ SECITEM_ZfreeItem(sig, 1);
+ }
+ }
+
+exit:
+ free(hash);
+ return res;
+
+}
+
+static pgpArmor decodePkts(uint8_t *b, uint8_t **pkt, size_t *pktlen)
+{
+ const char * enc = NULL;
+ const char * crcenc = NULL;
+ uint8_t * dec;
+ uint8_t * crcdec;
+ size_t declen;
+ size_t crclen;
+ uint32_t crcpkt, crc;
+ const char * armortype = NULL;
+ char * t, * te;
+ int pstate = 0;
+ pgpArmor ec = PGPARMOR_ERR_NO_BEGIN_PGP; /* XXX assume failure */
+ if (pgpIsPkt(b)) {
+#ifdef NOTYET /* XXX ASCII Pubkeys only, please. */
+ ec = 0; /* XXX fish out pkt type. */
+#endif
+ goto exit;
+ }
+
+#define TOKEQ(_s, _tok) (rstreqn((_s), (_tok), sizeof(_tok)-1))
+
+ for (t = (char *)b; t && *t; t = te) {
+ int rc;
+ if ((te = strchr(t, '\n')) == NULL)
+ te = t + strlen(t);
+ else
+ te++;
+
+ switch (pstate) {
+ case 0:
+ armortype = NULL;
+ if (!TOKEQ(t, "-----BEGIN PGP "))
+ continue;
+ t += sizeof("-----BEGIN PGP ")-1;
+
+ rc = pgpValTok(pgpArmorTbl, t, te);
+ if (rc < 0) {
+ ec = PGPARMOR_ERR_UNKNOWN_ARMOR_TYPE;
+ goto exit;
+ }
+ if (rc != PGPARMOR_PUBKEY) /* XXX ASCII Pubkeys only, please. */
+ continue;
+
+ armortype = pgpValStr(pgpArmorTbl, rc);
+ t += strlen(armortype);
+ if (!TOKEQ(t, "-----"))
+ continue;
+ t += sizeof("-----")-1;
+ if (*t != '\n' && *t != '\r')
+ continue;
+ *t = '\0';
+ pstate++;
+ break;
+ case 1:
+ enc = NULL;
+ rc = pgpValTok(pgpArmorKeyTbl, t, te);
+ if (rc >= 0)
+ continue;
+ if (*t != '\n' && *t != '\r') {
+ pstate = 0;
+ continue;
+ }
+ enc = te; /* Start of encoded packets */
+ pstate++;
+ break;
+ case 2:
+ crcenc = NULL;
+ if (*t != '=')
+ continue;
+ *t++ = '\0'; /* Terminate encoded packets */
+ crcenc = t; /* Start of encoded crc */
+ pstate++;
+ break;
+ case 3:
+ pstate = 0;
+ if (!TOKEQ(t, "-----END PGP ")) {
+ ec = PGPARMOR_ERR_NO_END_PGP;
+ goto exit;
+ }
+ *t = '\0'; /* Terminate encoded crc */
+ t += sizeof("-----END PGP ")-1;
+ if (t >= te) continue;
+
+ if (armortype == NULL) /* XXX can't happen */
+ continue;
+ if (!rstreqn(t, armortype, strlen(armortype)))
+ continue;
+
+ t += strlen(armortype);
+ if (t >= te) continue;
+
+ if (!TOKEQ(t, "-----")) {
+ ec = PGPARMOR_ERR_NO_END_PGP;
+ goto exit;
+ }
+ t += (sizeof("-----")-1);
+ if (t >= te) continue;
+ /* XXX permitting \r here is not RFC-2440 compliant <shrug> */
+ if (!(*t == '\n' || *t == '\r')) continue;
+
+ crcdec = NULL;
+ crclen = 0;
+ if (b64decode(crcenc, (void **)&crcdec, &crclen) != 0) {
+ ec = PGPARMOR_ERR_CRC_DECODE;
+ goto exit;
+ }
+ crcpkt = pgpGrab(crcdec, crclen);
+ crcdec = _free(crcdec);
+ dec = NULL;
+ declen = 0;
+ if (b64decode(enc, (void **)&dec, &declen) != 0) {
+ ec = PGPARMOR_ERR_BODY_DECODE;
+ goto exit;
+ }
+ crc = pgpCRC(dec, declen);
+ if (crcpkt != crc) {
+ ec = PGPARMOR_ERR_CRC_CHECK;
+ goto exit;
+ }
+ if (pkt) *pkt = dec;
+ if (pktlen) *pktlen = declen;
+ ec = PGPARMOR_PUBKEY; /* XXX ASCII Pubkeys only, please. */
+ goto exit;
+ break;
+ }
+ }
+ ec = PGPARMOR_NONE;
+
+exit:
+ return ec;
+}
+
+pgpArmor pgpReadPkts(const char * fn, uint8_t ** pkt, size_t * pktlen)
+{
+ uint8_t * b = NULL;
+ ssize_t blen;
+ pgpArmor ec = PGPARMOR_ERR_NO_BEGIN_PGP; /* XXX assume failure */
+ int rc = rpmioSlurp(fn, &b, &blen);
+ if (rc == 0 && b != NULL && blen > 0) {
+ ec = decodePkts(b, pkt, pktlen);
+ }
+ free(b);
+ return ec;
+}
+
+pgpArmor pgpParsePkts(const char *armor, uint8_t ** pkt, size_t * pktlen)
+{
+ pgpArmor ec = PGPARMOR_ERR_NO_BEGIN_PGP; /* XXX assume failure */
+ if (armor && strlen(armor) > 0) {
+ uint8_t *b = (uint8_t*) xstrdup(armor);
+ ec = decodePkts(b, pkt, pktlen);
+ free(b);
+ }
+ return ec;
+}
+
+char * pgpArmorWrap(int atype, const unsigned char * s, size_t ns)
+{
+ char *buf = NULL, *val = NULL;
+ char *enc = b64encode(s, ns, -1);
+ char *crc = b64crc(s, ns);
+ const char *valstr = pgpValStr(pgpArmorTbl, atype);
+
+ if (crc != NULL && enc != NULL) {
+ rasprintf(&buf, "%s=%s", enc, crc);
+ }
+ free(crc);
+ free(enc);
+
+ rasprintf(&val, "-----BEGIN PGP %s-----\nVersion: rpm-" VERSION " (NSS-3)\n\n"
+ "%s\n-----END PGP %s-----\n",
+ valstr, buf != NULL ? buf : "", valstr);
+
+ free(buf);
+ return val;
+}
+
+/*
+ * Only flag for re-initialization here, in the common case the child
+ * exec()'s something else shutting down NSS here would be waste of time.
+ */
+static void at_forkchild(void)
+{
+ _new_process = 1;
+}
+
+int rpmInitCrypto(void) {
+ int rc = 0;
+
+ /* Lazy NSS shutdown for re-initialization after fork() */
+ if (_new_process && _crypto_initialized) {
+ rpmFreeCrypto();
+ }
+
+ /* Initialize NSS if not already done */
+ if (!_crypto_initialized) {
+ if (NSS_NoDB_Init(NULL) != SECSuccess) {
+ rc = -1;
+ } else {
+ _crypto_initialized = 1;
+ }
+ }
+
+ /* Register one post-fork handler per process */
+ if (_new_process) {
+ if (pthread_atfork(NULL, NULL, at_forkchild) != 0) {
+ rpmlog(RPMLOG_WARNING, _("Failed to register fork handler: %m\n"));
+ }
+ _new_process = 0;
+ }
+ return rc;
+}
+
+int rpmFreeCrypto(void)
+{
+ int rc = 0;
+ if (_crypto_initialized) {
+ rc = (NSS_Shutdown() != SECSuccess);
+ _crypto_initialized = 0;
+ }
+ return rc;
+}
+
+
diff --git a/rpmio/rpmpgp.h b/rpmio/rpmpgp.h
new file mode 100644
index 0000000..6d484f3
--- /dev/null
+++ b/rpmio/rpmpgp.h
@@ -0,0 +1,1201 @@
+#ifndef H_RPMPGP
+#define H_RPMPGP
+
+/** \ingroup rpmpgp
+ * \file rpmio/rpmpgp.h
+ *
+ * OpenPGP constants and structures from RFC-2440.
+ *
+ * Text from RFC-2440 in comments is
+ * Copyright (C) The Internet Society (1998). All Rights Reserved.
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <rpm/rpmtypes.h>
+#include <rpm/rpmstring.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** \ingroup rpmpgp
+ */
+typedef struct DIGEST_CTX_s * DIGEST_CTX;
+typedef struct rpmDigestBundle_s * rpmDigestBundle;
+
+/** \ingroup rpmpgp
+ */
+typedef struct pgpDig_s * pgpDig;
+
+/** \ingroup rpmpgp
+ */
+typedef struct pgpDigParams_s * pgpDigParams;
+
+typedef uint8_t pgpKeyID_t[8];
+typedef uint8_t pgpTime_t[4];
+
+/** \ingroup rpmpgp
+ * 4.3. Packet Tags
+ *
+ * The packet tag denotes what type of packet the body holds. Note that
+ * old format headers can only have tags less than 16, whereas new
+ * format headers can have tags as great as 63.
+ */
+typedef enum pgpTag_e {
+ PGPTAG_RESERVED = 0, /*!< Reserved/Invalid */
+ PGPTAG_PUBLIC_SESSION_KEY = 1, /*!< Public-Key Encrypted Session Key */
+ PGPTAG_SIGNATURE = 2, /*!< Signature */
+ PGPTAG_SYMMETRIC_SESSION_KEY= 3, /*!< Symmetric-Key Encrypted Session Key*/
+ PGPTAG_ONEPASS_SIGNATURE = 4, /*!< One-Pass Signature */
+ PGPTAG_SECRET_KEY = 5, /*!< Secret Key */
+ PGPTAG_PUBLIC_KEY = 6, /*!< Public Key */
+ PGPTAG_SECRET_SUBKEY = 7, /*!< Secret Subkey */
+ PGPTAG_COMPRESSED_DATA = 8, /*!< Compressed Data */
+ PGPTAG_SYMMETRIC_DATA = 9, /*!< Symmetrically Encrypted Data */
+ PGPTAG_MARKER = 10, /*!< Marker */
+ PGPTAG_LITERAL_DATA = 11, /*!< Literal Data */
+ PGPTAG_TRUST = 12, /*!< Trust */
+ PGPTAG_USER_ID = 13, /*!< User ID */
+ PGPTAG_PUBLIC_SUBKEY = 14, /*!< Public Subkey */
+ PGPTAG_COMMENT_OLD = 16, /*!< Comment (from OpenPGP draft) */
+ PGPTAG_PHOTOID = 17, /*!< PGP's photo ID */
+ PGPTAG_ENCRYPTED_MDC = 18, /*!< Integrity protected encrypted data */
+ PGPTAG_MDC = 19, /*!< Manipulaion detection code packet */
+ PGPTAG_PRIVATE_60 = 60, /*!< Private or Experimental Values */
+ PGPTAG_COMMENT = 61, /*!< Comment */
+ PGPTAG_PRIVATE_62 = 62, /*!< Private or Experimental Values */
+ PGPTAG_CONTROL = 63 /*!< Control (GPG) */
+} pgpTag;
+
+/** \ingroup rpmpgp
+ * 5.1. Public-Key Encrypted Session Key Packets (Tag 1)
+ *
+ * A Public-Key Encrypted Session Key packet holds the session key used
+ * to encrypt a message. Zero or more Encrypted Session Key packets
+ * (either Public-Key or Symmetric-Key) may precede a Symmetrically
+ * Encrypted Data Packet, which holds an encrypted message. The message
+ * is encrypted with the session key, and the session key is itself
+ * encrypted and stored in the Encrypted Session Key packet(s). The
+ * Symmetrically Encrypted Data Packet is preceded by one Public-Key
+ * Encrypted Session Key packet for each OpenPGP key to which the
+ * message is encrypted. The recipient of the message finds a session
+ * key that is encrypted to their public key, decrypts the session key,
+ * and then uses the session key to decrypt the message.
+ *
+ * The body of this packet consists of:
+ * - A one-octet number giving the version number of the packet type.
+ * The currently defined value for packet version is 3. An
+ * implementation should accept, but not generate a version of 2,
+ * which is equivalent to V3 in all other respects.
+ * - An eight-octet number that gives the key ID of the public key
+ * that the session key is encrypted to.
+ * - A one-octet number giving the public key algorithm used.
+ * - A string of octets that is the encrypted session key. This string
+ * takes up the remainder of the packet, and its contents are
+ * dependent on the public key algorithm used.
+ *
+ * Algorithm Specific Fields for RSA encryption
+ * - multiprecision integer (MPI) of RSA encrypted value m**e mod n.
+ *
+ * Algorithm Specific Fields for Elgamal encryption:
+ * - MPI of Elgamal (Diffie-Hellman) value g**k mod p.
+ * - MPI of Elgamal (Diffie-Hellman) value m * y**k mod p.
+ */
+typedef struct pgpPktPubkey_s {
+ uint8_t version; /*!< version number (generate 3, accept 2). */
+ pgpKeyID_t keyid; /*!< key ID of the public key for session key. */
+ uint8_t algo; /*!< public key algorithm used. */
+} pgpPktPubkey;
+
+
+/** \ingroup rpmpgp
+ * 5.2.1. Signature Types
+ *
+ * There are a number of possible meanings for a signature, which are
+ * specified in a signature type octet in any given signature.
+ */
+typedef enum pgpSigType_e {
+ PGPSIGTYPE_BINARY = 0x00, /*!< Binary document */
+ PGPSIGTYPE_TEXT = 0x01, /*!< Canonical text document */
+ PGPSIGTYPE_STANDALONE = 0x02, /*!< Standalone */
+ PGPSIGTYPE_GENERIC_CERT = 0x10,
+ /*!< Generic certification of a User ID & Public Key */
+ PGPSIGTYPE_PERSONA_CERT = 0x11,
+ /*!< Persona certification of a User ID & Public Key */
+ PGPSIGTYPE_CASUAL_CERT = 0x12,
+ /*!< Casual certification of a User ID & Public Key */
+ PGPSIGTYPE_POSITIVE_CERT = 0x13,
+ /*!< Positive certification of a User ID & Public Key */
+ PGPSIGTYPE_SUBKEY_BINDING = 0x18, /*!< Subkey Binding */
+ PGPSIGTYPE_SIGNED_KEY = 0x1F, /*!< Signature directly on a key */
+ PGPSIGTYPE_KEY_REVOKE = 0x20, /*!< Key revocation */
+ PGPSIGTYPE_SUBKEY_REVOKE = 0x28, /*!< Subkey revocation */
+ PGPSIGTYPE_CERT_REVOKE = 0x30, /*!< Certification revocation */
+ PGPSIGTYPE_TIMESTAMP = 0x40 /*!< Timestamp */
+} pgpSigType;
+
+/** \ingroup rpmpgp
+ * 9.1. Public Key Algorithms
+ *
+\verbatim
+ ID Algorithm
+ -- ---------
+ 1 - RSA (Encrypt or Sign)
+ 2 - RSA Encrypt-Only
+ 3 - RSA Sign-Only
+ 16 - Elgamal (Encrypt-Only), see [ELGAMAL]
+ 17 - DSA (Digital Signature Standard)
+ 18 - Reserved for Elliptic Curve
+ 19 - Reserved for ECDSA
+ 20 - Elgamal (Encrypt or Sign)
+ 21 - Reserved for Diffie-Hellman (X9.42,
+ as defined for IETF-S/MIME)
+ 100 to 110 - Private/Experimental algorithm.
+\endverbatim
+ *
+ * Implementations MUST implement DSA for signatures, and Elgamal for
+ * encryption. Implementations SHOULD implement RSA keys.
+ * Implementations MAY implement any other algorithm.
+ */
+typedef enum pgpPubkeyAlgo_e {
+ PGPPUBKEYALGO_RSA = 1, /*!< RSA */
+ PGPPUBKEYALGO_RSA_ENCRYPT = 2, /*!< RSA(Encrypt-Only) */
+ PGPPUBKEYALGO_RSA_SIGN = 3, /*!< RSA(Sign-Only) */
+ PGPPUBKEYALGO_ELGAMAL_ENCRYPT = 16, /*!< Elgamal(Encrypt-Only) */
+ PGPPUBKEYALGO_DSA = 17, /*!< DSA */
+ PGPPUBKEYALGO_EC = 18, /*!< Elliptic Curve */
+ PGPPUBKEYALGO_ECDSA = 19, /*!< ECDSA */
+ PGPPUBKEYALGO_ELGAMAL = 20, /*!< Elgamal */
+ PGPPUBKEYALGO_DH = 21 /*!< Diffie-Hellman (X9.42) */
+} pgpPubkeyAlgo;
+
+/** \ingroup rpmpgp
+ * 9.2. Symmetric Key Algorithms
+ *
+\verbatim
+ ID Algorithm
+ -- ---------
+ 0 - Plaintext or unencrypted data
+ 1 - IDEA [IDEA]
+ 2 - Triple-DES (DES-EDE, as per spec -
+ 168 bit key derived from 192)
+ 3 - CAST5 (128 bit key, as per RFC 2144)
+ 4 - Blowfish (128 bit key, 16 rounds) [BLOWFISH]
+ 5 - SAFER-SK128 (13 rounds) [SAFER]
+ 6 - Reserved for DES/SK
+ 7 - Reserved for AES with 128-bit key
+ 8 - Reserved for AES with 192-bit key
+ 9 - Reserved for AES with 256-bit key
+ 100 to 110 - Private/Experimental algorithm.
+\endverbatim
+ *
+ * Implementations MUST implement Triple-DES. Implementations SHOULD
+ * implement IDEA and CAST5. Implementations MAY implement any other
+ * algorithm.
+ */
+typedef enum pgpSymkeyAlgo_e {
+ PGPSYMKEYALGO_PLAINTEXT = 0, /*!< Plaintext */
+ PGPSYMKEYALGO_IDEA = 1, /*!< IDEA */
+ PGPSYMKEYALGO_TRIPLE_DES = 2, /*!< 3DES */
+ PGPSYMKEYALGO_CAST5 = 3, /*!< CAST5 */
+ PGPSYMKEYALGO_BLOWFISH = 4, /*!< BLOWFISH */
+ PGPSYMKEYALGO_SAFER = 5, /*!< SAFER */
+ PGPSYMKEYALGO_DES_SK = 6, /*!< DES/SK */
+ PGPSYMKEYALGO_AES_128 = 7, /*!< AES(128-bit key) */
+ PGPSYMKEYALGO_AES_192 = 8, /*!< AES(192-bit key) */
+ PGPSYMKEYALGO_AES_256 = 9, /*!< AES(256-bit key) */
+ PGPSYMKEYALGO_TWOFISH = 10, /*!< TWOFISH(256-bit key) */
+ PGPSYMKEYALGO_NOENCRYPT = 110 /*!< no encryption */
+} pgpSymkeyAlgo;
+
+/** \ingroup rpmpgp
+ * 9.3. Compression Algorithms
+ *
+\verbatim
+ ID Algorithm
+ -- ---------
+ 0 - Uncompressed
+ 1 - ZIP (RFC 1951)
+ 2 - ZLIB (RFC 1950)
+ 100 to 110 - Private/Experimental algorithm.
+\endverbatim
+ *
+ * Implementations MUST implement uncompressed data. Implementations
+ * SHOULD implement ZIP. Implementations MAY implement ZLIB.
+ */
+typedef enum pgpCompressAlgo_e {
+ PGPCOMPRESSALGO_NONE = 0, /*!< Uncompressed */
+ PGPCOMPRESSALGO_ZIP = 1, /*!< ZIP */
+ PGPCOMPRESSALGO_ZLIB = 2, /*!< ZLIB */
+ PGPCOMPRESSALGO_BZIP2 = 3 /*!< BZIP2 */
+} pgpCompressAlgo;
+
+/** \ingroup rpmpgp
+ * 9.4. Hash Algorithms
+ *
+\verbatim
+ ID Algorithm Text Name
+ -- --------- ---- ----
+ 1 - MD5 "MD5"
+ 2 - SHA-1 "SHA1"
+ 3 - RIPE-MD/160 "RIPEMD160"
+ 4 - Reserved for double-width SHA (experimental)
+ 5 - MD2 "MD2"
+ 6 - Reserved for TIGER/192 "TIGER192"
+ 7 - Reserved for HAVAL (5 pass, 160-bit)
+ "HAVAL-5-160"
+ 100 to 110 - Private/Experimental algorithm.
+\endverbatim
+ *
+ * Implementations MUST implement SHA-1. Implementations SHOULD
+ * implement MD5.
+ * @todo Add SHA256.
+ */
+typedef enum pgpHashAlgo_e {
+ PGPHASHALGO_MD5 = 1, /*!< MD5 */
+ PGPHASHALGO_SHA1 = 2, /*!< SHA1 */
+ PGPHASHALGO_RIPEMD160 = 3, /*!< RIPEMD160 */
+ PGPHASHALGO_MD2 = 5, /*!< MD2 */
+ PGPHASHALGO_TIGER192 = 6, /*!< TIGER192 */
+ PGPHASHALGO_HAVAL_5_160 = 7, /*!< HAVAL-5-160 */
+ PGPHASHALGO_SHA256 = 8, /*!< SHA256 */
+ PGPHASHALGO_SHA384 = 9, /*!< SHA384 */
+ PGPHASHALGO_SHA512 = 10, /*!< SHA512 */
+ PGPHASHALGO_SHA224 = 11, /*!< SHA224 */
+} pgpHashAlgo;
+
+/** \ingroup rpmpgp
+ * 5.2.2. Version 3 Signature Packet Format
+ *
+ * The body of a version 3 Signature Packet contains:
+ * - One-octet version number (3).
+ * - One-octet length of following hashed material. MUST be 5.
+ * - One-octet signature type.
+ * - Four-octet creation time.
+ * - Eight-octet key ID of signer.
+ * - One-octet public key algorithm.
+ * - One-octet hash algorithm.
+ * - Two-octet field holding left 16 bits of signed hash value.
+ * - One or more multi-precision integers comprising the signature.
+ *
+ * Algorithm Specific Fields for RSA signatures:
+ * - multiprecision integer (MPI) of RSA signature value m**d.
+ *
+ * Algorithm Specific Fields for DSA signatures:
+ * - MPI of DSA value r.
+ * - MPI of DSA value s.
+ */
+typedef struct pgpPktSigV3_s {
+ uint8_t version; /*!< version number (3). */
+ uint8_t hashlen; /*!< length of following hashed material. MUST be 5. */
+ uint8_t sigtype; /*!< signature type. */
+ pgpTime_t time; /*!< 4 byte creation time. */
+ pgpKeyID_t signid; /*!< key ID of signer. */
+ uint8_t pubkey_algo; /*!< public key algorithm. */
+ uint8_t hash_algo; /*!< hash algorithm. */
+ uint8_t signhash16[2]; /*!< left 16 bits of signed hash value. */
+} * pgpPktSigV3;
+
+/** \ingroup rpmpgp
+ * 5.2.3. Version 4 Signature Packet Format
+ *
+ * The body of a version 4 Signature Packet contains:
+ * - One-octet version number (4).
+ * - One-octet signature type.
+ * - One-octet public key algorithm.
+ * - One-octet hash algorithm.
+ * - Two-octet scalar octet count for following hashed subpacket
+ * data. Note that this is the length in octets of all of the hashed
+ * subpackets; a pointer incremented by this number will skip over
+ * the hashed subpackets.
+ * - Hashed subpacket data. (zero or more subpackets)
+ * - Two-octet scalar octet count for following unhashed subpacket
+ * data. Note that this is the length in octets of all of the
+ * unhashed subpackets; a pointer incremented by this number will
+ * skip over the unhashed subpackets.
+ * - Unhashed subpacket data. (zero or more subpackets)
+ * - Two-octet field holding left 16 bits of signed hash value.
+ * - One or more multi-precision integers comprising the signature.
+ */
+typedef struct pgpPktSigV4_s {
+ uint8_t version; /*!< version number (4). */
+ uint8_t sigtype; /*!< signature type. */
+ uint8_t pubkey_algo; /*!< public key algorithm. */
+ uint8_t hash_algo; /*!< hash algorithm. */
+ uint8_t hashlen[2]; /*!< length of following hashed material. */
+} * pgpPktSigV4;
+
+/** \ingroup rpmpgp
+ * 5.2.3.1. Signature Subpacket Specification
+ *
+ * The subpacket fields consist of zero or more signature subpackets.
+ * Each set of subpackets is preceded by a two-octet scalar count of the
+ * length of the set of subpackets.
+ *
+ * Each subpacket consists of a subpacket header and a body. The header
+ * consists of:
+ * - the subpacket length (1, 2, or 5 octets)
+ * - the subpacket type (1 octet)
+ * and is followed by the subpacket specific data.
+ *
+ * The length includes the type octet but not this length. Its format is
+ * similar to the "new" format packet header lengths, but cannot have
+ * partial body lengths. That is:
+\verbatim
+ if the 1st octet < 192, then
+ lengthOfLength = 1
+ subpacketLen = 1st_octet
+
+ if the 1st octet >= 192 and < 255, then
+ lengthOfLength = 2
+ subpacketLen = ((1st_octet - 192) << 8) + (2nd_octet) + 192
+
+ if the 1st octet = 255, then
+ lengthOfLength = 5
+ subpacket length = [four-octet scalar starting at 2nd_octet]
+\endverbatim
+ *
+ * The value of the subpacket type octet may be:
+ *
+\verbatim
+ 2 = signature creation time
+ 3 = signature expiration time
+ 4 = exportable certification
+ 5 = trust signature
+ 6 = regular expression
+ 7 = revocable
+ 9 = key expiration time
+ 10 = placeholder for backward compatibility
+ 11 = preferred symmetric algorithms
+ 12 = revocation key
+ 16 = issuer key ID
+ 20 = notation data
+ 21 = preferred hash algorithms
+ 22 = preferred compression algorithms
+ 23 = key server preferences
+ 24 = preferred key server
+ 25 = primary user id
+ 26 = policy URL
+ 27 = key flags
+ 28 = signer's user id
+ 29 = reason for revocation
+ 100 to 110 = internal or user-defined
+\endverbatim
+ *
+ * An implementation SHOULD ignore any subpacket of a type that it does
+ * not recognize.
+ *
+ * Bit 7 of the subpacket type is the "critical" bit. If set, it
+ * denotes that the subpacket is one that is critical for the evaluator
+ * of the signature to recognize. If a subpacket is encountered that is
+ * marked critical but is unknown to the evaluating software, the
+ * evaluator SHOULD consider the signature to be in error.
+ */
+typedef enum pgpSubType_e {
+ PGPSUBTYPE_NONE = 0, /*!< none */
+ PGPSUBTYPE_SIG_CREATE_TIME = 2, /*!< signature creation time */
+ PGPSUBTYPE_SIG_EXPIRE_TIME = 3, /*!< signature expiration time */
+ PGPSUBTYPE_EXPORTABLE_CERT = 4, /*!< exportable certification */
+ PGPSUBTYPE_TRUST_SIG = 5, /*!< trust signature */
+ PGPSUBTYPE_REGEX = 6, /*!< regular expression */
+ PGPSUBTYPE_REVOCABLE = 7, /*!< revocable */
+ PGPSUBTYPE_KEY_EXPIRE_TIME = 9, /*!< key expiration time */
+ PGPSUBTYPE_ARR = 10, /*!< additional recipient request */
+ PGPSUBTYPE_PREFER_SYMKEY = 11, /*!< preferred symmetric algorithms */
+ PGPSUBTYPE_REVOKE_KEY = 12, /*!< revocation key */
+ PGPSUBTYPE_ISSUER_KEYID = 16, /*!< issuer key ID */
+ PGPSUBTYPE_NOTATION = 20, /*!< notation data */
+ PGPSUBTYPE_PREFER_HASH = 21, /*!< preferred hash algorithms */
+ PGPSUBTYPE_PREFER_COMPRESS = 22, /*!< preferred compression algorithms */
+ PGPSUBTYPE_KEYSERVER_PREFERS= 23, /*!< key server preferences */
+ PGPSUBTYPE_PREFER_KEYSERVER = 24, /*!< preferred key server */
+ PGPSUBTYPE_PRIMARY_USERID = 25, /*!< primary user id */
+ PGPSUBTYPE_POLICY_URL = 26, /*!< policy URL */
+ PGPSUBTYPE_KEY_FLAGS = 27, /*!< key flags */
+ PGPSUBTYPE_SIGNER_USERID = 28, /*!< signer's user id */
+ PGPSUBTYPE_REVOKE_REASON = 29, /*!< reason for revocation */
+ PGPSUBTYPE_FEATURES = 30, /*!< feature flags (gpg) */
+ PGPSUBTYPE_EMBEDDED_SIG = 32, /*!< embedded signature (gpg) */
+
+ PGPSUBTYPE_INTERNAL_100 = 100, /*!< internal or user-defined */
+ PGPSUBTYPE_INTERNAL_101 = 101, /*!< internal or user-defined */
+ PGPSUBTYPE_INTERNAL_102 = 102, /*!< internal or user-defined */
+ PGPSUBTYPE_INTERNAL_103 = 103, /*!< internal or user-defined */
+ PGPSUBTYPE_INTERNAL_104 = 104, /*!< internal or user-defined */
+ PGPSUBTYPE_INTERNAL_105 = 105, /*!< internal or user-defined */
+ PGPSUBTYPE_INTERNAL_106 = 106, /*!< internal or user-defined */
+ PGPSUBTYPE_INTERNAL_107 = 107, /*!< internal or user-defined */
+ PGPSUBTYPE_INTERNAL_108 = 108, /*!< internal or user-defined */
+ PGPSUBTYPE_INTERNAL_109 = 109, /*!< internal or user-defined */
+ PGPSUBTYPE_INTERNAL_110 = 110, /*!< internal or user-defined */
+
+ PGPSUBTYPE_CRITICAL = 128 /*!< critical subpacket marker */
+} pgpSubType;
+
+/** \ingroup rpmpgp
+ * 5.2. Signature Packet (Tag 2)
+ *
+ * A signature packet describes a binding between some public key and
+ * some data. The most common signatures are a signature of a file or a
+ * block of text, and a signature that is a certification of a user ID.
+ *
+ * Two versions of signature packets are defined. Version 3 provides
+ * basic signature information, while version 4 provides an expandable
+ * format with subpackets that can specify more information about the
+ * signature. PGP 2.6.x only accepts version 3 signatures.
+ *
+ * Implementations MUST accept V3 signatures. Implementations SHOULD
+ * generate V4 signatures. Implementations MAY generate a V3 signature
+ * that can be verified by PGP 2.6.x.
+ *
+ * Note that if an implementation is creating an encrypted and signed
+ * message that is encrypted to a V3 key, it is reasonable to create a
+ * V3 signature.
+ */
+typedef union pgpPktSig_u {
+ struct pgpPktSigV3_s v3;
+ struct pgpPktSigV4_s v4;
+} * pgpPktSig;
+
+/**
+ * 5.3. Symmetric-Key Encrypted Session-Key Packets (Tag 3)
+ *
+ * The Symmetric-Key Encrypted Session Key packet holds the symmetric-
+ * key encryption of a session key used to encrypt a message. Zero or
+ * more Encrypted Session Key packets and/or Symmetric-Key Encrypted
+ * Session Key packets may precede a Symmetrically Encrypted Data Packet
+ * that holds an encrypted message. The message is encrypted with a
+ * session key, and the session key is itself encrypted and stored in
+ * the Encrypted Session Key packet or the Symmetric-Key Encrypted
+ * Session Key packet.
+ *
+ * If the Symmetrically Encrypted Data Packet is preceded by one or more
+ * Symmetric-Key Encrypted Session Key packets, each specifies a
+ * passphrase that may be used to decrypt the message. This allows a
+ * message to be encrypted to a number of public keys, and also to one
+ * or more pass phrases. This packet type is new, and is not generated
+ * by PGP 2.x or PGP 5.0.
+ *
+ * The body of this packet consists of:
+ * - A one-octet version number. The only currently defined version
+ * is 4.
+ * - A one-octet number describing the symmetric algorithm used.
+ * - A string-to-key (S2K) specifier, length as defined above.
+ * - Optionally, the encrypted session key itself, which is decrypted
+ * with the string-to-key object.
+ *
+ */
+typedef struct pgpPktSymkey_s {
+ uint8_t version; /*!< version number (4). */
+ uint8_t symkey_algo;
+ uint8_t s2k[1];
+} pgpPktSymkey;
+
+/** \ingroup rpmpgp
+ * 5.4. One-Pass Signature Packets (Tag 4)
+ *
+ * The One-Pass Signature packet precedes the signed data and contains
+ * enough information to allow the receiver to begin calculating any
+ * hashes needed to verify the signature. It allows the Signature
+ * Packet to be placed at the end of the message, so that the signer can
+ * compute the entire signed message in one pass.
+ *
+ * A One-Pass Signature does not interoperate with PGP 2.6.x or earlier.
+ *
+ * The body of this packet consists of:
+ * - A one-octet version number. The current version is 3.
+ * - A one-octet signature type. Signature types are described in
+ * section 5.2.1.
+ * - A one-octet number describing the hash algorithm used.
+ * - A one-octet number describing the public key algorithm used.
+ * - An eight-octet number holding the key ID of the signing key.
+ * - A one-octet number holding a flag showing whether the signature
+ * is nested. A zero value indicates that the next packet is
+ * another One-Pass Signature packet that describes another
+ * signature to be applied to the same message data.
+ *
+ * Note that if a message contains more than one one-pass signature,
+ * then the signature packets bracket the message; that is, the first
+ * signature packet after the message corresponds to the last one-pass
+ * packet and the final signature packet corresponds to the first one-
+ * pass packet.
+ */
+typedef struct pgpPktOnepass_s {
+ uint8_t version; /*!< version number (3). */
+ uint8_t sigtype; /*!< signature type. */
+ uint8_t hash_algo; /*!< hash algorithm. */
+ uint8_t pubkey_algo; /*!< public key algorithm. */
+ pgpKeyID_t signid; /*!< key ID of signer. */
+ uint8_t nested;
+} * pgpPktOnepass;
+
+/** \ingroup rpmpgp
+ * 5.5.1. Key Packet Variants
+ *
+ * 5.5.1.1. Public Key Packet (Tag 6)
+ *
+ * A Public Key packet starts a series of packets that forms an OpenPGP
+ * key (sometimes called an OpenPGP certificate).
+ *
+ * 5.5.1.2. Public Subkey Packet (Tag 14)
+ *
+ * A Public Subkey packet (tag 14) has exactly the same format as a
+ * Public Key packet, but denotes a subkey. One or more subkeys may be
+ * associated with a top-level key. By convention, the top-level key
+ * provides signature services, and the subkeys provide encryption
+ * services.
+ *
+ * Note: in PGP 2.6.x, tag 14 was intended to indicate a comment packet.
+ * This tag was selected for reuse because no previous version of PGP
+ * ever emitted comment packets but they did properly ignore them.
+ * Public Subkey packets are ignored by PGP 2.6.x and do not cause it to
+ * fail, providing a limited degree of backward compatibility.
+ *
+ * 5.5.1.3. Secret Key Packet (Tag 5)
+ *
+ * A Secret Key packet contains all the information that is found in a
+ * Public Key packet, including the public key material, but also
+ * includes the secret key material after all the public key fields.
+ *
+ * 5.5.1.4. Secret Subkey Packet (Tag 7)
+ *
+ * A Secret Subkey packet (tag 7) is the subkey analog of the Secret Key
+ * packet, and has exactly the same format.
+ *
+ * 5.5.2. Public Key Packet Formats
+ *
+ * There are two versions of key-material packets. Version 3 packets
+ * were first generated by PGP 2.6. Version 2 packets are identical in
+ * format to Version 3 packets, but are generated by PGP 2.5 or before.
+ * V2 packets are deprecated and they MUST NOT be generated. PGP 5.0
+ * introduced version 4 packets, with new fields and semantics. PGP
+ * 2.6.x will not accept key-material packets with versions greater than
+ * 3.
+ *
+ * OpenPGP implementations SHOULD create keys with version 4 format. An
+ * implementation MAY generate a V3 key to ensure interoperability with
+ * old software; note, however, that V4 keys correct some security
+ * deficiencies in V3 keys. These deficiencies are described below. An
+ * implementation MUST NOT create a V3 key with a public key algorithm
+ * other than RSA.
+ *
+ * A version 3 public key or public subkey packet contains:
+ * - A one-octet version number (3).
+ * - A four-octet number denoting the time that the key was created.
+ * - A two-octet number denoting the time in days that this key is
+ * valid. If this number is zero, then it does not expire.
+ * - A one-octet number denoting the public key algorithm of this key
+ * - A series of multi-precision integers comprising the key
+ * material:
+ * - a multiprecision integer (MPI) of RSA public modulus n;
+ * - an MPI of RSA public encryption exponent e.
+ *
+ * V3 keys SHOULD only be used for backward compatibility because of
+ * three weaknesses in them. First, it is relatively easy to construct a
+ * V3 key that has the same key ID as any other key because the key ID
+ * is simply the low 64 bits of the public modulus. Secondly, because
+ * the fingerprint of a V3 key hashes the key material, but not its
+ * length, which increases the opportunity for fingerprint collisions.
+ * Third, there are minor weaknesses in the MD5 hash algorithm that make
+ * developers prefer other algorithms. See below for a fuller discussion
+ * of key IDs and fingerprints.
+ *
+ */
+typedef struct pgpPktKeyV3_s {
+ uint8_t version; /*!< version number (3). */
+ pgpTime_t time; /*!< time that the key was created. */
+ uint8_t valid[2]; /*!< time in days that this key is valid. */
+ uint8_t pubkey_algo; /*!< public key algorithm. */
+} * pgpPktKeyV3;
+
+/** \ingroup rpmpgp
+ * The version 4 format is similar to the version 3 format except for
+ * the absence of a validity period. This has been moved to the
+ * signature packet. In addition, fingerprints of version 4 keys are
+ * calculated differently from version 3 keys, as described in section
+ * "Enhanced Key Formats."
+ *
+ * A version 4 packet contains:
+ * - A one-octet version number (4).
+ * - A four-octet number denoting the time that the key was created.
+ * - A one-octet number denoting the public key algorithm of this key
+ * - A series of multi-precision integers comprising the key
+ * material. This algorithm-specific portion is:
+ *
+ * Algorithm Specific Fields for RSA public keys:
+ * - multiprecision integer (MPI) of RSA public modulus n;
+ * - MPI of RSA public encryption exponent e.
+ *
+ * Algorithm Specific Fields for DSA public keys:
+ * - MPI of DSA prime p;
+ * - MPI of DSA group order q (q is a prime divisor of p-1);
+ * - MPI of DSA group generator g;
+ * - MPI of DSA public key value y (= g**x where x is secret).
+ *
+ * Algorithm Specific Fields for Elgamal public keys:
+ * - MPI of Elgamal prime p;
+ * - MPI of Elgamal group generator g;
+ * - MPI of Elgamal public key value y (= g**x where x is
+ * secret).
+ *
+ */
+typedef struct pgpPktKeyV4_s {
+ uint8_t version; /*!< version number (4). */
+ pgpTime_t time; /*!< time that the key was created. */
+ uint8_t pubkey_algo; /*!< public key algorithm. */
+} * pgpPktKeyV4;
+
+/** \ingroup rpmpgp
+ * 5.5.3. Secret Key Packet Formats
+ *
+ * The Secret Key and Secret Subkey packets contain all the data of the
+ * Public Key and Public Subkey packets, with additional algorithm-
+ * specific secret key data appended, in encrypted form.
+ *
+ * The packet contains:
+ * - A Public Key or Public Subkey packet, as described above
+ * - One octet indicating string-to-key usage conventions. 0
+ * indicates that the secret key data is not encrypted. 255
+ * indicates that a string-to-key specifier is being given. Any
+ * other value is a symmetric-key encryption algorithm specifier.
+ * - [Optional] If string-to-key usage octet was 255, a one-octet
+ * symmetric encryption algorithm.
+ * - [Optional] If string-to-key usage octet was 255, a string-to-key
+ * specifier. The length of the string-to-key specifier is implied
+ * by its type, as described above.
+ * - [Optional] If secret data is encrypted, eight-octet Initial
+ * Vector (IV).
+ * - Encrypted multi-precision integers comprising the secret key
+ * data. These algorithm-specific fields are as described below.
+ * - Two-octet checksum of the plaintext of the algorithm-specific
+ * portion (sum of all octets, mod 65536).
+ *
+ * Algorithm Specific Fields for RSA secret keys:
+ * - multiprecision integer (MPI) of RSA secret exponent d.
+ * - MPI of RSA secret prime value p.
+ * - MPI of RSA secret prime value q (p < q).
+ * - MPI of u, the multiplicative inverse of p, mod q.
+ *
+ * Algorithm Specific Fields for DSA secret keys:
+ * - MPI of DSA secret exponent x.
+ *
+ * Algorithm Specific Fields for Elgamal secret keys:
+ * - MPI of Elgamal secret exponent x.
+ *
+ * Secret MPI values can be encrypted using a passphrase. If a string-
+ * to-key specifier is given, that describes the algorithm for
+ * converting the passphrase to a key, else a simple MD5 hash of the
+ * passphrase is used. Implementations SHOULD use a string-to-key
+ * specifier; the simple hash is for backward compatibility. The cipher
+ * for encrypting the MPIs is specified in the secret key packet.
+ *
+ * Encryption/decryption of the secret data is done in CFB mode using
+ * the key created from the passphrase and the Initial Vector from the
+ * packet. A different mode is used with V3 keys (which are only RSA)
+ * than with other key formats. With V3 keys, the MPI bit count prefix
+ * (i.e., the first two octets) is not encrypted. Only the MPI non-
+ * prefix data is encrypted. Furthermore, the CFB state is
+ * resynchronized at the beginning of each new MPI value, so that the
+ * CFB block boundary is aligned with the start of the MPI data.
+ *
+ * With V4 keys, a simpler method is used. All secret MPI values are
+ * encrypted in CFB mode, including the MPI bitcount prefix.
+ *
+ * The 16-bit checksum that follows the algorithm-specific portion is
+ * the algebraic sum, mod 65536, of the plaintext of all the algorithm-
+ * specific octets (including MPI prefix and data). With V3 keys, the
+ * checksum is stored in the clear. With V4 keys, the checksum is
+ * encrypted like the algorithm-specific data. This value is used to
+ * check that the passphrase was correct.
+ *
+ */
+typedef union pgpPktKey_u {
+ struct pgpPktKeyV3_s v3;
+ struct pgpPktKeyV4_s v4;
+} pgpPktKey;
+
+/* \ingroup rpmpgp
+ * 5.6. Compressed Data Packet (Tag 8)
+ *
+ * The Compressed Data packet contains compressed data. Typically, this
+ * packet is found as the contents of an encrypted packet, or following
+ * a Signature or One-Pass Signature packet, and contains literal data
+ * packets.
+ *
+ * The body of this packet consists of:
+ * - One octet that gives the algorithm used to compress the packet.
+ * - The remainder of the packet is compressed data.
+ *
+ * A Compressed Data Packet's body contains an block that compresses
+ * some set of packets. See section "Packet Composition" for details on
+ * how messages are formed.
+ *
+ * ZIP-compressed packets are compressed with raw RFC 1951 DEFLATE
+ * blocks. Note that PGP V2.6 uses 13 bits of compression. If an
+ * implementation uses more bits of compression, PGP V2.6 cannot
+ * decompress it.
+ *
+ * ZLIB-compressed packets are compressed with RFC 1950 ZLIB-style
+ * blocks.
+ */
+typedef struct pgpPktCdata_s {
+ uint8_t compressalgo;
+ uint8_t data[1];
+} pgpPktCdata;
+
+/* \ingroup rpmpgp
+ * 5.7. Symmetrically Encrypted Data Packet (Tag 9)
+ *
+ * The Symmetrically Encrypted Data packet contains data encrypted with
+ * a symmetric-key algorithm. When it has been decrypted, it will
+ * typically contain other packets (often literal data packets or
+ * compressed data packets).
+ *
+ * The body of this packet consists of:
+ * - Encrypted data, the output of the selected symmetric-key cipher
+ * operating in PGP's variant of Cipher Feedback (CFB) mode.
+ *
+ * The symmetric cipher used may be specified in an Public-Key or
+ * Symmetric-Key Encrypted Session Key packet that precedes the
+ * Symmetrically Encrypted Data Packet. In that case, the cipher
+ * algorithm octet is prefixed to the session key before it is
+ * encrypted. If no packets of these types precede the encrypted data,
+ * the IDEA algorithm is used with the session key calculated as the MD5
+ * hash of the passphrase.
+ *
+ * The data is encrypted in CFB mode, with a CFB shift size equal to the
+ * cipher's block size. The Initial Vector (IV) is specified as all
+ * zeros. Instead of using an IV, OpenPGP prefixes a 10-octet string to
+ * the data before it is encrypted. The first eight octets are random,
+ * and the 9th and 10th octets are copies of the 7th and 8th octets,
+ * respectively. After encrypting the first 10 octets, the CFB state is
+ * resynchronized if the cipher block size is 8 octets or less. The
+ * last 8 octets of ciphertext are passed through the cipher and the
+ * block boundary is reset.
+ *
+ * The repetition of 16 bits in the 80 bits of random data prefixed to
+ * the message allows the receiver to immediately check whether the
+ * session key is incorrect.
+ */
+typedef struct pgpPktEdata_s {
+ uint8_t data[1];
+} pgpPktEdata;
+
+/* \ingroup rpmpgp
+ * 5.8. Marker Packet (Obsolete Literal Packet) (Tag 10)
+ *
+ * An experimental version of PGP used this packet as the Literal
+ * packet, but no released version of PGP generated Literal packets with
+ * this tag. With PGP 5.x, this packet has been re-assigned and is
+ * reserved for use as the Marker packet.
+ *
+ * The body of this packet consists of:
+ * - The three octets 0x50, 0x47, 0x50 (which spell "PGP" in UTF-8).
+ *
+ * Such a packet MUST be ignored when received. It may be placed at the
+ * beginning of a message that uses features not available in PGP 2.6.x
+ * in order to cause that version to report that newer software is
+ * necessary to process the message.
+ */
+/* \ingroup rpmpgp
+ * 5.9. Literal Data Packet (Tag 11)
+ *
+ * A Literal Data packet contains the body of a message; data that is
+ * not to be further interpreted.
+ *
+ * The body of this packet consists of:
+ * - A one-octet field that describes how the data is formatted.
+ *
+ * If it is a 'b' (0x62), then the literal packet contains binary data.
+ * If it is a 't' (0x74), then it contains text data, and thus may need
+ * line ends converted to local form, or other text-mode changes. RFC
+ * 1991 also defined a value of 'l' as a 'local' mode for machine-local
+ * conversions. This use is now deprecated.
+ * - File name as a string (one-octet length, followed by file name),
+ * if the encrypted data should be saved as a file.
+ *
+ * If the special name "_CONSOLE" is used, the message is considered to
+ * be "for your eyes only". This advises that the message data is
+ * unusually sensitive, and the receiving program should process it more
+ * carefully, perhaps avoiding storing the received data to disk, for
+ * example.
+ * - A four-octet number that indicates the modification date of the
+ * file, or the creation time of the packet, or a zero that
+ * indicates the present time.
+ * - The remainder of the packet is literal data.
+ *
+ * Text data is stored with <CR><LF> text endings (i.e. network-normal
+ * line endings). These should be converted to native line endings by
+ * the receiving software.
+ */
+typedef struct pgpPktLdata_s {
+ uint8_t format;
+ uint8_t filenamelen;
+ uint8_t filename[1];
+} pgpPktLdata;
+
+/* \ingroup rpmpgp
+ * 5.10. Trust Packet (Tag 12)
+ *
+ * The Trust packet is used only within keyrings and is not normally
+ * exported. Trust packets contain data that record the user's
+ * specifications of which key holders are trustworthy introducers,
+ * along with other information that implementing software uses for
+ * trust information.
+ *
+ * Trust packets SHOULD NOT be emitted to output streams that are
+ * transferred to other users, and they SHOULD be ignored on any input
+ * other than local keyring files.
+ */
+typedef struct pgpPktTrust_s {
+ uint8_t flag;
+} pgpPktTrust;
+
+/* \ingroup rpmpgp
+ * 5.11. User ID Packet (Tag 13)
+ *
+ * A User ID packet consists of data that is intended to represent the
+ * name and email address of the key holder. By convention, it includes
+ * an RFC 822 mail name, but there are no restrictions on its content.
+ * The packet length in the header specifies the length of the user id.
+ * If it is text, it is encoded in UTF-8.
+ *
+ */
+typedef struct pgpPktUid_s {
+ uint8_t userid[1];
+} pgpPktUid;
+
+/** \ingroup rpmpgp
+ */
+union pgpPktPre_u {
+ pgpPktPubkey pubkey; /*!< 5.1. Public-Key Encrypted Session Key */
+ pgpPktSig sig; /*!< 5.2. Signature */
+ pgpPktSymkey symkey; /*!< 5.3. Symmetric-Key Encrypted Session-Key */
+ pgpPktOnepass onepass; /*!< 5.4. One-Pass Signature */
+ pgpPktKey key; /*!< 5.5. Key Material */
+ pgpPktCdata cdata; /*!< 5.6. Compressed Data */
+ pgpPktEdata edata; /*!< 5.7. Symmetrically Encrypted Data */
+ /*!< 5.8. Marker (obsolete) */
+ pgpPktLdata ldata; /*!< 5.9. Literal Data */
+ pgpPktTrust tdata; /*!< 5.10. Trust */
+ pgpPktUid uid; /*!< 5.11. User ID */
+};
+
+/** \ingroup rpmpgp
+ */
+typedef enum pgpArmor_e {
+ PGPARMOR_ERR_CRC_CHECK = -7,
+ PGPARMOR_ERR_BODY_DECODE = -6,
+ PGPARMOR_ERR_CRC_DECODE = -5,
+ PGPARMOR_ERR_NO_END_PGP = -4,
+ PGPARMOR_ERR_UNKNOWN_PREAMBLE_TAG = -3,
+ PGPARMOR_ERR_UNKNOWN_ARMOR_TYPE = -2,
+ PGPARMOR_ERR_NO_BEGIN_PGP = -1,
+#define PGPARMOR_ERROR PGPARMOR_ERR_NO_BEGIN_PGP
+ PGPARMOR_NONE = 0,
+ PGPARMOR_MESSAGE = 1, /*!< MESSAGE */
+ PGPARMOR_PUBKEY = 2, /*!< PUBLIC KEY BLOCK */
+ PGPARMOR_SIGNATURE = 3, /*!< SIGNATURE */
+ PGPARMOR_SIGNED_MESSAGE = 4, /*!< SIGNED MESSAGE */
+ PGPARMOR_FILE = 5, /*!< ARMORED FILE */
+ PGPARMOR_PRIVKEY = 6, /*!< PRIVATE KEY BLOCK */
+ PGPARMOR_SECKEY = 7 /*!< SECRET KEY BLOCK */
+} pgpArmor;
+
+/** \ingroup rpmpgp
+ */
+typedef enum pgpArmorKey_e {
+ PGPARMORKEY_VERSION = 1, /*!< Version: */
+ PGPARMORKEY_COMMENT = 2, /*!< Comment: */
+ PGPARMORKEY_MESSAGEID = 3, /*!< MessageID: */
+ PGPARMORKEY_HASH = 4, /*!< Hash: */
+ PGPARMORKEY_CHARSET = 5 /*!< Charset: */
+} pgpArmorKey;
+
+typedef enum pgpValType_e {
+ PGPVAL_TAG = 1,
+ PGPVAL_ARMORBLOCK = 2,
+ PGPVAL_ARMORKEY = 3,
+ PGPVAL_SIGTYPE = 4,
+ PGPVAL_SUBTYPE = 5,
+ PGPVAL_PUBKEYALGO = 6,
+ PGPVAL_SYMKEYALGO = 7,
+ PGPVAL_COMPRESSALGO = 8,
+ PGPVAL_HASHALGO = 9,
+ PGPVAL_SERVERPREFS = 10,
+} pgpValType;
+
+/** \ingroup rpmpgp
+ * Bit(s) to control digest operation.
+ */
+enum rpmDigestFlags_e {
+ RPMDIGEST_NONE = 0
+};
+
+typedef rpmFlags rpmDigestFlags;
+
+/** \ingroup rpmpgp
+ * Return string representation of am OpenPGP value.
+ * @param type type of value
+ * @param val byte value to lookup
+ * @return string value of byte
+ */
+const char * pgpValString(pgpValType type, uint8_t val);
+
+/** \ingroup rpmpgp
+ * Return (native-endian) integer from big-endian representation.
+ * @param s pointer to big-endian integer
+ * @param nbytes no. of bytes
+ * @return native-endian integer
+ */
+static inline
+unsigned int pgpGrab(const uint8_t *s, size_t nbytes)
+{
+ size_t i = 0;
+ size_t nb = (nbytes <= sizeof(i) ? nbytes : sizeof(i));
+ while (nb--)
+ i = (i << 8) | *s++;
+ return i;
+}
+
+/** \ingroup rpmpgp
+ * Return length of an OpenPGP packet.
+ * @param s pointer to packet
+ * @retval *lenp no. of bytes in packet
+ * @return no. of bytes in length prefix
+ */
+static inline
+size_t pgpLen(const uint8_t *s, size_t * lenp)
+{
+ if (*s < 192) {
+ (*lenp) = *s++;
+ return 1;
+ } else if (*s < 255) {
+ (*lenp) = ((((unsigned)s[0]) - 192) << 8) + s[1] + 192;
+ return 2;
+ } else {
+ (*lenp) = pgpGrab(s+1, (size_t) 4);
+ return 5;
+ }
+}
+
+/** \ingroup rpmpgp
+ * Return hex formatted representation of bytes.
+ * @param p bytes
+ * @param plen no. of bytes
+ * @return hex formatted string (malloc'ed)
+ */
+char * pgpHexStr(const uint8_t *p, size_t plen);
+
+/** \ingroup rpmpgp
+ * Calculate OpenPGP public key fingerprint.
+ * @todo V3 non-RSA public keys not implemented.
+ * @param pkt OpenPGP packet (i.e. PGPTAG_PUBLIC_KEY)
+ * @param pktlen OpenPGP packet length (no. of bytes)
+ * @retval keyid public key fingerprint
+ * @return 0 on sucess, else -1
+ */
+int pgpPubkeyFingerprint(const uint8_t * pkt, size_t pktlen,
+ pgpKeyID_t keyid);
+
+/** \ingroup rpmpgp
+* Extract OpenPGP public key fingerprint from base64 encoded packet.
+* @todo V3 non-RSA public keys not implemented.
+* @param b64pkt base64 encoded openpgp packet
+* @retval keyid public key fingerprint
+* @return 8 (no. of bytes) on success, < 0 on error
+*/
+int pgpExtractPubkeyFingerprint(const char * b64pkt, pgpKeyID_t keyid);
+
+/** \ingroup rpmpgp
+ * Print/parse a OpenPGP packet(s).
+ * @param pkts OpenPGP packet(s)
+ * @param pktlen OpenPGP packet(s) length (no. of bytes)
+ * @retval dig parsed output of signature/pubkey packet parameters
+ * @param printing should packets be printed?
+ * @return -1 on error, 0 on success
+ */
+int pgpPrtPkts(const uint8_t *pkts, size_t pktlen, pgpDig dig, int printing);
+
+/** \ingroup rpmpgp
+ * Parse armored OpenPGP packets from a file.
+ * @param fn file name
+ * @retval pkt dearmored OpenPGP packet(s) (malloced)
+ * @retval pktlen dearmored OpenPGP packet(s) length in bytes
+ * @return type of armor found
+ */
+pgpArmor pgpReadPkts(const char * fn, uint8_t ** pkt, size_t * pktlen);
+
+/** \ingroup rpmpgp
+ * Parse armored OpenPGP packets from memory.
+ * @param armor armored OpenPGP packet string
+ * @retval pkt dearmored OpenPGP packet(s) (malloced)
+ * @retval pktlen dearmored OpenPGP packet(s) length in bytes
+ * @return type of armor found
+ */
+pgpArmor pgpParsePkts(const char *armor, uint8_t ** pkt, size_t * pktlen);
+
+/** \ingroup rpmpgp
+ * Wrap a OpenPGP packets in ascii armor for transport.
+ * @param atype type of armor
+ * @param s binary pkt data
+ * @param ns binary pkt data length
+ * @return formatted string
+ */
+char * pgpArmorWrap(int atype, const unsigned char * s, size_t ns);
+
+/** \ingroup rpmpgp
+ * Create a container for parsed OpenPGP packet(s).
+ * @return container
+ */
+pgpDig pgpNewDig(void);
+
+/** \ingroup rpmpgp
+ * Release (malloc'd) data from container.
+ * @param dig container
+ */
+void pgpCleanDig(pgpDig dig);
+
+/** \ingroup rpmpgp
+ * Destroy a container for parsed OpenPGP packet(s).
+ * @param dig container
+ * @return NULL always
+ */
+pgpDig pgpFreeDig(pgpDig dig);
+
+/** \ingroup rpmpgp
+ * Verify a PGP signature.
+ * @param dig container
+ * @param hashctx digest context
+ * @return RPMRC_OK on success
+ */
+rpmRC pgpVerifySig(pgpDig dig, DIGEST_CTX hashctx);
+
+/** \ingroup rpmpgp
+ * Return a string identification of a PGP signature/pubkey.
+ * @param digp signature/pubkey container
+ * @return string describing the item and parameters
+ */
+char *pgpIdentItem(pgpDigParams digp);
+
+/** \ingroup rpmpgp
+ * Perform cryptography initialization.
+ * It must be called before any cryptography can be used within rpm.
+ * It's not normally necessary to call it directly as it's called in
+ * general rpm initialization routines.
+ * @return 0 on success, -1 on failure
+ */
+int rpmInitCrypto(void);
+
+/** \ingroup rpmpgp
+ * Shutdown cryptography
+ */
+int rpmFreeCrypto(void);
+
+/** \ingroup rpmpgp
+ * Duplicate a digest context.
+ * @param octx existing digest context
+ * @return duplicated digest context
+ */
+DIGEST_CTX rpmDigestDup(DIGEST_CTX octx);
+
+/** \ingroup rpmpgp
+ * Obtain digest length in bytes.
+ * @param hashalgo type of digest
+ * @return digest length, zero on invalid algorithm
+ */
+size_t rpmDigestLength(int hashalgo);
+
+/** \ingroup rpmpgp
+ * Initialize digest.
+ * Set bit count to 0 and buffer to mysterious initialization constants.
+ * @param hashalgo type of digest
+ * @param flags bit(s) to control digest operation
+ * @return digest context
+ */
+DIGEST_CTX rpmDigestInit(int hashalgo, rpmDigestFlags flags);
+
+/** \ingroup rpmpgp
+ * Update context with next plain text buffer.
+ * @param ctx digest context
+ * @param data next data buffer
+ * @param len no. bytes of data
+ * @return 0 on success
+ */
+int rpmDigestUpdate(DIGEST_CTX ctx, const void * data, size_t len);
+
+/** \ingroup rpmpgp
+ * Return digest and destroy context.
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ *
+ * @param ctx digest context
+ * @retval datap address of returned digest
+ * @retval lenp address of digest length
+ * @param asAscii return digest as ascii string?
+ * @return 0 on success
+ */
+int rpmDigestFinal(DIGEST_CTX ctx,
+ void ** datap,
+ size_t * lenp, int asAscii);
+
+/** \ingroup rpmpgp
+ * Create a new digest bundle.
+ * @return New digest bundle
+ */
+rpmDigestBundle rpmDigestBundleNew(void);
+
+/** \ingroup rpmpgp
+ * Free a digest bundle and all contained digest contexts.
+ * @param bundle digest bundle
+ * @return NULL always
+ */
+rpmDigestBundle rpmDigestBundleFree(rpmDigestBundle bundle);
+
+/** \ingroup rpmpgp
+ * Add a new type of digest to a bundle.
+ * @param bundle digest bundle
+ * @param algo type of digest
+ * @param flags bit(s) to control digest operation
+ * @return 0 on success
+ */
+int rpmDigestBundleAdd(rpmDigestBundle bundle, int algo,
+ rpmDigestFlags flags);
+
+/** \ingroup rpmpgp
+ * Update contexts within bundle with next plain text buffer.
+ * @param bundle digest bundle
+ * @param data next data buffer
+ * @param len no. bytes of data
+ * @return 0 on success
+ */
+int rpmDigestBundleUpdate(rpmDigestBundle bundle, const void *data, size_t len);
+
+/** \ingroup rpmpgp
+ * Return digest from a bundle and destroy context, see rpmDigestFinal().
+ *
+ * @param bundle digest bundle
+ * @param algo type of digest to return
+ * @retval datap address of returned digest
+ * @retval lenp address of digest length
+ * @param asAscii return digest as ascii string?
+ * @return 0 on success
+ */
+int rpmDigestBundleFinal(rpmDigestBundle bundle,
+ int algo, void ** datap, size_t * lenp, int asAscii);
+
+/** \ingroup rpmpgp
+ * Duplicate a digest context from a bundle.
+ * @param bundle digest bundle
+ * @param algo type of digest to dup
+ * @return duplicated digest context
+ */
+DIGEST_CTX rpmDigestBundleDupCtx(rpmDigestBundle bundle, int algo);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* H_RPMPGP */
diff --git a/rpmio/rpmsq.c b/rpmio/rpmsq.c
new file mode 100644
index 0000000..4e69ad0
--- /dev/null
+++ b/rpmio/rpmsq.c
@@ -0,0 +1,366 @@
+/** \ingroup rpmio
+ * \file rpmio/rpmsq.c
+ */
+
+#include "system.h"
+
+#include <signal.h>
+#include <sys/signal.h>
+#include <sys/wait.h>
+#include <search.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include <pthread.h>
+
+/* XXX suggested in bugzilla #159024 */
+#if PTHREAD_MUTEX_DEFAULT != PTHREAD_MUTEX_NORMAL
+ #error RPM expects PTHREAD_MUTEX_DEFAULT == PTHREAD_MUTEX_NORMAL
+#endif
+
+#ifndef PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
+static pthread_mutex_t rpmsigTbl_lock = PTHREAD_MUTEX_INITIALIZER;
+#else
+static pthread_mutex_t rpmsigTbl_lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
+#endif
+
+#define DO_LOCK() pthread_mutex_lock(&rpmsigTbl_lock);
+#define DO_UNLOCK() pthread_mutex_unlock(&rpmsigTbl_lock);
+#define ADD_REF(__tbl) (__tbl)->active++
+#define SUB_REF(__tbl) --(__tbl)->active
+
+#define ME() ((void *)pthread_self())
+
+#define _RPMSQ_INTERNAL
+#include <rpm/rpmsq.h>
+
+#include "debug.h"
+
+static struct rpmsqElem rpmsqRock;
+
+static rpmsq rpmsqQueue = &rpmsqRock;
+
+/** \ingroup rpmsq
+ * Insert node into from queue.
+ * @param elem node to link
+ * @param prev previous node from queue
+ * @return 0 on success
+ */
+static int rpmsqInsert(void * elem, void * prev)
+{
+ rpmsq sq = (rpmsq) elem;
+ int ret = -1;
+
+ if (sq != NULL) {
+ ret = sighold(SIGCHLD);
+ if (ret == 0) {
+ sq->child = 0;
+ sq->reaped = 0;
+ sq->status = 0;
+ sq->reaper = 1;
+ sq->pipes[0] = sq->pipes[1] = -1;
+
+ sq->id = ME();
+ ret = pthread_mutex_init(&sq->mutex, NULL);
+ insque(elem, (prev != NULL ? prev : rpmsqQueue));
+ ret = sigrelse(SIGCHLD);
+ }
+ }
+ return ret;
+}
+
+/** \ingroup rpmsq
+ * Remove node from queue.
+ * @param elem node to link
+ * @return 0 on success
+ */
+static int rpmsqRemove(void * elem)
+{
+ rpmsq sq = (rpmsq) elem;
+ int ret = -1;
+
+ if (elem != NULL) {
+ ret = sighold (SIGCHLD);
+ if (ret == 0) {
+ remque(elem);
+
+ /* Unlock the mutex and then destroy it */
+ if((ret = pthread_mutex_unlock(&sq->mutex)) == 0)
+ ret = pthread_mutex_destroy(&sq->mutex);
+
+ sq->id = NULL;
+ if (sq->pipes[1]) ret = close(sq->pipes[1]);
+ if (sq->pipes[0]) ret = close(sq->pipes[0]);
+ sq->pipes[0] = sq->pipes[1] = -1;
+ ret = sigrelse(SIGCHLD);
+ }
+ }
+ return ret;
+}
+
+static sigset_t rpmsqCaught;
+
+static struct rpmsig_s {
+ int signum;
+ rpmsqAction_t handler;
+ int active;
+ struct sigaction oact;
+} rpmsigTbl[] = {
+ { SIGINT, rpmsqAction },
+#define rpmsigTbl_sigint (&rpmsigTbl[0])
+ { SIGQUIT, rpmsqAction },
+#define rpmsigTbl_sigquit (&rpmsigTbl[1])
+ { SIGCHLD, rpmsqAction },
+#define rpmsigTbl_sigchld (&rpmsigTbl[2])
+ { SIGHUP, rpmsqAction },
+#define rpmsigTbl_sighup (&rpmsigTbl[3])
+ { SIGTERM, rpmsqAction },
+#define rpmsigTbl_sigterm (&rpmsigTbl[4])
+ { SIGPIPE, rpmsqAction },
+#define rpmsigTbl_sigpipe (&rpmsigTbl[5])
+ { -1, NULL },
+};
+
+int rpmsqIsCaught(int signum)
+{
+ return sigismember(&rpmsqCaught, signum);
+}
+
+#ifdef SA_SIGINFO
+void rpmsqAction(int signum, siginfo_t * info, void * context)
+#else
+void rpmsqAction(int signum)
+#endif
+{
+ int save = errno;
+ rpmsig tbl;
+
+ for (tbl = rpmsigTbl; tbl->signum >= 0; tbl++) {
+ if (tbl->signum != signum)
+ continue;
+
+ (void) sigaddset(&rpmsqCaught, signum);
+
+ switch (signum) {
+ case SIGCHLD:
+ while (1) {
+ rpmsq sq;
+ int status = 0;
+ pid_t reaped = waitpid(0, &status, WNOHANG);
+
+ /* XXX errno set to ECHILD/EINVAL/EINTR. */
+ if (reaped <= 0)
+ break;
+
+ /* XXX insque(3)/remque(3) are dequeue, not ring. */
+ for (sq = rpmsqQueue->q_forw;
+ sq != NULL && sq != rpmsqQueue;
+ sq = sq->q_forw)
+ {
+ int ret;
+
+ if (sq->child != reaped)
+ continue;
+ sq->reaped = reaped;
+ sq->status = status;
+
+ /* Unlock the mutex. The waiter will then be able to
+ * aquire the lock.
+ *
+ * XXX: jbj, wtd, if this fails?
+ */
+ ret = pthread_mutex_unlock(&sq->mutex);
+
+ break;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ errno = save;
+}
+
+int rpmsqEnable(int signum, rpmsqAction_t handler)
+{
+ int tblsignum = (signum >= 0 ? signum : -signum);
+ struct sigaction sa;
+ rpmsig tbl;
+ int ret = -1;
+
+ (void) DO_LOCK ();
+ if (rpmsqQueue->id == NULL)
+ rpmsqQueue->id = ME();
+ for (tbl = rpmsigTbl; tbl->signum >= 0; tbl++) {
+ if (tblsignum != tbl->signum)
+ continue;
+
+ if (signum >= 0) { /* Enable. */
+ if (ADD_REF(tbl) <= 0) {
+ (void) sigdelset(&rpmsqCaught, tbl->signum);
+
+ /* XXX Don't set a signal handler if already SIG_IGN */
+ (void) sigaction(tbl->signum, NULL, &tbl->oact);
+ if (tbl->oact.sa_handler == SIG_IGN)
+ continue;
+
+ (void) sigemptyset (&sa.sa_mask);
+#ifdef SA_SIGINFO
+ sa.sa_flags = SA_SIGINFO;
+#else
+ sa.sa_flags = 0;
+#endif
+ sa.sa_sigaction = (handler != NULL ? handler : tbl->handler);
+ if (sigaction(tbl->signum, &sa, &tbl->oact) < 0) {
+ SUB_REF(tbl);
+ break;
+ }
+ tbl->active = 1; /* XXX just in case */
+ if (handler != NULL)
+ tbl->handler = handler;
+ }
+ } else { /* Disable. */
+ if (SUB_REF(tbl) <= 0) {
+ if (sigaction(tbl->signum, &tbl->oact, NULL) < 0)
+ break;
+ tbl->active = 0; /* XXX just in case */
+ tbl->handler = (handler != NULL ? handler : rpmsqAction);
+ }
+ }
+ ret = tbl->active;
+ break;
+ }
+ (void) DO_UNLOCK ();
+ return ret;
+}
+
+pid_t rpmsqFork(rpmsq sq)
+{
+ pid_t pid;
+ int xx;
+ int nothreads = 0; /* XXX: Shouldn't this be a global? */
+
+ if (sq->reaper) {
+ xx = rpmsqInsert(sq, NULL);
+ xx = rpmsqEnable(SIGCHLD, NULL);
+ }
+
+ xx = pipe(sq->pipes);
+
+ xx = sighold(SIGCHLD);
+
+ /*
+ * Initialize the cond var mutex. We have to aquire the lock we
+ * use for the condition before we fork. Otherwise it is possible for
+ * the child to exit, we get sigchild and the sig handler to send
+ * the condition signal before we are waiting on the condition.
+ */
+ if (!nothreads) {
+ if(pthread_mutex_lock(&sq->mutex)) {
+ /* Yack we did not get the lock, lets just give up */
+ xx = close(sq->pipes[0]);
+ xx = close(sq->pipes[1]);
+ sq->pipes[0] = sq->pipes[1] = -1;
+ goto out;
+ }
+ }
+
+ pid = fork();
+ if (pid < (pid_t) 0) { /* fork failed. */
+ sq->child = (pid_t)-1;
+ xx = close(sq->pipes[0]);
+ xx = close(sq->pipes[1]);
+ sq->pipes[0] = sq->pipes[1] = -1;
+ goto out;
+ } else if (pid == (pid_t) 0) { /* Child. */
+ int yy;
+
+ /* Block to permit parent time to wait. */
+ xx = close(sq->pipes[1]);
+ xx = read(sq->pipes[0], &yy, sizeof(yy));
+ xx = close(sq->pipes[0]);
+ sq->pipes[0] = sq->pipes[1] = -1;
+ } else { /* Parent. */
+ sq->child = pid;
+ }
+
+out:
+ xx = sigrelse(SIGCHLD);
+ return sq->child;
+}
+
+/**
+ * Wait for child process to be reaped, and unregister SIGCHLD handler.
+ * @todo Rewrite to use waitpid on helper thread.
+ * @param sq scriptlet queue element
+ * @return 0 on success
+ */
+static int rpmsqWaitUnregister(rpmsq sq)
+{
+ int nothreads = 0;
+ int ret = 0;
+ int xx;
+
+ /* Protect sq->reaped from handler changes. */
+ ret = sighold(SIGCHLD);
+
+ /* Start the child, linux often runs child before parent. */
+ if (sq->pipes[0] >= 0)
+ xx = close(sq->pipes[0]);
+ if (sq->pipes[1] >= 0)
+ xx = close(sq->pipes[1]);
+ sq->pipes[0] = sq->pipes[1] = -1;
+
+ /* Put a stopwatch on the time spent waiting to measure performance gain. */
+ (void) rpmswEnter(&sq->op, -1);
+
+ /* Wait for handler to receive SIGCHLD. */
+ while (ret == 0 && sq->reaped != sq->child) {
+ if (nothreads)
+ /* Note that sigpause re-enables SIGCHLD. */
+ ret = sigpause(SIGCHLD);
+ else {
+ xx = sigrelse(SIGCHLD);
+
+ /*
+ * We start before the fork with this mutex locked;
+ * The only one that unlocks this the signal handler.
+ * So if we get the lock the child has been reaped.
+ */
+ ret = pthread_mutex_lock(&sq->mutex);
+ xx = sighold(SIGCHLD);
+ }
+ }
+
+ /* Accumulate stopwatch time spent waiting, potential performance gain. */
+ sq->ms_scriptlets += rpmswExit(&sq->op, -1)/1000;
+
+ xx = sigrelse(SIGCHLD);
+
+ /* Remove processed SIGCHLD item from queue. */
+ xx = rpmsqRemove(sq);
+
+ /* Disable SIGCHLD handler on refcount == 0. */
+ xx = rpmsqEnable(-SIGCHLD, NULL);
+
+ return ret;
+}
+
+pid_t rpmsqWait(rpmsq sq)
+{
+ if (sq->reaper) {
+ (void) rpmsqWaitUnregister(sq);
+ } else {
+ pid_t reaped;
+ int status;
+ do {
+ reaped = waitpid(sq->child, &status, 0);
+ } while (reaped >= 0 && reaped != sq->child);
+ sq->reaped = reaped;
+ sq->status = status;
+ }
+
+ return sq->reaped;
+}
diff --git a/rpmio/rpmsq.h b/rpmio/rpmsq.h
new file mode 100644
index 0000000..90087e0
--- /dev/null
+++ b/rpmio/rpmsq.h
@@ -0,0 +1,106 @@
+#ifndef H_RPMSQ
+#define H_RPMSQ
+
+/** \ingroup rpmio
+ * \file rpmio/rpmsq.h
+ *
+ */
+
+#include <rpm/rpmsw.h>
+#include <signal.h>
+#if defined(_RPMSQ_INTERNAL)
+#include <pthread.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** \ingroup rpmsq
+ */
+typedef struct rpmsig_s * rpmsig;
+
+/** \ingroup rpmsq
+ */
+typedef struct rpmsqElem * rpmsq;
+
+/** \ingroup rpmsq
+ * Default signal handler prototype.
+ * @param signum signal number
+ * @param info (siginfo_t) signal info
+ * @param context signal context
+ */
+#ifdef SA_SIGINFO
+typedef void (*rpmsqAction_t) (int signum, siginfo_t * info, void * context);
+#else
+typedef void (*rpmsqAction_t) (int signum);
+#endif
+
+/* XXX make this fully opaque? */
+#if defined(_RPMSQ_INTERNAL)
+/**
+ * SIGCHLD queue element.
+ */
+struct rpmsqElem {
+ struct rpmsqElem * q_forw; /*!< for use by insque(3)/remque(3). */
+ struct rpmsqElem * q_back;
+ pid_t child; /*!< Currently running child. */
+ volatile pid_t reaped; /*!< Reaped waitpid(3) return. */
+ volatile int status; /*!< Reaped waitpid(3) status. */
+ struct rpmop_s op; /*!< Scriptlet operation timestamp; */
+ rpmtime_t ms_scriptlets; /*!< Accumulated script duration (msecs). */
+ int reaper; /*!< Register SIGCHLD handler? */
+ int pipes[2]; /*!< Parent/child interlock. */
+ void * id; /*!< Blocking thread id (pthread_t). */
+ pthread_mutex_t mutex; /*!< Signal delivery to thread condvar. */
+ pthread_cond_t cond;
+};
+#endif /* _RPMSQ_INTERNAL */
+
+/** \ingroup rpmsq
+ * Test if given signal has been caught (while signals blocked).
+ * Similar to sigismember() but operates on internal signal queue.
+ * @param signum signal to test for
+ * @return 1 if caught, 0 if not and -1 on error
+ */
+int rpmsqIsCaught(int signum);
+
+/** \ingroup rpmsq
+ * Default signal handler.
+ * @param signum signal number
+ * @param info (siginfo_t) signal info
+ * @param context signal context
+ */
+#ifdef SA_SIGINFO
+void rpmsqAction(int signum, siginfo_t * info, void * context);
+#else
+void rpmsqAction(int signum);
+#endif
+
+/** \ingroup rpmsq
+ * Enable or disable a signal handler.
+ * @param signum signal to enable (or disable if negative)
+ * @param handler sa_sigaction handler (or NULL to use rpmsqHandler())
+ * @return no. of refs, -1 on error
+ */
+int rpmsqEnable(int signum, rpmsqAction_t handler);
+
+/** \ingroup rpmsq
+ * Fork a child process.
+ * @param sq scriptlet queue element
+ * @return fork(2) pid
+ */
+pid_t rpmsqFork(rpmsq sq);
+
+/** \ingroup rpmsq
+ * Wait for child process to be reaped.
+ * @param sq scriptlet queue element
+ * @return reaped child pid
+ */
+pid_t rpmsqWait(rpmsq sq);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* H_RPMSQ */
diff --git a/rpmio/rpmstring.c b/rpmio/rpmstring.c
new file mode 100644
index 0000000..0022b60
--- /dev/null
+++ b/rpmio/rpmstring.c
@@ -0,0 +1,192 @@
+/**
+ * \file rpmio/rpmstring.c
+ */
+
+#include "system.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#include <rpm/rpmstring.h>
+#include "debug.h"
+
+
+int rstrcasecmp(const char * s1, const char * s2)
+{
+ const char * p1 = s1;
+ const char * p2 = s2;
+ char c1, c2;
+
+ if (p1 == p2)
+ return 0;
+
+ do
+ {
+ c1 = rtolower (*p1++);
+ c2 = rtolower (*p2++);
+ if (c1 == '\0')
+ break;
+ }
+ while (c1 == c2);
+
+ return (int)(c1 - c2);
+}
+
+int rstrncasecmp(const char *s1, const char *s2, size_t n)
+{
+ const char * p1 = s1;
+ const char * p2 = s2;
+ char c1, c2;
+
+ if (p1 == p2 || n == 0)
+ return 0;
+
+ do
+ {
+ c1 = rtolower (*p1++);
+ c2 = rtolower (*p2++);
+ if (c1 == '\0' || c1 != c2)
+ break;
+ } while (--n > 0);
+
+ return (int)(c1 - c2);
+}
+
+/*
+ * Simple and stupid asprintf() clone.
+ * FIXME: write to work with non-C99 vsnprintf or check for one in configure.
+ */
+int rasprintf(char **strp, const char *fmt, ...)
+{
+ int n;
+ va_list ap;
+ char * p = NULL;
+
+ if (strp == NULL)
+ return -1;
+
+ va_start(ap, fmt);
+ n = vsnprintf(NULL, 0, fmt, ap);
+ va_end(ap);
+
+ if (n >= -1) {
+ size_t nb = n + 1;
+ p = xmalloc(nb);
+ va_start(ap, fmt);
+ n = vsnprintf(p, nb, fmt, ap);
+ va_end(ap);
+ }
+ *strp = p;
+ return n;
+}
+
+/*
+ * Concatenate two strings with dynamically (re)allocated
+ * memory what prevents static buffer overflows by design.
+ * *dest is reallocated to the size of strings to concatenate.
+ *
+ * Note:
+ * 1) char *buf = rstrcat(NULL,"string"); is the same like rstrcat(&buf,"string");
+ * 2) rstrcat(&buf,NULL) returns buf
+ * 3) rstrcat(NULL,NULL) returns NULL
+ * 4) *dest and src can overlap
+ */
+char *rstrcat(char **dest, const char *src)
+{
+ if ( src == NULL ) {
+ return dest != NULL ? *dest : NULL;
+ }
+
+ if ( dest == NULL ) {
+ return xstrdup(src);
+ }
+
+ {
+ size_t dest_size = *dest != NULL ? strlen(*dest) : 0;
+ size_t src_size = strlen(src);
+
+ *dest = xrealloc(*dest, dest_size+src_size+1); /* include '\0' */
+ memmove(&(*dest)[dest_size], src, src_size+1);
+ }
+
+ return *dest;
+}
+
+/*
+ * Concatenate strings with dynamically (re)allocated
+ * memory what prevents static buffer overflows by design.
+ * *dest is reallocated to the size of strings to concatenate.
+ * List of strings has to be NULL terminated.
+ *
+ * Note:
+ * 1) char *buf = rstrscat(NULL,"string",NULL); is the same like rstrscat(&buf,"string",NULL);
+ * 2) rstrscat(&buf,NULL) returns buf
+ * 3) rstrscat(NULL,NULL) returns NULL
+ * 4) *dest and argument strings can overlap
+ */
+char *rstrscat(char **dest, const char *arg, ...)
+{
+ va_list ap;
+ size_t arg_size, dst_size;
+ const char *s;
+ char *dst, *p;
+
+ dst = dest ? *dest : NULL;
+
+ if ( arg == NULL ) {
+ return dst;
+ }
+
+ va_start(ap, arg);
+ for (arg_size=0, s=arg; s; s = va_arg(ap, const char *))
+ arg_size += strlen(s);
+ va_end(ap);
+
+ dst_size = dst ? strlen(dst) : 0;
+ dst = xrealloc(dst, dst_size+arg_size+1); /* include '\0' */
+ p = &dst[dst_size];
+
+ va_start(ap, arg);
+ for (s = arg; s; s = va_arg(ap, const char *)) {
+ size_t size = strlen(s);
+ memmove(p, s, size);
+ p += size;
+ }
+ va_end(ap);
+ *p = '\0';
+
+ if ( dest ) {
+ *dest = dst;
+ }
+
+ return dst;
+}
+
+/*
+ * Adapted from OpenBSD, strlcpy() originally developed by
+ * Todd C. Miller <Todd.Miller@courtesan.com>
+ */
+size_t rstrlcpy(char *dest, const char *src, size_t n)
+{
+ char *d = dest;
+ const char *s = src;
+ size_t len = n;
+
+ /* Copy as many bytes as will fit */
+ if (len != 0) {
+ while (--len != 0) {
+ if ((*d++ = *s++) == '\0')
+ break;
+ }
+ }
+
+ /* Not enough room in dst, add NUL and traverse rest of src */
+ if (len == 0) {
+ if (n != 0)
+ *d = '\0'; /* NUL-terminate dst */
+ while (*s++)
+ ;
+ }
+
+ return s - src - 1; /* count does not include NUL */
+}
diff --git a/rpmio/rpmstring.h b/rpmio/rpmstring.h
new file mode 100644
index 0000000..4e8a5db
--- /dev/null
+++ b/rpmio/rpmstring.h
@@ -0,0 +1,179 @@
+#ifndef _RPMSTRING_H_
+#define _RPMSTRING_H_
+
+/** \ingroup rpmstring
+ * \file rpmio/rpmstring.h
+ * String manipulation helper functions
+ */
+
+#include <stddef.h>
+#include <string.h>
+
+#include <rpm/rpmutil.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** \ingroup rpmstring
+ * Locale insensitive islower(3)
+ */
+RPM_GNUC_CONST
+static inline int rislower(int c) {
+ return (c >= 'a' && c <= 'z');
+}
+
+/** \ingroup rpmstring
+ * Locale insensitive isupper(3)
+ */
+RPM_GNUC_CONST
+static inline int risupper(int c) {
+ return (c >= 'A' && c <= 'Z');
+}
+
+/** \ingroup rpmstring
+ * Locale insensitive isalpha(3)
+ */
+RPM_GNUC_CONST
+static inline int risalpha(int c) {
+ return (rislower(c) || risupper(c));
+}
+
+/** \ingroup rpmstring
+ * Locale insensitive isdigit(3)
+ */
+RPM_GNUC_CONST
+static inline int risdigit(int c) {
+ return (c >= '0' && c <= '9');
+}
+
+/** \ingroup rpmstring
+ * Locale insensitive isalnum(3)
+ */
+RPM_GNUC_CONST
+static inline int risalnum(int c) {
+ return (risalpha(c) || risdigit(c));
+}
+
+/** \ingroup rpmstring
+ * Locale insensitive isblank(3)
+ */
+RPM_GNUC_CONST
+static inline int risblank(int c) {
+ return (c == ' ' || c == '\t');
+}
+
+/** \ingroup rpmstring
+ * Locale insensitive isspace(3)
+ */
+RPM_GNUC_CONST
+static inline int risspace(int c) {
+ return (risblank(c) || c == '\n' || c == '\r' || c == '\f' || c == '\v');
+}
+
+/** \ingroup rpmstring
+ * Locale insensitive tolower(3)
+ */
+RPM_GNUC_CONST
+static inline int rtolower(int c) {
+ return ((risupper(c)) ? (c | ('a' - 'A')) : c);
+}
+
+/** \ingroup rpmstring
+ * Locale insensitive toupper(3)
+ */
+RPM_GNUC_CONST
+static inline int rtoupper(int c) {
+ return ((rislower(c)) ? (c & ~('a' - 'A')) : c);
+}
+
+/**
+ * Convert hex to binary nibble.
+ * @param c hex character
+ * @return binary nibble
+ */
+RPM_GNUC_CONST
+static inline unsigned char rnibble(char c)
+{
+ if (c >= '0' && c <= '9')
+ return (c - '0');
+ if (c >= 'A' && c <= 'F')
+ return (c - 'A') + 10;
+ if (c >= 'a' && c <= 'f')
+ return (c - 'a') + 10;
+ return 0;
+}
+
+/**
+ * Test for string equality
+ * @param s1 string 1
+ * @param s2 string 2
+ * @return 0 if strings differ, 1 if equal
+ */
+static inline int rstreq(const char *s1, const char *s2)
+{
+ return (strcmp(s1, s2) == 0);
+}
+
+/**
+ * Test for string equality
+ * @param s1 string 1
+ * @param s2 string 2
+ * @param n compare at most n characters
+ * @return 0 if strings differ, 1 if equal
+ */
+static inline int rstreqn(const char *s1, const char *s2, size_t n)
+{
+ return (strncmp(s1, s2, n) == 0);
+}
+
+/** \ingroup rpmstring
+ * Locale insensitive strcasecmp(3).
+ */
+RPM_GNUC_PURE
+int rstrcasecmp(const char * s1, const char * s2) ;
+
+/** \ingroup rpmstring
+ * Locale insensitive strncasecmp(3).
+ */
+RPM_GNUC_PURE
+int rstrncasecmp(const char *s1, const char * s2, size_t n) ;
+
+/** \ingroup rpmstring
+ * asprintf() clone
+ */
+int rasprintf(char **strp, const char *fmt, ...) RPM_GNUC_PRINTF(2, 3);
+
+/** \ingroup rpmstring
+ * Concatenate two strings with dynamically (re)allocated memory.
+ * @param dest pointer to destination string
+ * @param src source string
+ * @return realloc'd dest with src appended
+ */
+char *rstrcat(char **dest, const char *src);
+
+/** \ingroup rpmstring
+ * Concatenate multiple strings with dynamically (re)allocated memory.
+ * @param dest pointer to destination string
+ * @param arg NULL terminated list of strings to concatenate
+ * @return realloc'd dest with strings appended
+ */
+char *rstrscat(char **dest, const char *arg, ...) RPM_GNUC_NULL_TERMINATED;
+
+/** \ingroup rpmstring
+ * strlcpy() clone:
+ * Copy src to string dest of size n. At most n-1 characters
+ * will be copied. Always zero-terminates (unless n == 0).
+ * Length of src is returned; if retval >= n, truncation occurred.
+ * @param dest destination buffer
+ * @param src string to copy
+ * @param n destination buffer size
+ * @return length of src string
+ */
+size_t rstrlcpy(char *dest, const char *src, size_t n);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RPMSTRING_H_ */
diff --git a/rpmio/rpmsw.c b/rpmio/rpmsw.c
new file mode 100644
index 0000000..2a6d70a
--- /dev/null
+++ b/rpmio/rpmsw.c
@@ -0,0 +1,131 @@
+/** \ingroup rpmio
+ * \file rpmio/rpmsw.c
+ */
+
+#include "system.h"
+#include <rpm/rpmsw.h>
+#include "debug.h"
+
+static rpmtime_t rpmsw_overhead = 0;
+
+static rpmtime_t rpmsw_cycles = 1;
+
+static int rpmsw_initialized = 0;
+
+rpmsw rpmswNow(rpmsw sw)
+{
+ if (!rpmsw_initialized)
+ (void) rpmswInit();
+ if (sw == NULL)
+ return NULL;
+ if (gettimeofday(&sw->u.tv, NULL))
+ return NULL;
+ return sw;
+}
+
+/** \ingroup rpmio
+ * Return difference of 2 timeval stamps in micro-seconds.
+ * @param *etv end timeval
+ * @param *btv begin timeval
+ * @return difference in milli-seconds
+ */
+static inline
+rpmtime_t tvsub(const struct timeval * etv,
+ const struct timeval * btv)
+{
+ time_t secs, usecs;
+ if (etv == NULL || btv == NULL) return 0;
+ secs = etv->tv_sec - btv->tv_sec;
+ for (usecs = etv->tv_usec - btv->tv_usec; usecs < 0; usecs += 1000000)
+ secs--;
+ return ((secs * 1000000) + usecs);
+}
+
+rpmtime_t rpmswDiff(rpmsw end, rpmsw begin)
+{
+ unsigned long long ticks = 0;
+
+ if (end == NULL || begin == NULL)
+ return 0;
+ ticks = tvsub(&end->u.tv, &begin->u.tv);
+ if (ticks >= rpmsw_overhead)
+ ticks -= rpmsw_overhead;
+ if (rpmsw_cycles > 1)
+ ticks /= rpmsw_cycles;
+ return ticks;
+}
+
+rpmtime_t rpmswInit(void)
+{
+ struct rpmsw_s begin, end;
+ rpmtime_t sum_overhead = 0;
+ int i;
+
+ rpmsw_initialized = 1;
+
+ rpmsw_overhead = 0;
+ rpmsw_cycles = 0;
+
+ /* Convergence for simultaneous cycles and overhead is overkill ... */
+ for (i = 0; i < 3; i++) {
+ /* Calculate timing overhead in usecs. */
+ (void) rpmswNow(&begin);
+ sum_overhead += rpmswDiff(rpmswNow(&end), &begin);
+
+ rpmsw_overhead = sum_overhead/(i+1);
+ }
+
+ return rpmsw_overhead;
+}
+
+int rpmswEnter(rpmop op, ssize_t rc)
+{
+ if (op == NULL)
+ return 0;
+
+ op->count++;
+ if (rc < 0) {
+ op->bytes = 0;
+ op->usecs = 0;
+ }
+ (void) rpmswNow(&op->begin);
+ return 0;
+}
+
+rpmtime_t rpmswExit(rpmop op, ssize_t rc)
+{
+ struct rpmsw_s end;
+
+ if (op == NULL)
+ return 0;
+
+ op->usecs += rpmswDiff(rpmswNow(&end), &op->begin);
+ if (rc > 0)
+ op->bytes += rc;
+ op->begin = end; /* structure assignment */
+ return op->usecs;
+}
+
+rpmtime_t rpmswAdd(rpmop to, rpmop from)
+{
+ rpmtime_t usecs = 0;
+ if (to != NULL && from != NULL) {
+ to->count += from->count;
+ to->bytes += from->bytes;
+ to->usecs += from->usecs;
+ usecs = to->usecs;
+ }
+ return usecs;
+}
+
+rpmtime_t rpmswSub(rpmop to, rpmop from)
+{
+ rpmtime_t usecs = 0;
+ if (to != NULL && from != NULL) {
+ to->count -= from->count;
+ to->bytes -= from->bytes;
+ to->usecs -= from->usecs;
+ usecs = to->usecs;
+ }
+ return usecs;
+}
diff --git a/rpmio/rpmsw.h b/rpmio/rpmsw.h
new file mode 100644
index 0000000..7c285e7
--- /dev/null
+++ b/rpmio/rpmsw.h
@@ -0,0 +1,104 @@
+#ifndef H_RPMSW
+#define H_RPMSW
+
+/** \ingroup rpmio
+ * \file rpmio/rpmsw.h
+ */
+
+#include <unistd.h>
+#include <sys/time.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** \ingroup rpmsw
+ */
+typedef unsigned long int rpmtime_t;
+
+/** \ingroup rpmsw
+ */
+typedef struct rpmsw_s * rpmsw;
+
+/** \ingroup rpmsw
+ */
+typedef struct rpmop_s * rpmop;
+
+/** \ingroup rpmsw
+ */
+struct rpmsw_s {
+ union {
+ struct timeval tv;
+ unsigned long long int ticks;
+ unsigned long int tocks[2];
+ } u;
+};
+
+/** \ingroup rpmsw
+ * Cumulative statistics for an operation.
+ */
+struct rpmop_s {
+ struct rpmsw_s begin; /*!< Starting time stamp. */
+ int count; /*!< Number of operations. */
+ size_t bytes; /*!< Number of bytes transferred. */
+ rpmtime_t usecs; /*!< Number of ticks. */
+};
+
+/** \ingroup rpmsw
+ * Return benchmark time stamp.
+ * @param *sw time stamp
+ * @return 0 on success
+ */
+rpmsw rpmswNow(rpmsw sw);
+
+/** \ingroup rpmsw
+ * Return benchmark time stamp difference.
+ * @param *end end time stamp
+ * @param *begin begin time stamp
+ * @return difference in micro-seconds
+ */
+rpmtime_t rpmswDiff(rpmsw end, rpmsw begin);
+
+/** \ingroup rpmsw
+ * Return benchmark time stamp overhead.
+ * @return overhead in micro-seconds
+ */
+rpmtime_t rpmswInit(void);
+
+/** \ingroup rpmsw
+ * Enter timed operation.
+ * @param op operation statistics
+ * @param rc -1 clears usec counter
+ * @return 0 always
+ */
+int rpmswEnter(rpmop op, ssize_t rc);
+
+/** \ingroup rpmsw
+ * Exit timed operation.
+ * @param op operation statistics
+ * @param rc per-operation data (e.g. bytes transferred)
+ * @return cumulative usecs for operation
+ */
+rpmtime_t rpmswExit(rpmop op, ssize_t rc);
+
+/** \ingroup rpmsw
+ * Sum statistic counters.
+ * @param to result statistics
+ * @param from operation statistics
+ * @return cumulative usecs for operation
+ */
+rpmtime_t rpmswAdd(rpmop to, rpmop from);
+
+/** \ingroup rpmsw
+ * Subtract statistic counters.
+ * @param to result statistics
+ * @param from operation statistics
+ * @return cumulative usecs for operation
+ */
+rpmtime_t rpmswSub(rpmop to, rpmop from);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* H_RPMSW */
diff --git a/rpmio/rpmurl.h b/rpmio/rpmurl.h
new file mode 100644
index 0000000..3384bc4
--- /dev/null
+++ b/rpmio/rpmurl.h
@@ -0,0 +1,52 @@
+#ifndef H_RPMURL
+#define H_RPMURL
+
+/** \ingroup rpmio
+ * \file rpmio/rpmurl.h
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** \ingroup rpmurl
+ * Supported URL types.
+ */
+typedef enum urltype_e {
+ URL_IS_UNKNOWN = 0, /*!< unknown (aka a file) */
+ URL_IS_DASH = 1, /*!< stdin/stdout */
+ URL_IS_PATH = 2, /*!< file://... */
+ URL_IS_FTP = 3, /*!< ftp://... */
+ URL_IS_HTTP = 4, /*!< http://... */
+ URL_IS_HTTPS = 5, /*!< https://... */
+ URL_IS_HKP = 6 /*!< hkp://... */
+} urltype;
+
+/** \ingroup rpmurl
+ * Return type of URL.
+ * @param url url string
+ * @return type of url
+ */
+urltype urlIsURL(const char * url);
+
+/** \ingroup rpmurl
+ * Return path component of URL.
+ * @param url url string
+ * @retval pathp pointer to path component of url
+ * @return type of url
+ */
+urltype urlPath(const char * url, const char ** pathp);
+
+/** \ingroup rpmurl
+ * Copy data from URL to local file.
+ * @param url url string of source
+ * @param dest file name of destination
+ * @return 0 on success, otherwise FTPERR_* code
+ */
+int urlGetFile(const char * url, const char * dest);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* H_RPMURL */
diff --git a/rpmio/rpmutil.h b/rpmio/rpmutil.h
new file mode 100644
index 0000000..68a21f9
--- /dev/null
+++ b/rpmio/rpmutil.h
@@ -0,0 +1,159 @@
+#ifndef _RPMUTIL_H
+#define _RPMUTIL_H
+
+#include <unistd.h>
+
+/*
+ * Miscellanous utility macros:
+ * - portability wrappers for various gcc extensions like __attribute__()
+ * - ...
+ *
+ * Copied from glib, names replaced to avoid clashing with glib.
+ *
+ */
+
+/* Here we provide RPM_GNUC_EXTENSION as an alias for __extension__,
+ * where this is valid. This allows for warningless compilation of
+ * "long long" types even in the presence of '-ansi -pedantic'.
+ */
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 8)
+# define RPM_GNUC_EXTENSION __extension__
+#else
+# define RPM_GNUC_EXTENSION
+#endif
+
+/* Provide macros to feature the GCC function attribute.
+ */
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96)
+#define RPM_GNUC_PURE \
+ __attribute__((__pure__))
+#define RPM_GNUC_MALLOC \
+ __attribute__((__malloc__))
+#else
+#define RPM_GNUC_PURE
+#define RPM_GNUC_MALLOC
+#endif
+
+#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)
+#define RPM_GNUC_ALLOC_SIZE(x) __attribute__((__alloc_size__(x)))
+#define RPM_GNUC_ALLOC_SIZE2(x,y) __attribute__((__alloc_size__(x,y)))
+#else
+#define RPM_GNUC_ALLOC_SIZE(x)
+#define RPM_GNUC_ALLOC_SIZE2(x,y)
+#endif
+
+#if __GNUC__ >= 4
+#define RPM_GNUC_NULL_TERMINATED __attribute__((__sentinel__))
+#else
+#define RPM_GNUC_NULL_TERMINATED
+#endif
+
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)
+#define RPM_GNUC_PRINTF( format_idx, arg_idx ) \
+ __attribute__((__format__ (__printf__, format_idx, arg_idx)))
+#define RPM_GNUC_SCANF( format_idx, arg_idx ) \
+ __attribute__((__format__ (__scanf__, format_idx, arg_idx)))
+#define RPM_GNUC_FORMAT( arg_idx ) \
+ __attribute__((__format_arg__ (arg_idx)))
+#define RPM_GNUC_NORETURN \
+ __attribute__((__noreturn__))
+#define RPM_GNUC_CONST \
+ __attribute__((__const__))
+#define RPM_GNUC_UNUSED \
+ __attribute__((__unused__))
+#define RPM_GNUC_NO_INSTRUMENT \
+ __attribute__((__no_instrument_function__))
+#else /* !__GNUC__ */
+#define RPM_GNUC_PRINTF( format_idx, arg_idx )
+#define RPM_GNUC_SCANF( format_idx, arg_idx )
+#define RPM_GNUC_FORMAT( arg_idx )
+#define RPM_GNUC_NORETURN
+#define RPM_GNUC_CONST
+#define RPM_GNUC_UNUSED
+#define RPM_GNUC_NO_INSTRUMENT
+#endif /* !__GNUC__ */
+
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)
+#define RPM_GNUC_DEPRECATED \
+ __attribute__((__deprecated__))
+#else
+#define RPM_GNUC_DEPRECATED
+#endif /* __GNUC__ */
+
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)
+#define RPM_GNUC_MAY_ALIAS __attribute__((may_alias))
+#define RPM_GNUC_NONNULL( ... ) \
+ __attribute__((__nonnull__ (__VA_ARGS__)))
+#else
+#define RPM_GNUC_MAY_ALIAS
+#define RPM_GNUC_NONNULL( ... )
+#endif
+
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+#define RPM_GNUC_WARN_UNUSED_RESULT \
+ __attribute__((warn_unused_result))
+#else
+#define RPM_GNUC_WARN_UNUSED_RESULT
+#endif /* __GNUC__ */
+
+#if __GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)
+# define RPM_GNUC_INTERNAL __attribute__((visibility("hidden")))
+#else
+# define RPM_GNUC_INTERNAL
+#endif
+
+
+/* Guard C code in headers, while including them from C++ */
+#ifdef __cplusplus
+# define RPM_BEGIN_DECLS extern "C" {
+# define RPM_END_DECLS }
+#else
+# define RPM_BEGIN_DECLS
+# define RPM_END_DECLS
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Rpm specific allocators which never return NULL but terminate on failure */
+RPM_GNUC_MALLOC RPM_GNUC_ALLOC_SIZE(1)
+void * rmalloc(size_t size);
+
+RPM_GNUC_MALLOC RPM_GNUC_ALLOC_SIZE2(1,2)
+void * rcalloc(size_t nmemb, size_t size);
+
+RPM_GNUC_ALLOC_SIZE(2)
+void * rrealloc(void *ptr, size_t size);
+
+char * rstrdup(const char *str);
+
+/* Rpm specific free() which returns NULL */
+void * rfree(void *ptr);
+
+/** \ingroup rpmutil
+ * Memory allocation failure callback prototype. When registered through
+ * rpmSetMemFail(), this gets called if memory allocation through rmalloc()
+ * and friends fails. If the application can somehow recover memory here,
+ * it can return a newly allocated memory block of requested size, otherwise
+ * it must return NULL after performing it's own shutdown deeds or
+ * terminate itself.
+ * @param size Size of allocation request in bytes
+ * @param data User data (or NULL)
+ * @return Allocated memory block of requested size or NULL
+ */
+typedef void * (*rpmMemFailFunc) (size_t size, void *data);
+
+/** \ingroup rpmutil
+ * Set memory allocation failure callback.
+ * @param func Allocation failure callback function
+ * @param data User data (or NULL)
+ * @return Previous callback function
+ */
+rpmMemFailFunc rpmSetMemFail(rpmMemFailFunc func, void *data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RPMUTIL_H */
diff --git a/rpmio/url.c b/rpmio/url.c
new file mode 100644
index 0000000..c609fd1
--- /dev/null
+++ b/rpmio/url.c
@@ -0,0 +1,127 @@
+/** \ingroup rpmio
+ * \file rpmio/url.c
+ */
+
+#include "system.h"
+
+#include <sys/wait.h>
+
+#include <rpm/rpmmacro.h>
+#include <rpm/rpmlog.h>
+#include <rpm/rpmurl.h>
+#include <rpm/rpmio.h>
+#include <rpm/argv.h>
+#include <rpm/rpmstring.h>
+
+#include "debug.h"
+
+/**
+ */
+static struct urlstring {
+ const char * leadin;
+ urltype ret;
+} const urlstrings[] = {
+ { "file://", URL_IS_PATH },
+ { "ftp://", URL_IS_FTP },
+ { "hkp://", URL_IS_HKP },
+ { "http://", URL_IS_HTTP },
+ { "https://", URL_IS_HTTPS },
+ { NULL, URL_IS_UNKNOWN }
+};
+
+urltype urlIsURL(const char * url)
+{
+ const struct urlstring *us;
+
+ if (url && *url) {
+ for (us = urlstrings; us->leadin != NULL; us++) {
+ if (!rstreqn(url, us->leadin, strlen(us->leadin)))
+ continue;
+ return us->ret;
+ }
+ if (rstreq(url, "-"))
+ return URL_IS_DASH;
+ }
+
+ return URL_IS_UNKNOWN;
+}
+
+/* Return path portion of url (or pointer to NUL if url == NULL) */
+urltype urlPath(const char * url, const char ** pathp)
+{
+ const char *path;
+ urltype type;
+
+ path = url;
+ type = urlIsURL(url);
+ switch (type) {
+ case URL_IS_FTP:
+ url += sizeof("ftp://") - 1;
+ path = strchr(url, '/');
+ if (path == NULL) path = url + strlen(url);
+ break;
+ case URL_IS_PATH:
+ url += sizeof("file://") - 1;
+ path = strchr(url, '/');
+ if (path == NULL) path = url + strlen(url);
+ break;
+ case URL_IS_HKP:
+ url += sizeof("hkp://") - 1;
+ path = strchr(url, '/');
+ if (path == NULL) path = url + strlen(url);
+ break;
+ case URL_IS_HTTP:
+ url += sizeof("http://") - 1;
+ path = strchr(url, '/');
+ if (path == NULL) path = url + strlen(url);
+ break;
+ case URL_IS_HTTPS:
+ url += sizeof("https://") - 1;
+ path = strchr(url, '/');
+ if (path == NULL) path = url + strlen(url);
+ break;
+ case URL_IS_UNKNOWN:
+ if (path == NULL) path = "";
+ break;
+ case URL_IS_DASH:
+ path = "";
+ break;
+ }
+ if (pathp)
+ *pathp = path;
+ return type;
+}
+
+int urlGetFile(const char * url, const char * dest)
+{
+ char *cmd = NULL;
+ const char *target = NULL;
+ char *urlhelper = NULL;
+ int rc;
+ pid_t pid, wait;
+
+ urlhelper = rpmExpand("%{?_urlhelper}", NULL);
+
+ if (dest == NULL) {
+ urlPath(url, &target);
+ } else {
+ target = dest;
+ }
+
+ /* XXX TODO: sanity checks like target == dest... */
+
+ rasprintf(&cmd, "%s %s %s", urlhelper, target, url);
+ urlhelper = _free(urlhelper);
+
+ if ((pid = fork()) == 0) {
+ ARGV_t argv = NULL;
+ argvSplit(&argv, cmd, " ");
+ execvp(argv[0], argv);
+ exit(127); /* exit with 127 for compatibility with bash(1) */
+ }
+ wait = waitpid(pid, &rc, 0);
+ cmd = _free(cmd);
+
+ return rc;
+
+}