summaryrefslogtreecommitdiff
path: root/build
diff options
context:
space:
mode:
Diffstat (limited to 'build')
-rw-r--r--build/Makefile.am25
-rw-r--r--build/Makefile.in678
-rw-r--r--build/build.c304
-rw-r--r--build/expression.c692
-rw-r--r--build/files.c2171
-rw-r--r--build/misc.c103
-rw-r--r--build/pack.c822
-rw-r--r--build/parseBuildInstallClean.c64
-rw-r--r--build/parseChangelog.c245
-rw-r--r--build/parseDescription.c113
-rw-r--r--build/parseFiles.c99
-rw-r--r--build/parsePolicies.c89
-rw-r--r--build/parsePreamble.c1090
-rw-r--r--build/parsePrep.c546
-rw-r--r--build/parseReqs.c189
-rw-r--r--build/parseScript.c389
-rw-r--r--build/parseSpec.c698
-rw-r--r--build/policies.c316
-rw-r--r--build/reqprov.c123
-rw-r--r--build/rpmbuild.h112
-rw-r--r--build/rpmbuild_internal.h401
-rw-r--r--build/rpmbuild_misc.h94
-rw-r--r--build/rpmfc.c1405
-rw-r--r--build/rpmfc.h113
-rw-r--r--build/rpmspec.h88
-rw-r--r--build/spec.c428
26 files changed, 11397 insertions, 0 deletions
diff --git a/build/Makefile.am b/build/Makefile.am
new file mode 100644
index 0000000..8e6ca8f
--- /dev/null
+++ b/build/Makefile.am
@@ -0,0 +1,25 @@
+# Makefile for rpmbuild library.
+
+AM_CPPFLAGS = -I$(top_builddir) -I$(top_srcdir) -I$(top_builddir)/include/
+AM_CPPFLAGS += @WITH_NSS_INCLUDE@
+AM_CPPFLAGS += @WITH_MAGIC_INCLUDE@
+AM_CPPFLAGS += @WITH_POPT_INCLUDE@
+AM_CPPFLAGS += -I$(top_srcdir)/misc
+
+usrlibdir = $(libdir)
+usrlib_LTLIBRARIES = librpmbuild.la
+librpmbuild_la_SOURCES = \
+ build.c expression.c files.c misc.c pack.c \
+ parseBuildInstallClean.c parseChangelog.c parseDescription.c \
+ parseFiles.c parsePreamble.c parsePrep.c parseReqs.c parseScript.c \
+ parseSpec.c reqprov.c rpmfc.c spec.c \
+ parsePolicies.c policies.c \
+ rpmbuild_internal.h rpmbuild_misc.h
+
+librpmbuild_la_LDFLAGS = -version-info 2:1:0
+librpmbuild_la_LIBADD = \
+ $(top_builddir)/lib/librpm.la \
+ $(top_builddir)/rpmio/librpmio.la \
+ $(top_builddir)/misc/libmisc.la \
+ @WITH_POPT_LIB@ \
+ @WITH_MAGIC_LIB@
diff --git a/build/Makefile.in b/build/Makefile.in
new file mode 100644
index 0000000..a2fd429
--- /dev/null
+++ b/build/Makefile.in
@@ -0,0 +1,678 @@
+# 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 rpmbuild 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@
+subdir = build
+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)
+librpmbuild_la_DEPENDENCIES = $(top_builddir)/lib/librpm.la \
+ $(top_builddir)/rpmio/librpmio.la \
+ $(top_builddir)/misc/libmisc.la
+am_librpmbuild_la_OBJECTS = build.lo expression.lo files.lo misc.lo \
+ pack.lo parseBuildInstallClean.lo parseChangelog.lo \
+ parseDescription.lo parseFiles.lo parsePreamble.lo \
+ parsePrep.lo parseReqs.lo parseScript.lo parseSpec.lo \
+ reqprov.lo rpmfc.lo spec.lo parsePolicies.lo policies.lo
+librpmbuild_la_OBJECTS = $(am_librpmbuild_la_OBJECTS)
+librpmbuild_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(librpmbuild_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 = $(librpmbuild_la_SOURCES)
+DIST_SOURCES = $(librpmbuild_la_SOURCES)
+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_MAGIC_INCLUDE@ @WITH_POPT_INCLUDE@ -I$(top_srcdir)/misc
+usrlibdir = $(libdir)
+usrlib_LTLIBRARIES = librpmbuild.la
+librpmbuild_la_SOURCES = \
+ build.c expression.c files.c misc.c pack.c \
+ parseBuildInstallClean.c parseChangelog.c parseDescription.c \
+ parseFiles.c parsePreamble.c parsePrep.c parseReqs.c parseScript.c \
+ parseSpec.c reqprov.c rpmfc.c spec.c \
+ parsePolicies.c policies.c \
+ rpmbuild_internal.h rpmbuild_misc.h
+
+librpmbuild_la_LDFLAGS = -version-info 2:1:0
+librpmbuild_la_LIBADD = \
+ $(top_builddir)/lib/librpm.la \
+ $(top_builddir)/rpmio/librpmio.la \
+ $(top_builddir)/misc/libmisc.la \
+ @WITH_POPT_LIB@ \
+ @WITH_MAGIC_LIB@
+
+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 build/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign build/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
+librpmbuild.la: $(librpmbuild_la_OBJECTS) $(librpmbuild_la_DEPENDENCIES)
+ $(librpmbuild_la_LINK) -rpath $(usrlibdir) $(librpmbuild_la_OBJECTS) $(librpmbuild_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/build.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/expression.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/files.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/misc.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pack.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parseBuildInstallClean.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parseChangelog.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parseDescription.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parseFiles.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parsePolicies.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parsePreamble.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parsePrep.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parseReqs.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parseScript.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parseSpec.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/policies.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/reqprov.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rpmfc.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/spec.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
+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-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: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean 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/build/build.c b/build/build.c
new file mode 100644
index 0000000..5924ec6
--- /dev/null
+++ b/build/build.c
@@ -0,0 +1,304 @@
+/** \ingroup rpmbuild
+ * \file build/build.c
+ * Top-level build dispatcher.
+ */
+
+#include "system.h"
+
+#include <errno.h>
+#include <sys/wait.h>
+
+#include <rpm/rpmlog.h>
+#include <rpm/rpmfileutil.h>
+#include "build/rpmbuild_internal.h"
+#include "build/rpmbuild_misc.h"
+#include "lib/rpmug.h"
+
+#include "debug.h"
+
+/**
+ */
+static rpmRC doRmSource(rpmSpec spec)
+{
+ struct Source *p;
+ Package pkg;
+ int rc = 0;
+
+ for (p = spec->sources; p != NULL; p = p->next) {
+ if (! (p->flags & RPMBUILD_ISNO)) {
+ char *fn = rpmGetPath("%{_sourcedir}/", p->source, NULL);
+ rc = unlink(fn);
+ fn = _free(fn);
+ if (rc) goto exit;
+ }
+ }
+
+ for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
+ for (p = pkg->icon; p != NULL; p = p->next) {
+ if (! (p->flags & RPMBUILD_ISNO)) {
+ char *fn = rpmGetPath("%{_sourcedir}/", p->source, NULL);
+ rc = unlink(fn);
+ fn = _free(fn);
+ if (rc) goto exit;
+ }
+ }
+ }
+exit:
+ return !rc ? RPMRC_OK : RPMRC_FAIL;
+}
+
+/*
+ * @todo Single use by %%doc in files.c prevents static.
+ */
+rpmRC doScript(rpmSpec spec, rpmBuildFlags what, const char *name,
+ const char *sb, int test)
+{
+ const char * rootDir = spec->rootDir;
+ char *scriptName = NULL;
+ char * buildDir = rpmGenPath(rootDir, "%{_builddir}", "");
+ char * buildCmd = NULL;
+ char * buildTemplate = NULL;
+ char * buildPost = NULL;
+ const char * mTemplate = NULL;
+ const char * mCmd = NULL;
+ const char * mPost = NULL;
+ int argc = 0;
+ const char **argv = NULL;
+ FILE * fp = NULL;
+
+ FD_t fd;
+ FD_t xfd;
+ pid_t pid;
+ pid_t child;
+ int status;
+ rpmRC rc;
+
+ switch (what) {
+ case RPMBUILD_PREP:
+ mTemplate = "%{__spec_prep_template}";
+ mPost = "%{__spec_prep_post}";
+ mCmd = "%{__spec_prep_cmd}";
+ break;
+ case RPMBUILD_BUILD:
+ mTemplate = "%{__spec_build_template}";
+ mPost = "%{__spec_build_post}";
+ mCmd = "%{__spec_build_cmd}";
+ break;
+ case RPMBUILD_INSTALL:
+ mTemplate = "%{__spec_install_template}";
+ mPost = "%{__spec_install_post}";
+ mCmd = "%{__spec_install_cmd}";
+ break;
+ case RPMBUILD_CHECK:
+ mTemplate = "%{__spec_check_template}";
+ mPost = "%{__spec_check_post}";
+ mCmd = "%{__spec_check_cmd}";
+ break;
+ case RPMBUILD_CLEAN:
+ mTemplate = "%{__spec_clean_template}";
+ mPost = "%{__spec_clean_post}";
+ mCmd = "%{__spec_clean_cmd}";
+ break;
+ case RPMBUILD_RMBUILD:
+ mTemplate = "%{__spec_clean_template}";
+ mPost = "%{__spec_clean_post}";
+ mCmd = "%{__spec_clean_cmd}";
+ break;
+ case RPMBUILD_STRINGBUF:
+ default:
+ mTemplate = "%{___build_template}";
+ mPost = "%{___build_post}";
+ mCmd = "%{___build_cmd}";
+ break;
+ }
+
+ if ((what != RPMBUILD_RMBUILD) && sb == NULL) {
+ rc = RPMRC_OK;
+ goto exit;
+ }
+
+ fd = rpmMkTempFile(rootDir, &scriptName);
+ if (fd == NULL || Ferror(fd)) {
+ rpmlog(RPMLOG_ERR, _("Unable to open temp file.\n"));
+ rc = RPMRC_FAIL;
+ goto exit;
+ }
+
+ if (fdGetFILE(fd) == NULL)
+ xfd = Fdopen(fd, "w.fpio");
+ else
+ xfd = fd;
+
+ if ((fp = fdGetFILE(xfd)) == NULL) {
+ rc = RPMRC_FAIL;
+ goto exit;
+ }
+
+ if (*rootDir == '\0') rootDir = "/";
+
+ buildTemplate = rpmExpand(mTemplate, NULL);
+ buildPost = rpmExpand(mPost, NULL);
+
+ (void) fputs(buildTemplate, fp);
+
+ if (what != RPMBUILD_PREP && what != RPMBUILD_RMBUILD && spec->buildSubdir)
+ fprintf(fp, "cd '%s'\n", spec->buildSubdir);
+
+ if (what == RPMBUILD_RMBUILD) {
+ if (spec->buildSubdir)
+ fprintf(fp, "rm -rf '%s'\n", spec->buildSubdir);
+ } else if (sb != NULL)
+ fprintf(fp, "%s", sb);
+
+ (void) fputs(buildPost, fp);
+
+ (void) Fclose(xfd);
+
+ if (test) {
+ rc = RPMRC_OK;
+ goto exit;
+ }
+
+ if (buildDir && buildDir[0] != '/') {
+ rc = RPMRC_FAIL;
+ goto exit;
+ }
+
+ buildCmd = rpmExpand(mCmd, " ", scriptName, NULL);
+ (void) poptParseArgvString(buildCmd, &argc, &argv);
+
+ rpmlog(RPMLOG_NOTICE, _("Executing(%s): %s\n"), name, buildCmd);
+ if (!(child = fork())) {
+ /* NSPR messes with SIGPIPE, reset to default for the kids */
+ signal(SIGPIPE, SIG_DFL);
+ errno = 0;
+ (void) execvp(argv[0], (char *const *)argv);
+
+ rpmlog(RPMLOG_ERR, _("Exec of %s failed (%s): %s\n"),
+ scriptName, name, strerror(errno));
+
+ _exit(127); /* exit 127 for compatibility with bash(1) */
+ }
+
+ pid = waitpid(child, &status, 0);
+
+ if (!WIFEXITED(status) || WEXITSTATUS(status)) {
+ rpmlog(RPMLOG_ERR, _("Bad exit status from %s (%s)\n"),
+ scriptName, name);
+ rc = RPMRC_FAIL;
+ } else
+ rc = RPMRC_OK;
+
+exit:
+ if (scriptName) {
+ if (rc == RPMRC_OK)
+ (void) unlink(scriptName);
+ scriptName = _free(scriptName);
+ }
+ argv = _free(argv);
+ buildCmd = _free(buildCmd);
+ buildTemplate = _free(buildTemplate);
+ buildPost = _free(buildPost);
+ buildDir = _free(buildDir);
+
+ return rc;
+}
+
+static rpmRC buildSpec(BTA_t buildArgs, rpmSpec spec, int what)
+{
+ rpmRC rc = RPMRC_OK;
+ int test = (what & RPMBUILD_NOBUILD);
+ char *cookie = buildArgs->cookie ? xstrdup(buildArgs->cookie) : NULL;
+
+ /* XXX TODO: rootDir is only relevant during build, eliminate from spec */
+ spec->rootDir = buildArgs->rootdir;
+ if (!spec->recursing && spec->BACount) {
+ int x;
+ /* When iterating over BANames, do the source */
+ /* packaging on the first run, and skip RMSOURCE altogether */
+ if (spec->BASpecs != NULL)
+ for (x = 0; x < spec->BACount; x++) {
+ if ((rc = buildSpec(buildArgs, spec->BASpecs[x],
+ (what & ~RPMBUILD_RMSOURCE) |
+ (x ? 0 : (what & RPMBUILD_PACKAGESOURCE))))) {
+ goto exit;
+ }
+ }
+ } else {
+ int didBuild = (what & (RPMBUILD_PREP|RPMBUILD_BUILD|RPMBUILD_INSTALL));
+
+ if ((what & RPMBUILD_PREP) &&
+ (rc = doScript(spec, RPMBUILD_PREP, "%prep",
+ getStringBuf(spec->prep), test)))
+ goto exit;
+
+ if ((what & RPMBUILD_BUILD) &&
+ (rc = doScript(spec, RPMBUILD_BUILD, "%build",
+ getStringBuf(spec->build), test)))
+ goto exit;
+
+ if ((what & RPMBUILD_INSTALL) &&
+ (rc = doScript(spec, RPMBUILD_INSTALL, "%install",
+ getStringBuf(spec->install), test)))
+ goto exit;
+
+ if ((what & RPMBUILD_CHECK) &&
+ (rc = doScript(spec, RPMBUILD_CHECK, "%check",
+ getStringBuf(spec->check), test)))
+ goto exit;
+
+ if ((what & RPMBUILD_PACKAGESOURCE) &&
+ (rc = processSourceFiles(spec, buildArgs->pkgFlags)))
+ goto exit;
+
+ if (((what & RPMBUILD_INSTALL) || (what & RPMBUILD_PACKAGEBINARY) ||
+ (what & RPMBUILD_FILECHECK)) &&
+ (rc = processBinaryFiles(spec, buildArgs->pkgFlags,
+ what & RPMBUILD_INSTALL, test)))
+ goto exit;
+
+ if (((what & RPMBUILD_INSTALL) || (what & RPMBUILD_PACKAGEBINARY)) &&
+ (rc = processBinaryPolicies(spec, test)))
+ goto exit;
+
+ if (((what & RPMBUILD_PACKAGESOURCE) && !test) &&
+ (rc = packageSources(spec, &cookie)))
+ return rc;
+
+ if (((what & RPMBUILD_PACKAGEBINARY) && !test) &&
+ (rc = packageBinaries(spec, cookie, (didBuild == 0))))
+ goto exit;
+
+ if ((what & RPMBUILD_CLEAN) &&
+ (rc = doScript(spec, RPMBUILD_CLEAN, "%clean",
+ getStringBuf(spec->clean), test)))
+ goto exit;
+
+ if ((what & RPMBUILD_RMBUILD) &&
+ (rc = doScript(spec, RPMBUILD_RMBUILD, "--clean", NULL, test)))
+ goto exit;
+ }
+
+ if (what & RPMBUILD_RMSOURCE)
+ doRmSource(spec);
+
+ if (what & RPMBUILD_RMSPEC)
+ (void) unlink(spec->specFile);
+
+exit:
+ free(cookie);
+ spec->rootDir = NULL;
+ if (rc != RPMRC_OK && rpmlogGetNrecs() > 0) {
+ rpmlog(RPMLOG_NOTICE, _("\n\nRPM build errors:\n"));
+ rpmlogPrint(NULL);
+ }
+ rpmugFree();
+
+ return rc;
+}
+
+rpmRC rpmSpecBuild(rpmSpec spec, BTA_t buildArgs)
+{
+ /* buildSpec() can recurse with different buildAmount, pass it separately */
+ return buildSpec(buildArgs, spec, buildArgs->buildAmount);
+}
diff --git a/build/expression.c b/build/expression.c
new file mode 100644
index 0000000..7217290
--- /dev/null
+++ b/build/expression.c
@@ -0,0 +1,692 @@
+/** \ingroup rpmbuild
+ * \file build/expression.c
+ * Simple logical expression parser.
+ * This module implements a basic expression parser with support for
+ * integer and string datatypes. For ease of programming, we use the
+ * top-down "recursive descent" method of parsing. While a
+ * table-driven bottom-up parser might be faster, it does not really
+ * matter for the expressions we will be parsing.
+ *
+ * Copyright (C) 1998 Tom Dyas <tdyas@eden.rutgers.edu>
+ * This work is provided under the GPL or LGPL at your choice.
+ */
+
+#include "system.h"
+
+#include <rpm/rpmlog.h>
+#include "build/rpmbuild_internal.h"
+#include "debug.h"
+
+/* #define DEBUG_PARSER 1 */
+
+#ifdef DEBUG_PARSER
+#include <stdio.h>
+#define DEBUG(x) do { x ; } while (0)
+#else
+#define DEBUG(x)
+#endif
+
+typedef enum {
+ VALUE_TYPE_INTEGER,
+ VALUE_TYPE_STRING,
+} valueType;
+
+/**
+ * Encapsulation of a "value"
+ */
+typedef struct _value {
+ valueType type;
+ union {
+ char *s;
+ int i;
+ } data;
+} *Value;
+
+/**
+ */
+static Value valueMakeInteger(int i)
+{
+ Value v;
+
+ v = (Value) xmalloc(sizeof(*v));
+ v->type = VALUE_TYPE_INTEGER;
+ v->data.i = i;
+ return v;
+}
+
+/**
+ */
+static Value valueMakeString(char *s)
+{
+ Value v;
+
+ v = (Value) xmalloc(sizeof(*v));
+ v->type = VALUE_TYPE_STRING;
+ v->data.s = s;
+ return v;
+}
+
+/**
+ */
+static void valueFree( Value v)
+{
+ if (v) {
+ if (v->type == VALUE_TYPE_STRING)
+ v->data.s = _free(v->data.s);
+ v = _free(v);
+ }
+}
+
+#ifdef DEBUG_PARSER
+static void valueDump(const char *msg, Value v, FILE *fp)
+{
+ if (msg)
+ fprintf(fp, "%s ", msg);
+ if (v) {
+ if (v->type == VALUE_TYPE_INTEGER)
+ fprintf(fp, "INTEGER %d\n", v->data.i);
+ else
+ fprintf(fp, "STRING '%s'\n", v->data.s);
+ } else
+ fprintf(fp, "NULL\n");
+}
+#endif
+
+#define valueIsInteger(v) ((v)->type == VALUE_TYPE_INTEGER)
+#define valueIsString(v) ((v)->type == VALUE_TYPE_STRING)
+#define valueSameType(v1,v2) ((v1)->type == (v2)->type)
+
+
+/**
+ * Parser state.
+ */
+typedef struct _parseState {
+ char *str; /*!< expression string */
+ char *p; /*!< current position in expression string */
+ int nextToken; /*!< current lookahead token */
+ Value tokenValue; /*!< valid when TOK_INTEGER or TOK_STRING */
+ rpmSpec spec; /*!< spec file that we are parsing inside of */
+} *ParseState;
+
+
+/**
+ * \name Parser tokens
+ */
+#define TOK_EOF 1
+#define TOK_INTEGER 2
+#define TOK_STRING 3
+#define TOK_IDENTIFIER 4
+#define TOK_ADD 5
+#define TOK_MINUS 6
+#define TOK_MULTIPLY 7
+#define TOK_DIVIDE 8
+#define TOK_OPEN_P 9
+#define TOK_CLOSE_P 10
+#define TOK_EQ 11
+#define TOK_NEQ 12
+#define TOK_LT 13
+#define TOK_LE 14
+#define TOK_GT 15
+#define TOK_GE 16
+#define TOK_NOT 17
+#define TOK_LOGICAL_AND 18
+#define TOK_LOGICAL_OR 19
+
+#if defined(DEBUG_PARSER)
+typedef struct exprTokTableEntry {
+ const char *name;
+ int val;
+} ETTE_t;
+
+ETTE_t exprTokTable[] = {
+ { "EOF", TOK_EOF },
+ { "I", TOK_INTEGER },
+ { "S", TOK_STRING },
+ { "ID", TOK_IDENTIFIER },
+ { "+", TOK_ADD },
+ { "-", TOK_MINUS },
+ { "*", TOK_MULTIPLY },
+ { "/", TOK_DIVIDE },
+ { "( ", TOK_OPEN_P },
+ { " )", TOK_CLOSE_P },
+ { "==", TOK_EQ },
+ { "!=", TOK_NEQ },
+ { "<", TOK_LT },
+ { "<=", TOK_LE },
+ { ">", TOK_GT },
+ { ">=", TOK_GE },
+ { "!", TOK_NOT },
+ { "&&", TOK_LOGICAL_AND },
+ { "||", TOK_LOGICAL_OR },
+ { NULL, 0 }
+};
+
+static const char *prToken(int val)
+{
+ ETTE_t *et;
+
+ for (et = exprTokTable; et->name != NULL; et++) {
+ if (val == et->val)
+ return et->name;
+ }
+ return "???";
+}
+#endif /* DEBUG_PARSER */
+
+/**
+ * @param state expression parser state
+ */
+static int rdToken(ParseState state)
+{
+ int token;
+ Value v = NULL;
+ char *p = state->p;
+
+ /* Skip whitespace before the next token. */
+ while (*p && risspace(*p)) p++;
+
+ switch (*p) {
+ case '\0':
+ token = TOK_EOF;
+ p--;
+ break;
+ case '+':
+ token = TOK_ADD;
+ break;
+ case '-':
+ token = TOK_MINUS;
+ break;
+ case '*':
+ token = TOK_MULTIPLY;
+ break;
+ case '/':
+ token = TOK_DIVIDE;
+ break;
+ case '(':
+ token = TOK_OPEN_P;
+ break;
+ case ')':
+ token = TOK_CLOSE_P;
+ break;
+ case '=':
+ if (p[1] == '=') {
+ token = TOK_EQ;
+ p++;
+ } else {
+ rpmlog(RPMLOG_ERR, _("syntax error while parsing ==\n"));
+ return -1;
+ }
+ break;
+ case '!':
+ if (p[1] == '=') {
+ token = TOK_NEQ;
+ p++;
+ } else
+ token = TOK_NOT;
+ break;
+ case '<':
+ if (p[1] == '=') {
+ token = TOK_LE;
+ p++;
+ } else
+ token = TOK_LT;
+ break;
+ case '>':
+ if (p[1] == '=') {
+ token = TOK_GE;
+ p++;
+ } else
+ token = TOK_GT;
+ break;
+ case '&':
+ if (p[1] == '&') {
+ token = TOK_LOGICAL_AND;
+ p++;
+ } else {
+ rpmlog(RPMLOG_ERR, _("syntax error while parsing &&\n"));
+ return -1;
+ }
+ break;
+ case '|':
+ if (p[1] == '|') {
+ token = TOK_LOGICAL_OR;
+ p++;
+ } else {
+ rpmlog(RPMLOG_ERR, _("syntax error while parsing ||\n"));
+ return -1;
+ }
+ break;
+
+ default:
+ if (risdigit(*p)) {
+ char *temp;
+ size_t ts;
+
+ for (ts=1; p[ts] && risdigit(p[ts]); ts++);
+ temp = xmalloc(ts+1);
+ memcpy(temp, p, ts);
+ p += ts-1;
+ temp[ts] = '\0';
+
+ token = TOK_INTEGER;
+ v = valueMakeInteger(atoi(temp));
+ free(temp);
+
+ } else if (risalpha(*p)) {
+ char *temp;
+ size_t ts;
+
+ for (ts=1; p[ts] && (risalnum(p[ts]) || p[ts] == '_'); ts++);
+ temp = xmalloc(ts+1);
+ memcpy(temp, p, ts);
+ p += ts-1;
+ temp[ts] = '\0';
+
+ token = TOK_IDENTIFIER;
+ v = valueMakeString(temp);
+
+ } else if (*p == '\"') {
+ char *temp;
+ size_t ts;
+
+ p++;
+ for (ts=0; p[ts] && p[ts] != '\"'; ts++);
+ temp = xmalloc(ts+1);
+ memcpy(temp, p, ts);
+ p += ts-1;
+ temp[ts] = '\0';
+ p++;
+
+ token = TOK_STRING;
+ v = valueMakeString( rpmExpand(temp, NULL) );
+ free(temp);
+
+ } else {
+ rpmlog(RPMLOG_ERR, _("parse error in expression\n"));
+ return -1;
+ }
+ }
+
+ state->p = p + 1;
+ state->nextToken = token;
+ state->tokenValue = v;
+
+ DEBUG(printf("rdToken: \"%s\" (%d)\n", prToken(token), token));
+ DEBUG(valueDump("rdToken:", state->tokenValue, stdout));
+
+ return 0;
+}
+
+static Value doLogical(ParseState state);
+
+/**
+ * @param state expression parser state
+ */
+static Value doPrimary(ParseState state)
+{
+ Value v;
+
+ DEBUG(printf("doPrimary()\n"));
+
+ switch (state->nextToken) {
+ case TOK_OPEN_P:
+ if (rdToken(state))
+ return NULL;
+ v = doLogical(state);
+ if (state->nextToken != TOK_CLOSE_P) {
+ rpmlog(RPMLOG_ERR, _("unmatched (\n"));
+ return NULL;
+ }
+ if (rdToken(state))
+ return NULL;
+ break;
+
+ case TOK_INTEGER:
+ case TOK_STRING:
+ v = state->tokenValue;
+ if (rdToken(state))
+ return NULL;
+ break;
+
+ case TOK_IDENTIFIER: {
+ const char *name = state->tokenValue->data.s;
+
+ v = valueMakeString( rpmExpand(name, NULL) );
+ if (rdToken(state))
+ return NULL;
+ break;
+ }
+
+ case TOK_MINUS:
+ if (rdToken(state))
+ return NULL;
+
+ v = doPrimary(state);
+ if (v == NULL)
+ return NULL;
+
+ if (! valueIsInteger(v)) {
+ rpmlog(RPMLOG_ERR, _("- only on numbers\n"));
+ return NULL;
+ }
+
+ v = valueMakeInteger(- v->data.i);
+ break;
+
+ case TOK_NOT:
+ if (rdToken(state))
+ return NULL;
+
+ v = doPrimary(state);
+ if (v == NULL)
+ return NULL;
+
+ if (! valueIsInteger(v)) {
+ rpmlog(RPMLOG_ERR, _("! only on numbers\n"));
+ return NULL;
+ }
+
+ v = valueMakeInteger(! v->data.i);
+ break;
+ default:
+ return NULL;
+ break;
+ }
+
+ DEBUG(valueDump("doPrimary:", v, stdout));
+ return v;
+}
+
+/**
+ * @param state expression parser state
+ */
+static Value doMultiplyDivide(ParseState state)
+{
+ Value v1, v2 = NULL;
+
+ DEBUG(printf("doMultiplyDivide()\n"));
+
+ v1 = doPrimary(state);
+ if (v1 == NULL)
+ return NULL;
+
+ while (state->nextToken == TOK_MULTIPLY
+ || state->nextToken == TOK_DIVIDE) {
+ int op = state->nextToken;
+
+ if (rdToken(state))
+ return NULL;
+
+ if (v2) valueFree(v2);
+
+ v2 = doPrimary(state);
+ if (v2 == NULL)
+ return NULL;
+
+ if (! valueSameType(v1, v2)) {
+ rpmlog(RPMLOG_ERR, _("types must match\n"));
+ return NULL;
+ }
+
+ if (valueIsInteger(v1)) {
+ int i1 = v1->data.i, i2 = v2->data.i;
+
+ valueFree(v1);
+ if (op == TOK_MULTIPLY)
+ v1 = valueMakeInteger(i1 * i2);
+ else
+ v1 = valueMakeInteger(i1 / i2);
+ } else {
+ rpmlog(RPMLOG_ERR, _("* / not suported for strings\n"));
+ return NULL;
+ }
+ }
+
+ if (v2) valueFree(v2);
+ return v1;
+}
+
+/**
+ * @param state expression parser state
+ */
+static Value doAddSubtract(ParseState state)
+{
+ Value v1, v2 = NULL;
+
+ DEBUG(printf("doAddSubtract()\n"));
+
+ v1 = doMultiplyDivide(state);
+ if (v1 == NULL)
+ return NULL;
+
+ while (state->nextToken == TOK_ADD || state->nextToken == TOK_MINUS) {
+ int op = state->nextToken;
+
+ if (rdToken(state))
+ return NULL;
+
+ if (v2) valueFree(v2);
+
+ v2 = doMultiplyDivide(state);
+ if (v2 == NULL)
+ return NULL;
+
+ if (! valueSameType(v1, v2)) {
+ rpmlog(RPMLOG_ERR, _("types must match\n"));
+ return NULL;
+ }
+
+ if (valueIsInteger(v1)) {
+ int i1 = v1->data.i, i2 = v2->data.i;
+
+ valueFree(v1);
+ if (op == TOK_ADD)
+ v1 = valueMakeInteger(i1 + i2);
+ else
+ v1 = valueMakeInteger(i1 - i2);
+ } else {
+ char *copy;
+
+ if (op == TOK_MINUS) {
+ rpmlog(RPMLOG_ERR, _("- not suported for strings\n"));
+ return NULL;
+ }
+
+ copy = xmalloc(strlen(v1->data.s) + strlen(v2->data.s) + 1);
+ (void) stpcpy( stpcpy(copy, v1->data.s), v2->data.s);
+
+ valueFree(v1);
+ v1 = valueMakeString(copy);
+ }
+ }
+
+ if (v2) valueFree(v2);
+ return v1;
+}
+
+/**
+ * @param state expression parser state
+ */
+static Value doRelational(ParseState state)
+{
+ Value v1, v2 = NULL;
+
+ DEBUG(printf("doRelational()\n"));
+
+ v1 = doAddSubtract(state);
+ if (v1 == NULL)
+ return NULL;
+
+ while (state->nextToken >= TOK_EQ && state->nextToken <= TOK_GE) {
+ int op = state->nextToken;
+
+ if (rdToken(state))
+ return NULL;
+
+ if (v2) valueFree(v2);
+
+ v2 = doAddSubtract(state);
+ if (v2 == NULL)
+ return NULL;
+
+ if (! valueSameType(v1, v2)) {
+ rpmlog(RPMLOG_ERR, _("types must match\n"));
+ return NULL;
+ }
+
+ if (valueIsInteger(v1)) {
+ int i1 = v1->data.i, i2 = v2->data.i, r = 0;
+ switch (op) {
+ case TOK_EQ:
+ r = (i1 == i2);
+ break;
+ case TOK_NEQ:
+ r = (i1 != i2);
+ break;
+ case TOK_LT:
+ r = (i1 < i2);
+ break;
+ case TOK_LE:
+ r = (i1 <= i2);
+ break;
+ case TOK_GT:
+ r = (i1 > i2);
+ break;
+ case TOK_GE:
+ r = (i1 >= i2);
+ break;
+ default:
+ break;
+ }
+ valueFree(v1);
+ v1 = valueMakeInteger(r);
+ } else {
+ const char * s1 = v1->data.s;
+ const char * s2 = v2->data.s;
+ int r = 0;
+ switch (op) {
+ case TOK_EQ:
+ r = (strcmp(s1,s2) == 0);
+ break;
+ case TOK_NEQ:
+ r = (strcmp(s1,s2) != 0);
+ break;
+ case TOK_LT:
+ r = (strcmp(s1,s2) < 0);
+ break;
+ case TOK_LE:
+ r = (strcmp(s1,s2) <= 0);
+ break;
+ case TOK_GT:
+ r = (strcmp(s1,s2) > 0);
+ break;
+ case TOK_GE:
+ r = (strcmp(s1,s2) >= 0);
+ break;
+ default:
+ break;
+ }
+ valueFree(v1);
+ v1 = valueMakeInteger(r);
+ }
+ }
+
+ if (v2) valueFree(v2);
+ return v1;
+}
+
+/**
+ * @param state expression parser state
+ */
+static Value doLogical(ParseState state)
+{
+ Value v1, v2 = NULL;
+
+ DEBUG(printf("doLogical()\n"));
+
+ v1 = doRelational(state);
+ if (v1 == NULL)
+ return NULL;
+
+ while (state->nextToken == TOK_LOGICAL_AND
+ || state->nextToken == TOK_LOGICAL_OR) {
+ int op = state->nextToken;
+
+ if (rdToken(state))
+ return NULL;
+
+ if (v2) valueFree(v2);
+
+ v2 = doRelational(state);
+ if (v2 == NULL)
+ return NULL;
+
+ if (! valueSameType(v1, v2)) {
+ rpmlog(RPMLOG_ERR, _("types must match\n"));
+ return NULL;
+ }
+
+ if (valueIsInteger(v1)) {
+ int i1 = v1->data.i, i2 = v2->data.i;
+
+ valueFree(v1);
+ if (op == TOK_LOGICAL_AND)
+ v1 = valueMakeInteger(i1 && i2);
+ else
+ v1 = valueMakeInteger(i1 || i2);
+ } else {
+ rpmlog(RPMLOG_ERR, _("&& and || not suported for strings\n"));
+ return NULL;
+ }
+ }
+
+ if (v2) valueFree(v2);
+ return v1;
+}
+
+int parseExpressionBoolean(rpmSpec spec, const char *expr)
+{
+ struct _parseState state;
+ int result = -1;
+ Value v;
+
+ DEBUG(printf("parseExprBoolean(?, '%s')\n", expr));
+
+ /* Initialize the expression parser state. */
+ state.p = state.str = xstrdup(expr);
+ state.spec = spec;
+ state.nextToken = 0;
+ state.tokenValue = NULL;
+ (void) rdToken(&state);
+
+ /* Parse the expression. */
+ v = doLogical(&state);
+ if (!v) {
+ state.str = _free(state.str);
+ return -1;
+ }
+
+ /* If the next token is not TOK_EOF, we have a syntax error. */
+ if (state.nextToken != TOK_EOF) {
+ rpmlog(RPMLOG_ERR, _("syntax error in expression\n"));
+ state.str = _free(state.str);
+ return -1;
+ }
+
+ DEBUG(valueDump("parseExprBoolean:", v, stdout));
+
+ switch (v->type) {
+ case VALUE_TYPE_INTEGER:
+ result = v->data.i != 0;
+ break;
+ case VALUE_TYPE_STRING:
+ result = v->data.s[0] != '\0';
+ break;
+ default:
+ break;
+ }
+
+ state.str = _free(state.str);
+ valueFree(v);
+ return result;
+}
diff --git a/build/files.c b/build/files.c
new file mode 100644
index 0000000..b4b893a
--- /dev/null
+++ b/build/files.c
@@ -0,0 +1,2171 @@
+/** \ingroup rpmbuild
+ * \file build/files.c
+ * The post-build, pre-packaging file tree walk to assemble the package
+ * manifest.
+ */
+
+#include "system.h"
+
+#define MYALLPERMS 07777
+
+#include <errno.h>
+#include <regex.h>
+#if WITH_CAP
+#include <sys/capability.h>
+#endif
+
+#include <rpm/rpmpgp.h>
+#include <rpm/argv.h>
+#include <rpm/rpmfc.h>
+#include <rpm/rpmfileutil.h> /* rpmDoDigest() */
+#include <rpm/rpmlog.h>
+
+#include "rpmio/rpmio_internal.h" /* XXX rpmioSlurp */
+#include "rpmio/base64.h"
+#include "misc/fts.h"
+#include "lib/cpio.h"
+#include "lib/rpmfi_internal.h" /* XXX fi->apath */
+#include "lib/rpmug.h"
+#include "build/rpmbuild_internal.h"
+#include "build/rpmbuild_misc.h"
+
+#include "debug.h"
+#include <libgen.h>
+
+#define SKIPSPACE(s) { while (*(s) && risspace(*(s))) (s)++; }
+#define SKIPWHITE(_x) {while(*(_x) && (risspace(*_x) || *(_x) == ',')) (_x)++;}
+#define SKIPNONWHITE(_x){while(*(_x) &&!(risspace(*_x) || *(_x) == ',')) (_x)++;}
+
+/**
+ */
+enum specfFlags_e {
+ SPECD_DEFFILEMODE = (1 << 0),
+ SPECD_DEFDIRMODE = (1 << 1),
+ SPECD_DEFUID = (1 << 2),
+ SPECD_DEFGID = (1 << 3),
+ SPECD_DEFVERIFY = (1 << 4),
+
+ SPECD_FILEMODE = (1 << 8),
+ SPECD_DIRMODE = (1 << 9),
+ SPECD_UID = (1 << 10),
+ SPECD_GID = (1 << 11),
+ SPECD_VERIFY = (1 << 12)
+};
+
+typedef rpmFlags specfFlags;
+
+/**
+ */
+typedef struct FileListRec_s {
+ struct stat fl_st;
+#define fl_dev fl_st.st_dev
+#define fl_ino fl_st.st_ino
+#define fl_mode fl_st.st_mode
+#define fl_nlink fl_st.st_nlink
+#define fl_uid fl_st.st_uid
+#define fl_gid fl_st.st_gid
+#define fl_rdev fl_st.st_rdev
+#define fl_size fl_st.st_size
+#define fl_mtime fl_st.st_mtime
+
+ char *diskPath; /* get file from here */
+ char *cpioPath; /* filename in cpio archive */
+ const char *uname;
+ const char *gname;
+ unsigned flags;
+ specfFlags specdFlags; /* which attributes have been explicitly specified. */
+ rpmVerifyFlags verifyFlags;
+ char *langs; /* XXX locales separated with | */
+ char *caps;
+} * FileListRec;
+
+/**
+ */
+typedef struct AttrRec_s {
+ char *ar_fmodestr;
+ char *ar_dmodestr;
+ char *ar_user;
+ char *ar_group;
+ mode_t ar_fmode;
+ mode_t ar_dmode;
+} * AttrRec;
+
+static struct AttrRec_s root_ar = { NULL, NULL, "root", "root", 0, 0 };
+
+/* list of files */
+static StringBuf check_fileList = NULL;
+
+/**
+ * Package file tree walk data.
+ */
+typedef struct FileList_s {
+ char * buildRoot;
+
+ int fileCount;
+ int processingFailed;
+
+ int passedSpecialDoc;
+ int isSpecialDoc;
+
+ int noGlob;
+ unsigned devtype;
+ unsigned devmajor;
+ int devminor;
+
+ int isDir;
+ rpmBuildPkgFlags pkgFlags;
+ rpmfileAttrs currentFlags;
+ specfFlags currentSpecdFlags;
+ rpmVerifyFlags currentVerifyFlags;
+ struct AttrRec_s cur_ar;
+ struct AttrRec_s def_ar;
+ specfFlags defSpecdFlags;
+ rpmVerifyFlags defVerifyFlags;
+ int nLangs;
+ char ** currentLangs;
+ int haveCaps;
+ char *currentCaps;
+ ARGV_t docDirs;
+
+ FileListRec fileList;
+ int fileListRecsAlloced;
+ int fileListRecsUsed;
+ int largeFiles;
+} * FileList;
+
+/**
+ */
+static void nullAttrRec(AttrRec ar)
+{
+ ar->ar_fmodestr = NULL;
+ ar->ar_dmodestr = NULL;
+ ar->ar_user = NULL;
+ ar->ar_group = NULL;
+ ar->ar_fmode = 0;
+ ar->ar_dmode = 0;
+}
+
+/**
+ */
+static void freeAttrRec(AttrRec ar)
+{
+ ar->ar_fmodestr = _free(ar->ar_fmodestr);
+ ar->ar_dmodestr = _free(ar->ar_dmodestr);
+ ar->ar_user = _free(ar->ar_user);
+ ar->ar_group = _free(ar->ar_group);
+ /* XXX doesn't free ar (yet) */
+ return;
+}
+
+/**
+ */
+static void dupAttrRec(const AttrRec oar, AttrRec nar)
+{
+ if (oar == nar)
+ return;
+ freeAttrRec(nar);
+ nar->ar_fmodestr = (oar->ar_fmodestr ? xstrdup(oar->ar_fmodestr) : NULL);
+ nar->ar_dmodestr = (oar->ar_dmodestr ? xstrdup(oar->ar_dmodestr) : NULL);
+ nar->ar_user = (oar->ar_user ? xstrdup(oar->ar_user) : NULL);
+ nar->ar_group = (oar->ar_group ? xstrdup(oar->ar_group) : NULL);
+ nar->ar_fmode = oar->ar_fmode;
+ nar->ar_dmode = oar->ar_dmode;
+}
+
+#if 0
+/**
+ */
+static void dumpAttrRec(const char * msg, AttrRec ar)
+{
+ if (msg)
+ fprintf(stderr, "%s:\t", msg);
+ fprintf(stderr, "(%s, %s, %s, %s)\n",
+ ar->ar_fmodestr,
+ ar->ar_user,
+ ar->ar_group,
+ ar->ar_dmodestr);
+}
+#endif
+
+/**
+ * strtokWithQuotes.
+ * @param s
+ * @param delim
+ */
+static char *strtokWithQuotes(char *s, const char *delim)
+{
+ static char *olds = NULL;
+ char *token;
+
+ if (s == NULL)
+ s = olds;
+ if (s == NULL)
+ return NULL;
+
+ /* Skip leading delimiters */
+ s += strspn(s, delim);
+ if (*s == '\0')
+ return NULL;
+
+ /* Find the end of the token. */
+ token = s;
+ if (*token == '"') {
+ token++;
+ /* Find next " char */
+ s = strchr(token, '"');
+ } else {
+ s = strpbrk(token, delim);
+ }
+
+ /* Terminate it */
+ if (s == NULL) {
+ /* This token finishes the string */
+ olds = strchr(token, '\0');
+ } else {
+ /* Terminate the token and make olds point past it */
+ *s = '\0';
+ olds = s+1;
+ }
+
+ return token;
+}
+
+/**
+ */
+typedef const struct VFA {
+ const char * attribute;
+ int neg; /* XXX unused */
+ int flag;
+} VFA_t;
+
+/**
+ */
+static VFA_t const verifyAttrs[] = {
+ { "md5", 0, RPMVERIFY_FILEDIGEST },
+ { "filedigest", 0, RPMVERIFY_FILEDIGEST },
+ { "size", 0, RPMVERIFY_FILESIZE },
+ { "link", 0, RPMVERIFY_LINKTO },
+ { "user", 0, RPMVERIFY_USER },
+ { "group", 0, RPMVERIFY_GROUP },
+ { "mtime", 0, RPMVERIFY_MTIME },
+ { "mode", 0, RPMVERIFY_MODE },
+ { "rdev", 0, RPMVERIFY_RDEV },
+ { "caps", 0, RPMVERIFY_CAPS },
+ { NULL, 0, 0 }
+};
+
+/**
+ * Parse %verify and %defverify from file manifest.
+ * @param buf current spec file line
+ * @param fl package file tree walk data
+ * @return RPMRC_OK on success
+ */
+static rpmRC parseForVerify(char * buf, FileList fl)
+{
+ char *p, *pe, *q = NULL;
+ const char *name;
+ rpmVerifyFlags *resultVerify;
+ int negated;
+ rpmVerifyFlags verifyFlags;
+ specfFlags * specdFlags;
+ rpmRC rc = RPMRC_FAIL;
+
+ if ((p = strstr(buf, (name = "%verify"))) != NULL) {
+ resultVerify = &(fl->currentVerifyFlags);
+ specdFlags = &fl->currentSpecdFlags;
+ } else if ((p = strstr(buf, (name = "%defverify"))) != NULL) {
+ resultVerify = &(fl->defVerifyFlags);
+ specdFlags = &fl->defSpecdFlags;
+ } else
+ return RPMRC_OK;
+
+ for (pe = p; (pe-p) < strlen(name); pe++)
+ *pe = ' ';
+
+ SKIPSPACE(pe);
+
+ if (*pe != '(') {
+ rpmlog(RPMLOG_ERR, _("Missing '(' in %s %s\n"), name, pe);
+ goto exit;
+ }
+
+ /* Bracket %*verify args */
+ *pe++ = ' ';
+ for (p = pe; *pe && *pe != ')'; pe++)
+ {};
+
+ if (*pe == '\0') {
+ rpmlog(RPMLOG_ERR, _("Missing ')' in %s(%s\n"), name, p);
+ goto exit;
+ }
+
+ /* Localize. Erase parsed string */
+ q = xmalloc((pe-p) + 1);
+ rstrlcpy(q, p, (pe-p) + 1);
+ while (p <= pe)
+ *p++ = ' ';
+
+ negated = 0;
+ verifyFlags = RPMVERIFY_NONE;
+
+ for (p = q; *p != '\0'; p = pe) {
+ SKIPWHITE(p);
+ if (*p == '\0')
+ break;
+ pe = p;
+ SKIPNONWHITE(pe);
+ if (*pe != '\0')
+ *pe++ = '\0';
+
+ { VFA_t *vfa;
+ for (vfa = verifyAttrs; vfa->attribute != NULL; vfa++) {
+ if (!rstreq(p, vfa->attribute))
+ continue;
+ verifyFlags |= vfa->flag;
+ break;
+ }
+ if (vfa->attribute)
+ continue;
+ }
+
+ if (rstreq(p, "not")) {
+ negated ^= 1;
+ } else {
+ rpmlog(RPMLOG_ERR, _("Invalid %s token: %s\n"), name, p);
+ goto exit;
+ }
+ }
+
+ *resultVerify = negated ? ~(verifyFlags) : verifyFlags;
+ *specdFlags |= SPECD_VERIFY;
+ rc = RPMRC_OK;
+
+exit:
+ free(q);
+ if (rc != RPMRC_OK) {
+ fl->processingFailed = 1;
+ }
+
+ return rc;
+}
+
+#define isAttrDefault(_ars) ((_ars)[0] == '-' && (_ars)[1] == '\0')
+
+/**
+ * Parse %dev from file manifest.
+ * @param buf current spec file line
+ * @param fl package file tree walk data
+ * @return RPMRC_OK on success
+ */
+static rpmRC parseForDev(char * buf, FileList fl)
+{
+ const char * name;
+ const char * errstr = NULL;
+ char *p, *pe, *q = NULL;
+ rpmRC rc = RPMRC_FAIL; /* assume error */
+
+ if ((p = strstr(buf, (name = "%dev"))) == NULL)
+ return RPMRC_OK;
+
+ for (pe = p; (pe-p) < strlen(name); pe++)
+ *pe = ' ';
+ SKIPSPACE(pe);
+
+ if (*pe != '(') {
+ errstr = "'('";
+ goto exit;
+ }
+
+ /* Bracket %dev args */
+ *pe++ = ' ';
+ for (p = pe; *pe && *pe != ')'; pe++)
+ {};
+ if (*pe != ')') {
+ errstr = "')'";
+ goto exit;
+ }
+
+ /* Localize. Erase parsed string */
+ q = xmalloc((pe-p) + 1);
+ rstrlcpy(q, p, (pe-p) + 1);
+ while (p <= pe)
+ *p++ = ' ';
+
+ p = q; SKIPWHITE(p);
+ pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
+ if (*p == 'b')
+ fl->devtype = 'b';
+ else if (*p == 'c')
+ fl->devtype = 'c';
+ else {
+ errstr = "devtype";
+ goto exit;
+ }
+
+ p = pe; SKIPWHITE(p);
+ pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
+ for (pe = p; *pe && risdigit(*pe); pe++)
+ {} ;
+ if (*pe == '\0') {
+ fl->devmajor = atoi(p);
+ if (!(fl->devmajor >= 0 && fl->devmajor < 256)) {
+ errstr = "devmajor";
+ goto exit;
+ }
+ pe++;
+ } else {
+ errstr = "devmajor";
+ goto exit;
+ }
+
+ p = pe; SKIPWHITE(p);
+ pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
+ for (pe = p; *pe && risdigit(*pe); pe++)
+ {} ;
+ if (*pe == '\0') {
+ fl->devminor = atoi(p);
+ if (!(fl->devminor >= 0 && fl->devminor < 256)) {
+ errstr = "devminor";
+ goto exit;
+ }
+ pe++;
+ } else {
+ errstr = "devminor";
+ goto exit;
+ }
+
+ fl->noGlob = 1;
+
+ rc = RPMRC_OK;
+
+exit:
+ if (rc) {
+ rpmlog(RPMLOG_ERR, _("Missing %s in %s %s\n"), errstr, name, p);
+ fl->processingFailed = 1;
+ }
+ free(q);
+ return rc;
+}
+
+/**
+ * Parse %attr and %defattr from file manifest.
+ * @param buf current spec file line
+ * @param fl package file tree walk data
+ * @return 0 on success
+ */
+static rpmRC parseForAttr(char * buf, FileList fl)
+{
+ const char *name;
+ char *p, *pe, *q = NULL;
+ int x;
+ struct AttrRec_s arbuf;
+ AttrRec ar = &arbuf, ret_ar;
+ specfFlags * specdFlags;
+ rpmRC rc = RPMRC_FAIL;
+
+ if ((p = strstr(buf, (name = "%attr"))) != NULL) {
+ ret_ar = &(fl->cur_ar);
+ specdFlags = &fl->currentSpecdFlags;
+ } else if ((p = strstr(buf, (name = "%defattr"))) != NULL) {
+ ret_ar = &(fl->def_ar);
+ specdFlags = &fl->defSpecdFlags;
+ } else
+ return RPMRC_OK;
+
+ for (pe = p; (pe-p) < strlen(name); pe++)
+ *pe = ' ';
+
+ SKIPSPACE(pe);
+
+ if (*pe != '(') {
+ rpmlog(RPMLOG_ERR, _("Missing '(' in %s %s\n"), name, pe);
+ goto exit;
+ }
+
+ /* Bracket %*attr args */
+ *pe++ = ' ';
+ for (p = pe; *pe && *pe != ')'; pe++)
+ {};
+
+ if (ret_ar == &(fl->def_ar)) { /* %defattr */
+ char *r = pe;
+ r++;
+ SKIPSPACE(r);
+ if (*r != '\0') {
+ rpmlog(RPMLOG_ERR,
+ _("Non-white space follows %s(): %s\n"), name, r);
+ goto exit;
+ }
+ }
+
+ /* Localize. Erase parsed string */
+ q = xmalloc((pe-p) + 1);
+ rstrlcpy(q, p, (pe-p) + 1);
+ while (p <= pe)
+ *p++ = ' ';
+
+ nullAttrRec(ar);
+
+ p = q; SKIPWHITE(p);
+ if (*p != '\0') {
+ pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
+ ar->ar_fmodestr = p;
+ p = pe; SKIPWHITE(p);
+ }
+ if (*p != '\0') {
+ pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
+ ar->ar_user = p;
+ p = pe; SKIPWHITE(p);
+ }
+ if (*p != '\0') {
+ pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
+ ar->ar_group = p;
+ p = pe; SKIPWHITE(p);
+ }
+ if (*p != '\0' && ret_ar == &(fl->def_ar)) { /* %defattr */
+ pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
+ ar->ar_dmodestr = p;
+ p = pe; SKIPWHITE(p);
+ }
+
+ if (!(ar->ar_fmodestr && ar->ar_user && ar->ar_group) || *p != '\0') {
+ rpmlog(RPMLOG_ERR, _("Bad syntax: %s(%s)\n"), name, q);
+ goto exit;
+ }
+
+ /* Do a quick test on the mode argument and adjust for "-" */
+ if (ar->ar_fmodestr && !isAttrDefault(ar->ar_fmodestr)) {
+ unsigned int ui;
+ x = sscanf(ar->ar_fmodestr, "%o", &ui);
+ if ((x == 0) || (ar->ar_fmode & ~MYALLPERMS)) {
+ rpmlog(RPMLOG_ERR, _("Bad mode spec: %s(%s)\n"), name, q);
+ goto exit;
+ }
+ ar->ar_fmode = ui;
+ } else {
+ ar->ar_fmodestr = NULL;
+ }
+
+ if (ar->ar_dmodestr && !isAttrDefault(ar->ar_dmodestr)) {
+ unsigned int ui;
+ x = sscanf(ar->ar_dmodestr, "%o", &ui);
+ if ((x == 0) || (ar->ar_dmode & ~MYALLPERMS)) {
+ rpmlog(RPMLOG_ERR, _("Bad dirmode spec: %s(%s)\n"), name, q);
+ goto exit;
+ }
+ ar->ar_dmode = ui;
+ } else {
+ ar->ar_dmodestr = NULL;
+ }
+
+ if (!(ar->ar_user && !isAttrDefault(ar->ar_user))) {
+ ar->ar_user = NULL;
+ }
+
+ if (!(ar->ar_group && !isAttrDefault(ar->ar_group))) {
+ ar->ar_group = NULL;
+ }
+
+ dupAttrRec(ar, ret_ar);
+
+ /* XXX fix all this */
+ *specdFlags |= SPECD_UID | SPECD_GID | SPECD_FILEMODE | SPECD_DIRMODE;
+ rc = RPMRC_OK;
+
+exit:
+ free(q);
+ if (rc != RPMRC_OK) {
+ fl->processingFailed = 1;
+ }
+
+ return rc;
+}
+
+/**
+ * Parse %config from file manifest.
+ * @param buf current spec file line
+ * @param fl package file tree walk data
+ * @return RPMRC_OK on success
+ */
+static rpmRC parseForConfig(char * buf, FileList fl)
+{
+ char *p, *pe, *q = NULL;
+ const char *name;
+ rpmRC rc = RPMRC_FAIL;
+
+ if ((p = strstr(buf, (name = "%config"))) == NULL)
+ return RPMRC_OK;
+
+ fl->currentFlags |= RPMFILE_CONFIG;
+
+ /* Erase "%config" token. */
+ for (pe = p; (pe-p) < strlen(name); pe++)
+ *pe = ' ';
+ SKIPSPACE(pe);
+ if (*pe != '(')
+ return RPMRC_OK;
+
+ /* Bracket %config args */
+ *pe++ = ' ';
+ for (p = pe; *pe && *pe != ')'; pe++)
+ {};
+
+ if (*pe == '\0') {
+ rpmlog(RPMLOG_ERR, _("Missing ')' in %s(%s\n"), name, p);
+ goto exit;
+ }
+
+ /* Localize. Erase parsed string. */
+ q = xmalloc((pe-p) + 1);
+ rstrlcpy(q, p, (pe-p) + 1);
+ while (p <= pe)
+ *p++ = ' ';
+
+ for (p = q; *p != '\0'; p = pe) {
+ SKIPWHITE(p);
+ if (*p == '\0')
+ break;
+ pe = p;
+ SKIPNONWHITE(pe);
+ if (*pe != '\0')
+ *pe++ = '\0';
+ if (rstreq(p, "missingok")) {
+ fl->currentFlags |= RPMFILE_MISSINGOK;
+ } else if (rstreq(p, "noreplace")) {
+ fl->currentFlags |= RPMFILE_NOREPLACE;
+ } else {
+ rpmlog(RPMLOG_ERR, _("Invalid %s token: %s\n"), name, p);
+ goto exit;
+ }
+ }
+ rc = RPMRC_OK;
+
+exit:
+ free(q);
+ if (rc != RPMRC_OK) {
+ fl->processingFailed = 1;
+ }
+
+ return rc;
+}
+
+/**
+ */
+static int langCmp(const void * ap, const void * bp)
+{
+ return strcmp(*(const char **)ap, *(const char **)bp);
+}
+
+/**
+ * Parse %lang from file manifest.
+ * @param buf current spec file line
+ * @param fl package file tree walk data
+ * @return RPMRC_OK on success
+ */
+static rpmRC parseForLang(char * buf, FileList fl)
+{
+ char *p, *pe, *q = NULL;
+ const char *name;
+ rpmRC rc = RPMRC_FAIL;
+
+ while ((p = strstr(buf, (name = "%lang"))) != NULL) {
+
+ for (pe = p; (pe-p) < strlen(name); pe++)
+ *pe = ' ';
+ SKIPSPACE(pe);
+
+ if (*pe != '(') {
+ rpmlog(RPMLOG_ERR, _("Missing '(' in %s %s\n"), name, pe);
+ goto exit;
+ }
+
+ /* Bracket %lang args */
+ *pe++ = ' ';
+ for (pe = p; *pe && *pe != ')'; pe++)
+ {};
+
+ if (*pe == '\0') {
+ rpmlog(RPMLOG_ERR, _("Missing ')' in %s(%s\n"), name, p);
+ goto exit;
+ }
+
+ /* Localize. Erase parsed string. */
+ q = xmalloc((pe-p) + 1);
+ rstrlcpy(q, p, (pe-p) + 1);
+ while (p <= pe)
+ *p++ = ' ';
+
+ /* Parse multiple arguments from %lang */
+ for (p = q; *p != '\0'; p = pe) {
+ char *newp;
+ size_t np;
+ int i;
+
+ SKIPWHITE(p);
+ pe = p;
+ SKIPNONWHITE(pe);
+
+ np = pe - p;
+
+ /* Sanity check on locale lengths */
+ if (np < 1 || (np == 1 && *p != 'C') || np >= 32) {
+ rpmlog(RPMLOG_ERR,
+ _("Unusual locale length: \"%.*s\" in %%lang(%s)\n"),
+ (int)np, p, q);
+ goto exit;
+ }
+
+ /* Check for duplicate locales */
+ if (fl->currentLangs != NULL)
+ for (i = 0; i < fl->nLangs; i++) {
+ if (!rstreqn(fl->currentLangs[i], p, np))
+ continue;
+ rpmlog(RPMLOG_ERR, _("Duplicate locale %.*s in %%lang(%s)\n"),
+ (int)np, p, q);
+ goto exit;
+ }
+
+ /* Add new locale */
+ fl->currentLangs = xrealloc(fl->currentLangs,
+ (fl->nLangs + 1) * sizeof(*fl->currentLangs));
+ newp = xmalloc( np+1 );
+ rstrlcpy(newp, p, np + 1);
+ fl->currentLangs[fl->nLangs++] = newp;
+ if (*pe == ',') pe++; /* skip , if present */
+ }
+ }
+
+ /* Insure that locales are sorted. */
+ if (fl->currentLangs)
+ qsort(fl->currentLangs, fl->nLangs, sizeof(*fl->currentLangs), langCmp);
+ rc = RPMRC_OK;
+
+exit:
+ free(q);
+ if (rc != RPMRC_OK) {
+ fl->processingFailed = 1;
+ }
+
+ return rc;
+}
+
+/**
+ * Parse %caps from file manifest.
+ * @param buf current spec file line
+ * @param fl package file tree walk data
+ * @return RPMRC_OK on success
+ */
+static rpmRC parseForCaps(char * buf, FileList fl)
+{
+ char *p, *pe, *q = NULL;
+ const char *name;
+ rpmRC rc = RPMRC_FAIL;
+
+ if ((p = strstr(buf, (name = "%caps"))) == NULL)
+ return RPMRC_OK;
+
+ /* Erase "%caps" token. */
+ for (pe = p; (pe-p) < strlen(name); pe++)
+ *pe = ' ';
+ SKIPSPACE(pe);
+ if (*pe != '(')
+ return RPMRC_OK;
+
+ /* Bracket %caps args */
+ *pe++ = ' ';
+ for (p = pe; *pe && *pe != ')'; pe++)
+ {};
+
+ if (*pe == '\0') {
+ rpmlog(RPMLOG_ERR, _("Missing ')' in %s(%s\n"), name, p);
+ goto exit;
+ }
+
+ /* Localize. Erase parsed string. */
+ q = xmalloc((pe-p) + 1);
+ rstrlcpy(q, p, (pe-p) + 1);
+ while (p <= pe)
+ *p++ = ' ';
+
+#if WITH_CAP
+ {
+ char *captxt = NULL;
+ cap_t fcaps = cap_from_text(q);
+ if (fcaps == NULL) {
+ rpmlog(RPMLOG_ERR, _("Invalid capability: %s\n"), q);
+ goto exit;
+ }
+ /* run our string through cap_to_text() to get libcap presentation */
+ captxt = cap_to_text(fcaps, NULL);
+ fl->currentCaps = xstrdup(captxt);
+ fl->haveCaps = 1;
+ cap_free(captxt);
+ cap_free(fcaps);
+ }
+#else
+ rpmlog(RPMLOG_ERR, _("File capability support not built in\n"));
+ goto exit;
+#endif
+
+ rc = RPMRC_OK;
+
+exit:
+ free(q);
+ if (rc != RPMRC_OK) {
+ fl->processingFailed = 1;
+ }
+
+ return rc;
+}
+/**
+ */
+static VFA_t virtualFileAttributes[] = {
+ { "%dir", 0, 0 }, /* XXX why not RPMFILE_DIR? */
+ { "%doc", 0, RPMFILE_DOC },
+ { "%ghost", 0, RPMFILE_GHOST },
+ { "%exclude", 0, RPMFILE_EXCLUDE },
+ { "%readme", 0, RPMFILE_README },
+ { "%license", 0, RPMFILE_LICENSE },
+ { "%pubkey", 0, RPMFILE_PUBKEY },
+ { NULL, 0, 0 }
+};
+
+/**
+ * Parse simple attributes (e.g. %dir) from file manifest.
+ * @param spec
+ * @param pkg
+ * @param buf current spec file line
+ * @param fl package file tree walk data
+ * @retval *fileName file name
+ * @return RPMRC_OK on success
+ */
+static rpmRC parseForSimple(rpmSpec spec, Package pkg, char * buf,
+ FileList fl, const char ** fileName)
+{
+ char *s, *t;
+ rpmRC res;
+ char *specialDocBuf = NULL;
+
+ *fileName = NULL;
+ res = RPMRC_OK;
+
+ t = buf;
+ while ((s = strtokWithQuotes(t, " \t\n")) != NULL) {
+ VFA_t *vfa;
+ t = NULL;
+ if (rstreq(s, "%docdir")) {
+ s = strtokWithQuotes(NULL, " \t\n");
+
+ if (s == NULL || strtokWithQuotes(NULL, " \t\n")) {
+ rpmlog(RPMLOG_ERR, _("Only one arg for %%docdir\n"));
+ res = RPMRC_FAIL;
+ } else {
+ argvAdd(&(fl->docDirs), s);
+ }
+ break;
+ }
+
+ /* Set flags for virtual file attributes */
+ for (vfa = virtualFileAttributes; vfa->attribute != NULL; vfa++) {
+ if (!rstreq(s, vfa->attribute))
+ continue;
+ if (!vfa->flag) {
+ if (rstreq(s, "%dir"))
+ fl->isDir = 1; /* XXX why not RPMFILE_DIR? */
+ } else {
+ if (vfa->neg)
+ fl->currentFlags &= ~vfa->flag;
+ else
+ fl->currentFlags |= vfa->flag;
+ }
+ break;
+ }
+ /* if we got an attribute, continue with next token */
+ if (vfa->attribute != NULL)
+ continue;
+
+ if (*fileName) {
+ /* We already got a file -- error */
+ rpmlog(RPMLOG_ERR, _("Two files on one line: %s\n"), *fileName);
+ res = RPMRC_FAIL;
+ }
+
+ if (*s != '/') {
+ if (fl->currentFlags & RPMFILE_DOC) {
+ rstrscat(&specialDocBuf, " ", s, NULL);
+ } else
+ if (fl->currentFlags & RPMFILE_PUBKEY)
+ {
+ *fileName = s;
+ } else {
+ /* not in %doc, does not begin with / -- error */
+ rpmlog(RPMLOG_ERR, _("File must begin with \"/\": %s\n"), s);
+ res = RPMRC_FAIL;
+ }
+ } else {
+ *fileName = s;
+ }
+ }
+
+ if (specialDocBuf) {
+ if (*fileName || (fl->currentFlags & ~(RPMFILE_DOC))) {
+ rpmlog(RPMLOG_ERR,
+ _("Can't mix special %%doc with other forms: %s\n"),
+ (*fileName ? *fileName : ""));
+ res = RPMRC_FAIL;
+ } else {
+ /* XXX FIXME: this is easy to do as macro expansion */
+ if (! fl->passedSpecialDoc) {
+ char *mkdocdir = rpmExpand("%{__mkdir_p} $DOCDIR", NULL);
+ pkg->specialDoc = newStringBuf();
+ appendStringBuf(pkg->specialDoc, "DOCDIR=$RPM_BUILD_ROOT");
+ appendLineStringBuf(pkg->specialDoc, pkg->specialDocDir);
+ appendLineStringBuf(pkg->specialDoc, "export DOCDIR");
+ appendLineStringBuf(pkg->specialDoc, mkdocdir);
+ free(mkdocdir);
+
+ *fileName = pkg->specialDocDir;
+ fl->passedSpecialDoc = 1;
+ fl->isSpecialDoc = 1;
+ }
+
+ appendStringBuf(pkg->specialDoc, "cp -pr ");
+ appendStringBuf(pkg->specialDoc, specialDocBuf);
+ appendLineStringBuf(pkg->specialDoc, " $DOCDIR");
+ }
+ free(specialDocBuf);
+ }
+
+ if (res != RPMRC_OK) {
+ fl->processingFailed = 1;
+ }
+
+ return res;
+}
+
+/**
+ */
+static int compareFileListRecs(const void * ap, const void * bp)
+{
+ const char *a = ((FileListRec)ap)->cpioPath;
+ const char *b = ((FileListRec)bp)->cpioPath;
+ return strcmp(a, b);
+}
+
+/**
+ * Test if file is located in a %docdir.
+ * @param fl package file tree walk data
+ * @param fileName file path
+ * @return 1 if doc file, 0 if not
+ */
+static int isDoc(FileList fl, const char * fileName)
+{
+ size_t k, l;
+ ARGV_const_t dd;
+
+ k = strlen(fileName);
+ for (dd = fl->docDirs; *dd; dd++) {
+ l = strlen(*dd);
+ if (l < k && rstreqn(fileName, *dd, l) && fileName[l] == '/')
+ return 1;
+ }
+ return 0;
+}
+
+static int isHardLink(FileListRec flp, FileListRec tlp)
+{
+ return ((S_ISREG(flp->fl_mode) && S_ISREG(tlp->fl_mode)) &&
+ ((flp->fl_nlink > 1) && (flp->fl_nlink == tlp->fl_nlink)) &&
+ (flp->fl_ino == tlp->fl_ino) &&
+ (flp->fl_dev == tlp->fl_dev));
+}
+
+/**
+ * Verify that file attributes scope over hardlinks correctly.
+ * If partial hardlink sets are possible, then add tracking dependency.
+ * @param fl package file tree walk data
+ * @return 1 if partial hardlink sets can exist, 0 otherwise.
+ */
+static int checkHardLinks(FileList fl)
+{
+ FileListRec ilp, jlp;
+ int i, j;
+
+ for (i = 0; i < fl->fileListRecsUsed; i++) {
+ ilp = fl->fileList + i;
+ if (!(S_ISREG(ilp->fl_mode) && ilp->fl_nlink > 1))
+ continue;
+
+ for (j = i + 1; j < fl->fileListRecsUsed; j++) {
+ jlp = fl->fileList + j;
+ if (isHardLink(ilp, jlp)) {
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+static int seenHardLink(FileList fl, FileListRec flp)
+{
+ for (FileListRec ilp = fl->fileList; ilp < flp; ilp++) {
+ if (isHardLink(flp, ilp)) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/**
+ * Add file entries to header.
+ * @todo Should directories have %doc/%config attributes? (#14531)
+ * @todo Remove RPMTAG_OLDFILENAMES, add dirname/basename instead.
+ * @param fl package file tree walk data
+ * @retval *fip file info for package
+ * @param h
+ * @param isSrc
+ */
+static void genCpioListAndHeader(FileList fl,
+ rpmfi * fip, Header h, int isSrc)
+{
+ int _addDotSlash = !(isSrc || rpmExpandNumeric("%{_noPayloadPrefix}"));
+ size_t apathlen = 0;
+ size_t dpathlen = 0;
+ size_t skipLen = 0;
+ FileListRec flp;
+ char buf[BUFSIZ];
+ int i;
+ uint32_t defaultalgo = PGPHASHALGO_MD5, digestalgo;
+ rpm_loff_t totalFileSize = 0;
+
+ /*
+ * See if non-md5 file digest algorithm is requested. If not
+ * specified, quietly assume md5. Otherwise check if supported type.
+ */
+ digestalgo = rpmExpandNumeric(isSrc ? "%{_source_filedigest_algorithm}" :
+ "%{_binary_filedigest_algorithm}");
+ if (digestalgo == 0) {
+ digestalgo = defaultalgo;
+ }
+
+ if (rpmDigestLength(digestalgo) == 0) {
+ rpmlog(RPMLOG_WARNING,
+ _("Unknown file digest algorithm %u, falling back to MD5\n"),
+ digestalgo);
+ digestalgo = defaultalgo;
+ }
+
+ /* Sort the big list */
+ qsort(fl->fileList, fl->fileListRecsUsed,
+ sizeof(*(fl->fileList)), compareFileListRecs);
+
+ /* Generate the header. */
+ if (! isSrc) {
+ skipLen = 1;
+ }
+
+ for (i = 0, flp = fl->fileList; i < fl->fileListRecsUsed; i++, flp++) {
+ /* Merge duplicate entries. */
+ while (i < (fl->fileListRecsUsed - 1) &&
+ rstreq(flp->cpioPath, flp[1].cpioPath)) {
+
+ /* Two entries for the same file found, merge the entries. */
+ /* Note that an %exclude is a duplication of a file reference */
+
+ /* file flags */
+ flp[1].flags |= flp->flags;
+
+ if (!(flp[1].flags & RPMFILE_EXCLUDE))
+ rpmlog(RPMLOG_WARNING, _("File listed twice: %s\n"),
+ flp->cpioPath);
+
+ /* file mode */
+ if (S_ISDIR(flp->fl_mode)) {
+ if ((flp[1].specdFlags & (SPECD_DIRMODE | SPECD_DEFDIRMODE)) <
+ (flp->specdFlags & (SPECD_DIRMODE | SPECD_DEFDIRMODE)))
+ flp[1].fl_mode = flp->fl_mode;
+ } else {
+ if ((flp[1].specdFlags & (SPECD_FILEMODE | SPECD_DEFFILEMODE)) <
+ (flp->specdFlags & (SPECD_FILEMODE | SPECD_DEFFILEMODE)))
+ flp[1].fl_mode = flp->fl_mode;
+ }
+
+ /* uid */
+ if ((flp[1].specdFlags & (SPECD_UID | SPECD_DEFUID)) <
+ (flp->specdFlags & (SPECD_UID | SPECD_DEFUID)))
+ {
+ flp[1].fl_uid = flp->fl_uid;
+ flp[1].uname = flp->uname;
+ }
+
+ /* gid */
+ if ((flp[1].specdFlags & (SPECD_GID | SPECD_DEFGID)) <
+ (flp->specdFlags & (SPECD_GID | SPECD_DEFGID)))
+ {
+ flp[1].fl_gid = flp->fl_gid;
+ flp[1].gname = flp->gname;
+ }
+
+ /* verify flags */
+ if ((flp[1].specdFlags & (SPECD_VERIFY | SPECD_DEFVERIFY)) <
+ (flp->specdFlags & (SPECD_VERIFY | SPECD_DEFVERIFY)))
+ flp[1].verifyFlags = flp->verifyFlags;
+
+ /* XXX to-do: language */
+
+ flp++; i++;
+ }
+
+ /* Skip files that were marked with %exclude. */
+ if (flp->flags & RPMFILE_EXCLUDE) continue;
+
+ /* Omit '/' and/or URL prefix, leave room for "./" prefix */
+ apathlen += (strlen(flp->cpioPath) - skipLen + (_addDotSlash ? 3 : 1));
+
+ /* Leave room for both dirname and basename NUL's */
+ dpathlen += (strlen(flp->diskPath) + 2);
+
+ /*
+ * Make the header. Store the on-disk path to OLDFILENAMES for
+ * cpio list generation purposes for now, final path temporarily
+ * to ORIGFILENAMES, to be swapped later into OLDFILENAMES.
+ */
+ headerPutString(h, RPMTAG_OLDFILENAMES, flp->diskPath);
+ headerPutString(h, RPMTAG_ORIGFILENAMES, flp->cpioPath);
+ headerPutString(h, RPMTAG_FILEUSERNAME, flp->uname);
+ headerPutString(h, RPMTAG_FILEGROUPNAME, flp->gname);
+
+ /*
+ * Only use 64bit filesizes if file sizes require it.
+ * This is basically no-op for now as we error out in addFile() if
+ * any individual file size exceeds the cpio limit.
+ */
+ if (fl->largeFiles) {
+ rpm_loff_t rsize64 = (rpm_loff_t)flp->fl_size;
+ headerPutUint64(h, RPMTAG_LONGFILESIZES, &rsize64, 1);
+ /* XXX TODO: add rpmlib() dependency for large files */
+ } else {
+ rpm_off_t rsize32 = (rpm_off_t)flp->fl_size;
+ headerPutUint32(h, RPMTAG_FILESIZES, &rsize32, 1);
+ }
+ /* Excludes and dupes have been filtered out by now. */
+ if (S_ISREG(flp->fl_mode)) {
+ if (flp->fl_nlink == 1 || !seenHardLink(fl, flp)) {
+ totalFileSize += flp->fl_size;
+ }
+ }
+
+ /*
+ * For items whose size varies between systems, always explicitly
+ * cast to the header type before inserting.
+ * TODO: check and warn if header type overflows for each case.
+ */
+ { rpm_time_t rtime = (rpm_time_t) flp->fl_mtime;
+ headerPutUint32(h, RPMTAG_FILEMTIMES, &rtime, 1);
+ }
+
+ { rpm_mode_t rmode = (rpm_mode_t) flp->fl_mode;
+ headerPutUint16(h, RPMTAG_FILEMODES, &rmode, 1);
+ }
+
+ { rpm_rdev_t rrdev = (rpm_rdev_t) flp->fl_rdev;
+ headerPutUint16(h, RPMTAG_FILERDEVS, &rrdev, 1);
+ }
+
+ { rpm_dev_t rdev = (rpm_dev_t) flp->fl_dev;
+ headerPutUint32(h, RPMTAG_FILEDEVICES, &rdev, 1);
+ }
+
+ { rpm_ino_t rino = (rpm_ino_t) flp->fl_ino;
+ headerPutUint32(h, RPMTAG_FILEINODES, &rino, 1);
+ }
+
+ headerPutString(h, RPMTAG_FILELANGS, flp->langs);
+
+ if (fl->haveCaps) {
+ headerPutString(h, RPMTAG_FILECAPS, flp->caps);
+ }
+
+ buf[0] = '\0';
+ if (S_ISREG(flp->fl_mode))
+ (void) rpmDoDigest(digestalgo, flp->diskPath, 1,
+ (unsigned char *)buf, NULL);
+ headerPutString(h, RPMTAG_FILEDIGESTS, buf);
+
+ buf[0] = '\0';
+ if (S_ISLNK(flp->fl_mode)) {
+ ssize_t llen = readlink(flp->diskPath, buf, BUFSIZ-1);
+ if (llen == -1) {
+ rpmlog(RPMLOG_ERR, _("reading symlink %s failed: %s\n"),
+ flp->diskPath, strerror(errno));
+ fl->processingFailed = 1;
+ } else {
+ buf[llen] = '\0';
+ if (buf[0] == '/' && !rstreq(fl->buildRoot, "/") &&
+ rstreqn(buf, fl->buildRoot, strlen(fl->buildRoot))) {
+ rpmlog(RPMLOG_ERR,
+ _("Symlink points to BuildRoot: %s -> %s\n"),
+ flp->cpioPath, buf);
+ fl->processingFailed = 1;
+ }
+ }
+ }
+ headerPutString(h, RPMTAG_FILELINKTOS, buf);
+
+ if (flp->flags & RPMFILE_GHOST) {
+ flp->verifyFlags &= ~(RPMVERIFY_FILEDIGEST | RPMVERIFY_FILESIZE |
+ RPMVERIFY_LINKTO | RPMVERIFY_MTIME);
+ }
+ headerPutUint32(h, RPMTAG_FILEVERIFYFLAGS, &(flp->verifyFlags),1);
+
+ if (!isSrc && isDoc(fl, flp->cpioPath))
+ flp->flags |= RPMFILE_DOC;
+ /* XXX Should directories have %doc/%config attributes? (#14531) */
+ if (S_ISDIR(flp->fl_mode))
+ flp->flags &= ~(RPMFILE_CONFIG|RPMFILE_DOC);
+
+ headerPutUint32(h, RPMTAG_FILEFLAGS, &(flp->flags) ,1);
+ }
+
+ if (totalFileSize < UINT32_MAX) {
+ rpm_off_t totalsize = totalFileSize;
+ headerPutUint32(h, RPMTAG_SIZE, &totalsize, 1);
+ } else {
+ rpm_loff_t totalsize = totalFileSize;
+ headerPutUint64(h, RPMTAG_LONGSIZE, &totalsize, 1);
+ }
+
+ if (digestalgo != defaultalgo) {
+ headerPutUint32(h, RPMTAG_FILEDIGESTALGO, &digestalgo, 1);
+ rpmlibNeedsFeature(h, "FileDigests", "4.6.0-1");
+ }
+
+ if (fl->haveCaps) {
+ rpmlibNeedsFeature(h, "FileCaps", "4.6.1-1");
+ }
+
+ if (_addDotSlash)
+ (void) rpmlibNeedsFeature(h, "PayloadFilesHavePrefix", "4.0-1");
+
+ {
+ struct rpmtd_s filenames;
+ rpmfiFlags flags = RPMFI_NOHEADER|RPMFI_NOFILEUSER|RPMFI_NOFILEGROUP;
+ rpmfi fi;
+ int fc;
+ const char *fn;
+ char *a, **apath;
+
+ /* rpmfiNew() only groks compressed filelists */
+ headerConvert(h, HEADERCONV_COMPRESSFILELIST);
+ fi = rpmfiNew(NULL, h, RPMTAG_BASENAMES, flags);
+
+ /*
+ * Grab the real filenames from ORIGFILENAMES and put into OLDFILENAMES,
+ * remove temporary cruft and side-effects from filelist compression
+ * for rpmfiNew().
+ */
+ headerGet(h, RPMTAG_ORIGFILENAMES, &filenames, HEADERGET_ALLOC);
+ headerDel(h, RPMTAG_ORIGFILENAMES);
+ headerDel(h, RPMTAG_BASENAMES);
+ headerDel(h, RPMTAG_DIRNAMES);
+ headerDel(h, RPMTAG_DIRINDEXES);
+ rpmtdSetTag(&filenames, RPMTAG_OLDFILENAMES);
+ headerPut(h, &filenames, HEADERPUT_DEFAULT);
+
+ /* Create hge-style archive path array, normally adding "./" */
+ fc = rpmtdCount(&filenames);
+ apath = xmalloc(fc * sizeof(*apath) + apathlen + 1);
+ a = (char *)(apath + fc);
+ *a = '\0';
+ rpmtdInit(&filenames);
+ for (int i = 0; (fn = rpmtdNextString(&filenames)); i++) {
+ apath[i] = a;
+ if (_addDotSlash)
+ a = stpcpy(a, "./");
+ a = stpcpy(a, (fn + skipLen));
+ a++; /* skip apath NUL */
+ }
+ fi->apath = apath;
+ *fip = fi;
+ rpmtdFreeData(&filenames);
+ }
+
+ /* Compress filelist unless legacy format requested */
+ if (!(fl->pkgFlags & RPMBUILD_PKG_NODIRTOKENS)) {
+ headerConvert(h, HEADERCONV_COMPRESSFILELIST);
+ /* Binary packages with dirNames cannot be installed by legacy rpm. */
+ (void) rpmlibNeedsFeature(h, "CompressedFileNames", "3.0.4-1");
+ }
+}
+
+/**
+ */
+static FileListRec freeFileList(FileListRec fileList,
+ int count)
+{
+ while (count--) {
+ fileList[count].diskPath = _free(fileList[count].diskPath);
+ fileList[count].cpioPath = _free(fileList[count].cpioPath);
+ fileList[count].langs = _free(fileList[count].langs);
+ fileList[count].caps = _free(fileList[count].caps);
+ }
+ fileList = _free(fileList);
+ return NULL;
+}
+
+/* forward ref */
+static rpmRC recurseDir(FileList fl, const char * diskPath);
+
+/**
+ * Add a file to the package manifest.
+ * @param fl package file tree walk data
+ * @param diskPath path to file
+ * @param statp file stat (possibly NULL)
+ * @return RPMRC_OK on success
+ */
+static rpmRC addFile(FileList fl, const char * diskPath,
+ struct stat * statp)
+{
+ const char *cpioPath = diskPath;
+ struct stat statbuf;
+ mode_t fileMode;
+ uid_t fileUid;
+ gid_t fileGid;
+ const char *fileUname;
+ const char *fileGname;
+
+ /* Path may have prepended buildRoot, so locate the original filename. */
+ /*
+ * XXX There are 3 types of entry into addFile:
+ *
+ * From diskUrl statp
+ * =====================================================
+ * processBinaryFile path NULL
+ * processBinaryFile glob result path NULL
+ * myftw path stat
+ *
+ */
+ if (fl->buildRoot && !rstreq(fl->buildRoot, "/"))
+ cpioPath += strlen(fl->buildRoot);
+
+ /* XXX make sure '/' can be packaged also */
+ if (*cpioPath == '\0')
+ cpioPath = "/";
+
+ if (statp == NULL) {
+ time_t now = time(NULL);
+ statp = &statbuf;
+ memset(statp, 0, sizeof(*statp));
+ if (fl->devtype) {
+ /* XXX hack up a stat structure for a %dev(...) directive. */
+ statp->st_nlink = 1;
+ statp->st_rdev =
+ ((fl->devmajor & 0xff) << 8) | (fl->devminor & 0xff);
+ statp->st_dev = statp->st_rdev;
+ statp->st_mode = (fl->devtype == 'b' ? S_IFBLK : S_IFCHR);
+ statp->st_mode |= (fl->cur_ar.ar_fmode & 0777);
+ statp->st_atime = now;
+ statp->st_mtime = now;
+ statp->st_ctime = now;
+ } else {
+ int is_ghost = fl->currentFlags & RPMFILE_GHOST;
+
+ if (lstat(diskPath, statp)) {
+ if (is_ghost) { /* the file is %ghost missing from build root, assume regular file */
+ if (fl->cur_ar.ar_fmodestr != NULL) {
+ statp->st_mode = S_IFREG | (fl->cur_ar.ar_fmode & 0777);
+ } else {
+ rpmlog(RPMLOG_ERR, _("Explicit file attributes required in spec for: %s\n"), diskPath);
+ fl->processingFailed = 1;
+ return RPMRC_FAIL;
+ }
+ statp->st_atime = now;
+ statp->st_mtime = now;
+ statp->st_ctime = now;
+ } else {
+ const char *msg = fl->isDir ?
+ _("Directory not found: %s\n") :
+ _("File not found: %s\n");
+ rpmlog(RPMLOG_ERR, msg, diskPath);
+ fl->processingFailed = 1;
+ return RPMRC_FAIL;
+ }
+ }
+ }
+ }
+
+ if ((! fl->isDir) && S_ISDIR(statp->st_mode)) {
+/* FIX: fl->buildRoot may be NULL */
+ return recurseDir(fl, diskPath);
+ }
+
+ fileMode = statp->st_mode;
+ fileUid = statp->st_uid;
+ fileGid = statp->st_gid;
+
+ /* Explicit %attr() always wins */
+ if (fl->cur_ar.ar_fmodestr != NULL) {
+ fileMode &= S_IFMT;
+ fileMode |= fl->cur_ar.ar_fmode;
+ } else {
+ /* ...but %defattr() for directories and files is different */
+ if (S_ISDIR(fileMode)) {
+ if (fl->def_ar.ar_dmodestr) {
+ fileMode &= S_IFMT;
+ fileMode |= fl->def_ar.ar_dmode;
+ }
+ } else if (fl->def_ar.ar_fmodestr) {
+ fileMode &= S_IFMT;
+ fileMode |= fl->def_ar.ar_fmode;
+ }
+ }
+ if (fl->cur_ar.ar_user) {
+ fileUname = fl->cur_ar.ar_user;
+ } else if (fl->def_ar.ar_user) {
+ fileUname = fl->def_ar.ar_user;
+ } else {
+ fileUname = rpmugUname(fileUid);
+ }
+ if (fl->cur_ar.ar_group) {
+ fileGname = fl->cur_ar.ar_group;
+ } else if (fl->def_ar.ar_group) {
+ fileGname = fl->def_ar.ar_group;
+ } else {
+ fileGname = rpmugGname(fileGid);
+ }
+
+ /* Default user/group to builder's user/group */
+ if (fileUname == NULL)
+ fileUname = rpmugUname(getuid());
+ if (fileGname == NULL)
+ fileGname = rpmugGname(getgid());
+
+ /* S_XXX macro must be consistent with type in find call at check-files script */
+ if (check_fileList && (S_ISREG(fileMode) || S_ISLNK(fileMode))) {
+ appendStringBuf(check_fileList, diskPath);
+ appendStringBuf(check_fileList, "\n");
+ }
+
+ /* Add to the file list */
+ if (fl->fileListRecsUsed == fl->fileListRecsAlloced) {
+ fl->fileListRecsAlloced += 128;
+ fl->fileList = xrealloc(fl->fileList,
+ fl->fileListRecsAlloced * sizeof(*(fl->fileList)));
+ }
+
+ { FileListRec flp = &fl->fileList[fl->fileListRecsUsed];
+ int i;
+
+ flp->fl_st = *statp; /* structure assignment */
+ flp->fl_mode = fileMode;
+ flp->fl_uid = fileUid;
+ flp->fl_gid = fileGid;
+
+ flp->cpioPath = xstrdup(cpioPath);
+ flp->diskPath = xstrdup(diskPath);
+ flp->uname = rpmugStashStr(fileUname);
+ flp->gname = rpmugStashStr(fileGname);
+
+ if (fl->currentLangs && fl->nLangs > 0) {
+ char * ncl;
+ size_t nl = 0;
+
+ for (i = 0; i < fl->nLangs; i++)
+ nl += strlen(fl->currentLangs[i]) + 1;
+
+ flp->langs = ncl = xmalloc(nl);
+ for (i = 0; i < fl->nLangs; i++) {
+ const char *ocl;
+ if (i) *ncl++ = '|';
+ for (ocl = fl->currentLangs[i]; *ocl != '\0'; ocl++)
+ *ncl++ = *ocl;
+ *ncl = '\0';
+ }
+ } else {
+ flp->langs = xstrdup("");
+ }
+
+ if (fl->currentCaps) {
+ flp->caps = fl->currentCaps;
+ } else {
+ flp->caps = xstrdup("");
+ }
+
+ flp->flags = fl->currentFlags;
+ flp->specdFlags = fl->currentSpecdFlags;
+ flp->verifyFlags = fl->currentVerifyFlags;
+
+ if (!(flp->flags & RPMFILE_EXCLUDE) && S_ISREG(flp->fl_mode)) {
+ /*
+ * XXX Simple and stupid check for now, this needs to be per-payload
+ * format check once we have other payloads than good 'ole cpio.
+ */
+ if ((rpm_loff_t) flp->fl_size >= CPIO_FILESIZE_MAX) {
+ fl->largeFiles = 1;
+ rpmlog(RPMLOG_ERR, _("File %s too large for payload\n"),
+ flp->diskPath);
+ return RPMRC_FAIL;
+ }
+ }
+ }
+
+ fl->fileListRecsUsed++;
+ fl->fileCount++;
+
+ return RPMRC_OK;
+}
+
+/**
+ * Add directory (and all of its files) to the package manifest.
+ * @param fl package file tree walk data
+ * @param diskPath path to file
+ * @return RPMRC_OK on success
+ */
+static rpmRC recurseDir(FileList fl, const char * diskPath)
+{
+ char * ftsSet[2];
+ FTS * ftsp;
+ FTSENT * fts;
+ int myFtsOpts = (FTS_COMFOLLOW | FTS_NOCHDIR | FTS_PHYSICAL);
+ rpmRC rc = RPMRC_FAIL;
+
+ fl->isDir = 1; /* Keep it from following myftw() again */
+
+ ftsSet[0] = (char *) diskPath;
+ ftsSet[1] = NULL;
+ ftsp = Fts_open(ftsSet, myFtsOpts, NULL);
+ while ((fts = Fts_read(ftsp)) != NULL) {
+ switch (fts->fts_info) {
+ case FTS_D: /* preorder directory */
+ case FTS_F: /* regular file */
+ case FTS_SL: /* symbolic link */
+ case FTS_SLNONE: /* symbolic link without target */
+ case FTS_DEFAULT: /* none of the above */
+ rc = addFile(fl, fts->fts_accpath, fts->fts_statp);
+ break;
+ case FTS_DOT: /* dot or dot-dot */
+ case FTS_DP: /* postorder directory */
+ rc = RPMRC_OK;
+ break;
+ case FTS_NS: /* stat(2) failed */
+ case FTS_DNR: /* unreadable directory */
+ case FTS_ERR: /* error; errno is set */
+ case FTS_DC: /* directory that causes cycles */
+ case FTS_NSOK: /* no stat(2) requested */
+ case FTS_INIT: /* initialized only */
+ case FTS_W: /* whiteout object */
+ default:
+ rc = RPMRC_FAIL;
+ break;
+ }
+ if (rc)
+ break;
+ }
+ (void) Fts_close(ftsp);
+
+ fl->isDir = 0;
+
+ return rc;
+}
+
+/**
+ * Add a pubkey/icon to a binary package.
+ * @param pkg
+ * @param fl package file tree walk data
+ * @param fileName path to file, relative is builddir, absolute buildroot.
+ * @param tag tag to add
+ * @return RPMRC_OK on success
+ */
+static rpmRC processMetadataFile(Package pkg, FileList fl,
+ const char * fileName, rpmTagVal tag)
+{
+ const char * buildDir = "%{_builddir}/%{?buildsubdir}/";
+ char * fn = NULL;
+ char * apkt = NULL;
+ uint8_t * pkt = NULL;
+ ssize_t pktlen = 0;
+ int absolute = 0;
+ rpmRC rc = RPMRC_FAIL;
+ int xx;
+
+ if (*fileName == '/') {
+ fn = rpmGenPath(fl->buildRoot, NULL, fileName);
+ absolute = 1;
+ } else
+ fn = rpmGenPath(buildDir, NULL, fileName);
+
+ switch (tag) {
+ default:
+ rpmlog(RPMLOG_ERR, _("%s: can't load unknown tag (%d).\n"),
+ fn, tag);
+ goto exit;
+ break;
+ case RPMTAG_PUBKEYS: {
+ if ((xx = pgpReadPkts(fn, &pkt, (size_t *)&pktlen)) <= 0) {
+ rpmlog(RPMLOG_ERR, _("%s: public key read failed.\n"), fn);
+ goto exit;
+ }
+ if (xx != PGPARMOR_PUBKEY) {
+ rpmlog(RPMLOG_ERR, _("%s: not an armored public key.\n"), fn);
+ goto exit;
+ }
+ apkt = pgpArmorWrap(PGPARMOR_PUBKEY, pkt, pktlen);
+ break;
+ }
+ }
+
+ if (!apkt) {
+ rpmlog(RPMLOG_ERR, _("%s: failed to encode\n"), fn);
+ goto exit;
+ }
+
+ headerPutString(pkg->header, tag, apkt);
+ rc = RPMRC_OK;
+
+ if (absolute)
+ rc = addFile(fl, fn, NULL);
+
+exit:
+ apkt = _free(apkt);
+ pkt = _free(pkt);
+ fn = _free(fn);
+ if (rc) {
+ fl->processingFailed = 1;
+ rc = RPMRC_FAIL;
+ }
+ return rc;
+}
+
+/**
+ * Add a file to a binary package.
+ * @param pkg
+ * @param fl package file tree walk data
+ * @param fileName file to add
+ * @return RPMRC_OK on success
+ */
+static rpmRC processBinaryFile(Package pkg, FileList fl, const char * fileName)
+{
+ int quote = 1; /* XXX permit quoted glob characters. */
+ int doGlob;
+ char *diskPath = NULL;
+ rpmRC rc = RPMRC_OK;
+ size_t fnlen = strlen(fileName);
+ int trailing_slash = (fnlen > 0 && fileName[fnlen-1] == '/');
+
+ /* XXX differentiate other directories from explicit %dir */
+ if (trailing_slash && !fl->isDir)
+ fl->isDir = -1;
+
+ doGlob = glob_pattern_p(fileName, quote);
+
+ /* Check that file starts with leading "/" */
+ if (*fileName != '/') {
+ rpmlog(RPMLOG_ERR, _("File needs leading \"/\": %s\n"), fileName);
+ rc = RPMRC_FAIL;
+ goto exit;
+ }
+
+ /* Copy file name or glob pattern removing multiple "/" chars. */
+ /*
+ * Note: rpmGetPath should guarantee a "canonical" path. That means
+ * that the following pathologies should be weeded out:
+ * //bin//sh
+ * //usr//bin/
+ * /.././../usr/../bin//./sh
+ */
+ diskPath = rpmGenPath(fl->buildRoot, NULL, fileName);
+ /* Arrange trailing slash on directories */
+ if (fl->isDir)
+ diskPath = rstrcat(&diskPath, "/");
+
+ if (doGlob) {
+ ARGV_t argv = NULL;
+ int argc = 0;
+ int i;
+
+ /* XXX for %dev marker in file manifest only */
+ if (fl->noGlob) {
+ rpmlog(RPMLOG_ERR, _("Glob not permitted: %s\n"), diskPath);
+ rc = RPMRC_FAIL;
+ goto exit;
+ }
+
+ if (rpmGlob(diskPath, &argc, &argv) == 0 && argc >= 1) {
+ for (i = 0; i < argc; i++) {
+ rc = addFile(fl, argv[i], NULL);
+ }
+ argvFree(argv);
+ } else {
+ const char *msg = (fl->isDir) ?
+ _("Directory not found by glob: %s\n") :
+ _("File not found by glob: %s\n");
+ rpmlog(RPMLOG_ERR, msg, diskPath);
+ rc = RPMRC_FAIL;
+ goto exit;
+ }
+ } else {
+ rc = addFile(fl, diskPath, NULL);
+ }
+
+exit:
+ free(diskPath);
+ if (rc) {
+ fl->processingFailed = 1;
+ rc = RPMRC_FAIL;
+ }
+ return rc;
+}
+
+/**
+ */
+static rpmRC processPackageFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags,
+ Package pkg, int installSpecialDoc, int test)
+{
+ struct FileList_s fl;
+ const char *fileName;
+ char buf[BUFSIZ];
+ struct AttrRec_s arbuf;
+ AttrRec specialDocAttrRec = &arbuf;
+ char *specialDoc = NULL;
+
+ nullAttrRec(specialDocAttrRec);
+ pkg->cpioList = NULL;
+
+ if (pkg->fileFile) {
+ char *ffn;
+ FILE *fd;
+
+ for (ARGV_const_t fp = pkg->fileFile; *fp != NULL; fp++) {
+ if (**fp == '/') {
+ ffn = rpmGetPath(*fp, NULL);
+ } else {
+ ffn = rpmGetPath("%{_builddir}/",
+ (spec->buildSubdir ? spec->buildSubdir : "") ,
+ "/", *fp, NULL);
+ }
+ fd = fopen(ffn, "r");
+
+ if (fd == NULL || ferror(fd)) {
+ rpmlog(RPMLOG_ERR, _("Could not open %%files file %s: %m\n"), ffn);
+ return RPMRC_FAIL;
+ }
+ ffn = _free(ffn);
+
+ while (fgets(buf, sizeof(buf), fd)) {
+ handleComments(buf);
+ if (expandMacros(spec, spec->macros, buf, sizeof(buf))) {
+ rpmlog(RPMLOG_ERR, _("line: %s\n"), buf);
+ fclose(fd);
+ return RPMRC_FAIL;
+ }
+ argvAdd(&(pkg->fileList), buf);
+ }
+ (void) fclose(fd);
+ }
+ }
+
+ /* Init the file list structure */
+ memset(&fl, 0, sizeof(fl));
+
+ /* XXX spec->buildRoot == NULL, then xstrdup("") is returned */
+ fl.buildRoot = rpmGenPath(spec->rootDir, spec->buildRoot, NULL);
+
+ fl.fileCount = 0;
+ fl.processingFailed = 0;
+
+ fl.passedSpecialDoc = 0;
+ fl.isSpecialDoc = 0;
+
+ fl.isDir = 0;
+ fl.currentFlags = 0;
+ fl.currentVerifyFlags = 0;
+
+ fl.noGlob = 0;
+ fl.devtype = 0;
+ fl.devmajor = 0;
+ fl.devminor = 0;
+
+ nullAttrRec(&fl.cur_ar);
+ nullAttrRec(&fl.def_ar);
+ dupAttrRec(&root_ar, &fl.def_ar); /* XXX assume %defattr(-,root,root) */
+
+ fl.defVerifyFlags = RPMVERIFY_ALL;
+ fl.nLangs = 0;
+ fl.currentLangs = NULL;
+ fl.haveCaps = 0;
+ fl.currentCaps = NULL;
+
+ fl.currentSpecdFlags = 0;
+ fl.defSpecdFlags = 0;
+
+ fl.largeFiles = 0;
+ fl.pkgFlags = pkgFlags;
+
+ fl.docDirs = NULL;
+ { char *docs = rpmGetPath("%{?__docdir_path}", NULL);
+ argvSplit(&fl.docDirs, docs, ":");
+ free(docs);
+ }
+
+ fl.fileList = NULL;
+ fl.fileListRecsAlloced = 0;
+ fl.fileListRecsUsed = 0;
+
+ for (ARGV_const_t fp = pkg->fileList; *fp != NULL; fp++) {
+ const char *s = *fp;
+ SKIPSPACE(s);
+ if (*s == '\0')
+ continue;
+ fileName = NULL;
+ rstrlcpy(buf, s, sizeof(buf));
+
+ /* Reset for a new line in %files */
+ fl.isDir = 0;
+ fl.currentFlags = 0;
+ /* turn explicit flags into %def'd ones (gosh this is hacky...) */
+ fl.currentSpecdFlags = ((unsigned)fl.defSpecdFlags) >> 8;
+ fl.currentVerifyFlags = fl.defVerifyFlags;
+ fl.isSpecialDoc = 0;
+
+ fl.noGlob = 0;
+ fl.devtype = 0;
+ fl.devmajor = 0;
+ fl.devminor = 0;
+
+ /* XXX should reset to %deflang value */
+ if (fl.currentLangs) {
+ int i;
+ for (i = 0; i < fl.nLangs; i++)
+ fl.currentLangs[i] = _free(fl.currentLangs[i]);
+ fl.currentLangs = _free(fl.currentLangs);
+ }
+ fl.nLangs = 0;
+ fl.currentCaps = NULL;
+
+ freeAttrRec(&fl.cur_ar);
+
+ if (parseForVerify(buf, &fl))
+ continue;
+ if (parseForAttr(buf, &fl))
+ continue;
+ if (parseForDev(buf, &fl))
+ continue;
+ if (parseForConfig(buf, &fl))
+ continue;
+ if (parseForLang(buf, &fl))
+ continue;
+ if (parseForCaps(buf, &fl))
+ continue;
+ if (parseForSimple(spec, pkg, buf, &fl, &fileName))
+ continue;
+ if (fileName == NULL)
+ continue;
+
+ if (fl.isSpecialDoc) {
+ /* Save this stuff for last */
+ specialDoc = _free(specialDoc);
+ specialDoc = xstrdup(fileName);
+ dupAttrRec(&fl.cur_ar, specialDocAttrRec);
+ } else if (fl.currentFlags & RPMFILE_PUBKEY) {
+ (void) processMetadataFile(pkg, &fl, fileName, RPMTAG_PUBKEYS);
+ } else {
+ (void) processBinaryFile(pkg, &fl, fileName);
+ }
+ }
+
+ /* Now process special doc, if there is one */
+ if (specialDoc) {
+ if (installSpecialDoc) {
+ int _missing_doc_files_terminate_build =
+ rpmExpandNumeric("%{?_missing_doc_files_terminate_build}");
+ rpmRC rc;
+
+ rc = doScript(spec, RPMBUILD_STRINGBUF, "%doc",
+ getStringBuf(pkg->specialDoc), test);
+ if (rc != RPMRC_OK && _missing_doc_files_terminate_build)
+ fl.processingFailed = 1;
+ }
+
+ /* Reset for %doc */
+ fl.isDir = 0;
+ fl.currentFlags = 0;
+ fl.currentVerifyFlags = fl.defVerifyFlags;
+
+ fl.noGlob = 0;
+ fl.devtype = 0;
+ fl.devmajor = 0;
+ fl.devminor = 0;
+
+ /* XXX should reset to %deflang value */
+ if (fl.currentLangs) {
+ int i;
+ for (i = 0; i < fl.nLangs; i++)
+ fl.currentLangs[i] = _free(fl.currentLangs[i]);
+ fl.currentLangs = _free(fl.currentLangs);
+ }
+ fl.nLangs = 0;
+
+ dupAttrRec(specialDocAttrRec, &fl.cur_ar);
+ freeAttrRec(specialDocAttrRec);
+
+ (void) processBinaryFile(pkg, &fl, specialDoc);
+
+ specialDoc = _free(specialDoc);
+ }
+
+ if (fl.processingFailed)
+ goto exit;
+
+ /* Verify that file attributes scope over hardlinks correctly. */
+ if (checkHardLinks(&fl))
+ (void) rpmlibNeedsFeature(pkg->header,
+ "PartialHardlinkSets", "4.0.4-1");
+
+ genCpioListAndHeader(&fl, &pkg->cpioList, pkg->header, 0);
+
+exit:
+ fl.buildRoot = _free(fl.buildRoot);
+
+ freeAttrRec(&fl.cur_ar);
+ freeAttrRec(&fl.def_ar);
+
+ if (fl.currentLangs) {
+ int i;
+ for (i = 0; i < fl.nLangs; i++)
+ fl.currentLangs[i] = _free(fl.currentLangs[i]);
+ fl.currentLangs = _free(fl.currentLangs);
+ }
+
+ fl.fileList = freeFileList(fl.fileList, fl.fileListRecsUsed);
+ argvFree(fl.docDirs);
+ return fl.processingFailed ? RPMRC_FAIL : RPMRC_OK;
+}
+
+static void genSourceRpmName(rpmSpec spec)
+{
+ if (spec->sourceRpmName == NULL) {
+ char *nvr = headerGetAsString(spec->packages->header, RPMTAG_NVR);
+ rasprintf(&spec->sourceRpmName, "%s.%ssrc.rpm", nvr,
+ spec->noSource ? "no" : "");
+ free(nvr);
+ }
+}
+
+rpmRC processSourceFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags)
+{
+ struct Source *srcPtr;
+ int x, isSpec = 1;
+ struct FileList_s fl;
+ ARGV_t files = NULL;
+ Package pkg;
+ static char *_srcdefattr;
+ static int oneshot;
+
+ if (!oneshot) {
+ _srcdefattr = rpmExpand("%{?_srcdefattr}", NULL);
+ if (_srcdefattr && !*_srcdefattr)
+ _srcdefattr = _free(_srcdefattr);
+ oneshot = 1;
+ }
+
+ genSourceRpmName(spec);
+ /* Construct the file list and source entries */
+ argvAdd(&files, spec->specFile);
+ for (srcPtr = spec->sources; srcPtr != NULL; srcPtr = srcPtr->next) {
+ char * sfn = rpmGetPath( ((srcPtr->flags & RPMBUILD_ISNO) ? "!" : ""),
+ "%{_sourcedir}/", srcPtr->source, NULL);
+ argvAdd(&files, sfn);
+ sfn = _free(sfn);
+ }
+
+ for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
+ for (srcPtr = pkg->icon; srcPtr != NULL; srcPtr = srcPtr->next) {
+ char * sfn;
+ sfn = rpmGetPath( ((srcPtr->flags & RPMBUILD_ISNO) ? "!" : ""),
+ "%{_sourcedir}/", srcPtr->source, NULL);
+ argvAdd(&files, sfn);
+ sfn = _free(sfn);
+ }
+ }
+
+ spec->sourceCpioList = NULL;
+
+ /* Init the file list structure */
+ memset(&fl, 0, sizeof(fl));
+ if (_srcdefattr) {
+ char *a = rstrscat(NULL, "%defattr ", _srcdefattr, NULL);
+ parseForAttr(a, &fl);
+ free(a);
+ }
+ fl.fileList = xcalloc((spec->numSources + 1), sizeof(*fl.fileList));
+ fl.processingFailed = 0;
+ fl.fileListRecsUsed = 0;
+ fl.pkgFlags = pkgFlags;
+ fl.buildRoot = NULL;
+
+ /* The first source file is the spec file */
+ x = 0;
+ for (ARGV_const_t fp = files; *fp != NULL; fp++) {
+ const char *diskPath = *fp;
+ char *tmp;
+ FileListRec flp;
+
+ SKIPSPACE(diskPath);
+ if (! *diskPath)
+ continue;
+
+ flp = &fl.fileList[x];
+
+ flp->flags = isSpec ? RPMFILE_SPECFILE : 0;
+ /* files with leading ! are no source files */
+ if (*diskPath == '!') {
+ flp->flags |= RPMFILE_GHOST;
+ diskPath++;
+ }
+
+ tmp = xstrdup(diskPath); /* basename() might modify */
+ flp->diskPath = xstrdup(diskPath);
+ flp->cpioPath = xstrdup(basename(tmp));
+ flp->verifyFlags = RPMVERIFY_ALL;
+ free(tmp);
+
+ if (stat(diskPath, &flp->fl_st)) {
+ rpmlog(RPMLOG_ERR, _("Bad file: %s: %s\n"),
+ diskPath, strerror(errno));
+ fl.processingFailed = 1;
+ }
+
+ if (fl.def_ar.ar_fmodestr) {
+ flp->fl_mode &= S_IFMT;
+ flp->fl_mode |= fl.def_ar.ar_fmode;
+ }
+ if (fl.def_ar.ar_user) {
+ flp->uname = rpmugStashStr(fl.def_ar.ar_user);
+ } else {
+ flp->uname = rpmugStashStr(rpmugUname(flp->fl_uid));
+ }
+ if (fl.def_ar.ar_group) {
+ flp->gname = rpmugStashStr(fl.def_ar.ar_group);
+ } else {
+ flp->gname = rpmugStashStr(rpmugGname(flp->fl_gid));
+ }
+ flp->langs = xstrdup("");
+
+ if (! (flp->uname && flp->gname)) {
+ rpmlog(RPMLOG_ERR, _("Bad owner/group: %s\n"), diskPath);
+ fl.processingFailed = 1;
+ }
+
+ isSpec = 0;
+ x++;
+ }
+ fl.fileListRecsUsed = x;
+ argvFree(files);
+
+ if (! fl.processingFailed) {
+ if (spec->sourceHeader != NULL)
+ genCpioListAndHeader(&fl, &spec->sourceCpioList,
+ spec->sourceHeader, 1);
+ }
+
+ fl.fileList = freeFileList(fl.fileList, fl.fileListRecsUsed);
+ freeAttrRec(&fl.def_ar);
+ return fl.processingFailed ? RPMRC_FAIL : RPMRC_OK;
+}
+
+/**
+ * Check packaged file list against what's in the build root.
+ * @param fileList packaged file list
+ * @return -1 if skipped, 0 on OK, 1 on error
+ */
+static int checkFiles(const char *buildRoot, StringBuf fileList)
+{
+ static char * const av_ckfile[] = { "%{?__check_files}", NULL };
+ StringBuf sb_stdout = NULL;
+ char * s;
+ int rc;
+
+ s = rpmExpand(av_ckfile[0], NULL);
+ if (!(s && *s)) {
+ rc = -1;
+ goto exit;
+ }
+ rc = 0;
+
+ rpmlog(RPMLOG_NOTICE, _("Checking for unpackaged file(s): %s\n"), s);
+
+ rc = rpmfcExec(av_ckfile, fileList, &sb_stdout, 0, buildRoot);
+ if (rc < 0)
+ goto exit;
+
+ if (sb_stdout) {
+ int _unpackaged_files_terminate_build =
+ rpmExpandNumeric("%{?_unpackaged_files_terminate_build}");
+ const char * t;
+
+ t = getStringBuf(sb_stdout);
+ if ((*t != '\0') && (*t != '\n')) {
+ rc = (_unpackaged_files_terminate_build) ? 1 : 0;
+ rpmlog((rc ? RPMLOG_ERR : RPMLOG_WARNING),
+ _("Installed (but unpackaged) file(s) found:\n%s"), t);
+ }
+ }
+
+exit:
+ sb_stdout = freeStringBuf(sb_stdout);
+ s = _free(s);
+ return rc;
+}
+
+rpmRC processBinaryFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags,
+ int installSpecialDoc, int test)
+{
+ Package pkg;
+ rpmRC rc = RPMRC_OK;
+
+ check_fileList = newStringBuf();
+ genSourceRpmName(spec);
+
+ for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
+ char *nvr;
+ const char *a;
+
+ if (pkg->fileList == NULL)
+ continue;
+
+ headerPutString(pkg->header, RPMTAG_SOURCERPM, spec->sourceRpmName);
+
+ nvr = headerGetAsString(pkg->header, RPMTAG_NVRA);
+ rpmlog(RPMLOG_NOTICE, _("Processing files: %s\n"), nvr);
+ free(nvr);
+
+ if ((rc = processPackageFiles(spec, pkgFlags, pkg, installSpecialDoc, test)) != RPMRC_OK ||
+ (rc = rpmfcGenerateDepends(spec, pkg)) != RPMRC_OK)
+ goto exit;
+
+ a = headerGetString(pkg->header, RPMTAG_ARCH);
+ if (rstreq(a, "noarch") && headerGetNumber(pkg->header, RPMTAG_HEADERCOLOR) != 0) {
+ int terminate = rpmExpandNumeric("%{?_binaries_in_noarch_packages_terminate_build}");
+ rpmlog(terminate ? RPMLOG_ERR : RPMLOG_WARNING,
+ _("Arch dependent binaries in noarch package\n"));
+ if (terminate) {
+ rc = RPMRC_FAIL;
+ goto exit;
+ }
+ }
+ }
+
+ /* Now we have in fileList list of files from all packages.
+ * We pass it to a script which does the work of finding missing
+ * and duplicated files.
+ */
+
+
+ if (checkFiles(spec->buildRoot, check_fileList) > 0) {
+ rc = RPMRC_FAIL;
+ }
+exit:
+ check_fileList = freeStringBuf(check_fileList);
+
+ return rc;
+}
diff --git a/build/misc.c b/build/misc.c
new file mode 100644
index 0000000..06fd110
--- /dev/null
+++ b/build/misc.c
@@ -0,0 +1,103 @@
+/** \ingroup rpmbuild
+ * \file build/misc.c
+ */
+#include "system.h"
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <rpm/rpmstring.h>
+#include "build/rpmbuild_misc.h"
+#include "debug.h"
+
+#define BUF_CHUNK 1024
+
+struct StringBufRec {
+ char *buf;
+ char *tail; /* Points to first "free" char */
+ int allocated;
+ int free;
+};
+
+StringBuf newStringBuf(void)
+{
+ StringBuf sb = xmalloc(sizeof(*sb));
+
+ sb->free = sb->allocated = BUF_CHUNK;
+ sb->buf = xcalloc(sb->allocated, sizeof(*sb->buf));
+ sb->buf[0] = '\0';
+ sb->tail = sb->buf;
+
+ return sb;
+}
+
+StringBuf freeStringBuf(StringBuf sb)
+{
+ if (sb) {
+ sb->buf = _free(sb->buf);
+ sb = _free(sb);
+ }
+ return sb;
+}
+
+void stripTrailingBlanksStringBuf(StringBuf sb)
+{
+ while (sb->free != sb->allocated) {
+ if (! risspace(*(sb->tail - 1)))
+ break;
+ sb->free++;
+ sb->tail--;
+ }
+ sb->tail[0] = '\0';
+}
+
+const char * getStringBuf(StringBuf sb)
+{
+ return (sb != NULL) ? sb->buf : NULL;
+}
+
+void appendStringBufAux(StringBuf sb, const char *s, int nl)
+{
+ int l;
+
+ l = strlen(s);
+ /* If free == l there is no room for NULL terminator! */
+ while ((l + nl + 1) > sb->free) {
+ sb->allocated += BUF_CHUNK;
+ sb->free += BUF_CHUNK;
+ sb->buf = xrealloc(sb->buf, sb->allocated);
+ sb->tail = sb->buf + (sb->allocated - sb->free);
+ }
+
+ /* FIX: shrug */
+ strcpy(sb->tail, s);
+ sb->tail += l;
+ sb->free -= l;
+ if (nl) {
+ sb->tail[0] = '\n';
+ sb->tail[1] = '\0';
+ sb->tail++;
+ sb->free--;
+ }
+}
+
+uint32_t parseUnsignedNum(const char * line, uint32_t * res)
+{
+ char * s1 = NULL;
+ unsigned long rc;
+ uint32_t result;
+
+ if (line == NULL) return 1;
+
+ while (isspace(*line)) line++;
+ if (!isdigit(*line)) return 1;
+
+ rc = strtoul(line, &s1, 10);
+
+ if (*s1 || s1 == line || rc == ULONG_MAX || rc > UINT_MAX)
+ return 1;
+
+ result = (uint32_t)rc;
+ if (res) *res = result;
+
+ return 0;
+}
diff --git a/build/pack.c b/build/pack.c
new file mode 100644
index 0000000..981a03e
--- /dev/null
+++ b/build/pack.c
@@ -0,0 +1,822 @@
+/** \ingroup rpmbuild
+ * \file build/pack.c
+ * Assemble components of an RPM package.
+ */
+
+#include "system.h"
+
+#include <errno.h>
+#include <netdb.h>
+#include <time.h>
+
+#include <rpm/rpmlib.h> /* RPMSIGTAG*, rpmReadPackageFile */
+#include <rpm/rpmts.h>
+#include <rpm/rpmfileutil.h>
+#include <rpm/rpmlog.h>
+
+#include "rpmio/rpmio_internal.h" /* fdInitDigest, fdFiniDigest */
+#include "lib/cpio.h"
+#include "lib/fsm.h"
+#include "lib/rpmfi_internal.h" /* rpmfiFSM() */
+#include "lib/rpmte_internal.h" /* rpmfs */
+#include "lib/signature.h"
+#include "lib/rpmlead.h"
+#include "build/rpmbuild_internal.h"
+#include "build/rpmbuild_misc.h"
+
+#include "debug.h"
+
+typedef struct cpioSourceArchive_s {
+ rpm_loff_t cpioArchiveSize;
+ FD_t cpioFdIn;
+ rpmfi cpioList;
+} * CSA_t;
+
+/**
+ * @todo Create transaction set *much* earlier.
+ */
+static rpmRC cpio_doio(FD_t fdo, Header h, CSA_t csa,
+ const char * fmodeMacro)
+{
+ rpmts ts = rpmtsCreate();
+ rpmfi fi = csa->cpioList;
+ rpmte te = NULL;
+ rpmfs fs = NULL;
+ char *failedFile = NULL;
+ FD_t cfd;
+ rpmRC rc = RPMRC_OK;
+ int xx, i;
+
+ { char *fmode = rpmExpand(fmodeMacro, NULL);
+ if (!(fmode && fmode[0] == 'w'))
+ fmode = xstrdup("w9.gzdio");
+ (void) Fflush(fdo);
+ cfd = Fdopen(fdDup(Fileno(fdo)), fmode);
+ fmode = _free(fmode);
+ }
+ if (cfd == NULL)
+ return RPMRC_FAIL;
+
+ /* make up a transaction element for passing to fsm */
+ rpmtsAddInstallElement(ts, h, NULL, 0, NULL);
+ te = rpmtsElement(ts, 0);
+ fs = rpmteGetFileStates(te);
+
+ fi = rpmfiInit(fi, 0);
+ while ((i = rpmfiNext(fi)) >= 0) {
+ if (rpmfiFFlags(fi) & RPMFILE_GHOST)
+ rpmfsSetAction(fs, i, FA_SKIP);
+ else
+ rpmfsSetAction(fs, i, FA_COPYOUT);
+ }
+
+ xx = fsmSetup(rpmfiFSM(fi), FSM_PKGBUILD, ts, te, fi, cfd,
+ &csa->cpioArchiveSize, &failedFile);
+ if (xx)
+ rc = RPMRC_FAIL;
+ (void) Fclose(cfd);
+ xx = fsmTeardown(rpmfiFSM(fi));
+ if (rc == RPMRC_OK && xx) rc = RPMRC_FAIL;
+
+ if (rc) {
+ if (failedFile)
+ rpmlog(RPMLOG_ERR, _("create archive failed on file %s: %s\n"),
+ failedFile, cpioStrerror(rc));
+ else
+ rpmlog(RPMLOG_ERR, _("create archive failed: %s\n"),
+ cpioStrerror(rc));
+ rc = RPMRC_FAIL;
+ }
+ rpmtsEmpty(ts);
+
+ failedFile = _free(failedFile);
+ ts = rpmtsFree(ts);
+
+ return rc;
+}
+
+/**
+ */
+static rpmRC cpio_copy(FD_t fdo, CSA_t csa)
+{
+ char buf[BUFSIZ];
+ size_t nb;
+
+ while((nb = Fread(buf, sizeof(buf[0]), sizeof(buf), csa->cpioFdIn)) > 0) {
+ if (Fwrite(buf, sizeof(buf[0]), nb, fdo) != nb) {
+ rpmlog(RPMLOG_ERR, _("cpio_copy write failed: %s\n"),
+ Fstrerror(fdo));
+ return RPMRC_FAIL;
+ }
+ csa->cpioArchiveSize += nb;
+ }
+ if (Ferror(csa->cpioFdIn)) {
+ rpmlog(RPMLOG_ERR, _("cpio_copy read failed: %s\n"),
+ Fstrerror(csa->cpioFdIn));
+ return RPMRC_FAIL;
+ }
+ return RPMRC_OK;
+}
+
+/**
+ */
+static StringBuf addFileToTagAux(rpmSpec spec,
+ const char * file, StringBuf sb)
+{
+ char buf[BUFSIZ];
+ char * fn;
+ FILE * f;
+
+ fn = rpmGetPath("%{_builddir}/%{?buildsubdir:%{buildsubdir}/}", file, NULL);
+
+ f = fopen(fn, "r");
+ if (f == NULL || ferror(f)) {
+ sb = freeStringBuf(sb);
+ goto exit;
+ }
+ while (fgets(buf, sizeof(buf), f)) {
+ if (expandMacros(spec, spec->macros, buf, sizeof(buf))) {
+ rpmlog(RPMLOG_ERR, _("%s: line: %s\n"), fn, buf);
+ sb = freeStringBuf(sb);
+ break;
+ }
+ appendStringBuf(sb, buf);
+ }
+ (void) fclose(f);
+
+exit:
+ free(fn);
+
+ return sb;
+}
+
+static rpm_time_t * getBuildTime(void)
+{
+ static rpm_time_t buildTime[1];
+
+ if (buildTime[0] == 0)
+ buildTime[0] = (int32_t) time(NULL);
+ return buildTime;
+}
+
+static const char * buildHost(void)
+{
+ static char hostname[1024];
+ static int oneshot = 0;
+ struct hostent *hbn;
+
+ if (! oneshot) {
+ (void) gethostname(hostname, sizeof(hostname));
+ hbn = gethostbyname(hostname);
+ if (hbn)
+ strcpy(hostname, hbn->h_name);
+ else
+ rpmlog(RPMLOG_WARNING,
+ _("Could not canonicalize hostname: %s\n"), hostname);
+ oneshot = 1;
+ }
+ return(hostname);
+}
+/**
+ */
+static int addFileToTag(rpmSpec spec, const char * file, Header h, rpmTagVal tag)
+{
+ StringBuf sb = newStringBuf();
+ const char *s = headerGetString(h, tag);
+
+ if (s) {
+ appendLineStringBuf(sb, s);
+ (void) headerDel(h, tag);
+ }
+
+ if ((sb = addFileToTagAux(spec, file, sb)) == NULL)
+ return 1;
+
+ headerPutString(h, tag, getStringBuf(sb));
+
+ sb = freeStringBuf(sb);
+ return 0;
+}
+
+/**
+ */
+static int addFileToArrayTag(rpmSpec spec, const char *file, Header h, rpmTagVal tag)
+{
+ StringBuf sb = newStringBuf();
+ const char *s;
+
+ if ((sb = addFileToTagAux(spec, file, sb)) == NULL)
+ return 1;
+
+ s = getStringBuf(sb);
+ headerPutString(h, tag, s);
+
+ sb = freeStringBuf(sb);
+ return 0;
+}
+
+/**
+ */
+static rpmRC processScriptFiles(rpmSpec spec, Package pkg)
+{
+ struct TriggerFileEntry *p;
+ int addflags = 0;
+
+ if (pkg->preInFile) {
+ if (addFileToTag(spec, pkg->preInFile, pkg->header, RPMTAG_PREIN)) {
+ rpmlog(RPMLOG_ERR,
+ _("Could not open PreIn file: %s\n"), pkg->preInFile);
+ return RPMRC_FAIL;
+ }
+ }
+ if (pkg->preUnFile) {
+ if (addFileToTag(spec, pkg->preUnFile, pkg->header, RPMTAG_PREUN)) {
+ rpmlog(RPMLOG_ERR,
+ _("Could not open PreUn file: %s\n"), pkg->preUnFile);
+ return RPMRC_FAIL;
+ }
+ }
+ if (pkg->preTransFile) {
+ if (addFileToTag(spec, pkg->preTransFile, pkg->header, RPMTAG_PRETRANS)) {
+ rpmlog(RPMLOG_ERR,
+ _("Could not open PreTrans file: %s\n"), pkg->preTransFile);
+ return RPMRC_FAIL;
+ }
+ }
+ if (pkg->postInFile) {
+ if (addFileToTag(spec, pkg->postInFile, pkg->header, RPMTAG_POSTIN)) {
+ rpmlog(RPMLOG_ERR,
+ _("Could not open PostIn file: %s\n"), pkg->postInFile);
+ return RPMRC_FAIL;
+ }
+ }
+ if (pkg->postUnFile) {
+ if (addFileToTag(spec, pkg->postUnFile, pkg->header, RPMTAG_POSTUN)) {
+ rpmlog(RPMLOG_ERR,
+ _("Could not open PostUn file: %s\n"), pkg->postUnFile);
+ return RPMRC_FAIL;
+ }
+ }
+ if (pkg->postTransFile) {
+ if (addFileToTag(spec, pkg->postTransFile, pkg->header, RPMTAG_POSTTRANS)) {
+ rpmlog(RPMLOG_ERR,
+ _("Could not open PostTrans file: %s\n"), pkg->postTransFile);
+ return RPMRC_FAIL;
+ }
+ }
+ if (pkg->verifyFile) {
+ if (addFileToTag(spec, pkg->verifyFile, pkg->header,
+ RPMTAG_VERIFYSCRIPT)) {
+ rpmlog(RPMLOG_ERR,
+ _("Could not open VerifyScript file: %s\n"), pkg->verifyFile);
+ return RPMRC_FAIL;
+ }
+ }
+
+ /* if any trigger has flags, we need to add flags entry for all of them */
+ for (p = pkg->triggerFiles; p != NULL; p = p->next) {
+ if (p->flags) {
+ addflags = 1;
+ break;
+ }
+ }
+
+ for (p = pkg->triggerFiles; p != NULL; p = p->next) {
+ headerPutString(pkg->header, RPMTAG_TRIGGERSCRIPTPROG, p->prog);
+ if (addflags) {
+ headerPutUint32(pkg->header, RPMTAG_TRIGGERSCRIPTFLAGS,
+ &p->flags, 1);
+ }
+
+ if (p->script) {
+ headerPutString(pkg->header, RPMTAG_TRIGGERSCRIPTS, p->script);
+ } else if (p->fileName) {
+ if (addFileToArrayTag(spec, p->fileName, pkg->header,
+ RPMTAG_TRIGGERSCRIPTS)) {
+ rpmlog(RPMLOG_ERR,
+ _("Could not open Trigger script file: %s\n"),
+ p->fileName);
+ return RPMRC_FAIL;
+ }
+ } else {
+ /* This is dumb. When the header supports NULL string */
+ /* this will go away. */
+ headerPutString(pkg->header, RPMTAG_TRIGGERSCRIPTS, "");
+ }
+ }
+
+ return RPMRC_OK;
+}
+
+static rpmRC writeRPM(Header *hdrp, unsigned char ** pkgidp, const char *fileName,
+ CSA_t csa, char **cookie)
+{
+ FD_t fd = NULL;
+ FD_t ifd = NULL;
+ ssize_t count;
+ char * sigtarget = NULL;;
+ char * rpmio_flags = NULL;
+ char * SHA1 = NULL;
+ const char *s;
+ char *buf = NULL;
+ Header h;
+ Header sig = NULL;
+ int xx;
+ rpmRC rc = RPMRC_OK;
+ struct rpmtd_s td;
+ rpmTagVal sizetag;
+ rpmTagVal payloadtag;
+
+ /* Transfer header reference form *hdrp to h. */
+ h = headerLink(*hdrp);
+ *hdrp = headerFree(*hdrp);
+
+ if (pkgidp)
+ *pkgidp = NULL;
+
+ /* Save payload information */
+ if (headerIsSource(h))
+ rpmio_flags = rpmExpand("%{?_source_payload}", NULL);
+ else
+ rpmio_flags = rpmExpand("%{?_binary_payload}", NULL);
+
+ if (!(rpmio_flags && *rpmio_flags)) {
+ rpmio_flags = _free(rpmio_flags);
+ rpmio_flags = xstrdup("w9.gzdio");
+ }
+ s = strchr(rpmio_flags, '.');
+ if (s) {
+ const char *compr = NULL;
+ headerPutString(h, RPMTAG_PAYLOADFORMAT, "cpio");
+
+ if (rstreq(s+1, "ufdio")) {
+ compr = NULL;
+ } else if (rstreq(s+1, "gzdio")) {
+ compr = "gzip";
+#if HAVE_BZLIB_H
+ } else if (rstreq(s+1, "bzdio")) {
+ compr = "bzip2";
+ /* Add prereq on rpm version that understands bzip2 payloads */
+ (void) rpmlibNeedsFeature(h, "PayloadIsBzip2", "3.0.5-1");
+#endif
+#if HAVE_LZMA_H
+ } else if (rstreq(s+1, "xzdio")) {
+ compr = "xz";
+ (void) rpmlibNeedsFeature(h, "PayloadIsXz", "5.2-1");
+ } else if (rstreq(s+1, "lzdio")) {
+ compr = "lzma";
+ (void) rpmlibNeedsFeature(h, "PayloadIsLzma", "4.4.6-1");
+#endif
+ } else {
+ rpmlog(RPMLOG_ERR, _("Unknown payload compression: %s\n"),
+ rpmio_flags);
+ rc = RPMRC_FAIL;
+ goto exit;
+ }
+
+ if (compr)
+ headerPutString(h, RPMTAG_PAYLOADCOMPRESSOR, compr);
+ buf = xstrdup(rpmio_flags);
+ buf[s - rpmio_flags] = '\0';
+ headerPutString(h, RPMTAG_PAYLOADFLAGS, buf+1);
+ free(buf);
+ }
+
+ /* Create and add the cookie */
+ if (cookie) {
+ rasprintf(cookie, "%s %d", buildHost(), (int) (*getBuildTime()));
+ headerPutString(h, RPMTAG_COOKIE, *cookie);
+ }
+
+ /* Reallocate the header into one contiguous region. */
+ h = headerReload(h, RPMTAG_HEADERIMMUTABLE);
+ if (h == NULL) { /* XXX can't happen */
+ rc = RPMRC_FAIL;
+ rpmlog(RPMLOG_ERR, _("Unable to create immutable header region.\n"));
+ goto exit;
+ }
+ /* Re-reference reallocated header. */
+ *hdrp = headerLink(h);
+
+ /*
+ * Write the header+archive into a temp file so that the size of
+ * archive (after compression) can be added to the header.
+ */
+ fd = rpmMkTempFile(NULL, &sigtarget);
+ if (fd == NULL || Ferror(fd)) {
+ rc = RPMRC_FAIL;
+ rpmlog(RPMLOG_ERR, _("Unable to open temp file.\n"));
+ goto exit;
+ }
+
+ fdInitDigest(fd, PGPHASHALGO_SHA1, 0);
+ if (headerWrite(fd, h, HEADER_MAGIC_YES)) {
+ rc = RPMRC_FAIL;
+ rpmlog(RPMLOG_ERR, _("Unable to write temp header\n"));
+ } else { /* Write the archive and get the size */
+ (void) Fflush(fd);
+ fdFiniDigest(fd, PGPHASHALGO_SHA1, (void **)&SHA1, NULL, 1);
+ if (csa->cpioList != NULL) {
+ rc = cpio_doio(fd, h, csa, rpmio_flags);
+ } else if (Fileno(csa->cpioFdIn) >= 0) {
+ rc = cpio_copy(fd, csa);
+ } else {
+ rc = RPMRC_FAIL;
+ rpmlog(RPMLOG_ERR, _("Bad CSA data\n"));
+ }
+ }
+
+ if (rc != RPMRC_OK)
+ goto exit;
+
+ (void) Fclose(fd);
+ fd = NULL;
+ (void) unlink(fileName);
+
+ /* Generate the signature */
+ (void) fflush(stdout);
+ sig = rpmNewSignature();
+
+ /*
+ * There should be rpmlib() dependency on this, but that doesn't
+ * really do much good as these are signature tags that get read
+ * way before dependency checking has a chance to figure out anything.
+ * On the positive side, not inserting the 32bit tag at all means
+ * older rpm will just bail out with error message on attempt to read
+ * such a package.
+ */
+ if (csa->cpioArchiveSize < UINT32_MAX) {
+ sizetag = RPMSIGTAG_SIZE;
+ payloadtag = RPMSIGTAG_PAYLOADSIZE;
+ } else {
+ sizetag = RPMSIGTAG_LONGSIZE;
+ payloadtag = RPMSIGTAG_LONGARCHIVESIZE;
+ }
+ (void) rpmGenDigest(sig, sigtarget, sizetag);
+ (void) rpmGenDigest(sig, sigtarget, RPMSIGTAG_MD5);
+
+ if (SHA1) {
+ /* XXX can't use rpmtdFromFoo() on RPMSIGTAG_* items */
+ rpmtdReset(&td);
+ td.tag = RPMSIGTAG_SHA1;
+ td.type = RPM_STRING_TYPE;
+ td.data = SHA1;
+ td.count = 1;
+ headerPut(sig, &td, HEADERPUT_DEFAULT);
+ SHA1 = _free(SHA1);
+ }
+
+ {
+ /* XXX can't use headerPutType() on legacy RPMSIGTAG_* items */
+ rpmtdReset(&td);
+ td.tag = payloadtag;
+ td.count = 1;
+ if (payloadtag == RPMSIGTAG_PAYLOADSIZE) {
+ rpm_off_t asize = csa->cpioArchiveSize;
+ td.type = RPM_INT32_TYPE;
+ td.data = &asize;
+ headerPut(sig, &td, HEADERPUT_DEFAULT);
+ } else {
+ rpm_loff_t asize = csa->cpioArchiveSize;
+ td.type = RPM_INT64_TYPE;
+ td.data = &asize;
+ headerPut(sig, &td, HEADERPUT_DEFAULT);
+ }
+ }
+
+ /* Reallocate the signature into one contiguous region. */
+ sig = headerReload(sig, RPMTAG_HEADERSIGNATURES);
+ if (sig == NULL) { /* XXX can't happen */
+ rc = RPMRC_FAIL;
+ rpmlog(RPMLOG_ERR, _("Unable to reload signature header.\n"));
+ goto exit;
+ }
+
+ /* Open the output file */
+ fd = Fopen(fileName, "w.ufdio");
+ if (fd == NULL || Ferror(fd)) {
+ rc = RPMRC_FAIL;
+ rpmlog(RPMLOG_ERR, _("Could not open %s: %s\n"),
+ fileName, Fstrerror(fd));
+ goto exit;
+ }
+
+ /* Write the lead section into the package. */
+ {
+ rpmlead lead = rpmLeadFromHeader(h);
+ rc = rpmLeadWrite(fd, lead);
+ lead = rpmLeadFree(lead);
+ if (rc != RPMRC_OK) {
+ rc = RPMRC_FAIL;
+ rpmlog(RPMLOG_ERR, _("Unable to write package: %s\n"),
+ Fstrerror(fd));
+ goto exit;
+ }
+ }
+
+ /* Write the signature section into the package. */
+ if (rpmWriteSignature(fd, sig)) {
+ rc = RPMRC_FAIL;
+ goto exit;
+ }
+
+ /* Append the header and archive */
+ ifd = Fopen(sigtarget, "r.ufdio");
+ if (ifd == NULL || Ferror(ifd)) {
+ rc = RPMRC_FAIL;
+ rpmlog(RPMLOG_ERR, _("Unable to open sigtarget %s: %s\n"),
+ sigtarget, Fstrerror(ifd));
+ goto exit;
+ }
+
+ /* Add signatures to header, and write header into the package. */
+ /* XXX header+payload digests/signatures might be checked again here. */
+ { Header nh = headerRead(ifd, HEADER_MAGIC_YES);
+
+ if (nh == NULL) {
+ rc = RPMRC_FAIL;
+ rpmlog(RPMLOG_ERR, _("Unable to read header from %s: %s\n"),
+ sigtarget, Fstrerror(ifd));
+ goto exit;
+ }
+
+#ifdef NOTYET
+ (void) headerMergeLegacySigs(nh, sig);
+#endif
+
+ xx = headerWrite(fd, nh, HEADER_MAGIC_YES);
+ nh = headerFree(nh);
+
+ if (xx) {
+ rc = RPMRC_FAIL;
+ rpmlog(RPMLOG_ERR, _("Unable to write header to %s: %s\n"),
+ fileName, Fstrerror(fd));
+ goto exit;
+ }
+ }
+
+ /* Write the payload into the package. */
+ buf = xmalloc(BUFSIZ);
+ while ((count = Fread(buf, 1, BUFSIZ, ifd)) > 0) {
+ if (count == -1) {
+ free(buf);
+ rc = RPMRC_FAIL;
+ rpmlog(RPMLOG_ERR, _("Unable to read payload from %s: %s\n"),
+ sigtarget, Fstrerror(ifd));
+ goto exit;
+ }
+ if (Fwrite(buf, sizeof(buf[0]), count, fd) != count) {
+ free(buf);
+ rc = RPMRC_FAIL;
+ rpmlog(RPMLOG_ERR, _("Unable to write payload to %s: %s\n"),
+ fileName, Fstrerror(fd));
+ goto exit;
+ }
+ }
+ free(buf);
+ rc = RPMRC_OK;
+
+exit:
+ rpmio_flags = _free(rpmio_flags);
+ SHA1 = _free(SHA1);
+ h = headerFree(h);
+
+ /* XXX Fish the pkgid out of the signature header. */
+ if (sig != NULL && pkgidp != NULL) {
+ struct rpmtd_s md5tag;
+ headerGet(sig, RPMSIGTAG_MD5, &md5tag, HEADERGET_DEFAULT);
+ if (rpmtdType(&md5tag) == RPM_BIN_TYPE &&
+ md5tag.count == 16 && md5tag.data != NULL) {
+ *pkgidp = md5tag.data;
+ }
+ }
+
+ sig = rpmFreeSignature(sig);
+ if (ifd) {
+ (void) Fclose(ifd);
+ ifd = NULL;
+ }
+ if (fd) {
+ (void) Fclose(fd);
+ fd = NULL;
+ }
+ if (sigtarget) {
+ (void) unlink(sigtarget);
+ sigtarget = _free(sigtarget);
+ }
+
+ if (rc == RPMRC_OK)
+ rpmlog(RPMLOG_NOTICE, _("Wrote: %s\n"), fileName);
+ else
+ (void) unlink(fileName);
+
+ return rc;
+}
+
+static const rpmTagVal copyTags[] = {
+ RPMTAG_CHANGELOGTIME,
+ RPMTAG_CHANGELOGNAME,
+ RPMTAG_CHANGELOGTEXT,
+ 0
+};
+
+/*
+ * Add extra provides to package.
+ */
+static void addPackageProvides(Header h)
+{
+ const char *arch, *name;
+ char *evr, *isaprov;
+ rpmsenseFlags pflags = RPMSENSE_EQUAL;
+
+ /* <name> = <evr> provide */
+ name = headerGetString(h, RPMTAG_NAME);
+ arch = headerGetString(h, RPMTAG_ARCH);
+ evr = headerGetAsString(h, RPMTAG_EVR);
+ headerPutString(h, RPMTAG_PROVIDENAME, name);
+ headerPutString(h, RPMTAG_PROVIDEVERSION, evr);
+ headerPutUint32(h, RPMTAG_PROVIDEFLAGS, &pflags, 1);
+
+ /*
+ * <name>(<isa>) = <evr> provide
+ * FIXME: noarch needs special casing for now as BuildArch: noarch doesn't
+ * cause reading in the noarch macros :-/
+ */
+ isaprov = rpmExpand(name, "%{?_isa}", NULL);
+ if (!rstreq(arch, "noarch") && !rstreq(name, isaprov)) {
+ headerPutString(h, RPMTAG_PROVIDENAME, isaprov);
+ headerPutString(h, RPMTAG_PROVIDEVERSION, evr);
+ headerPutUint32(h, RPMTAG_PROVIDEFLAGS, &pflags, 1);
+ }
+ free(isaprov);
+ free(evr);
+}
+
+static rpmRC checkPackages(char *pkgcheck)
+{
+ int fail = rpmExpandNumeric("%{?_nonzero_exit_pkgcheck_terminate_build}");
+ int xx;
+
+ rpmlog(RPMLOG_NOTICE, _("Executing \"%s\":\n"), pkgcheck);
+ xx = system(pkgcheck);
+ if (WEXITSTATUS(xx) == -1 || WEXITSTATUS(xx) == 127) {
+ rpmlog(RPMLOG_ERR, _("Execution of \"%s\" failed.\n"), pkgcheck);
+ if (fail) return RPMRC_NOTFOUND;
+ }
+ if (WEXITSTATUS(xx) != 0) {
+ rpmlog(RPMLOG_ERR, _("Package check \"%s\" failed.\n"), pkgcheck);
+ if (fail) return RPMRC_FAIL;
+ }
+
+ return RPMRC_OK;
+}
+
+rpmRC packageBinaries(rpmSpec spec, const char *cookie, int cheating)
+{
+ struct cpioSourceArchive_s csabuf;
+ CSA_t csa = &csabuf;
+ rpmRC rc;
+ const char *errorString;
+ Package pkg;
+ char *pkglist = NULL;
+
+ for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
+ char *fn;
+
+ if (pkg->fileList == NULL)
+ continue;
+
+ if ((rc = processScriptFiles(spec, pkg)))
+ return rc;
+
+ if (cookie) {
+ headerPutString(pkg->header, RPMTAG_COOKIE, cookie);
+ }
+
+ /* Copy changelog from src rpm */
+ headerCopyTags(spec->packages->header, pkg->header, copyTags);
+
+ headerPutString(pkg->header, RPMTAG_RPMVERSION, VERSION);
+ headerPutString(pkg->header, RPMTAG_BUILDHOST, buildHost());
+ headerPutUint32(pkg->header, RPMTAG_BUILDTIME, getBuildTime(), 1);
+
+ addPackageProvides(pkg->header);
+
+ { char * optflags = rpmExpand("%{optflags}", NULL);
+ headerPutString(pkg->header, RPMTAG_OPTFLAGS, optflags);
+ optflags = _free(optflags);
+ }
+
+ if (spec->sourcePkgId != NULL) {
+ headerPutBin(pkg->header, RPMTAG_SOURCEPKGID, spec->sourcePkgId,16);
+ }
+
+ if (cheating) {
+ (void) rpmlibNeedsFeature(pkg->header, "ShortCircuited", "4.9.0-1");
+ }
+
+ { char *binFormat = rpmGetPath("%{_rpmfilename}", NULL);
+ char *binRpm, *binDir;
+ binRpm = headerFormat(pkg->header, binFormat, &errorString);
+ binFormat = _free(binFormat);
+ if (binRpm == NULL) {
+ rpmlog(RPMLOG_ERR, _("Could not generate output "
+ "filename for package %s: %s\n"),
+ headerGetString(pkg->header, RPMTAG_NAME), errorString);
+ return RPMRC_FAIL;
+ }
+ fn = rpmGetPath("%{_rpmdir}/", binRpm, NULL);
+ if ((binDir = strchr(binRpm, '/')) != NULL) {
+ struct stat st;
+ char *dn;
+ *binDir = '\0';
+ dn = rpmGetPath("%{_rpmdir}/", binRpm, NULL);
+ if (stat(dn, &st) < 0) {
+ switch(errno) {
+ case ENOENT:
+ if (mkdir(dn, 0755) == 0)
+ break;
+ default:
+ rpmlog(RPMLOG_ERR,_("cannot create %s: %s\n"),
+ dn, strerror(errno));
+ break;
+ }
+ }
+ dn = _free(dn);
+ }
+ binRpm = _free(binRpm);
+ }
+
+ memset(csa, 0, sizeof(*csa));
+ csa->cpioArchiveSize = 0;
+ csa->cpioFdIn = fdNew();
+ csa->cpioList = rpmfiLink(pkg->cpioList);
+
+ rc = writeRPM(&pkg->header, NULL, fn, csa, NULL);
+ csa->cpioList = rpmfiFree(csa->cpioList);
+ csa->cpioFdIn = fdFree(csa->cpioFdIn);
+ if (rc == RPMRC_OK) {
+ /* Do check each written package if enabled */
+ char *pkgcheck = rpmExpand("%{?_build_pkgcheck} ", fn, NULL);
+ if (pkgcheck[0] != ' ') {
+ rc = checkPackages(pkgcheck);
+ }
+ pkgcheck = _free(pkgcheck);
+ rstrcat(&pkglist, fn);
+ rstrcat(&pkglist, " ");
+ }
+ fn = _free(fn);
+ if (rc != RPMRC_OK) {
+ pkglist = _free(pkglist);
+ return rc;
+ }
+ }
+
+ /* Now check the package set if enabled */
+ if (pkglist != NULL) {
+ char *pkgcheck_set = rpmExpand("%{?_build_pkgcheck_set} ", pkglist, NULL);
+ if (pkgcheck_set[0] != ' ') { /* run only if _build_pkgcheck_set is defined */
+ checkPackages(pkgcheck_set);
+ }
+ pkgcheck_set = _free(pkgcheck_set);
+ pkglist = _free(pkglist);
+ }
+
+ return RPMRC_OK;
+}
+
+rpmRC packageSources(rpmSpec spec, char **cookie)
+{
+ struct cpioSourceArchive_s csabuf;
+ CSA_t csa = &csabuf;
+ rpmRC rc;
+
+ /* Add some cruft */
+ headerPutString(spec->sourceHeader, RPMTAG_RPMVERSION, VERSION);
+ headerPutString(spec->sourceHeader, RPMTAG_BUILDHOST, buildHost());
+ headerPutUint32(spec->sourceHeader, RPMTAG_BUILDTIME, getBuildTime(), 1);
+
+ /* XXX this should be %_srpmdir */
+ { char *fn = rpmGetPath("%{_srcrpmdir}/", spec->sourceRpmName,NULL);
+ char *pkgcheck = rpmExpand("%{?_build_pkgcheck_srpm} ", fn, NULL);
+
+ memset(csa, 0, sizeof(*csa));
+ csa->cpioArchiveSize = 0;
+ csa->cpioFdIn = fdNew();
+ csa->cpioList = rpmfiLink(spec->sourceCpioList);
+
+ spec->sourcePkgId = NULL;
+ rc = writeRPM(&spec->sourceHeader, &spec->sourcePkgId, fn, csa, cookie);
+
+ /* Do check SRPM package if enabled */
+ if (rc == RPMRC_OK && pkgcheck[0] != ' ') {
+ rc = checkPackages(pkgcheck);
+ }
+
+ csa->cpioList = rpmfiFree(csa->cpioList);
+ csa->cpioFdIn = fdFree(csa->cpioFdIn);
+ pkgcheck = _free(pkgcheck);
+ fn = _free(fn);
+ }
+ return rc;
+}
diff --git a/build/parseBuildInstallClean.c b/build/parseBuildInstallClean.c
new file mode 100644
index 0000000..6386c23
--- /dev/null
+++ b/build/parseBuildInstallClean.c
@@ -0,0 +1,64 @@
+/** \ingroup rpmbuild
+ * \file build/parseBuildInstallClean.c
+ * Parse %build/%install/%clean section from spec file.
+ */
+#include "system.h"
+
+#include <rpm/rpmlog.h>
+#include "build/rpmbuild_internal.h"
+#include "debug.h"
+
+
+int parseBuildInstallClean(rpmSpec spec, int parsePart)
+{
+ int nextPart, rc, res = PART_ERROR;
+ StringBuf *sbp = NULL;
+ const char *name = NULL;
+
+ if (parsePart == PART_BUILD) {
+ sbp = &(spec->build);
+ name = "%build";
+ } else if (parsePart == PART_INSTALL) {
+ sbp = &(spec->install);
+ name = "%install";
+ } else if (parsePart == PART_CHECK) {
+ sbp = &(spec->check);
+ name = "%check";
+ } else if (parsePart == PART_CLEAN) {
+ sbp = &(spec->clean);
+ name = "%clean";
+ } else {
+ goto exit; /* programmer error */
+ }
+
+ if (*sbp != NULL) {
+ rpmlog(RPMLOG_ERR, _("line %d: second %s\n"),
+ spec->lineNum, name);
+ goto exit;
+ }
+
+ *sbp = newStringBuf();
+
+ /* There are no options to %build, %install, %check, or %clean */
+ if ((rc = readLine(spec, STRIP_NOTHING)) > 0) {
+ res = PART_NONE;
+ goto exit;
+ } else if (rc < 0) {
+ goto exit;
+ }
+
+ while (! (nextPart = isPart(spec->line))) {
+ appendStringBuf(*sbp, spec->line);
+ if ((rc = readLine(spec, STRIP_NOTHING)) > 0) {
+ nextPart = PART_NONE;
+ break;
+ } else if (rc < 0) {
+ goto exit;
+ }
+ }
+ res = nextPart;
+
+exit:
+
+ return res;
+}
diff --git a/build/parseChangelog.c b/build/parseChangelog.c
new file mode 100644
index 0000000..d4681cb
--- /dev/null
+++ b/build/parseChangelog.c
@@ -0,0 +1,245 @@
+/** \ingroup rpmbuild
+ * \file build/parseChangelog.c
+ * Parse %changelog section from spec file.
+ */
+
+#include "system.h"
+
+#include <rpm/header.h>
+#include <rpm/rpmlog.h>
+#include "build/rpmbuild_internal.h"
+#include "debug.h"
+
+#define SKIPSPACE(s) { while (*(s) && risspace(*(s))) (s)++; }
+#define SKIPNONSPACE(s) { while (*(s) && !risspace(*(s))) (s)++; }
+
+static void addChangelogEntry(Header h, time_t time, const char *name, const char *text)
+{
+ rpm_time_t mytime = time; /* XXX convert to header representation */
+
+ headerPutUint32(h, RPMTAG_CHANGELOGTIME, &mytime, 1);
+ headerPutString(h, RPMTAG_CHANGELOGNAME, name);
+ headerPutString(h, RPMTAG_CHANGELOGTEXT, text);
+}
+
+/**
+ * Parse date string to seconds.
+ * @param datestr date string (e.g. 'Wed Jan 1 1997')
+ * @retval secs secs since the unix epoch
+ * @return 0 on success, -1 on error
+ */
+static int dateToTimet(const char * datestr, time_t * secs)
+{
+ int rc = -1; /* assume failure */
+ struct tm time;
+ const char * const * idx;
+ char *p, *pe, *q, *date, *tz;
+
+ static const char * const days[] =
+ { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL };
+ static const char * const months[] =
+ { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL };
+ static const char lengths[] =
+ { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+
+ memset(&time, 0, sizeof(time));
+
+ date = xstrdup(datestr);
+ pe = date;
+
+ /* day of week */
+ p = pe; SKIPSPACE(p);
+ if (*p == '\0') goto exit;
+ pe = p; SKIPNONSPACE(pe); if (*pe != '\0') *pe++ = '\0';
+ for (idx = days; *idx && !rstreq(*idx, p); idx++)
+ {};
+ if (*idx == NULL) goto exit;
+
+ /* month */
+ p = pe; SKIPSPACE(p);
+ if (*p == '\0') goto exit;
+ pe = p; SKIPNONSPACE(pe); if (*pe != '\0') *pe++ = '\0';
+ for (idx = months; *idx && !rstreq(*idx, p); idx++)
+ {};
+ if (*idx == NULL) goto exit;
+ time.tm_mon = idx - months;
+
+ /* day */
+ p = pe; SKIPSPACE(p);
+ if (*p == '\0') goto exit;
+ pe = p; SKIPNONSPACE(pe); if (*pe != '\0') *pe++ = '\0';
+
+ /* make this noon so the day is always right (as we make this UTC) */
+ time.tm_hour = 12;
+
+ time.tm_mday = strtol(p, &q, 10);
+ if (!(q && *q == '\0')) goto exit;
+ if (time.tm_mday < 0 || time.tm_mday > lengths[time.tm_mon]) goto exit;
+
+ /* year */
+ p = pe; SKIPSPACE(p);
+ if (*p == '\0') goto exit;
+ pe = p; SKIPNONSPACE(pe); if (*pe != '\0') *pe++ = '\0';
+ time.tm_year = strtol(p, &q, 10);
+ if (!(q && *q == '\0')) goto exit;
+ if (time.tm_year < 1990 || time.tm_year >= 3000) goto exit;
+ time.tm_year -= 1900;
+
+ /* chnagelog date is always in UTC */
+ tz = getenv("TZ");
+ if (tz) tz = xstrdup(tz);
+ setenv("TZ", "UTC", 1);
+ *secs = mktime(&time);
+ unsetenv("TZ");
+ if (tz) {
+ setenv("TZ", tz, 1);
+ free(tz);
+ }
+ if (*secs == -1) goto exit;
+
+ rc = 0;
+
+exit:
+ free(date);
+ return rc;
+}
+
+/**
+ * Add %changelog section to header.
+ * @param h header
+ * @param sb changelog strings
+ * @return RPMRC_OK on success
+ */
+static rpmRC addChangelog(Header h, ARGV_const_t sb)
+{
+ char *s, *sp;
+ int i;
+ time_t time;
+ time_t lastTime = 0;
+ time_t trimtime = rpmExpandNumeric("%{?_changelog_trimtime}");
+ char *date, *name, *text, *next;
+
+ s = sp = argvJoin(sb, "");
+
+ /* skip space */
+ SKIPSPACE(s);
+
+ while (*s != '\0') {
+ if (*s != '*') {
+ rpmlog(RPMLOG_ERR,
+ _("%%changelog entries must start with *\n"));
+ return RPMRC_FAIL;
+ }
+
+ /* find end of line */
+ date = s;
+ while(*s && *s != '\n') s++;
+ if (! *s) {
+ rpmlog(RPMLOG_ERR, _("incomplete %%changelog entry\n"));
+ return RPMRC_FAIL;
+ }
+ *s = '\0';
+ text = s + 1;
+
+ /* 4 fields of date */
+ date++;
+ s = date;
+ for (i = 0; i < 4; i++) {
+ SKIPSPACE(s);
+ SKIPNONSPACE(s);
+ }
+ SKIPSPACE(date);
+ if (dateToTimet(date, &time)) {
+ rpmlog(RPMLOG_ERR, _("bad date in %%changelog: %s\n"), date);
+ return RPMRC_FAIL;
+ }
+ if (lastTime && lastTime < time) {
+ rpmlog(RPMLOG_ERR,
+ _("%%changelog not in descending chronological order\n"));
+ return RPMRC_FAIL;
+ }
+ lastTime = time;
+
+ /* skip space to the name */
+ SKIPSPACE(s);
+ if (! *s) {
+ rpmlog(RPMLOG_ERR, _("missing name in %%changelog\n"));
+ return RPMRC_FAIL;
+ }
+
+ /* name */
+ name = s;
+ while (*s != '\0') s++;
+ while (s > name && risspace(*s)) {
+ *s-- = '\0';
+ }
+ if (s == name) {
+ rpmlog(RPMLOG_ERR, _("missing name in %%changelog\n"));
+ return RPMRC_FAIL;
+ }
+
+ /* text */
+ SKIPSPACE(text);
+ if (! *text) {
+ rpmlog(RPMLOG_ERR, _("no description in %%changelog\n"));
+ return RPMRC_FAIL;
+ }
+
+ /* find the next leading '*' (or eos) */
+ s = text;
+ do {
+ s++;
+ } while (*s && (*(s-1) != '\n' || *s != '*'));
+ next = s;
+ s--;
+
+ /* backup to end of description */
+ while ((s > text) && risspace(*s)) {
+ *s-- = '\0';
+ }
+
+ if ( !trimtime || time >= trimtime ) {
+ addChangelogEntry(h, time, name, text);
+ } else break;
+
+ s = next;
+ }
+ free(sp);
+
+ return RPMRC_OK;
+}
+
+int parseChangelog(rpmSpec spec)
+{
+ int nextPart, rc, res = PART_ERROR;
+ ARGV_t sb = NULL;
+
+ /* There are no options to %changelog */
+ if ((rc = readLine(spec, STRIP_COMMENTS)) > 0) {
+ res = PART_NONE;
+ goto exit;
+ } else if (rc < 0) {
+ goto exit;
+ }
+
+ while (! (nextPart = isPart(spec->line))) {
+ argvAdd(&sb, spec->line);
+ if ((rc = readLine(spec, STRIP_COMMENTS)) > 0) {
+ nextPart = PART_NONE;
+ break;
+ } else if (rc < 0) {
+ goto exit;
+ }
+ }
+
+ if (sb && addChangelog(spec->packages->header, sb)) {
+ goto exit;
+ }
+ res = nextPart;
+
+exit:
+ argvFree(sb);
+
+ return res;
+}
diff --git a/build/parseDescription.c b/build/parseDescription.c
new file mode 100644
index 0000000..f233d8d
--- /dev/null
+++ b/build/parseDescription.c
@@ -0,0 +1,113 @@
+/** \ingroup rpmbuild
+ * \file build/parseDescription.c
+ * Parse %description section from spec file.
+ */
+
+#include "system.h"
+
+#include <rpm/header.h>
+#include <rpm/rpmlog.h>
+#include "build/rpmbuild_internal.h"
+#include "debug.h"
+
+int parseDescription(rpmSpec spec)
+{
+ int nextPart = PART_ERROR; /* assume error */
+ StringBuf sb = NULL;
+ int flag = PART_SUBNAME;
+ Package pkg;
+ int rc, argc;
+ int arg;
+ const char **argv = NULL;
+ const char *name = NULL;
+ const char *lang = RPMBUILD_DEFAULT_LANG;
+ poptContext optCon = NULL;
+ struct poptOption optionsTable[] = {
+ { NULL, 'n', POPT_ARG_STRING, &name, 'n', NULL, NULL},
+ { NULL, 'l', POPT_ARG_STRING, &lang, 'l', NULL, NULL},
+ { 0, 0, 0, 0, 0, NULL, NULL}
+ };
+
+ if ((rc = poptParseArgvString(spec->line, &argc, &argv))) {
+ rpmlog(RPMLOG_ERR, _("line %d: Error parsing %%description: %s\n"),
+ spec->lineNum, poptStrerror(rc));
+ return PART_ERROR;
+ }
+
+ optCon = poptGetContext(NULL, argc, argv, optionsTable, 0);
+ while ((arg = poptGetNextOpt(optCon)) > 0) {
+ if (arg == 'n') {
+ flag = PART_NAME;
+ }
+ }
+
+ if (arg < -1) {
+ rpmlog(RPMLOG_ERR, _("line %d: Bad option %s: %s\n"),
+ spec->lineNum,
+ poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
+ spec->line);
+ goto exit;
+ }
+
+ if (poptPeekArg(optCon)) {
+ if (name == NULL)
+ name = poptGetArg(optCon);
+ if (poptPeekArg(optCon)) {
+ rpmlog(RPMLOG_ERR, _("line %d: Too many names: %s\n"),
+ spec->lineNum,
+ spec->line);
+ goto exit;
+ }
+ }
+
+ if (lookupPackage(spec, name, flag, &pkg)) {
+ rpmlog(RPMLOG_ERR, _("line %d: Package does not exist: %s\n"),
+ spec->lineNum, spec->line);
+ goto exit;
+ }
+
+
+ /******************/
+
+#if 0
+ if (headerIsEntry(pkg->header, RPMTAG_DESCRIPTION)) {
+ rpmlog(RPMLOG_ERR, _("line %d: Second description\n"),
+ spec->lineNum);
+ goto exit;
+ }
+#endif
+
+ sb = newStringBuf();
+
+ if ((rc = readLine(spec, STRIP_TRAILINGSPACE | STRIP_COMMENTS)) > 0) {
+ nextPart = PART_NONE;
+ } else if (rc < 0) {
+ nextPart = PART_ERROR;
+ goto exit;
+ } else {
+ while (! (nextPart = isPart(spec->line))) {
+ appendLineStringBuf(sb, spec->line);
+ if ((rc =
+ readLine(spec, STRIP_TRAILINGSPACE | STRIP_COMMENTS)) > 0) {
+ nextPart = PART_NONE;
+ break;
+ } else if (rc < 0) {
+ nextPart = PART_ERROR;
+ goto exit;
+ }
+ }
+ }
+
+ stripTrailingBlanksStringBuf(sb);
+ if (!((spec->flags & RPMSPEC_NOLANG) && !rstreq(lang, RPMBUILD_DEFAULT_LANG))) {
+ (void) headerAddI18NString(pkg->header, RPMTAG_DESCRIPTION,
+ getStringBuf(sb), lang);
+ }
+
+
+exit:
+ freeStringBuf(sb);
+ free(argv);
+ poptFreeContext(optCon);
+ return nextPart;
+}
diff --git a/build/parseFiles.c b/build/parseFiles.c
new file mode 100644
index 0000000..7c51c3a
--- /dev/null
+++ b/build/parseFiles.c
@@ -0,0 +1,99 @@
+/** \ingroup rpmbuild
+ * \file build/parseFiles.c
+ * Parse %files section from spec file.
+ */
+
+#include "system.h"
+
+#include <rpm/rpmlog.h>
+#include <rpm/rpmfileutil.h>
+#include "build/rpmbuild_internal.h"
+#include "debug.h"
+
+int parseFiles(rpmSpec spec)
+{
+ int nextPart, res = PART_ERROR;
+ Package pkg;
+ int rc, argc;
+ int arg;
+ const char ** argv = NULL;
+ const char *name = NULL;
+ int flag = PART_SUBNAME;
+ poptContext optCon = NULL;
+ struct poptOption optionsTable[] = {
+ { NULL, 'n', POPT_ARG_STRING, &name, 'n', NULL, NULL},
+ { NULL, 'f', POPT_ARG_STRING, NULL, 'f', NULL, NULL},
+ { 0, 0, 0, 0, 0, NULL, NULL}
+ };
+
+ if ((rc = poptParseArgvString(spec->line, &argc, &argv))) {
+ rpmlog(RPMLOG_ERR, _("line %d: Error parsing %%files: %s\n"),
+ spec->lineNum, poptStrerror(rc));
+ goto exit;
+ }
+
+ optCon = poptGetContext(NULL, argc, argv, optionsTable, 0);
+ while ((arg = poptGetNextOpt(optCon)) > 0) {
+ if (arg == 'n') {
+ flag = PART_NAME;
+ }
+ }
+
+ if (arg < -1) {
+ rpmlog(RPMLOG_ERR, _("line %d: Bad option %s: %s\n"),
+ spec->lineNum,
+ poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
+ spec->line);
+ goto exit;
+ }
+
+ if (poptPeekArg(optCon)) {
+ if (name == NULL)
+ name = poptGetArg(optCon);
+ if (poptPeekArg(optCon)) {
+ rpmlog(RPMLOG_ERR, _("line %d: Too many names: %s\n"),
+ spec->lineNum,
+ spec->line);
+ goto exit;
+ }
+ }
+
+ if (lookupPackage(spec, name, flag, &pkg)) {
+ rpmlog(RPMLOG_ERR, _("line %d: Package does not exist: %s\n"),
+ spec->lineNum, spec->line);
+ goto exit;
+ }
+
+ for (arg=1; arg<argc; arg++) {
+ if (rstreq(argv[arg], "-f") && argv[arg+1]) {
+ char *file = rpmGetPath(argv[arg+1], NULL);
+ argvAdd(&(pkg->fileFile), file);
+ free(file);
+ }
+ }
+
+ pkg->fileList = argvNew();
+
+ if ((rc = readLine(spec, STRIP_COMMENTS)) > 0) {
+ nextPart = PART_NONE;
+ } else if (rc < 0) {
+ goto exit;
+ } else {
+ while (! (nextPart = isPart(spec->line))) {
+ argvAdd(&(pkg->fileList), spec->line);
+ if ((rc = readLine(spec, STRIP_COMMENTS)) > 0) {
+ nextPart = PART_NONE;
+ break;
+ } else if (rc < 0) {
+ goto exit;
+ }
+ }
+ }
+ res = nextPart;
+
+exit:
+ argv = _free(argv);
+ optCon = poptFreeContext(optCon);
+
+ return res;
+}
diff --git a/build/parsePolicies.c b/build/parsePolicies.c
new file mode 100644
index 0000000..2abc00c
--- /dev/null
+++ b/build/parsePolicies.c
@@ -0,0 +1,89 @@
+/** \ingroup rpmbuild
+ * \file build/parsePolicies.c
+ * Parse %policies section from spec file.
+ */
+
+#include "system.h"
+
+#include <rpm/header.h>
+#include <rpm/rpmbuild.h>
+#include <rpm/rpmlog.h>
+#include <rpm/rpmfileutil.h>
+#include "build/rpmbuild_internal.h"
+#include "debug.h"
+
+int parsePolicies(rpmSpec spec)
+{
+ int nextPart, res = PART_ERROR;
+ Package pkg;
+ int rc, argc;
+ int arg;
+ const char **argv = NULL;
+ const char *name = NULL;
+ int flag = PART_SUBNAME;
+ poptContext optCon = NULL;
+
+ struct poptOption optionsTable[] = {
+ {NULL, 'n', POPT_ARG_STRING, &name, 'n', NULL, NULL},
+ {0, 0, 0, 0, 0, NULL, NULL}
+ };
+
+ if ((rc = poptParseArgvString(spec->line, &argc, &argv))) {
+ rpmlog(RPMLOG_ERR, _("line %d: Error parsing %%policies: %s\n"),
+ spec->lineNum, poptStrerror(rc));
+ goto exit;
+ }
+
+ optCon = poptGetContext(NULL, argc, argv, optionsTable, 0);
+ while ((arg = poptGetNextOpt(optCon)) > 0) {
+ if (arg == 'n') {
+ flag = PART_NAME;
+ }
+ }
+
+ if (arg < -1) {
+ rpmlog(RPMLOG_ERR, _("line %d: Bad option %s: %s\n"),
+ spec->lineNum,
+ poptBadOption(optCon, POPT_BADOPTION_NOALIAS), spec->line);
+ goto exit;
+ }
+
+ if (poptPeekArg(optCon)) {
+ if (name == NULL)
+ name = poptGetArg(optCon);
+ if (poptPeekArg(optCon)) {
+ rpmlog(RPMLOG_ERR, _("line %d: Too many names: %s\n"),
+ spec->lineNum, spec->line);
+ goto exit;
+ }
+ }
+
+ if (lookupPackage(spec, name, flag, &pkg)) {
+ rpmlog(RPMLOG_ERR, _("line %d: Package does not exist: %s\n"),
+ spec->lineNum, spec->line);
+ goto exit;
+ }
+
+ if ((rc = readLine(spec, STRIP_TRAILINGSPACE | STRIP_COMMENTS)) > 0) {
+ nextPart = PART_NONE;
+ } else if (rc < 0) {
+ goto exit;
+ } else {
+ while (!(nextPart = isPart(spec->line))) {
+ argvAdd(&(pkg->policyList), spec->line);
+ if ((rc = readLine(spec, STRIP_TRAILINGSPACE | STRIP_COMMENTS)) > 0) {
+ nextPart = PART_NONE;
+ break;
+ } else if (rc < 0) {
+ goto exit;
+ }
+ }
+ }
+ res = nextPart;
+
+ exit:
+ argv = _free(argv);
+ optCon = poptFreeContext(optCon);
+
+ return res;
+}
diff --git a/build/parsePreamble.c b/build/parsePreamble.c
new file mode 100644
index 0000000..e8e3133
--- /dev/null
+++ b/build/parsePreamble.c
@@ -0,0 +1,1090 @@
+/** \ingroup rpmbuild
+ * \file build/parsePreamble.c
+ * Parse tags in global section from spec file.
+ */
+
+#include "system.h"
+
+#include <ctype.h>
+
+#include <rpm/header.h>
+#include <rpm/rpmlog.h>
+#include <rpm/rpmfileutil.h>
+#include "rpmio/rpmlua.h"
+#include "build/rpmbuild_internal.h"
+#include "build/rpmbuild_misc.h"
+#include "debug.h"
+
+#define SKIPSPACE(s) { while (*(s) && risspace(*(s))) (s)++; }
+#define SKIPNONSPACE(s) { while (*(s) && !risspace(*(s))) (s)++; }
+#define SKIPWHITE(_x) {while(*(_x) && (risspace(*_x) || *(_x) == ',')) (_x)++;}
+#define SKIPNONWHITE(_x){while(*(_x) &&!(risspace(*_x) || *(_x) == ',')) (_x)++;}
+
+/**
+ */
+static const rpmTagVal copyTagsDuringParse[] = {
+ RPMTAG_EPOCH,
+ RPMTAG_VERSION,
+ RPMTAG_RELEASE,
+ RPMTAG_LICENSE,
+ RPMTAG_PACKAGER,
+ RPMTAG_DISTRIBUTION,
+ RPMTAG_DISTURL,
+ RPMTAG_VENDOR,
+ RPMTAG_ICON,
+ RPMTAG_URL,
+ RPMTAG_VCS,
+ RPMTAG_CHANGELOGTIME,
+ RPMTAG_CHANGELOGNAME,
+ RPMTAG_CHANGELOGTEXT,
+ RPMTAG_PREFIXES,
+ RPMTAG_DISTTAG,
+ RPMTAG_BUGURL,
+ RPMTAG_GROUP,
+ 0
+};
+
+/**
+ */
+static const rpmTagVal requiredTags[] = {
+ RPMTAG_NAME,
+ RPMTAG_VERSION,
+ RPMTAG_RELEASE,
+ RPMTAG_SUMMARY,
+ RPMTAG_LICENSE,
+ 0
+};
+
+/**
+ */
+static void addOrAppendListEntry(Header h, rpmTagVal tag, const char * line)
+{
+ int xx;
+ int argc;
+ const char **argv;
+
+ xx = poptParseArgvString(line, &argc, &argv);
+ if (argc)
+ headerPutStringArray(h, tag, argv, argc);
+ argv = _free(argv);
+}
+
+/* Parse a simple part line that only take -n <pkg> or <pkg> */
+/* <pkg> is returned in name as a pointer into a dynamic buffer */
+
+/**
+ */
+static int parseSimplePart(const char *line, char **name, int *flag)
+{
+ char *tok;
+ char *linebuf = xstrdup(line);
+ int rc;
+
+ /* Throw away the first token (the %xxxx) */
+ (void)strtok(linebuf, " \t\n");
+ *name = NULL;
+
+ if (!(tok = strtok(NULL, " \t\n"))) {
+ rc = 0;
+ goto exit;
+ }
+
+ if (rstreq(tok, "-n")) {
+ if (!(tok = strtok(NULL, " \t\n"))) {
+ rc = 1;
+ goto exit;
+ }
+ *flag = PART_NAME;
+ } else {
+ *flag = PART_SUBNAME;
+ }
+ *name = xstrdup(tok);
+ rc = strtok(NULL, " \t\n") ? 1 : 0;
+
+exit:
+ free(linebuf);
+ return rc;
+}
+
+/**
+ */
+static inline int parseYesNo(const char * s)
+{
+ return ((!s || (s[0] == 'n' || s[0] == 'N' || s[0] == '0') ||
+ !rstrcasecmp(s, "false") || !rstrcasecmp(s, "off"))
+ ? 0 : 1);
+}
+
+static struct Source *findSource(rpmSpec spec, uint32_t num, int flag)
+{
+ struct Source *p;
+
+ for (p = spec->sources; p != NULL; p = p->next)
+ if ((num == p->num) && (p->flags & flag)) return p;
+
+ return NULL;
+}
+
+static int parseNoSource(rpmSpec spec, const char * field, rpmTagVal tag)
+{
+ const char *f, *fe;
+ const char *name;
+ int flag;
+ uint32_t num;
+
+ if (tag == RPMTAG_NOSOURCE) {
+ flag = RPMBUILD_ISSOURCE;
+ name = "source";
+ } else {
+ flag = RPMBUILD_ISPATCH;
+ name = "patch";
+ }
+
+ fe = field;
+ for (f = fe; *f != '\0'; f = fe) {
+ struct Source *p;
+
+ SKIPWHITE(f);
+ if (*f == '\0')
+ break;
+ fe = f;
+ SKIPNONWHITE(fe);
+ if (*fe != '\0') fe++;
+
+ if (parseUnsignedNum(f, &num)) {
+ rpmlog(RPMLOG_ERR, _("line %d: Bad number: %s\n"),
+ spec->lineNum, f);
+ return RPMRC_FAIL;
+ }
+
+ if (! (p = findSource(spec, num, flag))) {
+ rpmlog(RPMLOG_ERR, _("line %d: Bad no%s number: %u\n"),
+ spec->lineNum, name, num);
+ return RPMRC_FAIL;
+ }
+
+ p->flags |= RPMBUILD_ISNO;
+
+ }
+
+ return 0;
+}
+
+static int addSource(rpmSpec spec, Package pkg, const char *field, rpmTagVal tag)
+{
+ struct Source *p;
+ int flag = 0;
+ const char *name = NULL;
+ char *nump;
+ char *fieldp = NULL;
+ char *buf = NULL;
+ uint32_t num = 0;
+
+ switch (tag) {
+ case RPMTAG_SOURCE:
+ flag = RPMBUILD_ISSOURCE;
+ name = "source";
+ fieldp = spec->line + 6;
+ break;
+ case RPMTAG_PATCH:
+ flag = RPMBUILD_ISPATCH;
+ name = "patch";
+ fieldp = spec->line + 5;
+ break;
+ case RPMTAG_ICON:
+ flag = RPMBUILD_ISICON;
+ fieldp = NULL;
+ break;
+ default:
+ return -1;
+ break;
+ }
+
+ /* Get the number */
+ if (tag != RPMTAG_ICON) {
+ /* We already know that a ':' exists, and that there */
+ /* are no spaces before it. */
+ /* This also now allows for spaces and tabs between */
+ /* the number and the ':' */
+ char ch;
+ char *fieldp_backup = fieldp;
+
+ while ((*fieldp != ':') && (*fieldp != ' ') && (*fieldp != '\t')) {
+ fieldp++;
+ }
+ ch = *fieldp;
+ *fieldp = '\0';
+
+ nump = fieldp_backup;
+ SKIPSPACE(nump);
+ if (nump == NULL || *nump == '\0') {
+ num = flag == RPMBUILD_ISSOURCE ? 0 : INT_MAX;
+ } else {
+ if (parseUnsignedNum(fieldp_backup, &num)) {
+ rpmlog(RPMLOG_ERR, _("line %d: Bad %s number: %s\n"),
+ spec->lineNum, name, spec->line);
+ *fieldp = ch;
+ return RPMRC_FAIL;
+ }
+ }
+ *fieldp = ch;
+ }
+
+ /* Check whether tags of the same number haven't already been defined */
+ for (p = spec->sources; p != NULL; p = p->next) {
+ if ( p->num != num ) continue;
+ if ((tag == RPMTAG_SOURCE && p->flags == RPMBUILD_ISSOURCE) ||
+ (tag == RPMTAG_PATCH && p->flags == RPMBUILD_ISPATCH)) {
+ rpmlog(RPMLOG_ERR, _("%s %d defined multiple times\n"), name, num);
+ return RPMRC_FAIL;
+ }
+ }
+
+ /* Create the entry and link it in */
+ p = xmalloc(sizeof(*p));
+ p->num = num;
+ p->fullSource = xstrdup(field);
+ p->flags = flag;
+ p->source = strrchr(p->fullSource, '/');
+ if (p->source) {
+ p->source++;
+ } else {
+ p->source = p->fullSource;
+ }
+
+ if (tag != RPMTAG_ICON) {
+ p->next = spec->sources;
+ spec->sources = p;
+ } else {
+ p->next = pkg->icon;
+ pkg->icon = p;
+ }
+
+ spec->numSources++;
+
+ if (tag != RPMTAG_ICON) {
+ char *body = rpmGetPath("%{_sourcedir}/", p->source, NULL);
+
+ rasprintf(&buf, "%s%d",
+ (flag & RPMBUILD_ISPATCH) ? "PATCH" : "SOURCE", num);
+ addMacro(spec->macros, buf, NULL, body, RMIL_SPEC);
+ free(buf);
+ rasprintf(&buf, "%sURL%d",
+ (flag & RPMBUILD_ISPATCH) ? "PATCH" : "SOURCE", num);
+ addMacro(spec->macros, buf, NULL, p->fullSource, RMIL_SPEC);
+ free(buf);
+#ifdef WITH_LUA
+ if (!spec->recursing) {
+ rpmlua lua = NULL; /* global state */
+ const char * what = (flag & RPMBUILD_ISPATCH) ? "patches" : "sources";
+ rpmluaPushTable(lua, what);
+ rpmluav var = rpmluavNew();
+ rpmluavSetListMode(var, 1);
+ rpmluavSetValue(var, RPMLUAV_STRING, body);
+ rpmluaSetVar(lua, var);
+ var = rpmluavFree(var);
+ rpmluaPop(lua);
+ }
+#endif
+ body = _free(body);
+ }
+
+ return 0;
+}
+
+typedef const struct tokenBits_s {
+ const char * name;
+ rpmsenseFlags bits;
+} * tokenBits;
+
+/**
+ */
+static struct tokenBits_s const installScriptBits[] = {
+ { "interp", RPMSENSE_INTERP },
+ { "prereq", RPMSENSE_PREREQ },
+ { "preun", RPMSENSE_SCRIPT_PREUN },
+ { "pre", RPMSENSE_SCRIPT_PRE },
+ { "postun", RPMSENSE_SCRIPT_POSTUN },
+ { "post", RPMSENSE_SCRIPT_POST },
+ { "rpmlib", RPMSENSE_RPMLIB },
+ { "verify", RPMSENSE_SCRIPT_VERIFY },
+ { "pretrans", RPMSENSE_PRETRANS },
+ { "posttrans", RPMSENSE_POSTTRANS },
+ { NULL, 0 }
+};
+
+/**
+ */
+static int parseBits(const char * s, const tokenBits tokbits,
+ rpmsenseFlags * bp)
+{
+ tokenBits tb;
+ const char * se;
+ rpmsenseFlags bits = RPMSENSE_ANY;
+ int c = 0;
+ int rc = RPMRC_OK;
+
+ if (s) {
+ while (*s != '\0') {
+ while ((c = *s) && risspace(c)) s++;
+ se = s;
+ while ((c = *se) && risalpha(c)) se++;
+ if (s == se)
+ break;
+ for (tb = tokbits; tb->name; tb++) {
+ if (tb->name != NULL &&
+ strlen(tb->name) == (se-s) && rstreqn(tb->name, s, (se-s)))
+ break;
+ }
+ if (tb->name == NULL) {
+ rc = RPMRC_FAIL;
+ break;
+ }
+ bits |= tb->bits;
+ while ((c = *se) && risspace(c)) se++;
+ if (c != ',')
+ break;
+ s = ++se;
+ }
+ }
+ *bp |= bits;
+ return rc;
+}
+
+/**
+ */
+static inline char * findLastChar(char * s)
+{
+ char *res = s;
+
+ while (*s != '\0') {
+ if (! risspace(*s))
+ res = s;
+ s++;
+ }
+
+ return res;
+}
+
+/**
+ */
+static int isMemberInEntry(Header h, const char *name, rpmTagVal tag)
+{
+ struct rpmtd_s td;
+ int found = 0;
+ const char *str;
+
+ if (!headerGet(h, tag, &td, HEADERGET_MINMEM))
+ return -1;
+
+ while ((str = rpmtdNextString(&td))) {
+ if (!rstrcasecmp(str, name)) {
+ found = 1;
+ break;
+ }
+ }
+ rpmtdFreeData(&td);
+
+ return found;
+}
+
+/**
+ */
+static rpmRC checkForValidArchitectures(rpmSpec spec)
+{
+ char *arch = rpmExpand("%{_target_cpu}", NULL);
+ char *os = rpmExpand("%{_target_os}", NULL);
+ rpmRC rc = RPMRC_FAIL; /* assume failure */
+
+ if (isMemberInEntry(spec->buildRestrictions,
+ arch, RPMTAG_EXCLUDEARCH) == 1) {
+ rpmlog(RPMLOG_ERR, _("Architecture is excluded: %s\n"), arch);
+ goto exit;
+ }
+ if (isMemberInEntry(spec->buildRestrictions,
+ arch, RPMTAG_EXCLUSIVEARCH) == 0) {
+ rpmlog(RPMLOG_ERR, _("Architecture is not included: %s\n"), arch);
+ goto exit;
+ }
+ if (isMemberInEntry(spec->buildRestrictions,
+ os, RPMTAG_EXCLUDEOS) == 1) {
+ rpmlog(RPMLOG_ERR, _("OS is excluded: %s\n"), os);
+ goto exit;
+ }
+ if (isMemberInEntry(spec->buildRestrictions,
+ os, RPMTAG_EXCLUSIVEOS) == 0) {
+ rpmlog(RPMLOG_ERR, _("OS is not included: %s\n"), os);
+ goto exit;
+ }
+ rc = RPMRC_OK;
+
+exit:
+ arch = _free(arch);
+ os = _free(os);
+
+ return rc;
+}
+
+/**
+ * Check that required tags are present in header.
+ * @param h header
+ * @param NVR package name-version-release
+ * @return RPMRC_OK if OK
+ */
+static int checkForRequired(Header h, const char * NVR)
+{
+ int res = RPMRC_OK;
+ const rpmTagVal * p;
+
+ for (p = requiredTags; *p != 0; p++) {
+ if (!headerIsEntry(h, *p)) {
+ rpmlog(RPMLOG_ERR,
+ _("%s field must be present in package: %s\n"),
+ rpmTagGetName(*p), NVR);
+ res = RPMRC_FAIL;
+ }
+ }
+
+ return res;
+}
+
+/**
+ * Check that no duplicate tags are present in header.
+ * @param h header
+ * @param NVR package name-version-release
+ * @return RPMRC_OK if OK
+ */
+static int checkForDuplicates(Header h, const char * NVR)
+{
+ int res = RPMRC_OK;
+ rpmTagVal tag, lastTag = RPMTAG_NOT_FOUND;
+ HeaderIterator hi = headerInitIterator(h);
+
+ while ((tag = headerNextTag(hi)) != RPMTAG_NOT_FOUND) {
+ if (tag == lastTag) {
+ rpmlog(RPMLOG_ERR, _("Duplicate %s entries in package: %s\n"),
+ rpmTagGetName(tag), NVR);
+ res = RPMRC_FAIL;
+ }
+ lastTag = tag;
+ }
+ hi = headerFreeIterator(hi);
+
+ return res;
+}
+
+/**
+ */
+static struct optionalTag {
+ rpmTagVal ot_tag;
+ const char * ot_mac;
+} const optionalTags[] = {
+ { RPMTAG_VENDOR, "%{vendor}" },
+ { RPMTAG_PACKAGER, "%{packager}" },
+ { RPMTAG_DISTRIBUTION, "%{distribution}" },
+ { RPMTAG_DISTURL, "%{disturl}" },
+ { RPMTAG_BUGURL, "%{bugurl}" },
+ { -1, NULL }
+};
+
+/**
+ */
+static void fillOutMainPackage(Header h)
+{
+ const struct optionalTag *ot;
+
+ for (ot = optionalTags; ot->ot_mac != NULL; ot++) {
+ if (!headerIsEntry(h, ot->ot_tag)) {
+ char *val = rpmExpand(ot->ot_mac, NULL);
+ if (val && *val != '%') {
+ headerPutString(h, ot->ot_tag, val);
+ }
+ val = _free(val);
+ }
+ }
+}
+
+static int getSpecialDocDir(Package pkg)
+{
+ const char *errstr, *docdir_fmt = "%{NAME}-%{VERSION}";
+ char *fmt_macro, *fmt;
+ int rc = -1;
+
+ fmt_macro = rpmExpand("%{?_docdir_fmt}", NULL);
+ if (fmt_macro && strlen(fmt_macro) > 0) {
+ docdir_fmt = fmt_macro;
+ }
+ fmt = headerFormat(pkg->header, docdir_fmt, &errstr);
+ if (!fmt) {
+ rpmlog(RPMLOG_ERR, _("illegal _docdir_fmt: %s\n"), errstr);
+ goto exit;
+ }
+ pkg->specialDocDir = rpmGetPath("%{_docdir}/", fmt, NULL);
+ rc = 0;
+
+exit:
+ free(fmt);
+ free(fmt_macro);
+ return rc;
+}
+
+/**
+ */
+static rpmRC readIcon(Header h, const char * file)
+{
+ char *fn = NULL;
+ uint8_t *icon = NULL;
+ FD_t fd = NULL;
+ rpmRC rc = RPMRC_FAIL; /* assume failure */
+ off_t size;
+ size_t nb, iconsize;
+
+ /* XXX use rpmGenPath(rootdir, "%{_sourcedir}/", file) for icon path. */
+ fn = rpmGetPath("%{_sourcedir}/", file, NULL);
+
+ fd = Fopen(fn, "r.ufdio");
+ if (fd == NULL) {
+ rpmlog(RPMLOG_ERR, _("Unable to open icon %s: %s\n"),
+ fn, Fstrerror(fd));
+ goto exit;
+ }
+ size = fdSize(fd);
+ iconsize = (size >= 0 ? size : (8 * BUFSIZ));
+ if (iconsize == 0) {
+ rc = RPMRC_OK; /* XXX Eh? */
+ goto exit;
+ }
+
+ icon = xmalloc(iconsize + 1);
+ *icon = '\0';
+
+ nb = Fread(icon, sizeof(icon[0]), iconsize, fd);
+ if (Ferror(fd) || (size >= 0 && nb != size)) {
+ rpmlog(RPMLOG_ERR, _("Unable to read icon %s: %s\n"),
+ fn, Fstrerror(fd));
+ goto exit;
+ }
+
+ if (rstreqn((char*)icon, "GIF", sizeof("GIF")-1)) {
+ headerPutBin(h, RPMTAG_GIF, icon, iconsize);
+ } else if (rstreqn((char*)icon, "/* XPM", sizeof("/* XPM")-1)) {
+ headerPutBin(h, RPMTAG_XPM, icon, iconsize);
+ } else {
+ rpmlog(RPMLOG_ERR, _("Unknown icon type: %s\n"), file);
+ goto exit;
+ }
+ rc = RPMRC_OK;
+
+exit:
+ Fclose(fd);
+ free(fn);
+ free(icon);
+ return rc;
+}
+
+#define SINGLE_TOKEN_ONLY \
+if (multiToken) { \
+ rpmlog(RPMLOG_ERR, _("line %d: Tag takes single token only: %s\n"), \
+ spec->lineNum, spec->line); \
+ return RPMRC_FAIL; \
+}
+
+/**
+ * Check for inappropriate characters. All alphanums are considered sane.
+ * @param spec spec
+ * @param field string to check
+ * @param fsize size of string to check
+ * @param whitelist string of permitted characters
+ * @return RPMRC_OK if OK
+ */
+rpmRC rpmCharCheck(rpmSpec spec, const char *field, size_t fsize, const char *whitelist)
+{
+ const char *ch, *stop = &field[fsize];
+
+ for (ch=field; *ch && ch < stop; ch++) {
+ if (risalnum(*ch) || strchr(whitelist, *ch)) continue;
+ if (isprint(*ch)) {
+ rpmlog(RPMLOG_ERR, _("line %d: Illegal char '%c' in: %s\n"),
+ spec->lineNum, *ch, spec->line);
+ } else {
+ rpmlog(RPMLOG_ERR, _("line %d: Illegal char in: %s\n"),
+ spec->lineNum, spec->line);
+ }
+ return RPMRC_FAIL;
+ }
+ if (strstr(field, "..") != NULL) {
+ rpmlog(RPMLOG_ERR, _("line %d: Illegal sequence \"..\" in: %s\n"),
+ spec->lineNum, spec->line);
+ return RPMRC_FAIL;
+ }
+
+ return RPMRC_OK;
+}
+
+/**
+ */
+static int handlePreambleTag(rpmSpec spec, Package pkg, rpmTagVal tag,
+ const char *macro, const char *lang)
+{
+ char * field = spec->line;
+ char * end;
+ int multiToken = 0;
+ rpmsenseFlags tagflags = RPMSENSE_ANY;
+ int rc;
+ int xx;
+
+ if (field == NULL) return RPMRC_FAIL; /* XXX can't happen */
+ /* Find the start of the "field" and strip trailing space */
+ while ((*field) && (*field != ':'))
+ field++;
+ if (*field != ':') {
+ rpmlog(RPMLOG_ERR, _("line %d: Malformed tag: %s\n"),
+ spec->lineNum, spec->line);
+ return RPMRC_FAIL;
+ }
+ field++;
+ SKIPSPACE(field);
+ if (!*field) {
+ /* Empty field */
+ rpmlog(RPMLOG_ERR, _("line %d: Empty tag: %s\n"),
+ spec->lineNum, spec->line);
+ return RPMRC_FAIL;
+ }
+ end = findLastChar(field);
+ *(end+1) = '\0';
+
+ /* See if this is multi-token */
+ end = field;
+ SKIPNONSPACE(end);
+ if (*end != '\0')
+ multiToken = 1;
+
+ switch (tag) {
+ case RPMTAG_NAME:
+ SINGLE_TOKEN_ONLY;
+ if (rpmCharCheck(spec, field, strlen(field), ".-_+%{}") != RPMRC_OK) return RPMRC_FAIL;
+ headerPutString(pkg->header, tag, field);
+ break;
+ case RPMTAG_VERSION:
+ case RPMTAG_RELEASE:
+ SINGLE_TOKEN_ONLY;
+ if (rpmCharCheck(spec, field, strlen(field), "._+%{}") != RPMRC_OK) return RPMRC_FAIL;
+ headerPutString(pkg->header, tag, field);
+ break;
+ case RPMTAG_URL:
+ case RPMTAG_DISTTAG:
+ case RPMTAG_BUGURL:
+ /* XXX TODO: validate format somehow */
+ case RPMTAG_VCS:
+ SINGLE_TOKEN_ONLY;
+ headerPutString(pkg->header, tag, field);
+ break;
+ case RPMTAG_GROUP:
+ case RPMTAG_SUMMARY:
+ case RPMTAG_DISTRIBUTION:
+ case RPMTAG_VENDOR:
+ case RPMTAG_LICENSE:
+ case RPMTAG_PACKAGER:
+ if (!*lang) {
+ headerPutString(pkg->header, tag, field);
+ } else if (!((spec->flags & RPMSPEC_NOLANG) && !rstreq(lang, RPMBUILD_DEFAULT_LANG)))
+ (void) headerAddI18NString(pkg->header, tag, field, lang);
+ break;
+ case RPMTAG_BUILDROOT:
+ /* just silently ignore BuildRoot */
+ macro = NULL;
+ break;
+ case RPMTAG_PREFIXES: {
+ struct rpmtd_s td;
+ const char *str;
+ addOrAppendListEntry(pkg->header, tag, field);
+ xx = headerGet(pkg->header, tag, &td, HEADERGET_MINMEM);
+ while ((str = rpmtdNextString(&td))) {
+ size_t len = strlen(str);
+ if (len > 1 && str[len-1] == '/') {
+ rpmlog(RPMLOG_ERR,
+ _("line %d: Prefixes must not end with \"/\": %s\n"),
+ spec->lineNum, spec->line);
+ rpmtdFreeData(&td);
+ return RPMRC_FAIL;
+ }
+ }
+ rpmtdFreeData(&td);
+ break;
+ }
+ case RPMTAG_DOCDIR:
+ SINGLE_TOKEN_ONLY;
+ if (field[0] != '/') {
+ rpmlog(RPMLOG_ERR,
+ _("line %d: Docdir must begin with '/': %s\n"),
+ spec->lineNum, spec->line);
+ return RPMRC_FAIL;
+ }
+ macro = NULL;
+ delMacro(NULL, "_docdir");
+ addMacro(NULL, "_docdir", NULL, field, RMIL_SPEC);
+ break;
+ case RPMTAG_EPOCH: {
+ SINGLE_TOKEN_ONLY;
+ uint32_t epoch;
+ if (parseUnsignedNum(field, &epoch)) {
+ rpmlog(RPMLOG_ERR,
+ _("line %d: Epoch field must be an unsigned number: %s\n"),
+ spec->lineNum, spec->line);
+ return RPMRC_FAIL;
+ }
+ headerPutUint32(pkg->header, tag, &epoch, 1);
+ break;
+ }
+ case RPMTAG_AUTOREQPROV:
+ pkg->autoReq = parseYesNo(field);
+ pkg->autoProv = pkg->autoReq;
+ break;
+ case RPMTAG_AUTOREQ:
+ pkg->autoReq = parseYesNo(field);
+ break;
+ case RPMTAG_AUTOPROV:
+ pkg->autoProv = parseYesNo(field);
+ break;
+ case RPMTAG_SOURCE:
+ case RPMTAG_PATCH:
+ macro = NULL;
+ if ((rc = addSource(spec, pkg, field, tag)))
+ return rc;
+ break;
+ case RPMTAG_ICON:
+ SINGLE_TOKEN_ONLY;
+ if ((rc = addSource(spec, pkg, field, tag)))
+ return rc;
+ if ((rc = readIcon(pkg->header, field)))
+ return RPMRC_FAIL;
+ break;
+ case RPMTAG_NOSOURCE:
+ case RPMTAG_NOPATCH:
+ spec->noSource = 1;
+ if ((rc = parseNoSource(spec, field, tag)))
+ return rc;
+ break;
+ case RPMTAG_ORDERFLAGS:
+ case RPMTAG_REQUIREFLAGS:
+ case RPMTAG_PREREQ:
+ if ((rc = parseBits(lang, installScriptBits, &tagflags))) {
+ rpmlog(RPMLOG_ERR,
+ _("line %d: Bad %s: qualifiers: %s\n"),
+ spec->lineNum, rpmTagGetName(tag), spec->line);
+ return rc;
+ }
+ if ((rc = parseRCPOT(spec, pkg, field, tag, 0, tagflags)))
+ return rc;
+ break;
+ case RPMTAG_BUILDPREREQ:
+ case RPMTAG_BUILDREQUIRES:
+ case RPMTAG_BUILDCONFLICTS:
+ case RPMTAG_CONFLICTFLAGS:
+ case RPMTAG_OBSOLETEFLAGS:
+ case RPMTAG_PROVIDEFLAGS:
+ if ((rc = parseRCPOT(spec, pkg, field, tag, 0, tagflags)))
+ return rc;
+ break;
+ case RPMTAG_EXCLUDEARCH:
+ case RPMTAG_EXCLUSIVEARCH:
+ case RPMTAG_EXCLUDEOS:
+ case RPMTAG_EXCLUSIVEOS:
+ addOrAppendListEntry(spec->buildRestrictions, tag, field);
+ break;
+ case RPMTAG_BUILDARCHS: {
+ int BACount;
+ const char **BANames = NULL;
+ if ((rc = poptParseArgvString(field, &BACount, &BANames))) {
+ rpmlog(RPMLOG_ERR,
+ _("line %d: Bad BuildArchitecture format: %s\n"),
+ spec->lineNum, spec->line);
+ return RPMRC_FAIL;
+ }
+ if (spec->packages == pkg) {
+ spec->BACount = BACount;
+ spec->BANames = BANames;
+ } else {
+ if (BACount != 1 || !rstreq(BANames[0], "noarch")) {
+ rpmlog(RPMLOG_ERR,
+ _("line %d: Only noarch subpackages are supported: %s\n"),
+ spec->lineNum, spec->line);
+ BANames = _free(BANames);
+ return RPMRC_FAIL;
+ }
+ headerPutString(pkg->header, RPMTAG_ARCH, "noarch");
+ }
+ if (!BACount)
+ spec->BANames = _free(spec->BANames);
+ break;
+ }
+ case RPMTAG_COLLECTIONS:
+ addOrAppendListEntry(pkg->header, tag, field);
+ break;
+ default:
+ rpmlog(RPMLOG_ERR, _("Internal error: Bogus tag %d\n"), tag);
+ return RPMRC_FAIL;
+ }
+
+ if (macro)
+ addMacro(spec->macros, macro, NULL, field, RMIL_SPEC);
+
+ return RPMRC_OK;
+}
+
+/* This table has to be in a peculiar order. If one tag is the */
+/* same as another, plus a few letters, it must come first. */
+
+/**
+ */
+typedef const struct PreambleRec_s {
+ rpmTagVal tag;
+ int type;
+ int deprecated;
+ size_t len;
+ const char * token;
+} * PreambleRec;
+
+#define LEN_AND_STR(_tag) (sizeof(_tag)-1), _tag
+
+static struct PreambleRec_s const preambleList[] = {
+ {RPMTAG_NAME, 0, 0, LEN_AND_STR("name")},
+ {RPMTAG_VERSION, 0, 0, LEN_AND_STR("version")},
+ {RPMTAG_RELEASE, 0, 0, LEN_AND_STR("release")},
+ {RPMTAG_EPOCH, 0, 0, LEN_AND_STR("epoch")},
+ {RPMTAG_SUMMARY, 1, 0, LEN_AND_STR("summary")},
+ {RPMTAG_LICENSE, 0, 0, LEN_AND_STR("license")},
+ {RPMTAG_DISTRIBUTION, 0, 0, LEN_AND_STR("distribution")},
+ {RPMTAG_DISTURL, 0, 0, LEN_AND_STR("disturl")},
+ {RPMTAG_VENDOR, 0, 0, LEN_AND_STR("vendor")},
+ {RPMTAG_GROUP, 1, 0, LEN_AND_STR("group")},
+ {RPMTAG_PACKAGER, 0, 0, LEN_AND_STR("packager")},
+ {RPMTAG_URL, 0, 0, LEN_AND_STR("url")},
+ {RPMTAG_VCS, 0, 0, LEN_AND_STR("vcs")},
+ {RPMTAG_SOURCE, 0, 0, LEN_AND_STR("source")},
+ {RPMTAG_PATCH, 0, 0, LEN_AND_STR("patch")},
+ {RPMTAG_NOSOURCE, 0, 0, LEN_AND_STR("nosource")},
+ {RPMTAG_NOPATCH, 0, 0, LEN_AND_STR("nopatch")},
+ {RPMTAG_EXCLUDEARCH, 0, 0, LEN_AND_STR("excludearch")},
+ {RPMTAG_EXCLUSIVEARCH, 0, 0, LEN_AND_STR("exclusivearch")},
+ {RPMTAG_EXCLUDEOS, 0, 0, LEN_AND_STR("excludeos")},
+ {RPMTAG_EXCLUSIVEOS, 0, 0, LEN_AND_STR("exclusiveos")},
+ {RPMTAG_ICON, 0, 0, LEN_AND_STR("icon")},
+ {RPMTAG_PROVIDEFLAGS, 0, 0, LEN_AND_STR("provides")},
+ {RPMTAG_REQUIREFLAGS, 2, 0, LEN_AND_STR("requires")},
+ {RPMTAG_PREREQ, 2, 1, LEN_AND_STR("prereq")},
+ {RPMTAG_CONFLICTFLAGS, 0, 0, LEN_AND_STR("conflicts")},
+ {RPMTAG_OBSOLETEFLAGS, 0, 0, LEN_AND_STR("obsoletes")},
+ {RPMTAG_PREFIXES, 0, 0, LEN_AND_STR("prefixes")},
+ {RPMTAG_PREFIXES, 0, 0, LEN_AND_STR("prefix")},
+ {RPMTAG_BUILDROOT, 0, 0, LEN_AND_STR("buildroot")},
+ {RPMTAG_BUILDARCHS, 0, 0, LEN_AND_STR("buildarchitectures")},
+ {RPMTAG_BUILDARCHS, 0, 0, LEN_AND_STR("buildarch")},
+ {RPMTAG_BUILDCONFLICTS, 0, 0, LEN_AND_STR("buildconflicts")},
+ {RPMTAG_BUILDPREREQ, 0, 1, LEN_AND_STR("buildprereq")},
+ {RPMTAG_BUILDREQUIRES, 0, 0, LEN_AND_STR("buildrequires")},
+ {RPMTAG_AUTOREQPROV, 0, 0, LEN_AND_STR("autoreqprov")},
+ {RPMTAG_AUTOREQ, 0, 0, LEN_AND_STR("autoreq")},
+ {RPMTAG_AUTOPROV, 0, 0, LEN_AND_STR("autoprov")},
+ {RPMTAG_DOCDIR, 0, 0, LEN_AND_STR("docdir")},
+ {RPMTAG_DISTTAG, 0, 0, LEN_AND_STR("disttag")},
+ {RPMTAG_BUGURL, 0, 0, LEN_AND_STR("bugurl")},
+ {RPMTAG_COLLECTIONS, 0, 0, LEN_AND_STR("collections")},
+ {RPMTAG_ORDERFLAGS, 2, 0, LEN_AND_STR("orderwithrequires")},
+ {0, 0, 0, 0}
+};
+
+/**
+ */
+static int findPreambleTag(rpmSpec spec,rpmTagVal * tag,
+ const char ** macro, char * lang)
+{
+ PreambleRec p;
+ char *s;
+
+ for (p = preambleList; p->token != NULL; p++) {
+ if (!(p->token && !rstrncasecmp(spec->line, p->token, p->len)))
+ continue;
+ if (p->deprecated) {
+ rpmlog(RPMLOG_WARNING, _("line %d: %s is deprecated: %s\n"),
+ spec->lineNum, p->token, spec->line);
+ }
+ break;
+ }
+ if (p == NULL || p->token == NULL)
+ return 1;
+
+ s = spec->line + p->len;
+ SKIPSPACE(s);
+
+ switch (p->type) {
+ default:
+ case 0:
+ /* Unless this is a source or a patch, a ':' better be next */
+ if (p->tag != RPMTAG_SOURCE && p->tag != RPMTAG_PATCH) {
+ if (*s != ':') return 1;
+ }
+ *lang = '\0';
+ break;
+ case 1: /* Parse optional ( <token> ). */
+ case 2:
+ if (*s == ':') {
+ /* Type 1 is multilang, 2 is qualifiers with no defaults */
+ strcpy(lang, (p->type == 1) ? RPMBUILD_DEFAULT_LANG : "");
+ break;
+ }
+ if (*s != '(') return 1;
+ s++;
+ SKIPSPACE(s);
+ while (!risspace(*s) && *s != ')')
+ *lang++ = *s++;
+ *lang = '\0';
+ SKIPSPACE(s);
+ if (*s != ')') return 1;
+ s++;
+ SKIPSPACE(s);
+ if (*s != ':') return 1;
+ break;
+ }
+
+ *tag = p->tag;
+ if (macro)
+ *macro = p->token;
+ return 0;
+}
+
+int parsePreamble(rpmSpec spec, int initialPackage)
+{
+ int nextPart = PART_ERROR;
+ int res = PART_ERROR; /* assume failure */
+ int rc;
+ char *name, *linep;
+ int flag = 0;
+ Package pkg;
+ char *NVR = NULL;
+ char lang[BUFSIZ];
+
+ pkg = newPackage(spec);
+
+ if (! initialPackage) {
+ /* There is one option to %package: <pkg> or -n <pkg> */
+ if (parseSimplePart(spec->line, &name, &flag)) {
+ rpmlog(RPMLOG_ERR, _("Bad package specification: %s\n"),
+ spec->line);
+ goto exit;
+ }
+
+ if (!lookupPackage(spec, name, flag, NULL)) {
+ rpmlog(RPMLOG_ERR, _("Package already exists: %s\n"), spec->line);
+ free(name);
+ goto exit;
+ }
+
+ /* Construct the package */
+ if (flag == PART_SUBNAME) {
+ rasprintf(&NVR, "%s-%s",
+ headerGetString(spec->packages->header, RPMTAG_NAME), name);
+ } else
+ NVR = xstrdup(name);
+ free(name);
+ headerPutString(pkg->header, RPMTAG_NAME, NVR);
+ } else {
+ NVR = xstrdup("(main package)");
+ }
+
+ if ((rc = readLine(spec, STRIP_TRAILINGSPACE | STRIP_COMMENTS)) > 0) {
+ nextPart = PART_NONE;
+ } else if (rc < 0) {
+ goto exit;
+ } else {
+ while (! (nextPart = isPart(spec->line))) {
+ const char * macro;
+ rpmTagVal tag;
+
+ /* Skip blank lines */
+ linep = spec->line;
+ SKIPSPACE(linep);
+ if (*linep != '\0') {
+ if (findPreambleTag(spec, &tag, &macro, lang)) {
+ rpmlog(RPMLOG_ERR, _("line %d: Unknown tag: %s\n"),
+ spec->lineNum, spec->line);
+ goto exit;
+ }
+ if (handlePreambleTag(spec, pkg, tag, macro, lang)) {
+ goto exit;
+ }
+ if (spec->BANames && !spec->recursing) {
+ res = PART_BUILDARCHITECTURES;
+ goto exit;
+ }
+ }
+ if ((rc =
+ readLine(spec, STRIP_TRAILINGSPACE | STRIP_COMMENTS)) > 0) {
+ nextPart = PART_NONE;
+ break;
+ }
+ if (rc) {
+ goto exit;
+ }
+ }
+ }
+
+ /*
+ * Expand buildroot one more time to get %{version} and the like
+ * from the main package, validate sanity. The spec->buildRoot could
+ * still contain unexpanded macros but it cannot be empty or '/', and it
+ * can't be messed with by anything spec does beyond this point.
+ */
+ if (initialPackage) {
+ char *buildRoot = rpmGetPath(spec->buildRoot, NULL);
+ if (*buildRoot == '\0') {
+ rpmlog(RPMLOG_ERR, _("%%{buildroot} couldn't be empty\n"));
+ goto exit;
+ }
+ if (rstreq(buildRoot, "/")) {
+ rpmlog(RPMLOG_ERR, _("%%{buildroot} can not be \"/\"\n"));
+ goto exit;
+ }
+ free(spec->buildRoot);
+ spec->buildRoot = buildRoot;
+ addMacro(spec->macros, "buildroot", NULL, spec->buildRoot, RMIL_SPEC);
+ }
+
+ /* XXX Skip valid arch check if not building binary package */
+ if (!(spec->flags & RPMSPEC_ANYARCH) && checkForValidArchitectures(spec)) {
+ goto exit;
+ }
+
+ /* It is the main package */
+ if (pkg == spec->packages) {
+ fillOutMainPackage(pkg->header);
+ /* Define group tag to something when group is undefined in main package*/
+ if (!headerIsEntry(pkg->header, RPMTAG_GROUP)) {
+ headerPutString(pkg->header, RPMTAG_GROUP, "Unspecified");
+ }
+ }
+
+ if (checkForDuplicates(pkg->header, NVR)) {
+ goto exit;
+ }
+
+ if (pkg != spec->packages) {
+ headerCopyTags(spec->packages->header, pkg->header,
+ (rpmTagVal *)copyTagsDuringParse);
+ }
+
+ if (checkForRequired(pkg->header, NVR)) {
+ goto exit;
+ }
+
+ if (getSpecialDocDir(pkg)) {
+ goto exit;
+ }
+
+ /* if we get down here nextPart has been set to non-error */
+ res = nextPart;
+
+exit:
+ free(NVR);
+ return res;
+}
diff --git a/build/parsePrep.c b/build/parsePrep.c
new file mode 100644
index 0000000..3dab37f
--- /dev/null
+++ b/build/parsePrep.c
@@ -0,0 +1,546 @@
+/** \ingroup rpmbuild
+ * \file build/parsePrep.c
+ * Parse %prep section from spec file.
+ */
+
+#include "system.h"
+
+#include <errno.h>
+
+#include <rpm/header.h>
+#include <rpm/rpmlog.h>
+#include <rpm/rpmfileutil.h>
+#include "build/rpmbuild_internal.h"
+#include "build/rpmbuild_misc.h"
+#include "lib/rpmug.h"
+#include "debug.h"
+
+/**
+ * Check that file owner and group are known.
+ * @param urlfn file url
+ * @return RPMRC_OK on success
+ */
+static rpmRC checkOwners(const char * urlfn)
+{
+ struct stat sb;
+
+ if (lstat(urlfn, &sb)) {
+ rpmlog(RPMLOG_ERR, _("Bad source: %s: %s\n"),
+ urlfn, strerror(errno));
+ return RPMRC_FAIL;
+ }
+ if (!rpmugUname(sb.st_uid) || !rpmugGname(sb.st_gid)) {
+ rpmlog(RPMLOG_ERR, _("Bad owner/group: %s\n"), urlfn);
+ return RPMRC_FAIL;
+ }
+
+ return RPMRC_OK;
+}
+
+/**
+ * Expand %patchN macro into %prep scriptlet.
+ * @param spec build info
+ * @param c patch index
+ * @param strip patch level (i.e. patch -p argument)
+ * @param db saved file suffix (i.e. patch --suffix argument)
+ * @param reverse include -R?
+ * @param removeEmpties include -E?
+ * @param fuzz fuzz factor, fuzz<0 means no fuzz set
+ * @param dir dir to change to (i.e. patch -d argument)
+ * @return expanded %patch macro (NULL on error)
+ */
+
+static char *doPatch(rpmSpec spec, uint32_t c, int strip, const char *db,
+ int reverse, int removeEmpties, int fuzz, const char *dir)
+{
+ char *fn = NULL;
+ char *buf = NULL;
+ char *arg_backup = NULL;
+ char *arg_fuzz = NULL;
+ char *arg_dir = NULL;
+ char *args = NULL;
+ char *arg_patch_flags = rpmExpand("%{?_default_patch_flags}", NULL);
+ struct Source *sp;
+ char *patchcmd;
+
+ for (sp = spec->sources; sp != NULL; sp = sp->next) {
+ if ((sp->flags & RPMBUILD_ISPATCH) && (sp->num == c)) {
+ break;
+ }
+ }
+ if (sp == NULL) {
+ if (c != INT_MAX) {
+ rpmlog(RPMLOG_ERR, _("No patch number %u\n"), c);
+ } else {
+ rpmlog(RPMLOG_ERR, _("%%patch without corresponding \"Patch:\" tag\n"));
+ }
+ goto exit;
+ }
+
+ fn = rpmGetPath("%{_sourcedir}/", sp->source, NULL);
+
+ /* On non-build parse's, file cannot be stat'd or read. */
+ if ((spec->flags & RPMSPEC_FORCE) || checkOwners(fn)) goto exit;
+
+ if (db) {
+ rasprintf(&arg_backup,
+#if HAVE_OLDPATCH_21 == 0
+ "-b "
+#endif
+ "--suffix %s", db);
+ } else arg_backup = xstrdup("");
+
+ if (dir) {
+ rasprintf(&arg_dir, " -d %s", dir);
+ } else arg_dir = xstrdup("");
+
+ if (fuzz >= 0) {
+ rasprintf(&arg_fuzz, " --fuzz=%d", fuzz);
+ } else arg_fuzz = xstrdup("");
+
+ rasprintf(&args, "%s -p%d %s%s%s%s%s", arg_patch_flags, strip, arg_backup, arg_fuzz, arg_dir,
+ reverse ? " -R" : "",
+ removeEmpties ? " -E" : "");
+
+ patchcmd = rpmExpand("%{uncompress: ", fn, "} | %{__patch} ", args, NULL);
+
+ free(arg_fuzz);
+ free(arg_dir);
+ free(arg_backup);
+ free(args);
+
+ if (c != INT_MAX) {
+ rasprintf(&buf, "echo \"Patch #%u (%s):\"\n"
+ "%s\n",
+ c, basename(fn), patchcmd);
+ } else {
+ rasprintf(&buf, "echo \"Patch (%s):\"\n"
+ "%s\n",
+ basename(fn), patchcmd);
+ }
+ free(patchcmd);
+
+exit:
+ free(arg_patch_flags);
+ free(fn);
+ return buf;
+}
+
+/**
+ * Expand %setup macro into %prep scriptlet.
+ * @param spec build info
+ * @param c source index
+ * @param quietly should -vv be omitted from tar?
+ * @return expanded %setup macro (NULL on error)
+ */
+static char *doUntar(rpmSpec spec, uint32_t c, int quietly)
+{
+ char *fn;
+ char *buf = NULL;
+ char *tar;
+ const char *taropts = ((rpmIsVerbose() && !quietly) ? "-xvvf" : "-xf");
+ struct Source *sp;
+ rpmCompressedMagic compressed = COMPRESSED_NOT;
+
+ for (sp = spec->sources; sp != NULL; sp = sp->next) {
+ if ((sp->flags & RPMBUILD_ISSOURCE) && (sp->num == c)) {
+ break;
+ }
+ }
+ if (sp == NULL) {
+ if (c) {
+ rpmlog(RPMLOG_ERR, _("No source number %u\n"), c);
+ } else {
+ rpmlog(RPMLOG_ERR, _("No \"Source:\" tag in the spec file\n"));
+ }
+ return NULL;
+ }
+
+ fn = rpmGetPath("%{_sourcedir}/", sp->source, NULL);
+
+#ifdef AUTOFETCH_NOT /* XXX don't expect this code to be enabled */
+ /* XXX
+ * XXX If nosource file doesn't exist, try to fetch from url.
+ * XXX TODO: add a "--fetch" enabler.
+ */
+ if (sp->flags & RPMTAG_NOSOURCE && autofetchnosource) {
+ struct stat st;
+ int rc;
+ if (lstat(fn, &st) != 0 && errno == ENOENT &&
+ urlIsUrl(sp->fullSource) != URL_IS_UNKNOWN) {
+ if ((rc = urlGetFile(sp->fullSource, fn)) != 0) {
+ rpmlog(RPMLOG_ERR,
+ _("Couldn't download nosource %s: %s\n"),
+ sp->fullSource);
+ return NULL;
+ }
+ }
+ }
+#endif
+
+ /* XXX On non-build parse's, file cannot be stat'd or read */
+ if (!(spec->flags & RPMSPEC_FORCE) && (rpmFileIsCompressed(fn, &compressed) || checkOwners(fn))) {
+ fn = _free(fn);
+ return NULL;
+ }
+
+ tar = rpmGetPath("%{__tar}", NULL);
+ if (compressed != COMPRESSED_NOT) {
+ char *zipper, *t = NULL;
+ int needtar = 1;
+
+ switch (compressed) {
+ case COMPRESSED_NOT: /* XXX can't happen */
+ case COMPRESSED_OTHER:
+ t = "%{__gzip} -dc";
+ break;
+ case COMPRESSED_BZIP2:
+ t = "%{__bzip2} -dc";
+ break;
+ case COMPRESSED_ZIP:
+ if (rpmIsVerbose() && !quietly)
+ t = "%{__unzip}";
+ else
+ t = "%{__unzip} -qq";
+ needtar = 0;
+ break;
+ case COMPRESSED_LZMA:
+ case COMPRESSED_XZ:
+ t = "%{__xz} -dc";
+ break;
+ case COMPRESSED_LZIP:
+ t = "%{__lzip} -dc";
+ break;
+ case COMPRESSED_LRZIP:
+ t = "%{__lrzip} -dqo-";
+ break;
+ }
+ zipper = rpmGetPath(t, NULL);
+ if (needtar) {
+ rasprintf(&buf, "%s '%s' | %s %s - \n"
+ "STATUS=$?\n"
+ "if [ $STATUS -ne 0 ]; then\n"
+ " exit $STATUS\n"
+ "fi", zipper, fn, tar, taropts);
+ } else {
+ rasprintf(&buf, "%s '%s'\n"
+ "STATUS=$?\n"
+ "if [ $STATUS -ne 0 ]; then\n"
+ " exit $STATUS\n"
+ "fi", zipper, fn);
+ }
+ zipper = _free(zipper);
+ } else {
+ rasprintf(&buf, "%s %s %s", tar, taropts, fn);
+ }
+
+ fn = _free(fn);
+ tar = _free(tar);
+ return buf;
+}
+
+/**
+ * Parse %setup macro.
+ * @todo FIXME: Option -q broken when not immediately after %setup.
+ * @param spec build info
+ * @param line current line from spec file
+ * @return RPMRC_OK on success
+ */
+static int doSetupMacro(rpmSpec spec, const char *line)
+{
+ char *buf = NULL;
+ StringBuf before = newStringBuf();
+ StringBuf after = newStringBuf();
+ poptContext optCon = NULL;
+ int argc;
+ const char ** argv = NULL;
+ int arg;
+ const char * optArg;
+ int xx;
+ rpmRC rc = RPMRC_FAIL;
+ uint32_t num;
+ int leaveDirs = 0, skipDefaultAction = 0;
+ int createDir = 0, quietly = 0;
+ const char * dirName = NULL;
+ struct poptOption optionsTable[] = {
+ { NULL, 'a', POPT_ARG_STRING, NULL, 'a', NULL, NULL},
+ { NULL, 'b', POPT_ARG_STRING, NULL, 'b', NULL, NULL},
+ { NULL, 'c', 0, &createDir, 0, NULL, NULL},
+ { NULL, 'D', 0, &leaveDirs, 0, NULL, NULL},
+ { NULL, 'n', POPT_ARG_STRING, &dirName, 0, NULL, NULL},
+ { NULL, 'T', 0, &skipDefaultAction, 0, NULL, NULL},
+ { NULL, 'q', 0, &quietly, 0, NULL, NULL},
+ { 0, 0, 0, 0, 0, NULL, NULL}
+ };
+
+ if ((xx = poptParseArgvString(line, &argc, &argv))) {
+ rpmlog(RPMLOG_ERR, _("Error parsing %%setup: %s\n"), poptStrerror(xx));
+ goto exit;
+ }
+
+ optCon = poptGetContext(NULL, argc, argv, optionsTable, 0);
+ while ((arg = poptGetNextOpt(optCon)) > 0) {
+ optArg = poptGetOptArg(optCon);
+
+ /* We only parse -a and -b here */
+
+ if (parseUnsignedNum(optArg, &num)) {
+ rpmlog(RPMLOG_ERR, _("line %d: Bad arg to %%setup: %s\n"),
+ spec->lineNum, (optArg ? optArg : "???"));
+ goto exit;
+ }
+
+ { char *chptr = doUntar(spec, num, quietly);
+ if (chptr == NULL)
+ goto exit;
+
+ appendLineStringBuf((arg == 'a' ? after : before), chptr);
+ free(chptr);
+ }
+ }
+
+ if (arg < -1) {
+ rpmlog(RPMLOG_ERR, _("line %d: Bad %%setup option %s: %s\n"),
+ spec->lineNum,
+ poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
+ poptStrerror(arg));
+ goto exit;
+ }
+
+ if (dirName) {
+ spec->buildSubdir = xstrdup(dirName);
+ } else {
+ rasprintf(&spec->buildSubdir, "%s-%s",
+ headerGetString(spec->packages->header, RPMTAG_NAME),
+ headerGetString(spec->packages->header, RPMTAG_VERSION));
+ }
+ addMacro(spec->macros, "buildsubdir", NULL, spec->buildSubdir, RMIL_SPEC);
+
+ /* cd to the build dir */
+ { char * buildDir = rpmGenPath(spec->rootDir, "%{_builddir}", "");
+
+ rasprintf(&buf, "cd '%s'", buildDir);
+ appendLineStringBuf(spec->prep, buf);
+ free(buf);
+ free(buildDir);
+ }
+
+ /* delete any old sources */
+ if (!leaveDirs) {
+ rasprintf(&buf, "rm -rf '%s'", spec->buildSubdir);
+ appendLineStringBuf(spec->prep, buf);
+ free(buf);
+ }
+
+ /* if necessary, create and cd into the proper dir */
+ if (createDir) {
+ buf = rpmExpand("%{__mkdir_p} ", spec->buildSubdir, "\n",
+ "cd '", spec->buildSubdir, "'", NULL);
+ appendLineStringBuf(spec->prep, buf);
+ free(buf);
+ }
+
+ /* do the default action */
+ if (!createDir && !skipDefaultAction) {
+ char *chptr = doUntar(spec, 0, quietly);
+ if (!chptr)
+ goto exit;
+ appendLineStringBuf(spec->prep, chptr);
+ free(chptr);
+ }
+
+ appendStringBuf(spec->prep, getStringBuf(before));
+
+ if (!createDir) {
+ rasprintf(&buf, "cd '%s'", spec->buildSubdir);
+ appendLineStringBuf(spec->prep, buf);
+ free(buf);
+ }
+
+ if (createDir && !skipDefaultAction) {
+ char *chptr = doUntar(spec, 0, quietly);
+ if (chptr == NULL)
+ goto exit;
+ appendLineStringBuf(spec->prep, chptr);
+ free(chptr);
+ }
+
+ appendStringBuf(spec->prep, getStringBuf(after));
+
+ /* Fix the permissions of the setup build tree */
+ { char *fix = rpmExpand("%{_fixperms} .", NULL);
+ if (fix && *fix != '%') {
+ appendLineStringBuf(spec->prep, fix);
+ }
+ free(fix);
+ }
+ rc = RPMRC_OK;
+
+exit:
+ freeStringBuf(before);
+ freeStringBuf(after);
+ poptFreeContext(optCon);
+ free(argv);
+
+ return rc;
+}
+
+/**
+ * Parse %patch line.
+ * This supports too many crazy syntaxes:
+ * - %patchN is equal to %patch -P<N>
+ * - -P<N> -P<N+1>... can be used to apply several patch on a single line
+ * - Any trailing arguments are treated as patch numbers
+ * - Any combination of the above, except unless at least one -P is specified,
+ * %patch is treated as %patch -P0 so that "%patch 1" is actually
+ * equal to "%patch -P0 -P1".
+ *
+ * @param spec build info
+ * @param line current line from spec file
+ * @return RPMRC_OK on success
+ */
+static rpmRC doPatchMacro(rpmSpec spec, const char *line)
+{
+ char *opt_b, *opt_P, *opt_d;
+ char *buf = NULL;
+ int opt_p, opt_R, opt_E, opt_F;
+ int argc, c;
+ const char **argv = NULL;
+ ARGV_t patch, patchnums = NULL;
+ rpmRC rc = RPMRC_FAIL; /* assume failure */
+
+ struct poptOption const patchOpts[] = {
+ { NULL, 'P', POPT_ARG_STRING, &opt_P, 'P', NULL, NULL },
+ { NULL, 'p', POPT_ARG_INT, &opt_p, 'p', NULL, NULL },
+ { NULL, 'R', POPT_ARG_NONE, &opt_R, 'R', NULL, NULL },
+ { NULL, 'E', POPT_ARG_NONE, &opt_E, 'E', NULL, NULL },
+ { NULL, 'b', POPT_ARG_STRING, &opt_b, 'b', NULL, NULL },
+ { NULL, 'z', POPT_ARG_STRING, &opt_b, 'z', NULL, NULL },
+ { NULL, 'F', POPT_ARG_INT, &opt_F, 'F', NULL, NULL },
+ { NULL, 'd', POPT_ARG_STRING, &opt_d, 'd', NULL, NULL },
+ { NULL, 0, 0, NULL, 0, NULL, NULL }
+ };
+ poptContext optCon = NULL;
+
+ opt_p = opt_R = opt_E = 0;
+ opt_F = rpmExpandNumeric("%{_default_patch_fuzz}"); /* get default fuzz factor for %patch */
+ opt_b = opt_d = NULL;
+
+ /* Convert %patchN to %patch -PN to simplify further processing */
+ if (! strchr(" \t\n", line[6])) {
+ rasprintf(&buf, "%%patch -P %s", line + 6);
+ } else {
+ if (strstr(line+6, " -P") == NULL)
+ rasprintf(&buf, "%%patch -P %d %s", INT_MAX, line + 6); /* INT_MAX denotes not numbered %patch */
+ else
+ buf = xstrdup(line); /* it is not numberless patch because -P is present */
+ }
+ poptParseArgvString(buf, &argc, &argv);
+ free(buf);
+
+ /*
+ * Grab all -P<N> numbers for later processing. Stored as strings
+ * at this point so we only have to worry about conversion in one place.
+ */
+ optCon = poptGetContext(NULL, argc, argv, patchOpts, 0);
+ while ((c = poptGetNextOpt(optCon)) > 0) {
+ switch (c) {
+ case 'P': {
+ char *arg = poptGetOptArg(optCon);
+ if (arg) {
+ argvAdd(&patchnums, arg);
+ free(arg);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ if (c < -1) {
+ rpmlog(RPMLOG_ERR, _("%s: %s: %s\n"), poptStrerror(c),
+ poptBadOption(optCon, POPT_BADOPTION_NOALIAS), line);
+ goto exit;
+ }
+
+ /* Any trailing arguments are treated as patch numbers */
+ argvAppend(&patchnums, (ARGV_const_t) poptGetArgs(optCon));
+
+ /* Convert to number, generate patch command and append to %prep script */
+ for (patch = patchnums; *patch; patch++) {
+ uint32_t pnum;
+ char *s;
+ if (parseUnsignedNum(*patch, &pnum)) {
+ rpmlog(RPMLOG_ERR, _("Invalid patch number %s: %s\n"),
+ *patch, line);
+ goto exit;
+ }
+ s = doPatch(spec, pnum, opt_p, opt_b, opt_R, opt_E, opt_F, opt_d);
+ if (s == NULL) {
+ goto exit;
+ }
+ appendLineStringBuf(spec->prep, s);
+ free(s);
+ }
+
+ rc = RPMRC_OK;
+
+exit:
+ argvFree(patchnums);
+ free(argv);
+ poptFreeContext(optCon);
+ return rc;
+}
+
+int parsePrep(rpmSpec spec)
+{
+ int nextPart, rc, res = PART_ERROR;
+ ARGV_t saveLines = NULL;
+
+ if (spec->prep != NULL) {
+ rpmlog(RPMLOG_ERR, _("line %d: second %%prep\n"), spec->lineNum);
+ return PART_ERROR;
+ }
+
+ spec->prep = newStringBuf();
+
+ /* There are no options to %prep */
+ if ((rc = readLine(spec, STRIP_NOTHING)) > 0) {
+ return PART_NONE;
+ } else if (rc < 0) {
+ return PART_ERROR;
+ }
+
+ while (! (nextPart = isPart(spec->line))) {
+ /* Need to expand the macros inline. That way we */
+ /* can give good line number information on error. */
+ argvAdd(&saveLines, spec->line);
+ if ((rc = readLine(spec, STRIP_NOTHING)) > 0) {
+ nextPart = PART_NONE;
+ break;
+ } else if (rc < 0) {
+ goto exit;
+ }
+ }
+
+ for (ARGV_const_t lines = saveLines; lines && *lines; lines++) {
+ rc = RPMRC_OK;
+ if (rstreqn(*lines, "%setup", sizeof("%setup")-1)) {
+ rc = doSetupMacro(spec, *lines);
+ } else if (rstreqn(*lines, "%patch", sizeof("%patch")-1)) {
+ rc = doPatchMacro(spec, *lines);
+ } else {
+ appendStringBuf(spec->prep, *lines);
+ }
+ if (rc != RPMRC_OK && !(spec->flags & RPMSPEC_FORCE)) {
+ goto exit;
+ }
+ }
+ res = nextPart;
+
+exit:
+ argvFree(saveLines);
+
+ return res;
+}
diff --git a/build/parseReqs.c b/build/parseReqs.c
new file mode 100644
index 0000000..de2680c
--- /dev/null
+++ b/build/parseReqs.c
@@ -0,0 +1,189 @@
+/** \ingroup rpmbuild
+ * \file build/parseReqs.c
+ * Parse dependency tag from spec file or from auto-dependency generator.
+ */
+
+#include "system.h"
+
+#include <ctype.h>
+#include <rpm/rpmtypes.h>
+#include <rpm/rpmlog.h>
+#include "build/rpmbuild_internal.h"
+#include "build/rpmbuild_misc.h"
+#include "debug.h"
+
+/**
+ */
+static struct ReqComp {
+const char * token;
+ rpmsenseFlags sense;
+} const ReqComparisons[] = {
+ { "<=", RPMSENSE_LESS | RPMSENSE_EQUAL},
+ { "=<", RPMSENSE_LESS | RPMSENSE_EQUAL},
+ { "<", RPMSENSE_LESS},
+
+ { "==", RPMSENSE_EQUAL},
+ { "=", RPMSENSE_EQUAL},
+
+ { ">=", RPMSENSE_GREATER | RPMSENSE_EQUAL},
+ { "=>", RPMSENSE_GREATER | RPMSENSE_EQUAL},
+ { ">", RPMSENSE_GREATER},
+
+ { NULL, 0 },
+};
+
+#define SKIPWHITE(_x) {while(*(_x) && (risspace(*_x) || *(_x) == ',')) (_x)++;}
+#define SKIPNONWHITE(_x){while(*(_x) &&!(risspace(*_x) || *(_x) == ',')) (_x)++;}
+
+rpmRC parseRCPOT(rpmSpec spec, Package pkg, const char *field, rpmTagVal tagN,
+ int index, rpmsenseFlags tagflags)
+{
+ const char *r, *re, *v, *ve;
+ const char *emsg = NULL;
+ char * N = NULL, * EVR = NULL;
+ rpmTagVal nametag = RPMTAG_NOT_FOUND;
+ rpmsenseFlags Flags;
+ Header h = pkg->header; /* everything except buildrequires go here */
+ rpmRC rc = RPMRC_FAIL; /* assume failure */
+
+ switch (tagN) {
+ default:
+ case RPMTAG_REQUIREFLAGS:
+ nametag = RPMTAG_REQUIRENAME;
+ tagflags |= RPMSENSE_ANY;
+ break;
+ case RPMTAG_PROVIDEFLAGS:
+ nametag = RPMTAG_PROVIDENAME;
+ break;
+ case RPMTAG_OBSOLETEFLAGS:
+ nametag = RPMTAG_OBSOLETENAME;
+ break;
+ case RPMTAG_CONFLICTFLAGS:
+ nametag = RPMTAG_CONFLICTNAME;
+ break;
+ case RPMTAG_ORDERFLAGS:
+ nametag = RPMTAG_ORDERNAME;
+ break;
+ case RPMTAG_PREREQ:
+ /* XXX map legacy PreReq into Requires(pre,preun) */
+ nametag = RPMTAG_REQUIRENAME;
+ tagflags |= (RPMSENSE_SCRIPT_PRE|RPMSENSE_SCRIPT_PREUN);
+ break;
+ case RPMTAG_TRIGGERPREIN:
+ nametag = RPMTAG_TRIGGERNAME;
+ tagflags |= RPMSENSE_TRIGGERPREIN;
+ break;
+ case RPMTAG_TRIGGERIN:
+ nametag = RPMTAG_TRIGGERNAME;
+ tagflags |= RPMSENSE_TRIGGERIN;
+ break;
+ case RPMTAG_TRIGGERPOSTUN:
+ nametag = RPMTAG_TRIGGERNAME;
+ tagflags |= RPMSENSE_TRIGGERPOSTUN;
+ break;
+ case RPMTAG_TRIGGERUN:
+ nametag = RPMTAG_TRIGGERNAME;
+ tagflags |= RPMSENSE_TRIGGERUN;
+ break;
+ case RPMTAG_BUILDPREREQ:
+ case RPMTAG_BUILDREQUIRES:
+ nametag = RPMTAG_REQUIRENAME;
+ tagflags |= RPMSENSE_ANY;
+ h = spec->buildRestrictions;
+ break;
+ case RPMTAG_BUILDCONFLICTS:
+ nametag = RPMTAG_CONFLICTNAME;
+ h = spec->buildRestrictions;
+ break;
+ }
+
+ for (r = field; *r != '\0'; r = re) {
+ SKIPWHITE(r);
+ if (*r == '\0')
+ break;
+
+ Flags = (tagflags & ~RPMSENSE_SENSEMASK);
+
+ /*
+ * Tokens must begin with alphanumeric, _, or /, but we don't know
+ * the spec's encoding so we only check what we can: plain ascii.
+ */
+ if (isascii(r[0]) && !(risalnum(r[0]) || r[0] == '_' || r[0] == '/')) {
+ emsg = _("Dependency tokens must begin with alpha-numeric, '_' or '/'");
+ goto exit;
+ }
+
+ re = r;
+ SKIPNONWHITE(re);
+ N = xmalloc((re-r) + 1);
+ rstrlcpy(N, r, (re-r) + 1);
+
+ /* Parse EVR */
+ v = re;
+ SKIPWHITE(v);
+ ve = v;
+ SKIPNONWHITE(ve);
+
+ re = v; /* ==> next token (if no EVR found) starts here */
+
+ /* Check for possible logical operator */
+ if (ve > v) {
+ const struct ReqComp *rc;
+ for (rc = ReqComparisons; rc->token != NULL; rc++) {
+ if ((ve-v) != strlen(rc->token) || !rstreqn(v, rc->token, (ve-v)))
+ continue;
+
+ if (r[0] == '/') {
+ emsg = _("Versioned file name not permitted");
+ goto exit;
+ }
+
+ Flags |= rc->sense;
+
+ /* now parse EVR */
+ v = ve;
+ SKIPWHITE(v);
+ ve = v;
+ SKIPNONWHITE(ve);
+ break;
+ }
+ }
+
+ if (Flags & RPMSENSE_SENSEMASK) {
+ if (*v == '\0' || ve == v) {
+ emsg = _("Version required");
+ goto exit;
+ }
+ EVR = xmalloc((ve-v) + 1);
+ rstrlcpy(EVR, v, (ve-v) + 1);
+ if (rpmCharCheck(spec, EVR, ve-v, ".-_+:%{}")) goto exit;
+ re = ve; /* ==> next token after EVR string starts here */
+ } else
+ EVR = NULL;
+
+ if (addReqProv(h, nametag, N, EVR, Flags, index)) {
+ emsg = _("invalid dependency");
+ goto exit;
+ }
+
+ N = _free(N);
+ EVR = _free(EVR);
+
+ }
+ rc = RPMRC_OK;
+
+exit:
+ if (emsg) {
+ /* Automatic dependencies don't relate to spec lines */
+ if (tagflags & (RPMSENSE_FIND_REQUIRES|RPMSENSE_FIND_PROVIDES)) {
+ rpmlog(RPMLOG_ERR, "%s: %s\n", emsg, r);
+ } else {
+ rpmlog(RPMLOG_ERR, _("line %d: %s: %s\n"),
+ spec->lineNum, emsg, spec->line);
+ }
+ }
+ free(N);
+ free(EVR);
+
+ return rc;
+}
diff --git a/build/parseScript.c b/build/parseScript.c
new file mode 100644
index 0000000..87b3d58
--- /dev/null
+++ b/build/parseScript.c
@@ -0,0 +1,389 @@
+/** \ingroup rpmbuild
+ * \file build/parseScript.c
+ * Parse install-time script section from spec file.
+ */
+
+#include "system.h"
+
+#include <rpm/header.h>
+#include <rpm/rpmlog.h>
+
+#include "rpmio/rpmlua.h"
+#include "lib/rpmscript.h" /* script flags */
+#include "build/rpmbuild_internal.h"
+#include "build/rpmbuild_misc.h"
+
+#include "debug.h"
+
+
+/**
+ */
+static int addTriggerIndex(Package pkg, const char *file,
+ const char *script, const char *prog, rpmscriptFlags flags)
+{
+ struct TriggerFileEntry *tfe;
+ struct TriggerFileEntry *list = pkg->triggerFiles;
+ struct TriggerFileEntry *last = NULL;
+ int index = 0;
+
+ while (list) {
+ last = list;
+ list = list->next;
+ }
+
+ if (last)
+ index = last->index + 1;
+
+ tfe = xcalloc(1, sizeof(*tfe));
+
+ tfe->fileName = (file) ? xstrdup(file) : NULL;
+ tfe->script = (script && *script != '\0') ? xstrdup(script) : NULL;
+ tfe->prog = xstrdup(prog);
+ tfe->flags = flags;
+ tfe->index = index;
+ tfe->next = NULL;
+
+ if (last)
+ last->next = tfe;
+ else
+ pkg->triggerFiles = tfe;
+
+ return index;
+}
+
+/* %trigger is a strange combination of %pre and Requires: behavior */
+/* We can handle it by parsing the args before "--" in parseScript. */
+/* We then pass the remaining arguments to parseRCPOT, along with */
+/* an index we just determined. */
+
+int parseScript(rpmSpec spec, int parsePart)
+{
+ /* There are a few options to scripts: */
+ /* <pkg> */
+ /* -n <pkg> */
+ /* -p <sh> */
+ /* -p "<sh> <args>..." */
+ /* -f <file> */
+
+ const char *p;
+ const char **progArgv = NULL;
+ int progArgc;
+ const char *partname = NULL;
+ rpmTagVal reqtag = 0;
+ rpmTagVal tag = 0;
+ rpmsenseFlags tagflags = 0;
+ rpmTagVal progtag = 0;
+ rpmTagVal flagtag = 0;
+ rpmscriptFlags scriptFlags = 0;
+ int flag = PART_SUBNAME;
+ Package pkg;
+ StringBuf sb = NULL;
+ int nextPart;
+ int index;
+ char * reqargs = NULL;
+
+ int res = PART_ERROR; /* assume failure */
+ int rc, argc;
+ int arg;
+ const char **argv = NULL;
+ poptContext optCon = NULL;
+ const char *name = NULL;
+ const char *prog = "/bin/sh";
+ const char *file = NULL;
+ int expand = 0;
+ int qformat = 0;
+ struct poptOption optionsTable[] = {
+ { NULL, 'p', POPT_ARG_STRING, &prog, 'p', NULL, NULL},
+ { NULL, 'n', POPT_ARG_STRING, &name, 'n', NULL, NULL},
+ { NULL, 'f', POPT_ARG_STRING, &file, 'f', NULL, NULL},
+ { NULL, 'e', POPT_ARG_NONE, &expand, 'e', NULL, NULL},
+ { NULL, 'q', POPT_ARG_NONE, &qformat, 'q', NULL, NULL},
+ { 0, 0, 0, 0, 0, NULL, NULL}
+ };
+
+ switch (parsePart) {
+ case PART_PRE:
+ tag = RPMTAG_PREIN;
+ tagflags = RPMSENSE_SCRIPT_PRE;
+ progtag = RPMTAG_PREINPROG;
+ flagtag = RPMTAG_PREINFLAGS;
+ partname = "%pre";
+ break;
+ case PART_POST:
+ tag = RPMTAG_POSTIN;
+ tagflags = RPMSENSE_SCRIPT_POST;
+ progtag = RPMTAG_POSTINPROG;
+ flagtag = RPMTAG_POSTINFLAGS;
+ partname = "%post";
+ break;
+ case PART_PREUN:
+ tag = RPMTAG_PREUN;
+ tagflags = RPMSENSE_SCRIPT_PREUN;
+ progtag = RPMTAG_PREUNPROG;
+ flagtag = RPMTAG_PREUNFLAGS;
+ partname = "%preun";
+ break;
+ case PART_POSTUN:
+ tag = RPMTAG_POSTUN;
+ tagflags = RPMSENSE_SCRIPT_POSTUN;
+ progtag = RPMTAG_POSTUNPROG;
+ flagtag = RPMTAG_POSTUNFLAGS;
+ partname = "%postun";
+ break;
+ case PART_PRETRANS:
+ tag = RPMTAG_PRETRANS;
+ tagflags = RPMSENSE_PRETRANS;
+ progtag = RPMTAG_PRETRANSPROG;
+ flagtag = RPMTAG_PRETRANSFLAGS;
+ partname = "%pretrans";
+ break;
+ case PART_POSTTRANS:
+ tag = RPMTAG_POSTTRANS;
+ tagflags = RPMSENSE_POSTTRANS;
+ progtag = RPMTAG_POSTTRANSPROG;
+ flagtag = RPMTAG_POSTTRANSFLAGS;
+ partname = "%posttrans";
+ break;
+ case PART_VERIFYSCRIPT:
+ tag = RPMTAG_VERIFYSCRIPT;
+ tagflags = RPMSENSE_SCRIPT_VERIFY;
+ progtag = RPMTAG_VERIFYSCRIPTPROG;
+ flagtag = RPMTAG_VERIFYSCRIPTFLAGS;
+ partname = "%verifyscript";
+ break;
+ case PART_TRIGGERPREIN:
+ tag = RPMTAG_TRIGGERSCRIPTS;
+ tagflags = 0;
+ reqtag = RPMTAG_TRIGGERPREIN;
+ progtag = RPMTAG_TRIGGERSCRIPTPROG;
+ flagtag = RPMTAG_TRIGGERSCRIPTFLAGS;
+ partname = "%triggerprein";
+ break;
+ case PART_TRIGGERIN:
+ tag = RPMTAG_TRIGGERSCRIPTS;
+ tagflags = 0;
+ reqtag = RPMTAG_TRIGGERIN;
+ progtag = RPMTAG_TRIGGERSCRIPTPROG;
+ flagtag = RPMTAG_TRIGGERSCRIPTFLAGS;
+ partname = "%triggerin";
+ break;
+ case PART_TRIGGERUN:
+ tag = RPMTAG_TRIGGERSCRIPTS;
+ tagflags = 0;
+ reqtag = RPMTAG_TRIGGERUN;
+ progtag = RPMTAG_TRIGGERSCRIPTPROG;
+ flagtag = RPMTAG_TRIGGERSCRIPTFLAGS;
+ partname = "%triggerun";
+ break;
+ case PART_TRIGGERPOSTUN:
+ tag = RPMTAG_TRIGGERSCRIPTS;
+ tagflags = 0;
+ reqtag = RPMTAG_TRIGGERPOSTUN;
+ progtag = RPMTAG_TRIGGERSCRIPTPROG;
+ flagtag = RPMTAG_TRIGGERSCRIPTFLAGS;
+ partname = "%triggerpostun";
+ break;
+ }
+
+ if (tag == RPMTAG_TRIGGERSCRIPTS) {
+ /* break line into two */
+ char *s = strstr(spec->line, "--");
+ if (!s) {
+ rpmlog(RPMLOG_ERR, _("line %d: triggers must have --: %s\n"),
+ spec->lineNum, spec->line);
+ return PART_ERROR;
+ }
+
+ *s = '\0';
+ reqargs = xstrdup(s + 2);
+ }
+
+ if ((rc = poptParseArgvString(spec->line, &argc, &argv))) {
+ rpmlog(RPMLOG_ERR, _("line %d: Error parsing %s: %s\n"),
+ spec->lineNum, partname, poptStrerror(rc));
+ goto exit;
+ }
+
+ optCon = poptGetContext(NULL, argc, argv, optionsTable, 0);
+ while ((arg = poptGetNextOpt(optCon)) > 0) {
+ switch (arg) {
+ case 'p':
+ if (prog[0] == '<') {
+ if (prog[strlen(prog)-1] != '>') {
+ rpmlog(RPMLOG_ERR,
+ _("line %d: internal script must end "
+ "with \'>\': %s\n"), spec->lineNum, prog);
+ goto exit;
+ }
+ } else if (prog[0] != '/') {
+ rpmlog(RPMLOG_ERR,
+ _("line %d: script program must begin "
+ "with \'/\': %s\n"), spec->lineNum, prog);
+ goto exit;
+ }
+ break;
+ case 'n':
+ flag = PART_NAME;
+ break;
+ }
+ }
+
+ if (arg < -1) {
+ rpmlog(RPMLOG_ERR, _("line %d: Bad option %s: %s\n"),
+ spec->lineNum,
+ poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
+ spec->line);
+ goto exit;
+ }
+
+ if (poptPeekArg(optCon)) {
+ if (name == NULL)
+ name = poptGetArg(optCon);
+ if (poptPeekArg(optCon)) {
+ rpmlog(RPMLOG_ERR, _("line %d: Too many names: %s\n"),
+ spec->lineNum,
+ spec->line);
+ goto exit;
+ }
+ }
+
+ if (lookupPackage(spec, name, flag, &pkg)) {
+ rpmlog(RPMLOG_ERR, _("line %d: Package does not exist: %s\n"),
+ spec->lineNum, spec->line);
+ goto exit;
+ }
+
+ if (tag != RPMTAG_TRIGGERSCRIPTS) {
+ if (headerIsEntry(pkg->header, progtag)) {
+ rpmlog(RPMLOG_ERR, _("line %d: Second %s\n"),
+ spec->lineNum, partname);
+ goto exit;
+ }
+ }
+
+ if ((rc = poptParseArgvString(prog, &progArgc, &progArgv))) {
+ rpmlog(RPMLOG_ERR, _("line %d: Error parsing %s: %s\n"),
+ spec->lineNum, partname, poptStrerror(rc));
+ goto exit;
+ }
+
+ scriptFlags |= expand ? RPMSCRIPT_EXPAND : 0;
+ scriptFlags |= qformat ? RPMSCRIPT_QFORMAT : 0;
+
+ sb = newStringBuf();
+ if ((rc = readLine(spec, STRIP_NOTHING)) > 0) {
+ nextPart = PART_NONE;
+ } else if (rc < 0) {
+ goto exit;
+ } else {
+ while (! (nextPart = isPart(spec->line))) {
+ appendStringBuf(sb, spec->line);
+ if ((rc = readLine(spec, STRIP_NOTHING)) > 0) {
+ nextPart = PART_NONE;
+ break;
+ } else if (rc < 0) {
+ goto exit;
+ }
+ }
+ }
+ stripTrailingBlanksStringBuf(sb);
+ p = getStringBuf(sb);
+
+#ifdef WITH_LUA
+ if (rstreq(progArgv[0], "<lua>")) {
+ rpmlua lua = NULL; /* Global state. */
+ if (rpmluaCheckScript(lua, p, partname) != RPMRC_OK) {
+ goto exit;
+ }
+ (void) rpmlibNeedsFeature(pkg->header,
+ "BuiltinLuaScripts", "4.2.2-1");
+ } else
+#endif
+ if (progArgv[0][0] == '<') {
+ rpmlog(RPMLOG_ERR,
+ _("line %d: unsupported internal script: %s\n"),
+ spec->lineNum, progArgv[0]);
+ goto exit;
+ } else {
+ (void) addReqProv(pkg->header, RPMTAG_REQUIRENAME,
+ progArgv[0], NULL, (tagflags | RPMSENSE_INTERP), 0);
+ }
+
+ if (scriptFlags) {
+ rpmlibNeedsFeature(pkg->header, "ScriptletExpansion", "4.9.0-1");
+ }
+
+ /* Trigger script insertion is always delayed in order to */
+ /* get the index right. */
+ if (tag == RPMTAG_TRIGGERSCRIPTS) {
+ /* Add file/index/prog triple to the trigger file list */
+ index = addTriggerIndex(pkg, file, p, progArgv[0], scriptFlags);
+
+ /* Generate the trigger tags */
+ if ((rc = parseRCPOT(spec, pkg, reqargs, reqtag, index, tagflags)))
+ goto exit;
+ } else {
+ struct rpmtd_s td;
+
+ /*
+ * XXX Ancient rpm uses STRING, not STRING_ARRAY type here. Construct
+ * the td manually and preserve legacy compat for now...
+ */
+ rpmtdReset(&td);
+ td.tag = progtag;
+ td.count = progArgc;
+ if (progArgc == 1) {
+ td.data = (void *) *progArgv;
+ td.type = RPM_STRING_TYPE;
+ } else {
+ (void) rpmlibNeedsFeature(pkg->header,
+ "ScriptletInterpreterArgs", "4.0.3-1");
+ td.data = progArgv;
+ td.type = RPM_STRING_ARRAY_TYPE;
+ }
+ headerPut(pkg->header, &td, HEADERPUT_DEFAULT);
+
+ if (*p != '\0') {
+ headerPutString(pkg->header, tag, p);
+ }
+ if (scriptFlags) {
+ headerPutUint32(pkg->header, flagtag, &scriptFlags, 1);
+ }
+
+ if (file) {
+ switch (parsePart) {
+ case PART_PRE:
+ pkg->preInFile = xstrdup(file);
+ break;
+ case PART_POST:
+ pkg->postInFile = xstrdup(file);
+ break;
+ case PART_PREUN:
+ pkg->preUnFile = xstrdup(file);
+ break;
+ case PART_POSTUN:
+ pkg->postUnFile = xstrdup(file);
+ break;
+ case PART_PRETRANS:
+ pkg->preTransFile = xstrdup(file);
+ break;
+ case PART_POSTTRANS:
+ pkg->postTransFile = xstrdup(file);
+ break;
+ case PART_VERIFYSCRIPT:
+ pkg->verifyFile = xstrdup(file);
+ break;
+ }
+ }
+ }
+ res = nextPart;
+
+exit:
+ free(reqargs);
+ sb = freeStringBuf(sb);
+ progArgv = _free(progArgv);
+ argv = _free(argv);
+ optCon = poptFreeContext(optCon);
+
+ return res;
+}
diff --git a/build/parseSpec.c b/build/parseSpec.c
new file mode 100644
index 0000000..01620bd
--- /dev/null
+++ b/build/parseSpec.c
@@ -0,0 +1,698 @@
+/** \ingroup rpmbuild
+ * \file build/parseSpec.c
+ * Top level dispatcher for spec file parsing.
+ */
+
+#include "system.h"
+
+#include <errno.h>
+
+#include <rpm/rpmtypes.h>
+#include <rpm/rpmlib.h> /* RPM_MACHTABLE & related */
+#include <rpm/rpmds.h>
+#include <rpm/rpmts.h>
+#include <rpm/rpmlog.h>
+#include <rpm/rpmfileutil.h>
+#include "build/rpmbuild_internal.h"
+#include "build/rpmbuild_misc.h"
+#include "debug.h"
+
+#define SKIPSPACE(s) { while (*(s) && risspace(*(s))) (s)++; }
+#define SKIPNONSPACE(s) { while (*(s) && !risspace(*(s))) (s)++; }
+
+#define LEN_AND_STR(_tag) (sizeof(_tag)-1), (_tag)
+
+typedef struct OpenFileInfo {
+ char * fileName;
+ FILE *fp;
+ int lineNum;
+ char readBuf[BUFSIZ];
+ char * readPtr;
+ struct OpenFileInfo * next;
+} OFI_t;
+
+static const struct PartRec {
+ int part;
+ size_t len;
+ const char * token;
+} partList[] = {
+ { PART_PREAMBLE, LEN_AND_STR("%package")},
+ { PART_PREP, LEN_AND_STR("%prep")},
+ { PART_BUILD, LEN_AND_STR("%build")},
+ { PART_INSTALL, LEN_AND_STR("%install")},
+ { PART_CHECK, LEN_AND_STR("%check")},
+ { PART_CLEAN, LEN_AND_STR("%clean")},
+ { PART_PREUN, LEN_AND_STR("%preun")},
+ { PART_POSTUN, LEN_AND_STR("%postun")},
+ { PART_PRETRANS, LEN_AND_STR("%pretrans")},
+ { PART_POSTTRANS, LEN_AND_STR("%posttrans")},
+ { PART_PRE, LEN_AND_STR("%pre")},
+ { PART_POST, LEN_AND_STR("%post")},
+ { PART_FILES, LEN_AND_STR("%files")},
+ { PART_CHANGELOG, LEN_AND_STR("%changelog")},
+ { PART_DESCRIPTION, LEN_AND_STR("%description")},
+ { PART_TRIGGERPOSTUN, LEN_AND_STR("%triggerpostun")},
+ { PART_TRIGGERPREIN, LEN_AND_STR("%triggerprein")},
+ { PART_TRIGGERUN, LEN_AND_STR("%triggerun")},
+ { PART_TRIGGERIN, LEN_AND_STR("%triggerin")},
+ { PART_TRIGGERIN, LEN_AND_STR("%trigger")},
+ { PART_VERIFYSCRIPT, LEN_AND_STR("%verifyscript")},
+ { PART_POLICIES, LEN_AND_STR("%sepolicy")},
+ {0, 0, 0}
+};
+
+int isPart(const char *line)
+{
+ const struct PartRec *p;
+
+ for (p = partList; p->token != NULL; p++) {
+ char c;
+ if (rstrncasecmp(line, p->token, p->len))
+ continue;
+ c = *(line + p->len);
+ if (c == '\0' || risspace(c))
+ break;
+ }
+
+ return (p->token ? p->part : PART_NONE);
+}
+
+/**
+ */
+static int matchTok(const char *token, const char *line)
+{
+ const char *b, *be = line;
+ size_t toklen = strlen(token);
+ int rc = 0;
+
+ while ( *(b = be) != '\0' ) {
+ SKIPSPACE(b);
+ be = b;
+ SKIPNONSPACE(be);
+ if (be == b)
+ break;
+ if (toklen != (be-b) || rstrncasecmp(token, b, (be-b)))
+ continue;
+ rc = 1;
+ break;
+ }
+
+ return rc;
+}
+
+void handleComments(char *s)
+{
+ SKIPSPACE(s);
+ if (*s == '#')
+ *s = '\0';
+}
+
+static struct OpenFileInfo * newOpenFileInfo(void)
+{
+ struct OpenFileInfo *ofi;
+
+ ofi = xmalloc(sizeof(*ofi));
+ ofi->fp = NULL;
+ ofi->fileName = NULL;
+ ofi->lineNum = 0;
+ ofi->readBuf[0] = '\0';
+ ofi->readPtr = NULL;
+ ofi->next = NULL;
+
+ return ofi;
+}
+
+/**
+ */
+static void forceIncludeFile(rpmSpec spec, const char * fileName)
+{
+ OFI_t * ofi;
+
+ ofi = newOpenFileInfo();
+ ofi->fileName = xstrdup(fileName);
+ ofi->next = spec->fileStack;
+ spec->fileStack = ofi;
+}
+
+static int restoreFirstChar(rpmSpec spec)
+{
+ /* Restore 1st char in (possible) next line */
+ if (spec->nextline != NULL && spec->nextpeekc != '\0') {
+ *spec->nextline = spec->nextpeekc;
+ spec->nextpeekc = '\0';
+ return 1;
+ }
+ return 0;
+}
+
+/* Return zero on success, 1 if we need to read more and -1 on errors. */
+static int copyNextLineFromOFI(rpmSpec spec, OFI_t *ofi)
+{
+ char ch;
+
+ /* Expand next line from file into line buffer */
+ if (!(spec->nextline && *spec->nextline)) {
+ int pc = 0, bc = 0, nc = 0;
+ char *from, *to, *p;
+ to = spec->lbufPtr ? spec->lbufPtr : spec->lbuf;
+ from = ofi->readPtr;
+ ch = ' ';
+ while (from && *from && ch != '\n')
+ ch = *to++ = *from++;
+ spec->lbufPtr = to;
+ *to++ = '\0';
+ ofi->readPtr = from;
+
+ /* Check if we need another line before expanding the buffer. */
+ for (p = spec->lbuf; *p; p++) {
+ switch (*p) {
+ case '\\':
+ switch (*(p+1)) {
+ case '\n': p++, nc = 1; break;
+ case '\0': break;
+ default: p++; break;
+ }
+ break;
+ case '\n': nc = 0; 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 it doesn't, ask for one more line. */
+ if (pc || bc || nc ) {
+ spec->nextline = "";
+ return 1;
+ }
+ spec->lbufPtr = spec->lbuf;
+
+ /* Don't expand macros (eg. %define) in false branch of %if clause */
+ if (spec->readStack->reading &&
+ expandMacros(spec, spec->macros, spec->lbuf, sizeof(spec->lbuf))) {
+ rpmlog(RPMLOG_ERR, _("line %d: %s\n"),
+ spec->lineNum, spec->lbuf);
+ return -1;
+ }
+ spec->nextline = spec->lbuf;
+ }
+ return 0;
+}
+
+static void copyNextLineFinish(rpmSpec spec, int strip)
+{
+ char *last;
+ char ch;
+
+ /* Find next line in expanded line buffer */
+ spec->line = last = spec->nextline;
+ ch = ' ';
+ while (*spec->nextline && ch != '\n') {
+ ch = *spec->nextline++;
+ if (!risspace(ch))
+ last = spec->nextline;
+ }
+
+ /* Save 1st char of next line in order to terminate current line. */
+ if (*spec->nextline != '\0') {
+ spec->nextpeekc = *spec->nextline;
+ *spec->nextline = '\0';
+ }
+
+ if (strip & STRIP_COMMENTS)
+ handleComments(spec->line);
+
+ if (strip & STRIP_TRAILINGSPACE)
+ *last = '\0';
+}
+
+static int readLineFromOFI(rpmSpec spec, OFI_t *ofi)
+{
+retry:
+ /* Make sure the current file is open */
+ if (ofi->fp == NULL) {
+ ofi->fp = fopen(ofi->fileName, "r");
+ if (ofi->fp == NULL || ferror(ofi->fp)) {
+ /* XXX Fstrerror */
+ rpmlog(RPMLOG_ERR, _("Unable to open %s: %s\n"),
+ ofi->fileName, strerror(errno));
+ return PART_ERROR;
+ }
+ spec->lineNum = ofi->lineNum = 0;
+ }
+
+ /* Make sure we have something in the read buffer */
+ if (!(ofi->readPtr && *(ofi->readPtr))) {
+ if (!fgets(ofi->readBuf, BUFSIZ, ofi->fp)) {
+ /* EOF */
+ if (spec->readStack->next) {
+ rpmlog(RPMLOG_ERR, _("Unclosed %%if\n"));
+ return PART_ERROR;
+ }
+
+ /* remove this file from the stack */
+ spec->fileStack = ofi->next;
+ fclose(ofi->fp);
+ ofi->fileName = _free(ofi->fileName);
+ ofi = _free(ofi);
+
+ /* only on last file do we signal EOF to caller */
+ ofi = spec->fileStack;
+ if (ofi == NULL)
+ return 1;
+
+ /* otherwise, go back and try the read again. */
+ goto retry;
+ }
+ ofi->readPtr = ofi->readBuf;
+ ofi->lineNum++;
+ spec->lineNum = ofi->lineNum;
+ }
+ return 0;
+}
+
+int readLine(rpmSpec spec, int strip)
+{
+ char *s;
+ int match;
+ struct ReadLevelEntry *rl;
+ OFI_t *ofi = spec->fileStack;
+ int rc;
+ int startLine = 0;
+
+ if (!restoreFirstChar(spec)) {
+ retry:
+ if ((rc = readLineFromOFI(spec, ofi)) != 0) {
+ if (startLine > 0) {
+ rpmlog(RPMLOG_ERR,
+ _("line %d: unclosed macro or bad line continuation\n"),
+ startLine);
+ rc = PART_ERROR;
+ }
+ return rc;
+ }
+ ofi = spec->fileStack;
+
+ /* Copy next file line into the spec line buffer */
+ rc = copyNextLineFromOFI(spec, ofi);
+ if (rc > 0) {
+ if (startLine == 0)
+ startLine = spec->lineNum;
+ goto retry;
+ } else if (rc < 0) {
+ return PART_ERROR;
+ }
+ }
+
+ copyNextLineFinish(spec, strip);
+
+ s = spec->line;
+ SKIPSPACE(s);
+
+ match = -1;
+ if (!spec->readStack->reading && rstreqn("%if", s, sizeof("%if")-1)) {
+ match = 0;
+ } else if (rstreqn("%ifarch", s, sizeof("%ifarch")-1)) {
+ char *arch = rpmExpand("%{_target_cpu}", NULL);
+ s += 7;
+ match = matchTok(arch, s);
+ arch = _free(arch);
+ } else if (rstreqn("%ifnarch", s, sizeof("%ifnarch")-1)) {
+ char *arch = rpmExpand("%{_target_cpu}", NULL);
+ s += 8;
+ match = !matchTok(arch, s);
+ arch = _free(arch);
+ } else if (rstreqn("%ifos", s, sizeof("%ifos")-1)) {
+ char *os = rpmExpand("%{_target_os}", NULL);
+ s += 5;
+ match = matchTok(os, s);
+ os = _free(os);
+ } else if (rstreqn("%ifnos", s, sizeof("%ifnos")-1)) {
+ char *os = rpmExpand("%{_target_os}", NULL);
+ s += 6;
+ match = !matchTok(os, s);
+ os = _free(os);
+ } else if (rstreqn("%if", s, sizeof("%if")-1)) {
+ s += 3;
+ match = parseExpressionBoolean(spec, s);
+ if (match < 0) {
+ rpmlog(RPMLOG_ERR,
+ _("%s:%d: parseExpressionBoolean returns %d\n"),
+ ofi->fileName, ofi->lineNum, match);
+ return PART_ERROR;
+ }
+ } else if (rstreqn("%else", s, sizeof("%else")-1)) {
+ s += 5;
+ if (! spec->readStack->next) {
+ /* Got an else with no %if ! */
+ rpmlog(RPMLOG_ERR,
+ _("%s:%d: Got a %%else with no %%if\n"),
+ ofi->fileName, ofi->lineNum);
+ return PART_ERROR;
+ }
+ spec->readStack->reading =
+ spec->readStack->next->reading && ! spec->readStack->reading;
+ spec->line[0] = '\0';
+ } else if (rstreqn("%endif", s, sizeof("%endif")-1)) {
+ s += 6;
+ if (! spec->readStack->next) {
+ /* Got an end with no %if ! */
+ rpmlog(RPMLOG_ERR,
+ _("%s:%d: Got a %%endif with no %%if\n"),
+ ofi->fileName, ofi->lineNum);
+ return PART_ERROR;
+ }
+ rl = spec->readStack;
+ spec->readStack = spec->readStack->next;
+ free(rl);
+ spec->line[0] = '\0';
+ } else if (rstreqn("%include", s, sizeof("%include")-1)) {
+ char *fileName, *endFileName, *p;
+
+ s += 8;
+ fileName = s;
+ if (! risspace(*fileName)) {
+ rpmlog(RPMLOG_ERR, _("malformed %%include statement\n"));
+ return PART_ERROR;
+ }
+ SKIPSPACE(fileName);
+ endFileName = fileName;
+ SKIPNONSPACE(endFileName);
+ p = endFileName;
+ SKIPSPACE(p);
+ if (*p != '\0') {
+ rpmlog(RPMLOG_ERR, _("malformed %%include statement\n"));
+ return PART_ERROR;
+ }
+ *endFileName = '\0';
+
+ forceIncludeFile(spec, fileName);
+
+ ofi = spec->fileStack;
+ goto retry;
+ }
+
+ if (match != -1) {
+ rl = xmalloc(sizeof(*rl));
+ rl->reading = spec->readStack->reading && match;
+ rl->next = spec->readStack;
+ spec->readStack = rl;
+ spec->line[0] = '\0';
+ }
+
+ if (! spec->readStack->reading) {
+ spec->line[0] = '\0';
+ }
+
+ /* Collect parsed line */
+ if (spec->parsed == NULL)
+ spec->parsed = newStringBuf();
+ appendStringBufAux(spec->parsed, spec->line,(strip & STRIP_TRAILINGSPACE));
+
+ /* FIX: spec->readStack->next should be dependent */
+ return 0;
+}
+
+void closeSpec(rpmSpec spec)
+{
+ OFI_t *ofi;
+
+ while (spec->fileStack) {
+ ofi = spec->fileStack;
+ spec->fileStack = spec->fileStack->next;
+ if (ofi->fp) (void) fclose(ofi->fp);
+ ofi->fileName = _free(ofi->fileName);
+ ofi = _free(ofi);
+ }
+}
+
+static const rpmTagVal sourceTags[] = {
+ RPMTAG_NAME,
+ RPMTAG_VERSION,
+ RPMTAG_RELEASE,
+ RPMTAG_EPOCH,
+ RPMTAG_SUMMARY,
+ RPMTAG_DESCRIPTION,
+ RPMTAG_PACKAGER,
+ RPMTAG_DISTRIBUTION,
+ RPMTAG_DISTURL,
+ RPMTAG_VENDOR,
+ RPMTAG_LICENSE,
+ RPMTAG_GROUP,
+ RPMTAG_OS,
+ RPMTAG_ARCH,
+ RPMTAG_CHANGELOGTIME,
+ RPMTAG_CHANGELOGNAME,
+ RPMTAG_CHANGELOGTEXT,
+ RPMTAG_URL,
+ RPMTAG_BUGURL,
+ RPMTAG_HEADERI18NTABLE,
+ 0
+};
+
+static void initSourceHeader(rpmSpec spec)
+{
+ HeaderIterator hi;
+ struct rpmtd_s td;
+ struct Source *srcPtr;
+
+ spec->sourceHeader = headerNew();
+ /* Only specific tags are added to the source package header */
+ headerCopyTags(spec->packages->header, spec->sourceHeader, sourceTags);
+
+ /* Add the build restrictions */
+ hi = headerInitIterator(spec->buildRestrictions);
+ while (headerNext(hi, &td)) {
+ if (rpmtdCount(&td) > 0) {
+ (void) headerPut(spec->sourceHeader, &td, HEADERPUT_DEFAULT);
+ }
+ rpmtdFreeData(&td);
+ }
+ hi = headerFreeIterator(hi);
+
+ if (spec->BANames && spec->BACount > 0) {
+ headerPutStringArray(spec->sourceHeader, RPMTAG_BUILDARCHS,
+ spec->BANames, spec->BACount);
+ }
+
+ /* Add tags for sources and patches */
+ for (srcPtr = spec->sources; srcPtr != NULL; srcPtr = srcPtr->next) {
+ if (srcPtr->flags & RPMBUILD_ISSOURCE) {
+ headerPutString(spec->sourceHeader, RPMTAG_SOURCE, srcPtr->source);
+ if (srcPtr->flags & RPMBUILD_ISNO) {
+ headerPutUint32(spec->sourceHeader, RPMTAG_NOSOURCE,
+ &srcPtr->num, 1);
+ }
+ }
+ if (srcPtr->flags & RPMBUILD_ISPATCH) {
+ headerPutString(spec->sourceHeader, RPMTAG_PATCH, srcPtr->source);
+ if (srcPtr->flags & RPMBUILD_ISNO) {
+ headerPutUint32(spec->sourceHeader, RPMTAG_NOPATCH,
+ &srcPtr->num, 1);
+ }
+ }
+ }
+}
+
+static void addTargets(Package Pkgs)
+{
+ char *platform = rpmExpand("%{_target_platform}", NULL);
+ char *arch = rpmExpand("%{_target_cpu}", NULL);
+ char *os = rpmExpand("%{_target_os}", NULL);
+
+ for (Package pkg = Pkgs; pkg != NULL; pkg = pkg->next) {
+ headerPutString(pkg->header, RPMTAG_OS, os);
+ /* noarch subpackages already have arch set here, leave it alone */
+ if (!headerIsEntry(pkg->header, RPMTAG_ARCH)) {
+ headerPutString(pkg->header, RPMTAG_ARCH, arch);
+ }
+ headerPutString(pkg->header, RPMTAG_PLATFORM, platform);
+
+ pkg->ds = rpmdsThis(pkg->header, RPMTAG_REQUIRENAME, RPMSENSE_EQUAL);
+ }
+ free(platform);
+ free(arch);
+ free(os);
+}
+
+static rpmSpec parseSpec(const char *specFile, rpmSpecFlags flags,
+ const char *buildRoot, int recursing)
+{
+ int parsePart = PART_PREAMBLE;
+ int initialPackage = 1;
+ rpmSpec spec;
+
+ /* Set up a new Spec structure with no packages. */
+ spec = newSpec();
+
+ spec->specFile = rpmGetPath(specFile, NULL);
+ spec->fileStack = newOpenFileInfo();
+ spec->fileStack->fileName = xstrdup(spec->specFile);
+ /* If buildRoot not specified, use default %{buildroot} */
+ if (buildRoot) {
+ spec->buildRoot = xstrdup(buildRoot);
+ } else {
+ spec->buildRoot = rpmGetPath("%{?buildroot:%{buildroot}}", NULL);
+ }
+ addMacro(NULL, "_docdir", NULL, "%{_defaultdocdir}", RMIL_SPEC);
+ spec->recursing = recursing;
+ spec->flags = flags;
+
+ /* All the parse*() functions expect to have a line pre-read */
+ /* in the spec's line buffer. Except for parsePreamble(), */
+ /* which handles the initial entry into a spec file. */
+
+ while (parsePart != PART_NONE) {
+ int goterror = 0;
+ switch (parsePart) {
+ case PART_ERROR: /* fallthrough */
+ default:
+ goterror = 1;
+ break;
+ case PART_PREAMBLE:
+ parsePart = parsePreamble(spec, initialPackage);
+ initialPackage = 0;
+ break;
+ case PART_PREP:
+ parsePart = parsePrep(spec);
+ break;
+ case PART_BUILD:
+ case PART_INSTALL:
+ case PART_CHECK:
+ case PART_CLEAN:
+ parsePart = parseBuildInstallClean(spec, parsePart);
+ break;
+ case PART_CHANGELOG:
+ parsePart = parseChangelog(spec);
+ break;
+ case PART_DESCRIPTION:
+ parsePart = parseDescription(spec);
+ break;
+
+ case PART_PRE:
+ case PART_POST:
+ case PART_PREUN:
+ case PART_POSTUN:
+ case PART_PRETRANS:
+ case PART_POSTTRANS:
+ case PART_VERIFYSCRIPT:
+ case PART_TRIGGERPREIN:
+ case PART_TRIGGERIN:
+ case PART_TRIGGERUN:
+ case PART_TRIGGERPOSTUN:
+ parsePart = parseScript(spec, parsePart);
+ break;
+
+ case PART_FILES:
+ parsePart = parseFiles(spec);
+ break;
+
+ case PART_POLICIES:
+ parsePart = parsePolicies(spec);
+ break;
+
+ case PART_NONE: /* XXX avoid gcc whining */
+ case PART_LAST:
+ case PART_BUILDARCHITECTURES:
+ break;
+ }
+
+ if (goterror || parsePart >= PART_LAST) {
+ goto errxit;
+ }
+
+ if (parsePart == PART_BUILDARCHITECTURES) {
+ int index;
+ int x;
+
+ closeSpec(spec);
+
+ spec->BASpecs = xcalloc(spec->BACount, sizeof(*spec->BASpecs));
+ index = 0;
+ if (spec->BANames != NULL)
+ for (x = 0; x < spec->BACount; x++) {
+
+ /* Skip if not arch is not compatible. */
+ if (!rpmMachineScore(RPM_MACHTABLE_BUILDARCH, spec->BANames[x]))
+ continue;
+ addMacro(NULL, "_target_cpu", NULL, spec->BANames[x], RMIL_RPMRC);
+ spec->BASpecs[index] = parseSpec(specFile, flags, buildRoot, 1);
+ if (spec->BASpecs[index] == NULL) {
+ spec->BACount = index;
+ goto errxit;
+ }
+ delMacro(NULL, "_target_cpu");
+ index++;
+ }
+
+ spec->BACount = index;
+ if (! index) {
+ rpmlog(RPMLOG_ERR,
+ _("No compatible architectures found for build\n"));
+ goto errxit;
+ }
+
+ /*
+ * Return the 1st child's fully parsed Spec structure.
+ * The restart of the parse when encountering BuildArch
+ * causes problems for "rpm -q --specfile". This is
+ * still a hack because there may be more than 1 arch
+ * specified (unlikely but possible.) There's also the
+ * further problem that the macro context, particularly
+ * %{_target_cpu}, disagrees with the info in the header.
+ */
+ if (spec->BACount >= 1) {
+ rpmSpec nspec = spec->BASpecs[0];
+ spec->BASpecs = _free(spec->BASpecs);
+ spec = rpmSpecFree(spec);
+ spec = nspec;
+ }
+
+ goto exit;
+ }
+ }
+
+ if (spec->clean == NULL) {
+ char *body = rpmExpand("%{?buildroot: %{__rm} -rf %{buildroot}}", NULL);
+ spec->clean = newStringBuf();
+ appendLineStringBuf(spec->clean, body);
+ free(body);
+ }
+
+ /* Check for description in each package */
+ for (Package pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
+ if (!headerIsEntry(pkg->header, RPMTAG_DESCRIPTION)) {
+ rpmlog(RPMLOG_ERR, _("Package has no %%description: %s\n"),
+ headerGetString(pkg->header, RPMTAG_NAME));
+ goto errxit;
+ }
+ }
+
+ /* Add arch, os and platform for each package */
+ addTargets(spec->packages);
+
+ closeSpec(spec);
+exit:
+ /* Assemble source header from parsed components */
+ initSourceHeader(spec);
+
+ return spec;
+
+errxit:
+ spec = rpmSpecFree(spec);
+ return NULL;
+}
+
+rpmSpec rpmSpecParse(const char *specFile, rpmSpecFlags flags,
+ const char *buildRoot)
+{
+ return parseSpec(specFile, flags, buildRoot, 0);
+}
diff --git a/build/policies.c b/build/policies.c
new file mode 100644
index 0000000..f8bb0c4
--- /dev/null
+++ b/build/policies.c
@@ -0,0 +1,316 @@
+/** \ingroup rpmbuild
+ * \file build/policies.c
+ * The post-build, packaging of policies
+ */
+
+#include "system.h"
+
+#include <rpm/rpmbuild.h>
+#include <rpm/argv.h>
+#include <rpm/rpmlog.h>
+#include <rpm/rpmpol.h>
+
+#include "rpmio/rpmio_internal.h"
+#include "build/rpmbuild_internal.h"
+#include "rpmio/base64.h"
+
+#include "debug.h"
+
+typedef struct ModuleRec_s {
+ char *path;
+ char *data;
+ char *name;
+ ARGV_t types;
+ uint32_t flags;
+} *ModuleRec;
+
+static rpmRC writeModuleToHeader(ModuleRec mod, Package pkg)
+{
+ rpmRC rc = RPMRC_FAIL;
+ ARGV_t av;
+ uint32_t count;
+ struct rpmtd_s policies;
+ struct rpmtd_s policynames;
+ struct rpmtd_s policyflags;
+ struct rpmtd_s policytypes;
+ struct rpmtd_s policytypesidx;
+ rpmtdReset(&policies);
+ rpmtdReset(&policynames);
+ rpmtdReset(&policyflags);
+ rpmtdReset(&policytypes);
+ rpmtdReset(&policytypesidx);
+
+ if (!mod || !pkg) {
+ goto exit;
+ }
+
+ /* check for duplicates */
+ if (headerIsEntry(pkg->header, RPMTAG_POLICYNAMES)) {
+ int typeCount;
+ const char *name;
+ char *type;
+ int i;
+ int idx;
+
+ headerGet(pkg->header, RPMTAG_POLICYNAMES, &policynames, HEADERGET_MINMEM);
+ headerGet(pkg->header, RPMTAG_POLICYFLAGS, &policyflags, HEADERGET_ARGV | HEADERGET_MINMEM);
+ headerGet(pkg->header, RPMTAG_POLICYTYPES, &policytypes, HEADERGET_ARGV | HEADERGET_MINMEM);
+ headerGet(pkg->header, RPMTAG_POLICYTYPESINDEXES, &policytypesidx, HEADERGET_ARGV | HEADERGET_MINMEM);
+ typeCount = rpmtdCount(&policytypes);
+
+ while ((name = rpmtdNextString(&policynames))) {
+ int overlappingtypes = 0;
+
+ idx = rpmtdGetIndex(&policynames);
+
+ for (i = 0; i < typeCount; i++) {
+ if (((int *) policytypesidx.data)[i] != idx) {
+ continue;
+ }
+
+ type = ((char **) policytypes.data)[i];
+
+ if (rstreq(type, RPMPOL_TYPE_DEFAULT) ||
+ argvSearch(mod->types, type, NULL) ||
+ argvSearch(mod->types, RPMPOL_TYPE_DEFAULT, NULL)) {
+ overlappingtypes = 1;
+ break;
+ }
+ }
+
+ if (!overlappingtypes) {
+ continue;
+ }
+
+ if (rstreq(mod->name, name)) {
+ rpmlog(RPMLOG_ERR, _("Policy module '%s' duplicated with overlapping types\n"), name);
+ goto exit;
+ }
+
+ if ((mod->flags && RPMPOL_FLAG_BASE) &&
+ (((int *) policyflags.data)[idx] & RPMPOL_FLAG_BASE)) {
+ rpmlog(RPMLOG_ERR, _("Base modules '%s' and '%s' have overlapping types\n"), mod->name, name);
+ goto exit;
+ }
+ }
+ }
+
+ if (headerIsEntry(pkg->header, RPMTAG_POLICIES)) {
+ if (!headerGet(pkg->header, RPMTAG_POLICIES, &policies, HEADERGET_MINMEM)) {
+ rpmlog(RPMLOG_ERR, _("Failed to get policies from header\n"));
+ goto exit;
+ }
+ count = rpmtdCount(&policies);
+ } else {
+ count = 0;
+ }
+
+ /* save everything to the header */
+ headerPutString(pkg->header, RPMTAG_POLICIES, mod->data);
+ headerPutString(pkg->header, RPMTAG_POLICYNAMES, mod->name);
+ headerPutUint32(pkg->header, RPMTAG_POLICYFLAGS, &mod->flags, 1);
+
+ for (av = mod->types; av && *av; av++) {
+ headerPutString(pkg->header, RPMTAG_POLICYTYPES, *av);
+ headerPutUint32(pkg->header, RPMTAG_POLICYTYPESINDEXES, &count, 1);
+ }
+
+ rc = RPMRC_OK;
+
+ exit:
+
+ rpmtdFreeData(&policies);
+ rpmtdFreeData(&policynames);
+ rpmtdFreeData(&policyflags);
+ rpmtdFreeData(&policytypes);
+ rpmtdFreeData(&policytypesidx);
+
+ return rc;
+}
+
+static ModuleRec freeModule(ModuleRec mod)
+{
+ if (mod) {
+ _free(mod->path);
+ _free(mod->data);
+ _free(mod->name);
+ argvFree(mod->types);
+ _free(mod);
+ }
+
+ return NULL;
+}
+
+static ModuleRec newModule(const char *path, const char *name,
+ const char *types, uint32_t flags)
+{
+ ModuleRec mod;
+ uint8_t *raw = NULL;
+ ssize_t rawlen = 0;
+ const char *buildDir = "%{_builddir}/%{?buildsubdir}/";
+
+ if (!path) {
+ rpmlog(RPMLOG_ERR, _("%%semodule requires a file path\n"));
+ return NULL;
+ }
+
+ mod = xcalloc(1, sizeof(*mod));
+
+ mod->path = rpmGenPath(buildDir, NULL, path);
+
+ if ((rpmioSlurp(mod->path, &raw, &rawlen)) != 0 || raw == NULL) {
+ rpmlog(RPMLOG_ERR, _("Failed to read policy file: %s\n"),
+ mod->path);
+ goto err;
+ }
+
+ mod->data = b64encode(raw, rawlen, -1);
+ if (!mod->data) {
+ rpmlog(RPMLOG_ERR, _("Failed to encode policy file: %s\n"),
+ mod->path);
+ goto err;
+ }
+
+ if (name) {
+ mod->name = xstrdup(name);
+ } else {
+ /* assume base name (minus extension) if name is not given */
+ char *tmp = xstrdup(mod->path);
+ char *bname = basename(tmp);
+ char *end = strchr(bname, '.');
+ if (end)
+ *end = '\0';
+ if (strlen(bname) > 0) {
+ mod->name = xstrdup(bname);
+ } else {
+ rpmlog(RPMLOG_ERR, _("Failed to determine a policy name: %s\n"),
+ mod->path);
+ _free(tmp);
+ goto err;
+ }
+ _free(tmp);
+ }
+
+ if (types) {
+ mod->types = argvSplitString(types, ",", ARGV_SKIPEMPTY);
+ argvSort(mod->types, NULL);
+ if (argvSearch(mod->types, RPMPOL_TYPE_DEFAULT, NULL) && argvCount(mod->types) > 1) {
+ rpmlog(RPMLOG_WARNING, _("'%s' type given with other types in %%semodule %s. Compacting types to '%s'.\n"),
+ RPMPOL_TYPE_DEFAULT, mod->path, RPMPOL_TYPE_DEFAULT);
+ mod->types = argvFree(mod->types);
+ argvAdd(&mod->types, RPMPOL_TYPE_DEFAULT);
+ }
+ } else {
+ argvAdd(&mod->types, RPMPOL_TYPE_DEFAULT);
+ }
+
+ mod->flags = flags;
+
+ return mod;
+
+ err:
+ freeModule(mod);
+ return NULL;
+}
+
+static rpmRC processPolicies(rpmSpec spec, Package pkg, int test)
+{
+ const char *path = NULL;
+ char *name = NULL;
+ char *types = NULL;
+ uint32_t flags = 0;
+ poptContext optCon = NULL;
+
+ rpmRC rc = RPMRC_FAIL;
+
+ struct poptOption optionsTable[] = {
+ {"name", 'n', POPT_ARG_STRING, &name, 'n', NULL, NULL},
+ {"types", 't', POPT_ARG_STRING, &types, 't', NULL, NULL},
+ {"base", 'b', POPT_ARGFLAG_OR, &flags, RPMPOL_FLAG_BASE, NULL, NULL},
+ POPT_TABLEEND
+ };
+
+ if (!spec || !pkg) {
+ goto exit;
+ }
+
+ for (ARGV_const_t pol = pkg->policyList; *pol != NULL; pol++) {
+ ModuleRec mod;
+ const char *line = *pol;
+ const char **argv = NULL;
+ int argc = 0;
+ int err;
+
+ if ((err = poptParseArgvString(line, &argc, &argv))) {
+ rpmlog(RPMLOG_ERR, _("Error parsing %s: %s\n"),
+ line, poptStrerror(err));
+ goto exit;
+ }
+
+ if (!rstreq(argv[0], "%semodule")) {
+ rpmlog(RPMLOG_ERR, _("Expecting %%semodule tag: %s\n"), line);
+ goto exit;
+ }
+
+ optCon = poptGetContext(NULL, argc, argv, optionsTable, 0);
+ while (poptGetNextOpt(optCon) > 0) {
+ }
+
+ path = poptGetArg(optCon);
+ if (!path) {
+ rpmlog(RPMLOG_ERR, _("Missing module path in line: %s\n"),
+ line);
+ goto exit;
+ }
+
+ if (poptPeekArg(optCon)) {
+ rpmlog(RPMLOG_ERR, _("Too many arguments in line: %s\n"),
+ line);
+ goto exit;
+ }
+
+ mod = newModule(path, name, types, flags);
+ if (!mod) {
+ goto exit;
+ }
+
+ if (writeModuleToHeader(mod, pkg) != RPMRC_OK) {
+ freeModule(mod);
+ goto exit;
+ }
+
+ freeModule(mod);
+ }
+
+ rc = RPMRC_OK;
+
+ exit:
+
+ return rc;
+}
+
+rpmRC processBinaryPolicies(rpmSpec spec, int test)
+{
+ Package pkg;
+ rpmRC rc = RPMRC_OK;
+ char *nvr;
+
+#if WITH_SELINUX
+ for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
+ if (pkg->policyList == NULL) {
+ continue;
+ }
+
+ nvr = headerGetAsString(pkg->header, RPMTAG_NVRA);
+ rpmlog(RPMLOG_NOTICE, _("Processing policies: %s\n"), nvr);
+ free(nvr);
+
+ if (processPolicies(spec, pkg, test) != RPMRC_OK) {
+ rc = RPMRC_FAIL;
+ break;
+ }
+ }
+#endif
+
+ return rc;
+}
diff --git a/build/reqprov.c b/build/reqprov.c
new file mode 100644
index 0000000..f2cdb5c
--- /dev/null
+++ b/build/reqprov.c
@@ -0,0 +1,123 @@
+/** \ingroup rpmbuild
+ * \file build/reqprov.c
+ * Add dependency tags to package header(s).
+ */
+
+#include "system.h"
+
+#include <rpm/header.h>
+#include <rpm/rpmstring.h>
+#include "build/rpmbuild_misc.h"
+#include "debug.h"
+
+static int isNewDep(Header h, rpmTagVal nametag,
+ const char *N, const char *EVR, rpmsenseFlags Flags,
+ rpmTagVal indextag, uint32_t index)
+{
+ int isnew = 1;
+ struct rpmtd_s idx;
+ rpmds ads = rpmdsNew(h, nametag, 0);
+ rpmds bds = rpmdsSingle(nametag, N, EVR, Flags);
+
+ if (indextag) {
+ headerGet(h, indextag, &idx, HEADERGET_MINMEM);
+ }
+
+ /* XXX there's no guarantee the ds is sorted here so rpmdsFind() wont do */
+ rpmdsInit(ads);
+ while (isnew && rpmdsNext(ads) >= 0) {
+ if (!rstreq(rpmdsN(ads), rpmdsN(bds))) continue;
+ if (!rstreq(rpmdsEVR(ads), rpmdsEVR(bds))) continue;
+ if (rpmdsFlags(ads) != rpmdsFlags(bds)) continue;
+ if (indextag && rpmtdSetIndex(&idx, rpmdsIx(ads)) >= 0 &&
+ rpmtdGetNumber(&idx) != index) continue;
+ isnew = 0;
+ }
+
+ if (indextag) {
+ rpmtdFreeData(&idx);
+ }
+ rpmdsFree(ads);
+ rpmdsFree(bds);
+ return isnew;
+}
+
+int addReqProv(Header h, rpmTagVal tagN,
+ const char * N, const char * EVR, rpmsenseFlags Flags,
+ uint32_t index)
+{
+ rpmTagVal versiontag = 0;
+ rpmTagVal flagtag = 0;
+ rpmTagVal indextag = 0;
+ rpmsenseFlags extra = RPMSENSE_ANY;
+
+ switch (tagN) {
+ case RPMTAG_PROVIDENAME:
+ versiontag = RPMTAG_PROVIDEVERSION;
+ flagtag = RPMTAG_PROVIDEFLAGS;
+ extra = Flags & RPMSENSE_FIND_PROVIDES;
+ break;
+ case RPMTAG_OBSOLETENAME:
+ versiontag = RPMTAG_OBSOLETEVERSION;
+ flagtag = RPMTAG_OBSOLETEFLAGS;
+ break;
+ case RPMTAG_CONFLICTNAME:
+ versiontag = RPMTAG_CONFLICTVERSION;
+ flagtag = RPMTAG_CONFLICTFLAGS;
+ break;
+ case RPMTAG_ORDERNAME:
+ versiontag = RPMTAG_ORDERVERSION;
+ flagtag = RPMTAG_ORDERFLAGS;
+ break;
+ case RPMTAG_TRIGGERNAME:
+ versiontag = RPMTAG_TRIGGERVERSION;
+ flagtag = RPMTAG_TRIGGERFLAGS;
+ indextag = RPMTAG_TRIGGERINDEX;
+ extra = Flags & RPMSENSE_TRIGGER;
+ break;
+ case RPMTAG_REQUIRENAME:
+ default:
+ tagN = RPMTAG_REQUIRENAME;
+ versiontag = RPMTAG_REQUIREVERSION;
+ flagtag = RPMTAG_REQUIREFLAGS;
+ extra = Flags & _ALL_REQUIRES_MASK;
+ }
+
+ /* rpmlib() dependency sanity: only requires permitted, ensure sense bit */
+ if (rstreqn(N, "rpmlib(", sizeof("rpmlib(")-1)) {
+ if (tagN != RPMTAG_REQUIRENAME) return 1;
+ extra |= RPMSENSE_RPMLIB;
+ }
+
+ Flags = (Flags & RPMSENSE_SENSEMASK) | extra;
+
+ if (EVR == NULL)
+ EVR = "";
+
+ /* Avoid adding duplicate dependencies. */
+ if (isNewDep(h, tagN, N, EVR, Flags, indextag, index)) {
+ headerPutString(h, tagN, N);
+ headerPutString(h, versiontag, EVR);
+ headerPutUint32(h, flagtag, &Flags, 1);
+ if (indextag) {
+ headerPutUint32(h, indextag, &index, 1);
+ }
+ }
+
+ return 0;
+}
+
+int rpmlibNeedsFeature(Header h, const char * feature, const char * featureEVR)
+{
+ char *reqname = NULL;
+ int res;
+
+ rasprintf(&reqname, "rpmlib(%s)", feature);
+
+ res = addReqProv(h, RPMTAG_REQUIRENAME, reqname, featureEVR,
+ RPMSENSE_RPMLIB|(RPMSENSE_LESS|RPMSENSE_EQUAL), 0);
+
+ free(reqname);
+
+ return res;
+}
diff --git a/build/rpmbuild.h b/build/rpmbuild.h
new file mode 100644
index 0000000..51a735d
--- /dev/null
+++ b/build/rpmbuild.h
@@ -0,0 +1,112 @@
+#ifndef _H_RPMBUILD_
+#define _H_RPMBUILD_
+
+/** \ingroup rpmbuild
+ * \file build/rpmbuild.h
+ * This is the *only* module users of librpmbuild should need to include.
+ */
+
+#include <rpm/rpmcli.h>
+#include <rpm/rpmds.h>
+#include <rpm/rpmspec.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** \ingroup rpmbuild
+ * Bit(s) to control rpmSpecBuild() operation.
+ */
+enum rpmBuildFlags_e {
+ RPMBUILD_NONE = 0,
+ RPMBUILD_PREP = (1 << 0), /*!< Execute %%prep. */
+ RPMBUILD_BUILD = (1 << 1), /*!< Execute %%build. */
+ RPMBUILD_INSTALL = (1 << 2), /*!< Execute %%install. */
+ RPMBUILD_CHECK = (1 << 3), /*!< Execute %%check. */
+ RPMBUILD_CLEAN = (1 << 4), /*!< Execute %%clean. */
+ RPMBUILD_FILECHECK = (1 << 5), /*!< Check %%files manifest. */
+ RPMBUILD_PACKAGESOURCE = (1 << 6), /*!< Create source package. */
+ RPMBUILD_PACKAGEBINARY = (1 << 7), /*!< Create binary package(s). */
+ RPMBUILD_RMSOURCE = (1 << 8), /*!< Remove source(s) and patch(s). */
+ RPMBUILD_RMBUILD = (1 << 9), /*!< Remove build sub-tree. */
+ RPMBUILD_STRINGBUF = (1 << 10), /*!< Internal use only */
+ RPMBUILD_RMSPEC = (1 << 11), /*!< Remove spec file. */
+
+ RPMBUILD_NOBUILD = (1 << 31) /*!< Don't execute or package. */
+};
+
+typedef rpmFlags rpmBuildFlags;
+
+/** \ingroup rpmbuild
+ * Bit(s) to control package generation
+ */
+enum rpmBuildPkgFlags_e {
+ RPMBUILD_PKG_NONE = 0,
+ RPMBUILD_PKG_NODIRTOKENS = (1 << 0), /*!< Legacy filename layout */
+};
+
+typedef rpmFlags rpmBuildPkgFlags;
+
+/** \ingroup rpmbuild
+ * Describe build request.
+ */
+struct rpmBuildArguments_s {
+ rpmBuildPkgFlags pkgFlags; /*!< Bit(s) to control package generation. */
+ rpmBuildFlags buildAmount; /*!< Bit(s) to control build execution. */
+ char * buildRootOverride; /*!< from --buildroot */
+ char * cookie; /*!< NULL for binary, ??? for source, rpm's */
+ const char * rootdir;
+};
+
+/** \ingroup rpmbuild
+ */
+typedef struct rpmBuildArguments_s * BTA_t;
+
+/** \ingroup rpmbuild
+ * Parse spec file into spec control structure.
+ * @todo Eliminate buildRoot from here, its a build, not spec property
+ *
+ * @param specFile path to spec file
+ * @param flags flags to control operation
+ * @param buildRoot buildRoot override or NULL for default
+ * @return new spec control structure
+ */
+rpmSpec rpmSpecParse(const char *specFile, rpmSpecFlags flags,
+ const char *buildRoot);
+
+/** \ingroup rpmbuild
+ * Return the headers of the SRPM that would be built from the spec file
+ * @param spec path to spec file
+ * @return Header
+ */
+Header rpmSpecSourceHeader(rpmSpec spec);
+
+/** \ingroup rpmbuild
+ * Verify build depencies of a spec against.
+ * @param ts (empty) transaction set
+ * @param spec parsed spec control structure
+ * @return rpm problem set or NULL on no problems
+ */
+rpmps rpmSpecCheckDeps(rpmts ts, rpmSpec spec);
+
+/** \ingroup rpmbuild
+ * Retrieve build dependency set from spec.
+ * @param spec parsed spec control structure
+ * @param tag dependency tag
+ * @return dependency set of tag (or NULL)
+ */
+rpmds rpmSpecDS(rpmSpec spec, rpmTagVal tag);
+
+/** \ingroup rpmbuild
+ * Spec build stages state machine driver.
+ * @param spec spec file control structure
+ * @param buildArgs build arguments
+ * @return RPMRC_OK on success
+ */
+rpmRC rpmSpecBuild(rpmSpec spec, BTA_t buildArgs);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _H_RPMBUILD_ */
diff --git a/build/rpmbuild_internal.h b/build/rpmbuild_internal.h
new file mode 100644
index 0000000..5f963a3
--- /dev/null
+++ b/build/rpmbuild_internal.h
@@ -0,0 +1,401 @@
+#ifndef _RPMBUILD_INTERNAL_H
+#define _RPMBUILD_INTERNAL_H
+
+#include <rpm/rpmbuild.h>
+#include <rpm/rpmutil.h>
+#include "build/rpmbuild_misc.h"
+
+struct TriggerFileEntry {
+ int index;
+ char * fileName;
+ char * script;
+ char * prog;
+ uint32_t flags;
+ struct TriggerFileEntry * next;
+};
+
+typedef struct ReadLevelEntry {
+ int reading;
+ struct ReadLevelEntry * next;
+} RLE_t;
+
+/** \ingroup rpmbuild
+ */
+struct Source {
+ char * fullSource;
+ const char * source; /* Pointer into fullSource */
+ int flags;
+ uint32_t num;
+struct Source * next;
+};
+
+typedef struct Package_s * Package;
+
+/** \ingroup rpmbuild
+ * The structure used to store values parsed from a spec file.
+ */
+struct rpmSpec_s {
+ char * specFile; /*!< Name of the spec file. */
+ char * buildRoot;
+ char * buildSubdir;
+ const char * rootDir;
+
+ struct OpenFileInfo * fileStack;
+ char lbuf[10*BUFSIZ];
+ char *lbufPtr;
+ char nextpeekc;
+ char * nextline;
+ char * line;
+ int lineNum;
+
+ struct ReadLevelEntry * readStack;
+
+ Header buildRestrictions;
+ rpmSpec * BASpecs;
+ const char ** BANames;
+ int BACount;
+ int recursing; /*!< parse is recursive? */
+
+ rpmSpecFlags flags;
+
+ struct Source * sources;
+ int numSources;
+ int noSource;
+
+ char * sourceRpmName;
+ unsigned char * sourcePkgId;
+ Header sourceHeader;
+ rpmfi sourceCpioList;
+
+ rpmMacroContext macros;
+
+ StringBuf prep; /*!< %prep scriptlet. */
+ StringBuf build; /*!< %build scriptlet. */
+ StringBuf install; /*!< %install scriptlet. */
+ StringBuf check; /*!< %check scriptlet. */
+ StringBuf clean; /*!< %clean scriptlet. */
+
+ StringBuf parsed; /*!< parsed spec contents */
+
+ Package packages; /*!< Package list. */
+};
+
+/** \ingroup rpmbuild
+ * The structure used to store values for a package.
+ */
+struct Package_s {
+ Header header;
+ rpmds ds; /*!< Requires: N = EVR */
+ rpmfi cpioList;
+
+ struct Source * icon;
+
+ int autoReq;
+ int autoProv;
+
+ char * preInFile; /*!< %pre scriptlet. */
+ char * postInFile; /*!< %post scriptlet. */
+ char * preUnFile; /*!< %preun scriptlet. */
+ char * postUnFile; /*!< %postun scriptlet. */
+ char * preTransFile; /*!< %pretrans scriptlet. */
+ char * postTransFile; /*!< %posttrans scriptlet. */
+ char * verifyFile; /*!< %verifyscript scriptlet. */
+
+ StringBuf specialDoc;
+ char *specialDocDir;
+
+ struct TriggerFileEntry * triggerFiles;
+
+ ARGV_t fileFile;
+ ARGV_t fileList; /* If NULL, package will not be written */
+ ARGV_t policyList;
+
+ Package next;
+};
+
+#define PART_SUBNAME 0
+#define PART_NAME 1
+
+/** \ingroup rpmbuild
+ * rpmSpec file parser states.
+ */
+#define PART_BASE 0
+typedef enum rpmParseState_e {
+ PART_ERROR = -1, /*!< */
+ PART_NONE = 0+PART_BASE, /*!< */
+ /* leave room for RPMRC_NOTFOUND returns. */
+ PART_PREAMBLE = 11+PART_BASE, /*!< */
+ PART_PREP = 12+PART_BASE, /*!< */
+ PART_BUILD = 13+PART_BASE, /*!< */
+ PART_INSTALL = 14+PART_BASE, /*!< */
+ PART_CHECK = 15+PART_BASE, /*!< */
+ PART_CLEAN = 16+PART_BASE, /*!< */
+ PART_FILES = 17+PART_BASE, /*!< */
+ PART_PRE = 18+PART_BASE, /*!< */
+ PART_POST = 19+PART_BASE, /*!< */
+ PART_PREUN = 20+PART_BASE, /*!< */
+ PART_POSTUN = 21+PART_BASE, /*!< */
+ PART_PRETRANS = 22+PART_BASE, /*!< */
+ PART_POSTTRANS = 23+PART_BASE, /*!< */
+ PART_DESCRIPTION = 24+PART_BASE, /*!< */
+ PART_CHANGELOG = 25+PART_BASE, /*!< */
+ PART_TRIGGERIN = 26+PART_BASE, /*!< */
+ PART_TRIGGERUN = 27+PART_BASE, /*!< */
+ PART_VERIFYSCRIPT = 28+PART_BASE, /*!< */
+ PART_BUILDARCHITECTURES= 29+PART_BASE,/*!< */
+ PART_TRIGGERPOSTUN = 30+PART_BASE, /*!< */
+ PART_TRIGGERPREIN = 31+PART_BASE, /*!< */
+ PART_POLICIES = 32+PART_BASE, /*!< */
+ PART_LAST = 33+PART_BASE /*!< */
+} rpmParseState;
+
+
+#define STRIP_NOTHING 0
+#define STRIP_TRAILINGSPACE (1 << 0)
+#define STRIP_COMMENTS (1 << 1)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** \ingroup rpmbuild
+ * Create and initialize rpmSpec structure.
+ * @return spec spec file control structure
+ */
+RPM_GNUC_INTERNAL
+rpmSpec newSpec(void);
+
+/** \ingroup rpmbuild
+ * Stop reading from spec file, freeing resources.
+ * @param spec spec file control structure
+ */
+RPM_GNUC_INTERNAL
+void closeSpec(rpmSpec spec);
+
+/** \ingroup rpmbuild
+ * Read next line from spec file.
+ * @param spec spec file control structure
+ * @param strip truncate comments?
+ * @return 0 on success, 1 on EOF, <0 on error
+ */
+RPM_GNUC_INTERNAL
+int readLine(rpmSpec spec, int strip);
+
+/** \ingroup rpmbuild
+ * Check line for section separator, return next parser state.
+ * @param line from spec file
+ * @return next parser state
+ */
+RPM_GNUC_INTERNAL
+int isPart(const char * line) ;
+
+/** \ingroup rpmbuild
+ * Parse %%build/%%install/%%clean section(s) of a spec file.
+ * @param spec spec file control structure
+ * @param parsePart current rpmParseState
+ * @return >= 0 next rpmParseState, < 0 on error
+ */
+RPM_GNUC_INTERNAL
+int parseBuildInstallClean(rpmSpec spec, int parsePart);
+
+/** \ingroup rpmbuild
+ * Parse %%changelog section of a spec file.
+ * @param spec spec file control structure
+ * @return >= 0 next rpmParseState, < 0 on error
+ */
+RPM_GNUC_INTERNAL
+int parseChangelog(rpmSpec spec);
+
+/** \ingroup rpmbuild
+ * Parse %%description section of a spec file.
+ * @param spec spec file control structure
+ * @return >= 0 next rpmParseState, < 0 on error
+ */
+RPM_GNUC_INTERNAL
+int parseDescription(rpmSpec spec);
+
+/** \ingroup rpmbuild
+ * Parse %%files section of a spec file.
+ * @param spec spec file control structure
+ * @return >= 0 next rpmParseState, < 0 on error
+ */
+RPM_GNUC_INTERNAL
+int parseFiles(rpmSpec spec);
+
+/** \ingroup rpmbuild
+ * Parse %%sepolicy section of a spec file.
+ * @param spec spec file control structure
+ * @return >= 0 next rpmParseState, < 0 on error
+ */
+RPM_GNUC_INTERNAL
+int parsePolicies(rpmSpec spec);
+
+/** \ingroup rpmbuild
+ * Parse tags from preamble of a spec file.
+ * @param spec spec file control structure
+ * @param initialPackage
+ * @return >= 0 next rpmParseState, < 0 on error
+ */
+RPM_GNUC_INTERNAL
+int parsePreamble(rpmSpec spec, int initialPackage);
+
+/** \ingroup rpmbuild
+ * Parse %%prep section of a spec file.
+ * @param spec spec file control structure
+ * @return >= 0 next rpmParseState, < 0 on error
+ */
+RPM_GNUC_INTERNAL
+int parsePrep(rpmSpec spec);
+
+/** \ingroup rpmbuild
+ * Parse %%pre et al scriptlets from a spec file.
+ * @param spec spec file control structure
+ * @param parsePart current rpmParseState
+ * @return >= 0 next rpmParseState, < 0 on error
+ */
+RPM_GNUC_INTERNAL
+int parseScript(rpmSpec spec, int parsePart);
+
+/** \ingroup rpmbuild
+ * Check for inappropriate characters. All alphanums are considered sane.
+ * @param spec spec
+ * @param field string to check
+ * @param fsize size of string to check
+ * @param whitelist string of permitted characters
+ * @return RPMRC_OK if OK
+ */
+RPM_GNUC_INTERNAL
+rpmRC rpmCharCheck(rpmSpec spec, const char *field, size_t fsize, const char *whitelist);
+
+/** \ingroup rpmbuild
+ * Parse dependency relations from spec file and/or autogenerated output buffer.
+ * @param spec spec file control structure
+ * @param pkg package control structure
+ * @param field text to parse (e.g. "foo < 0:1.2-3, bar = 5:6.7")
+ * @param tagN tag, identifies type of dependency
+ * @param index (0 always)
+ * @param tagflags dependency flags already known from context
+ * @return RPMRC_OK on success, RPMRC_FAIL on failure
+ */
+RPM_GNUC_INTERNAL
+rpmRC parseRCPOT(rpmSpec spec, Package pkg, const char * field, rpmTagVal tagN,
+ int index, rpmsenseFlags tagflags);
+
+/** \ingroup rpmbuild
+ * Evaluate boolean expression.
+ * @param spec spec file control structure
+ * @param expr expression to parse
+ * @return
+ */
+RPM_GNUC_INTERNAL
+int parseExpressionBoolean(rpmSpec spec, const char * expr);
+
+/** \ingroup rpmbuild
+ * Run a build script, assembled from spec file scriptlet section.
+ *
+ * @param spec spec file control structure
+ * @param what type of script
+ * @param name name of scriptlet section
+ * @param sb lines that compose script body
+ * @param test don't execute scripts or package if testing
+ * @return RPMRC_OK on success
+ */
+RPM_GNUC_INTERNAL
+rpmRC doScript(rpmSpec spec, rpmBuildFlags what, const char * name,
+ const char * sb, int test);
+
+/** \ingroup rpmbuild
+ * Find sub-package control structure by name.
+ * @param spec spec file control structure
+ * @param name (sub-)package name
+ * @param flag if PART_SUBNAME, then 1st package name is prepended
+ * @retval pkg package control structure
+ * @return 0 on success, 1 on failure
+ */
+RPM_GNUC_INTERNAL
+rpmRC lookupPackage(rpmSpec spec, const char * name, int flag,
+ Package * pkg);
+
+/** \ingroup rpmbuild
+ * Create and initialize package control structure.
+ * @param spec spec file control structure
+ * @return package control structure
+ */
+RPM_GNUC_INTERNAL
+Package newPackage(rpmSpec spec);
+
+/** \ingroup rpmbuild
+ * Post-build processing for binary package(s).
+ * @param spec spec file control structure
+ * @param pkgFlags bit(s) to control package generation
+ * @param installSpecialDoc
+ * @param test don't execute scripts or package if testing
+ * @return 0 on success
+ */
+RPM_GNUC_INTERNAL
+rpmRC processBinaryFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags,
+ int installSpecialDoc, int test);
+
+/** \ingroup rpmfc
+ * Generate package dependencies.
+ * @param spec spec file control
+ * @param pkg package control
+ * @return RPMRC_OK on success
+ */
+RPM_GNUC_INTERNAL
+rpmRC rpmfcGenerateDepends(const rpmSpec spec, Package pkg);
+
+/** \ingroup rpmfc
+ * Return helper output.
+ * @param av helper argv (with possible macros)
+ * @param sb_stdin helper input
+ * @retval *sb_stdoutp helper output
+ * @param failnonzero IS non-zero helper exit status a failure?
+ * @param buildRoot buildRoot directory (or NULL)
+ */
+RPM_GNUC_INTERNAL
+int rpmfcExec(ARGV_const_t av, StringBuf sb_stdin, StringBuf * sb_stdoutp,
+ int failnonzero, const char *buildRoot);
+
+/** \ingroup rpmbuild
+ * Post-build processing for policies in binary package(s).
+ * @param spec spec file control structure
+ * @param test don't execute scripts or package if testing
+ * @return 0 on success
+ */
+RPM_GNUC_INTERNAL
+rpmRC processBinaryPolicies(rpmSpec spec, int test);
+
+/** \ingroup rpmbuild
+ * Post-build processing for source package.
+ * @param spec spec file control structure
+ * @param pkgFlags bit(s) to control package generation
+ * @return 0 on success
+ */
+RPM_GNUC_INTERNAL
+rpmRC processSourceFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags);
+
+/** \ingroup rpmbuild
+ * Generate binary package(s).
+ * @param spec spec file control structure
+ * @param cookie build identifier "cookie" or NULL
+ * @param cheating was build shortcircuited?
+ * @return RPMRC_OK on success
+ */
+RPM_GNUC_INTERNAL
+rpmRC packageBinaries(rpmSpec spec, const char *cookie, int cheating);
+
+/** \ingroup rpmbuild
+ * Generate source package.
+ * @param spec spec file control structure
+ * @retval cookie build identifier "cookie" or NULL
+ * @return RPMRC_OK on success
+ */
+RPM_GNUC_INTERNAL
+rpmRC packageSources(rpmSpec spec, char **cookie);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RPMBUILD_INTERNAL_H */
diff --git a/build/rpmbuild_misc.h b/build/rpmbuild_misc.h
new file mode 100644
index 0000000..9665c97
--- /dev/null
+++ b/build/rpmbuild_misc.h
@@ -0,0 +1,94 @@
+#ifndef _RPMBUILD_MISC_H
+#define _RPMBUILD_MISC_H
+
+#include <sys/types.h>
+#include <rpm/rpmtypes.h>
+#include <rpm/rpmds.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** \ingroup rpmbuild
+ * Truncate comment lines.
+ * @param s skip white space, truncate line at '#'
+ */
+RPM_GNUC_INTERNAL
+void handleComments(char * s);
+
+/** \ingroup rpmstring
+ */
+typedef struct StringBufRec *StringBuf;
+
+/** \ingroup rpmstring
+ */
+RPM_GNUC_INTERNAL
+StringBuf newStringBuf(void);
+
+/** \ingroup rpmstring
+ */
+RPM_GNUC_INTERNAL
+StringBuf freeStringBuf( StringBuf sb);
+
+/** \ingroup rpmstring
+ */
+RPM_GNUC_INTERNAL
+const char * getStringBuf(StringBuf sb);
+
+/** \ingroup rpmstring
+ */
+RPM_GNUC_INTERNAL
+void stripTrailingBlanksStringBuf(StringBuf sb);
+
+/** \ingroup rpmstring
+ */
+#define appendStringBuf(sb, s) appendStringBufAux(sb, s, 0)
+
+/** \ingroup rpmstring
+ */
+#define appendLineStringBuf(sb, s) appendStringBufAux(sb, s, 1)
+
+/** \ingroup rpmstring
+ */
+RPM_GNUC_INTERNAL
+void appendStringBufAux(StringBuf sb, const char * s, int nl);
+
+/** \ingroup rpmbuild
+ * Parse an unsigned number.
+ * @param line from spec file
+ * @retval res pointer to uint32_t
+ * @return 0 on success, 1 on failure
+ */
+RPM_GNUC_INTERNAL
+uint32_t parseUnsignedNum(const char * line, uint32_t * res);
+
+/** \ingroup rpmbuild
+ * Add dependency to header, filtering duplicates.
+ * @param h header
+ * @param tagN tag, identifies type of dependency
+ * @param N (e.g. Requires: foo < 0:1.2-3, "foo")
+ * @param EVR (e.g. Requires: foo < 0:1.2-3, "0:1.2-3")
+ * @param Flags (e.g. Requires: foo < 0:1.2-3, both "Requires:" and "<")
+ * @param index (0 always)
+ * @return 0 on success, 1 on error
+ */
+RPM_GNUC_INTERNAL
+int addReqProv(Header h, rpmTagVal tagN,
+ const char * N, const char * EVR, rpmsenseFlags Flags,
+ uint32_t index);
+
+/** \ingroup rpmbuild
+ * Add rpmlib feature dependency.
+ * @param h header
+ * @param feature rpm feature name (i.e. "rpmlib(Foo)" for feature Foo)
+ * @param featureEVR rpm feature epoch/version/release
+ * @return 0 always
+ */
+RPM_GNUC_INTERNAL
+int rpmlibNeedsFeature(Header h, const char * feature, const char * featureEVR);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RPMBUILD_MISC_H */
diff --git a/build/rpmfc.c b/build/rpmfc.c
new file mode 100644
index 0000000..a779b6a
--- /dev/null
+++ b/build/rpmfc.c
@@ -0,0 +1,1405 @@
+#include "system.h"
+
+#include <errno.h>
+#include <sys/select.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <magic.h>
+#include <regex.h>
+
+#include <rpm/header.h>
+#include <rpm/argv.h>
+#include <rpm/rpmfc.h>
+#include <rpm/rpmlog.h>
+#include <rpm/rpmfileutil.h>
+#include <rpm/rpmds.h>
+#include <rpm/rpmfi.h>
+
+#include "build/rpmbuild_internal.h"
+
+#include "debug.h"
+
+struct matchRule {
+ regex_t *path;
+ regex_t *magic;
+ ARGV_t flags;
+};
+
+typedef struct rpmfcAttr_s {
+ char *name;
+ struct matchRule incl;
+ struct matchRule excl;
+} * rpmfcAttr;
+
+/**
+ */
+struct rpmfc_s {
+ int nfiles; /*!< no. of files */
+ int fknown; /*!< no. of classified files */
+ int fwhite; /*!< no. of "white" files */
+ int ix; /*!< current file index */
+ int skipProv; /*!< Don't auto-generate Provides:? */
+ int skipReq; /*!< Don't auto-generate Requires:? */
+ char *buildRoot; /*!< (Build) root dir */
+ size_t brlen; /*!< rootDir length */
+
+ rpmfcAttr *atypes; /*!< known file attribute types */
+
+ ARGV_t fn; /*!< (no. files) file names */
+ ARGV_t *fattrs; /*!< (no. files) file attribute tokens */
+ ARGI_t fcolor; /*!< (no. files) file colors */
+ ARGI_t fcdictx; /*!< (no. files) file class dictionary indices */
+ ARGI_t fddictx; /*!< (no. files) file depends dictionary start */
+ ARGI_t fddictn; /*!< (no. files) file depends dictionary no. entries */
+ ARGV_t cdict; /*!< (no. classes) file class dictionary */
+ ARGV_t ddict; /*!< (no. dependencies) file depends dictionary */
+ ARGI_t ddictx; /*!< (no. dependencies) file->dependency mapping */
+
+ rpmds provides; /*!< (no. provides) package provides */
+ rpmds requires; /*!< (no. requires) package requires */
+};
+
+struct rpmfcTokens_s {
+ const char * token;
+ rpm_color_t colors;
+};
+
+static int regMatch(regex_t *reg, const char *val)
+{
+ return (reg && regexec(reg, val, 0, NULL, 0) == 0);
+}
+
+static regex_t * regFree(regex_t *reg)
+{
+ if (reg) {
+ regfree(reg);
+ free(reg);
+ }
+ return NULL;
+}
+
+static void ruleFree(struct matchRule *rule)
+{
+ regFree(rule->path);
+ regFree(rule->magic);
+ argvFree(rule->flags);
+}
+
+static char *rpmfcAttrMacro(const char *name,
+ const char *attr_prefix, const char *attr)
+{
+ char *ret;
+ if (attr_prefix && attr_prefix[0] != '\0')
+ ret = rpmExpand("%{?__", name, "_", attr_prefix, "_", attr, "}", NULL);
+ else
+ ret = rpmExpand("%{?__", name, "_", attr, "}", NULL);
+ return rstreq(ret, "") ? _free(ret) : ret;
+}
+
+static regex_t *rpmfcAttrReg(const char *name,
+ const char *attr_prefix, const char *attr)
+{
+ regex_t *reg = NULL;
+ char *pattern = rpmfcAttrMacro(name, attr_prefix, attr);
+ if (pattern) {
+ reg = xcalloc(1, sizeof(*reg));
+ if (regcomp(reg, pattern, REG_EXTENDED) != 0) {
+ rpmlog(RPMLOG_WARNING, _("Ignoring invalid regex %s\n"), pattern);
+ reg = _free(reg);
+ }
+ rfree(pattern);
+ }
+ return reg;
+}
+
+static rpmfcAttr rpmfcAttrNew(const char *name)
+{
+ rpmfcAttr attr = xcalloc(1, sizeof(*attr));
+ struct matchRule *rules[] = { &attr->incl, &attr->excl, NULL };
+
+ attr->name = xstrdup(name);
+ for (struct matchRule **rule = rules; rule && *rule; rule++) {
+ const char *prefix = (*rule == &attr->incl) ? NULL : "exclude";
+ char *flags = rpmfcAttrMacro(name, prefix, "flags");
+
+ (*rule)->path = rpmfcAttrReg(name, prefix, "path");
+ (*rule)->magic = rpmfcAttrReg(name, prefix, "magic");
+ (*rule)->flags = argvSplitString(flags, ",", ARGV_SKIPEMPTY);
+ argvSort((*rule)->flags, NULL);
+
+ free(flags);
+ }
+
+ return attr;
+}
+
+static rpmfcAttr rpmfcAttrFree(rpmfcAttr attr)
+{
+ if (attr) {
+ ruleFree(&attr->incl);
+ ruleFree(&attr->excl);
+ rfree(attr->name);
+ rfree(attr);
+ }
+ return NULL;
+}
+
+/**
+ */
+static int rpmfcExpandAppend(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] = rpmExpand(av[i], NULL);
+ argv[argc + ac] = NULL;
+ *argvp = argv;
+ return 0;
+}
+
+static int _sigpipe[2] = { -1, -1 };
+
+static void sigpipe_handler(int sig)
+{
+ char sigc = sig;
+ int xx; /* shut up gcc, we can't handle an error here */
+ xx = write(_sigpipe[1], &sigc, 1);
+}
+
+static int sigpipe_init(void)
+{
+ if (pipe(_sigpipe) < 0)
+ return -1;
+ /* Set close on exec and non-blocking (must not get stuck in sighandler) */
+ fcntl(_sigpipe[0], F_SETFL, (fcntl(_sigpipe[0], F_GETFL)|O_NONBLOCK));
+ fcntl(_sigpipe[0], F_SETFD, (fcntl(_sigpipe[0], F_GETFD)|FD_CLOEXEC));
+ fcntl(_sigpipe[1], F_SETFL, (fcntl(_sigpipe[1], F_GETFL)|O_NONBLOCK));
+ fcntl(_sigpipe[1], F_SETFD, (fcntl(_sigpipe[1], F_GETFD)|FD_CLOEXEC));
+ /* XXX SIGPIPE too, but NSPR disables it already, dont mess with it */
+ signal(SIGCHLD, sigpipe_handler);
+ return _sigpipe[0];
+}
+
+static void sigpipe_finish(void)
+{
+ signal(SIGCHLD, SIG_DFL);
+ close(_sigpipe[0]);
+ close(_sigpipe[1]);
+ _sigpipe[0] = -1;
+ _sigpipe[1] = -1;
+}
+
+#define max(x,y) ((x) > (y) ? (x) : (y))
+
+/** \ingroup rpmbuild
+ * Return output from helper script.
+ * @todo Use poll(2) rather than select(2), if available.
+ * @param argv program and arguments to run
+ * @param writePtr bytes to feed to script on stdin (or NULL)
+ * @param writeBytesLeft no. of bytes to feed to script on stdin
+ * @param failNonZero is script failure an error?
+ * @param buildRoot buildRoot directory (or NULL)
+ * @return buffered stdout from script, NULL on error
+ */
+static StringBuf getOutputFrom(ARGV_t argv,
+ const char * writePtr, size_t writeBytesLeft,
+ int failNonZero, const char *buildRoot)
+{
+ pid_t child, reaped;
+ int toProg[2] = { -1, -1 };
+ int fromProg[2] = { -1, -1 };
+ int status;
+ StringBuf readBuff;
+ int myerrno = 0;
+ int sigpipe = sigpipe_init();
+ int ret = 1; /* assume failure */
+
+ if (sigpipe < 0 || pipe(toProg) < 0 || pipe(fromProg) < 0) {
+ rpmlog(RPMLOG_ERR, _("Couldn't create pipe for %s: %m\n"), argv[0]);
+ return NULL;
+ }
+
+ child = fork();
+ if (child == 0) {
+ /* NSPR messes with SIGPIPE, reset to default for the kids */
+ signal(SIGPIPE, SIG_DFL);
+ close(toProg[1]);
+ close(fromProg[0]);
+
+ dup2(toProg[0], STDIN_FILENO); /* Make stdin the in pipe */
+ close(toProg[0]);
+
+ dup2(fromProg[1], STDOUT_FILENO); /* Make stdout the out pipe */
+ close(fromProg[1]);
+
+ rpmlog(RPMLOG_DEBUG, "\texecv(%s) pid %d\n",
+ argv[0], (unsigned)getpid());
+
+ if (buildRoot)
+ setenv("RPM_BUILD_ROOT", buildRoot, 1);
+
+ unsetenv("MALLOC_CHECK_");
+ execvp(argv[0], (char *const *)argv);
+ rpmlog(RPMLOG_ERR, _("Couldn't exec %s: %s\n"),
+ argv[0], strerror(errno));
+ _exit(EXIT_FAILURE);
+ }
+ if (child < 0) {
+ rpmlog(RPMLOG_ERR, _("Couldn't fork %s: %s\n"),
+ argv[0], strerror(errno));
+ return NULL;
+ }
+
+ close(toProg[0]);
+ close(fromProg[1]);
+
+ readBuff = newStringBuf();
+
+ while (1) {
+ fd_set ibits, obits;
+ int nfd = 0;
+ ssize_t iorc;
+ char buf[BUFSIZ+1];
+
+ FD_ZERO(&ibits);
+ FD_ZERO(&obits);
+
+ FD_SET(sigpipe, &ibits);
+ nfd = max(nfd, sigpipe);
+ FD_SET(fromProg[0], &ibits);
+ nfd = max(nfd, fromProg[0]);
+
+ if (writeBytesLeft > 0) {
+ FD_SET(toProg[1], &obits);
+ nfd = max(nfd, toProg[1]);
+ } else if (toProg[1] >= 0) {
+ /* Close write-side pipe to notify child on EOF */
+ close(toProg[1]);
+ toProg[1] = -1;
+ }
+
+ do {
+ iorc = select(nfd + 1, &ibits, &obits, NULL, NULL);
+ } while (iorc == -1 && errno == EINTR);
+
+ if (iorc < 0) {
+ myerrno = errno;
+ break;
+ }
+
+ /* Write data to child */
+ if (writeBytesLeft > 0 && FD_ISSET(toProg[1], &obits)) {
+ size_t nb = (1024 < writeBytesLeft) ? 1024 : writeBytesLeft;
+ do {
+ iorc = write(toProg[1], writePtr, nb);
+ } while (iorc == -1 && errno == EINTR);
+
+ if (iorc < 0) {
+ myerrno = errno;
+ break;
+ }
+ writeBytesLeft -= iorc;
+ writePtr += iorc;
+ }
+
+ /* Read when we get data back from the child */
+ if (FD_ISSET(fromProg[0], &ibits)) {
+ do {
+ iorc = read(fromProg[0], buf, sizeof(buf)-1);
+ } while (iorc == -1 && errno == EINTR);
+
+ if (iorc == 0) break; /* EOF, we're done */
+ if (iorc < 0) {
+ myerrno = errno;
+ break;
+ }
+ buf[iorc] = '\0';
+ appendStringBuf(readBuff, buf);
+ }
+
+ /* Child exited */
+ if (FD_ISSET(sigpipe, &ibits)) {
+ while (read(sigpipe, buf, sizeof(buf)) > 0) {};
+ }
+ }
+
+ /* Clean up */
+ if (toProg[1] >= 0)
+ close(toProg[1]);
+ if (fromProg[0] >= 0)
+ close(fromProg[0]);
+
+ /* Collect status from prog */
+ reaped = waitpid(child, &status, 0);
+ rpmlog(RPMLOG_DEBUG, "\twaitpid(%d) rc %d status %x\n",
+ (unsigned)child, (unsigned)reaped, status);
+
+ sigpipe_finish();
+ if (failNonZero && (!WIFEXITED(status) || WEXITSTATUS(status))) {
+ rpmlog(RPMLOG_ERR, _("%s failed: %x\n"), argv[0], status);
+ goto exit;
+ }
+ if (writeBytesLeft || myerrno) {
+ rpmlog(RPMLOG_ERR, _("failed to write all data to %s: %s\n"),
+ argv[0], strerror(myerrno));
+ goto exit;
+ }
+ ret = 0;
+
+exit:
+ if (ret) {
+ readBuff = freeStringBuf(readBuff);
+ }
+
+ return readBuff;
+}
+
+int rpmfcExec(ARGV_const_t av, StringBuf sb_stdin, StringBuf * sb_stdoutp,
+ int failnonzero, const char *buildRoot)
+{
+ char * s = NULL;
+ ARGV_t xav = NULL;
+ ARGV_t pav = NULL;
+ int pac = 0;
+ int ec = -1;
+ StringBuf sb = NULL;
+ const char * buf_stdin = NULL;
+ size_t buf_stdin_len = 0;
+ int xx;
+
+ if (sb_stdoutp)
+ *sb_stdoutp = NULL;
+ if (!(av && *av))
+ goto exit;
+
+ /* Find path to executable with (possible) args. */
+ s = rpmExpand(av[0], NULL);
+ if (!(s && *s))
+ goto exit;
+
+ /* Parse args buried within expanded exacutable. */
+ pac = 0;
+ xx = poptParseArgvString(s, &pac, (const char ***)&pav);
+ if (!(xx == 0 && pac > 0 && pav != NULL))
+ goto exit;
+
+ /* Build argv, appending args to the executable args. */
+ xav = NULL;
+ xx = argvAppend(&xav, pav);
+ if (av[1])
+ xx = rpmfcExpandAppend(&xav, av + 1);
+
+ if (sb_stdin != NULL) {
+ buf_stdin = getStringBuf(sb_stdin);
+ buf_stdin_len = strlen(buf_stdin);
+ }
+
+ /* Read output from exec'd helper. */
+ sb = getOutputFrom(xav, buf_stdin, buf_stdin_len, failnonzero, buildRoot);
+
+ if (sb_stdoutp != NULL) {
+ *sb_stdoutp = sb;
+ sb = NULL; /* XXX don't free */
+ }
+
+ ec = 0;
+
+exit:
+ sb = freeStringBuf(sb);
+ xav = argvFree(xav);
+ pav = _free(pav); /* XXX popt mallocs in single blob. */
+ s = _free(s);
+ return ec;
+}
+
+static void argvAddUniq(ARGV_t * argvp, const char * key)
+{
+ if (argvSearch(*argvp, key, NULL) == NULL) {
+ argvAdd(argvp, key);
+ argvSort(*argvp, NULL);
+ }
+}
+
+#define hasAttr(_a, _n) (argvSearch((_a), (_n), NULL) != NULL)
+
+static void rpmfcAddFileDep(ARGV_t * argvp, int ix, rpmds ds, char deptype)
+{
+ if (ds) {
+ char *key = NULL;
+ rasprintf(&key, "%08d%c %s %s 0x%08x", ix, deptype,
+ rpmdsN(ds), rpmdsEVR(ds), rpmdsFlags(ds));
+ argvAddUniq(argvp, key);
+ free(key);
+ }
+}
+
+static ARGV_t runCmd(const char *nsdep, const char *depname,
+ const char *buildRoot, const char *fn)
+{
+ ARGV_t output = NULL;
+ char *buf = NULL;
+ char *mname = rstrscat(NULL, "__", nsdep, "_", depname, NULL);
+
+ rasprintf(&buf, "%%{?%s:%%{%s} %%{?%s_opts}}", mname, mname, mname);
+ if (!rstreq(buf, "")) {
+ ARGV_t av = NULL;
+ StringBuf sb_stdout = NULL;
+ StringBuf sb_stdin = newStringBuf();
+ argvAdd(&av, buf);
+
+ appendLineStringBuf(sb_stdin, fn);
+ if (rpmfcExec(av, sb_stdin, &sb_stdout, 0, buildRoot) == 0) {
+ argvSplit(&output, getStringBuf(sb_stdout), " \t\n\r");
+ }
+
+ argvFree(av);
+ freeStringBuf(sb_stdin);
+ freeStringBuf(sb_stdout);
+ }
+ free(buf);
+ free(mname);
+ return output;
+}
+
+/**
+ * Run per-interpreter dependency helper.
+ * @param fc file classifier
+ * @param deptype 'P' == Provides:, 'R' == Requires:, helper
+ * @param nsdep class name for interpreter (e.g. "perl")
+ * @return 0 on success
+ */
+static int rpmfcHelper(rpmfc fc, unsigned char deptype, const char * nsdep)
+{
+ ARGV_t pav = NULL;
+ const char * fn = fc->fn[fc->ix];
+ const char * depname = NULL;
+ rpmds * depsp;
+ rpmsenseFlags dsContext;
+ rpmTagVal tagN;
+ int pac;
+ regex_t *exclude = NULL;
+ regex_t *exclude_from = NULL;
+
+ switch (deptype) {
+ default:
+ return -1;
+ break;
+ case 'P':
+ if (fc->skipProv)
+ return 0;
+ depname = "provides";
+ depsp = &fc->provides;
+ dsContext = RPMSENSE_FIND_PROVIDES;
+ tagN = RPMTAG_PROVIDENAME;
+ break;
+ case 'R':
+ if (fc->skipReq)
+ return 0;
+ depname = "requires";
+ depsp = &fc->requires;
+ dsContext = RPMSENSE_FIND_REQUIRES;
+ tagN = RPMTAG_REQUIRENAME;
+ break;
+ }
+
+ /* If the entire path is filtered out, there's nothing more to do */
+ exclude_from = rpmfcAttrReg(depname, "exclude", "from");
+ if (regMatch(exclude_from, fn+fc->brlen))
+ goto exit;
+
+ pav = runCmd(nsdep, depname, fc->buildRoot, fn);
+ pac = argvCount(pav);
+
+ if (pav)
+ exclude = rpmfcAttrReg(depname, NULL, "exclude");
+
+ for (int i = 0; i < pac; i++) {
+ rpmds ds = NULL;
+ const char *N = pav[i];
+ const char *EVR = "";
+ rpmsenseFlags Flags = dsContext;
+ if (pav[i+1] && strchr("=<>", *pav[i+1])) {
+ i++;
+ for (const char *s = pav[i]; *s; s++) {
+ switch(*s) {
+ default:
+ break;
+ case '=':
+ Flags |= RPMSENSE_EQUAL;
+ break;
+ case '<':
+ Flags |= RPMSENSE_LESS;
+ break;
+ case '>':
+ Flags |= RPMSENSE_GREATER;
+ break;
+ }
+ }
+ i++;
+ EVR = pav[i];
+ }
+
+ ds = rpmdsSingle(tagN, N, EVR, Flags);
+
+ /* Add to package and file dependencies unless filtered */
+ if (regMatch(exclude, rpmdsDNEVR(ds)+2) == 0) {
+ (void) rpmdsMerge(depsp, ds);
+ rpmfcAddFileDep(&fc->ddict, fc->ix, ds, deptype);
+ }
+
+ ds = rpmdsFree(ds);
+ }
+
+ argvFree(pav);
+
+exit:
+ regFree(exclude);
+ regFree(exclude_from);
+ return 0;
+}
+
+/* Only used for elf coloring and controlling RPMTAG_FILECLASS inclusion now */
+static const struct rpmfcTokens_s rpmfcTokens[] = {
+ { "directory", RPMFC_INCLUDE },
+
+ { "ELF 32-bit", RPMFC_ELF32|RPMFC_INCLUDE },
+ { "ELF 64-bit", RPMFC_ELF64|RPMFC_INCLUDE },
+
+ { "troff or preprocessor input", RPMFC_INCLUDE },
+ { "GNU Info", RPMFC_INCLUDE },
+
+ { "perl ", RPMFC_INCLUDE },
+ { "Perl5 module source text", RPMFC_INCLUDE },
+ { "python ", RPMFC_INCLUDE },
+
+ { "libtool library ", RPMFC_INCLUDE },
+ { "pkgconfig ", RPMFC_INCLUDE },
+
+ { "Objective caml ", RPMFC_INCLUDE },
+ { "Mono/.Net assembly", RPMFC_INCLUDE },
+
+ { "current ar archive", RPMFC_INCLUDE },
+ { "Zip archive data", RPMFC_INCLUDE },
+ { "tar archive", RPMFC_INCLUDE },
+ { "cpio archive", RPMFC_INCLUDE },
+ { "RPM v3", RPMFC_INCLUDE },
+ { "RPM v4", RPMFC_INCLUDE },
+
+ { " image", RPMFC_INCLUDE },
+ { " font", RPMFC_INCLUDE },
+ { " Font", RPMFC_INCLUDE },
+
+ { " commands", RPMFC_INCLUDE },
+ { " script", RPMFC_INCLUDE },
+
+ { "empty", RPMFC_INCLUDE },
+
+ { "HTML", RPMFC_INCLUDE },
+ { "SGML", RPMFC_INCLUDE },
+ { "XML", RPMFC_INCLUDE },
+
+ { " source", RPMFC_INCLUDE },
+ { "GLS_BINARY_LSB_FIRST", RPMFC_INCLUDE },
+ { " DB ", RPMFC_INCLUDE },
+
+ { " text", RPMFC_INCLUDE },
+
+ { NULL, RPMFC_BLACK }
+};
+
+static void argvAddTokens(ARGV_t *argv, const char *tnames)
+{
+ if (tnames) {
+ ARGV_t tokens = NULL;
+ argvSplit(&tokens, tnames, ",");
+ for (ARGV_t token = tokens; token && *token; token++)
+ argvAddUniq(argv, *token);
+ argvFree(tokens);
+ }
+}
+
+static int matches(const struct matchRule *rule,
+ const char *ftype, const char *path, int executable)
+{
+ if (!executable && hasAttr(rule->flags, "exeonly"))
+ return 0;
+ if (rule->magic && rule->path && hasAttr(rule->flags, "magic_and_path")) {
+ return (regMatch(rule->magic, ftype) && regMatch(rule->path, path));
+ } else {
+ return (regMatch(rule->magic, ftype) || regMatch(rule->path, path));
+ }
+}
+
+static void rpmfcAttributes(rpmfc fc, const char *ftype, const char *fullpath)
+{
+ const char *path = fullpath + fc->brlen;
+ int is_executable = 0;
+ struct stat st;
+ if (stat(fullpath, &st) == 0) {
+ is_executable = (S_ISREG(st.st_mode)) &&
+ (st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH));
+ }
+
+ for (rpmfcAttr *attr = fc->atypes; attr && *attr; attr++) {
+ /* Filter out excludes */
+ if (matches(&(*attr)->excl, ftype, path, is_executable))
+ continue;
+
+ /* Add attributes on libmagic type & path pattern matches */
+ if (matches(&(*attr)->incl, ftype, path, is_executable))
+ argvAddTokens(&fc->fattrs[fc->ix], (*attr)->name);
+ }
+}
+
+/* Return color for a given libmagic classification string */
+static rpm_color_t rpmfcColor(const char * fmstr)
+{
+ rpmfcToken fct;
+ rpm_color_t fcolor = RPMFC_BLACK;
+
+ for (fct = rpmfcTokens; fct->token != NULL; fct++) {
+ if (strstr(fmstr, fct->token) == NULL)
+ continue;
+
+ fcolor |= fct->colors;
+ if (fcolor & RPMFC_INCLUDE)
+ break;
+ }
+
+ return fcolor;
+}
+
+void rpmfcPrint(const char * msg, rpmfc fc, FILE * fp)
+{
+ rpm_color_t fcolor;
+ int ndx;
+ int cx;
+ int dx;
+ int fx;
+
+int nprovides;
+int nrequires;
+
+ if (fp == NULL) fp = stderr;
+
+ if (msg)
+ fprintf(fp, "===================================== %s\n", msg);
+
+nprovides = rpmdsCount(fc->provides);
+nrequires = rpmdsCount(fc->requires);
+
+ if (fc)
+ for (fx = 0; fx < fc->nfiles; fx++) {
+assert(fx < fc->fcdictx->nvals);
+ cx = fc->fcdictx->vals[fx];
+assert(fx < fc->fcolor->nvals);
+ fcolor = fc->fcolor->vals[fx];
+ ARGV_t fattrs = fc->fattrs[fx];
+
+ fprintf(fp, "%3d %s", fx, fc->fn[fx]);
+ if (fcolor != RPMFC_BLACK)
+ fprintf(fp, "\t0x%x", fc->fcolor->vals[fx]);
+ else
+ fprintf(fp, "\t%s", fc->cdict[cx]);
+ if (fattrs) {
+ char *attrs = argvJoin(fattrs, ",");
+ fprintf(fp, " [%s]", attrs);
+ free(attrs);
+ } else {
+ fprintf(fp, " [none]");
+ }
+ fprintf(fp, "\n");
+
+ if (fc->fddictx == NULL || fc->fddictn == NULL)
+ continue;
+
+assert(fx < fc->fddictx->nvals);
+ dx = fc->fddictx->vals[fx];
+assert(fx < fc->fddictn->nvals);
+ ndx = fc->fddictn->vals[fx];
+
+ while (ndx-- > 0) {
+ const char * depval;
+ unsigned char deptype;
+ unsigned ix;
+
+ ix = fc->ddictx->vals[dx++];
+ deptype = ((ix >> 24) & 0xff);
+ ix &= 0x00ffffff;
+ depval = NULL;
+ switch (deptype) {
+ default:
+assert(depval != NULL);
+ break;
+ case 'P':
+ if (nprovides > 0) {
+assert(ix < nprovides);
+ (void) rpmdsSetIx(fc->provides, ix-1);
+ if (rpmdsNext(fc->provides) >= 0)
+ depval = rpmdsDNEVR(fc->provides);
+ }
+ break;
+ case 'R':
+ if (nrequires > 0) {
+assert(ix < nrequires);
+ (void) rpmdsSetIx(fc->requires, ix-1);
+ if (rpmdsNext(fc->requires) >= 0)
+ depval = rpmdsDNEVR(fc->requires);
+ }
+ break;
+ }
+ if (depval)
+ fprintf(fp, "\t%s\n", depval);
+ }
+ }
+}
+
+rpmfc rpmfcFree(rpmfc fc)
+{
+ if (fc) {
+ for (rpmfcAttr *attr = fc->atypes; attr && *attr; attr++)
+ rpmfcAttrFree(*attr);
+ rfree(fc->atypes);
+ rfree(fc->buildRoot);
+ fc->fn = argvFree(fc->fn);
+ for (int i = 0; i < fc->nfiles; i++)
+ argvFree(fc->fattrs[i]);
+ fc->fattrs = _free(fc->fattrs);
+ fc->fcolor = argiFree(fc->fcolor);
+ fc->fcdictx = argiFree(fc->fcdictx);
+ fc->fddictx = argiFree(fc->fddictx);
+ fc->fddictn = argiFree(fc->fddictn);
+ fc->cdict = argvFree(fc->cdict);
+ fc->ddict = argvFree(fc->ddict);
+ fc->ddictx = argiFree(fc->ddictx);
+
+ fc->provides = rpmdsFree(fc->provides);
+ fc->requires = rpmdsFree(fc->requires);
+ }
+ fc = _free(fc);
+ return NULL;
+}
+
+rpmfc rpmfcCreate(const char *buildRoot, rpmFlags flags)
+{
+ rpmfc fc = xcalloc(1, sizeof(*fc));
+ if (buildRoot) {
+ fc->buildRoot = xstrdup(buildRoot);
+ fc->brlen = strlen(buildRoot);
+ }
+ return fc;
+}
+
+rpmfc rpmfcNew(void)
+{
+ return rpmfcCreate(NULL, 0);
+}
+
+rpmds rpmfcProvides(rpmfc fc)
+{
+ return (fc != NULL ? fc->provides : NULL);
+}
+
+rpmds rpmfcRequires(rpmfc fc)
+{
+ return (fc != NULL ? fc->requires : NULL);
+}
+
+rpmRC rpmfcApply(rpmfc fc)
+{
+ const char * s;
+ char * se;
+ rpmds ds;
+ const char * N;
+ const char * EVR;
+ rpmsenseFlags Flags;
+ unsigned char deptype;
+ int nddict;
+ int previx;
+ unsigned int val;
+ int dix;
+ int ix;
+ int i;
+ int xx = 0;
+
+ /* Generate package and per-file dependencies. */
+ for (fc->ix = 0; fc->fn != NULL && fc->fn[fc->ix] != NULL; fc->ix++) {
+ for (ARGV_t fattr = fc->fattrs[fc->ix]; fattr && *fattr; fattr++) {
+ xx += rpmfcHelper(fc, 'P', *fattr);
+ xx += rpmfcHelper(fc, 'R', *fattr);
+ }
+ }
+
+ /* Generate per-file indices into package dependencies. */
+ nddict = argvCount(fc->ddict);
+ previx = -1;
+ for (i = 0; i < nddict; i++) {
+ s = fc->ddict[i];
+
+ /* Parse out (file#,deptype,N,EVR,Flags) */
+ ix = strtol(s, &se, 10);
+ if ( se == NULL ) {
+ rpmlog(RPMLOG_ERR, _("Conversion of %s to long integer failed.\n"), s);
+ return RPMRC_FAIL;
+ }
+
+ deptype = *se++;
+ se++;
+ N = se;
+ while (*se && *se != ' ')
+ se++;
+ *se++ = '\0';
+ EVR = se;
+ while (*se && *se != ' ')
+ se++;
+ *se++ = '\0';
+ Flags = strtol(se, NULL, 16);
+
+ dix = -1;
+ switch (deptype) {
+ default:
+ break;
+ case 'P':
+ ds = rpmdsSingle(RPMTAG_PROVIDENAME, N, EVR, Flags);
+ dix = rpmdsFind(fc->provides, ds);
+ ds = rpmdsFree(ds);
+ break;
+ case 'R':
+ ds = rpmdsSingle(RPMTAG_REQUIRENAME, N, EVR, Flags);
+ dix = rpmdsFind(fc->requires, ds);
+ ds = rpmdsFree(ds);
+ break;
+ }
+
+/* XXX assertion incorrect while generating -debuginfo deps. */
+#if 0
+assert(dix >= 0);
+#else
+ if (dix < 0)
+ continue;
+#endif
+
+ val = (deptype << 24) | (dix & 0x00ffffff);
+ xx = argiAdd(&fc->ddictx, -1, val);
+
+ if (previx != ix) {
+ previx = ix;
+ xx = argiAdd(&fc->fddictx, ix, argiCount(fc->ddictx)-1);
+ }
+ if (fc->fddictn && fc->fddictn->vals)
+ fc->fddictn->vals[ix]++;
+ }
+
+ return RPMRC_OK;
+}
+
+static int initAttrs(rpmfc fc)
+{
+ ARGV_t files = NULL;
+ char * attrPath = rpmExpand("%{_fileattrsdir}/*.attr", NULL);
+ int nattrs = 0;
+
+ /* Discover known attributes from pathnames + initialize them */
+ if (rpmGlob(attrPath, NULL, &files) == 0) {
+ nattrs = argvCount(files);
+ fc->atypes = xcalloc(nattrs + 1, sizeof(*fc->atypes));
+ for (int i = 0; i < nattrs; i++) {
+ char *bn = basename(files[i]);
+ bn[strlen(bn)-strlen(".attr")] = '\0';
+ fc->atypes[i] = rpmfcAttrNew(bn);
+ }
+ fc->atypes[nattrs] = NULL;
+ argvFree(files);
+ }
+ free(attrPath);
+ return nattrs;
+}
+
+rpmRC rpmfcClassify(rpmfc fc, ARGV_t argv, rpm_mode_t * fmode)
+{
+ ARGV_t fcav = NULL;
+ int xx;
+ int msflags = MAGIC_CHECK | MAGIC_COMPRESS | MAGIC_NO_CHECK_TOKENS;
+ magic_t ms = NULL;
+ rpmRC rc = RPMRC_FAIL;
+
+ if (fc == NULL || argv == NULL)
+ return RPMRC_OK; /* XXX looks very wrong */
+
+ if (initAttrs(fc) < 1) {
+ rpmlog(RPMLOG_ERR, _("No file attributes configured\n"));
+ goto exit;
+ }
+
+ fc->nfiles = argvCount(argv);
+ fc->fattrs = xcalloc(fc->nfiles, sizeof(*fc->fattrs));
+
+ /* Initialize the per-file dictionary indices. */
+ xx = argiAdd(&fc->fddictx, fc->nfiles-1, 0);
+ xx = argiAdd(&fc->fddictn, fc->nfiles-1, 0);
+
+ /* Build (sorted) file class dictionary. */
+ xx = argvAdd(&fc->cdict, "");
+ xx = argvAdd(&fc->cdict, "directory");
+
+ ms = magic_open(msflags);
+ if (ms == NULL) {
+ rpmlog(RPMLOG_ERR, _("magic_open(0x%x) failed: %s\n"),
+ msflags, strerror(errno));
+ goto exit;
+ }
+
+ xx = magic_load(ms, NULL);
+ if (xx == -1) {
+ rpmlog(RPMLOG_ERR, _("magic_load failed: %s\n"), magic_error(ms));
+ goto exit;
+ }
+
+ for (fc->ix = 0; fc->ix < fc->nfiles; fc->ix++) {
+ const char * ftype;
+ const char * s = argv[fc->ix];
+ size_t slen = strlen(s);
+ int fcolor = RPMFC_BLACK;
+ rpm_mode_t mode = (fmode ? fmode[fc->ix] : 0);
+ int is_executable = (mode & (S_IXUSR|S_IXGRP|S_IXOTH));
+
+ switch (mode & S_IFMT) {
+ case S_IFCHR: ftype = "character special"; break;
+ case S_IFBLK: ftype = "block special"; break;
+ case S_IFIFO: ftype = "fifo (named pipe)"; break;
+ case S_IFSOCK: ftype = "socket"; break;
+ case S_IFDIR:
+ case S_IFLNK:
+ case S_IFREG:
+ default:
+ /* XXX all files with extension ".pm" are perl modules for now. */
+ if (rpmFileHasSuffix(s, ".pm"))
+ ftype = "Perl5 module source text";
+
+ /* XXX all files with extension ".la" are libtool for now. */
+ else if (rpmFileHasSuffix(s, ".la"))
+ ftype = "libtool library file";
+
+ /* XXX all files with extension ".pc" are pkgconfig for now. */
+ else if (rpmFileHasSuffix(s, ".pc"))
+ ftype = "pkgconfig file";
+
+ /* XXX skip all files in /dev/ which are (or should be) %dev dummies. */
+ else if (slen >= fc->brlen+sizeof("/dev/") && rstreqn(s+fc->brlen, "/dev/", sizeof("/dev/")-1))
+ ftype = "";
+ else
+ ftype = magic_file(ms, s);
+
+ if (ftype == NULL) {
+ rpmlog(is_executable ? RPMLOG_ERR : RPMLOG_WARNING,
+ _("Recognition of file \"%s\" failed: mode %06o %s\n"),
+ s, mode, magic_error(ms));
+ /* only executable files are critical to dep extraction */
+ if (is_executable) {
+ goto exit;
+ }
+ /* unrecognized non-executables get treated as "data" */
+ ftype = "data";
+ }
+ }
+
+ rpmlog(RPMLOG_DEBUG, "%s: %s\n", s, ftype);
+
+ /* Save the path. */
+ xx = argvAdd(&fc->fn, s);
+
+ /* Save the file type string. */
+ xx = argvAdd(&fcav, ftype);
+
+ /* Add (filtered) file coloring */
+ fcolor |= rpmfcColor(ftype);
+
+ /* Add attributes based on file type and/or path */
+ rpmfcAttributes(fc, ftype, s);
+
+ xx = argiAdd(&fc->fcolor, fc->ix, fcolor);
+
+ if (fcolor != RPMFC_WHITE && (fcolor & RPMFC_INCLUDE))
+ argvAddUniq(&fc->cdict, ftype);
+ }
+
+ /* Build per-file class index array. */
+ fc->fknown = 0;
+ for (fc->ix = 0; fc->ix < fc->nfiles; fc->ix++) {
+ const char *se = fcav[fc->ix];
+ ARGV_t dav = argvSearch(fc->cdict, se, NULL);
+ if (dav) {
+ xx = argiAdd(&fc->fcdictx, fc->ix, (dav - fc->cdict));
+ fc->fknown++;
+ } else {
+ xx = argiAdd(&fc->fcdictx, fc->ix, 0);
+ fc->fwhite++;
+ }
+ }
+ rc = RPMRC_OK;
+
+exit:
+ fcav = argvFree(fcav);
+ if (ms != NULL)
+ magic_close(ms);
+
+ return rc;
+}
+
+/**
+ */
+typedef struct DepMsg_s * DepMsg_t;
+
+/**
+ */
+struct DepMsg_s {
+ const char * msg;
+ char * const argv[4];
+ rpmTagVal ntag;
+ rpmTagVal vtag;
+ rpmTagVal ftag;
+ int mask;
+ int xormask;
+};
+
+/**
+ */
+static struct DepMsg_s depMsgs[] = {
+ { "Provides", { "%{?__find_provides}", NULL, NULL, NULL },
+ RPMTAG_PROVIDENAME, RPMTAG_PROVIDEVERSION, RPMTAG_PROVIDEFLAGS,
+ 0, -1 },
+ { "Requires(interp)", { NULL, "interp", NULL, NULL },
+ RPMTAG_REQUIRENAME, RPMTAG_REQUIREVERSION, RPMTAG_REQUIREFLAGS,
+ RPMSENSE_INTERP, 0 },
+ { "Requires(rpmlib)", { NULL, "rpmlib", NULL, NULL },
+ -1, -1, RPMTAG_REQUIREFLAGS,
+ RPMSENSE_RPMLIB, 0 },
+ { "Requires(verify)", { NULL, "verify", NULL, NULL },
+ -1, -1, RPMTAG_REQUIREFLAGS,
+ RPMSENSE_SCRIPT_VERIFY, 0 },
+ { "Requires(pre)", { NULL, "pre", NULL, NULL },
+ -1, -1, RPMTAG_REQUIREFLAGS,
+ RPMSENSE_SCRIPT_PRE, 0 },
+ { "Requires(post)", { NULL, "post", NULL, NULL },
+ -1, -1, RPMTAG_REQUIREFLAGS,
+ RPMSENSE_SCRIPT_POST, 0 },
+ { "Requires(preun)", { NULL, "preun", NULL, NULL },
+ -1, -1, RPMTAG_REQUIREFLAGS,
+ RPMSENSE_SCRIPT_PREUN, 0 },
+ { "Requires(postun)", { NULL, "postun", NULL, NULL },
+ -1, -1, RPMTAG_REQUIREFLAGS,
+ RPMSENSE_SCRIPT_POSTUN, 0 },
+ { "Requires", { "%{?__find_requires}", NULL, NULL, NULL },
+ -1, -1, RPMTAG_REQUIREFLAGS, /* XXX inherit name/version arrays */
+ RPMSENSE_FIND_REQUIRES|RPMSENSE_TRIGGERIN|RPMSENSE_TRIGGERUN|RPMSENSE_TRIGGERPOSTUN|RPMSENSE_TRIGGERPREIN, 0 },
+ { "Conflicts", { "%{?__find_conflicts}", NULL, NULL, NULL },
+ RPMTAG_CONFLICTNAME, RPMTAG_CONFLICTVERSION, RPMTAG_CONFLICTFLAGS,
+ 0, -1 },
+ { "Obsoletes", { "%{?__find_obsoletes}", NULL, NULL, NULL },
+ RPMTAG_OBSOLETENAME, RPMTAG_OBSOLETEVERSION, RPMTAG_OBSOLETEFLAGS,
+ 0, -1 },
+ { NULL, { NULL, NULL, NULL, NULL }, 0, 0, 0, 0, 0 }
+};
+
+static DepMsg_t DepMsgs = depMsgs;
+
+/**
+ */
+static void printDeps(Header h)
+{
+ DepMsg_t dm;
+ rpmds ds = NULL;
+ const char * DNEVR;
+ rpmsenseFlags Flags;
+ int bingo = 0;
+
+ for (dm = DepMsgs; dm->msg != NULL; dm++) {
+ if (dm->ntag != -1) {
+ ds = rpmdsFree(ds);
+ ds = rpmdsNew(h, dm->ntag, 0);
+ }
+ if (dm->ftag == 0)
+ continue;
+
+ ds = rpmdsInit(ds);
+ if (ds == NULL)
+ continue; /* XXX can't happen */
+
+ bingo = 0;
+ while (rpmdsNext(ds) >= 0) {
+
+ Flags = rpmdsFlags(ds);
+
+ if (!((Flags & dm->mask) ^ dm->xormask))
+ continue;
+ if (bingo == 0) {
+ rpmlog(RPMLOG_NOTICE, "%s:", (dm->msg ? dm->msg : ""));
+ bingo = 1;
+ }
+ if ((DNEVR = rpmdsDNEVR(ds)) == NULL)
+ continue; /* XXX can't happen */
+ rpmlog(RPMLOG_NOTICE, " %s", DNEVR+2);
+ }
+ if (bingo)
+ rpmlog(RPMLOG_NOTICE, "\n");
+ }
+ ds = rpmdsFree(ds);
+}
+
+/**
+ */
+static rpmRC rpmfcGenerateDependsHelper(const rpmSpec spec, Package pkg, rpmfi fi)
+{
+ StringBuf sb_stdin;
+ StringBuf sb_stdout;
+ DepMsg_t dm;
+ int failnonzero = 0;
+ rpmRC rc = RPMRC_OK;
+
+ /*
+ * Create file manifest buffer to deliver to dependency finder.
+ */
+ sb_stdin = newStringBuf();
+ fi = rpmfiInit(fi, 0);
+ if (fi != NULL)
+ while (rpmfiNext(fi) >= 0)
+ appendLineStringBuf(sb_stdin, rpmfiFN(fi));
+
+ for (dm = DepMsgs; dm->msg != NULL; dm++) {
+ rpmTagVal tag = (dm->ftag > 0) ? dm->ftag : dm->ntag;
+ rpmsenseFlags tagflags;
+ char * s = NULL;
+
+ switch(tag) {
+ case RPMTAG_PROVIDEFLAGS:
+ if (!pkg->autoProv)
+ continue;
+ failnonzero = 1;
+ tagflags = RPMSENSE_FIND_PROVIDES;
+ break;
+ case RPMTAG_REQUIREFLAGS:
+ if (!pkg->autoReq)
+ continue;
+ failnonzero = 0;
+ tagflags = RPMSENSE_FIND_REQUIRES;
+ break;
+ default:
+ continue;
+ break;
+ }
+
+ if (rpmfcExec(dm->argv, sb_stdin, &sb_stdout,
+ failnonzero, spec->buildRoot) == -1)
+ continue;
+
+ s = rpmExpand(dm->argv[0], NULL);
+ rpmlog(RPMLOG_NOTICE, _("Finding %s: %s\n"), dm->msg, (s ? s : ""));
+ free(s);
+
+ if (sb_stdout == NULL) {
+ rc = RPMRC_FAIL;
+ rpmlog(RPMLOG_ERR, _("Failed to find %s:\n"), dm->msg);
+ break;
+ }
+
+ /* Parse dependencies into header */
+ rc = parseRCPOT(spec, pkg, getStringBuf(sb_stdout), tag, 0, tagflags);
+ sb_stdout = freeStringBuf(sb_stdout);
+
+ if (rc) {
+ rpmlog(RPMLOG_ERR, _("Failed to find %s:\n"), dm->msg);
+ break;
+ }
+ }
+
+ sb_stdin = freeStringBuf(sb_stdin);
+
+ return rc;
+}
+
+rpmRC rpmfcGenerateDepends(const rpmSpec spec, Package pkg)
+{
+ rpmfi fi = pkg->cpioList;
+ rpmfc fc = NULL;
+ rpmds ds;
+ ARGV_t av;
+ rpm_mode_t * fmode;
+ int ac = rpmfiFC(fi);
+ char *buf = NULL;
+ int genConfigDeps;
+ rpmRC rc = RPMRC_OK;
+ int xx;
+ int idx;
+ struct rpmtd_s td;
+
+ /* Skip packages with no files. */
+ if (ac <= 0)
+ return rc;
+
+ /* Skip packages that have dependency generation disabled. */
+ if (! (pkg->autoReq || pkg->autoProv))
+ return rc;
+
+ /* If new-fangled dependency generation is disabled ... */
+ if (!rpmExpandNumeric("%{?_use_internal_dependency_generator}")) {
+ /* ... then generate dependencies using %{__find_requires} et al. */
+ rc = rpmfcGenerateDependsHelper(spec, pkg, fi);
+ printDeps(pkg->header);
+ return rc;
+ }
+
+ /* Extract absolute file paths in argv format. */
+ av = xcalloc(ac+1, sizeof(*av));
+ fmode = xcalloc(ac+1, sizeof(*fmode));
+
+ genConfigDeps = 0;
+ fi = rpmfiInit(fi, 0);
+ if (fi != NULL)
+ while ((idx = rpmfiNext(fi)) >= 0) {
+ rpmfileAttrs fileAttrs;
+
+ /* Does package have any %config files? */
+ fileAttrs = rpmfiFFlags(fi);
+ genConfigDeps |= (fileAttrs & RPMFILE_CONFIG);
+
+ av[idx] = xstrdup(rpmfiFN(fi));
+ fmode[idx] = rpmfiFMode(fi);
+ }
+ av[ac] = NULL;
+
+ fc = rpmfcCreate(spec->buildRoot, 0);
+ fc->skipProv = !pkg->autoProv;
+ fc->skipReq = !pkg->autoReq;
+
+ /* Copy (and delete) manually generated dependencies to dictionary. */
+ if (!fc->skipProv) {
+ ds = rpmdsNew(pkg->header, RPMTAG_PROVIDENAME, 0);
+ xx = rpmdsMerge(&fc->provides, ds);
+ ds = rpmdsFree(ds);
+ xx = headerDel(pkg->header, RPMTAG_PROVIDENAME);
+ xx = headerDel(pkg->header, RPMTAG_PROVIDEVERSION);
+ xx = headerDel(pkg->header, RPMTAG_PROVIDEFLAGS);
+
+ /* Add config dependency, Provides: config(N) = EVR */
+ if (genConfigDeps) {
+ rasprintf(&buf, "config(%s)", rpmdsN(pkg->ds));
+ ds = rpmdsSingle(RPMTAG_PROVIDENAME, buf, rpmdsEVR(pkg->ds),
+ (RPMSENSE_EQUAL|RPMSENSE_CONFIG));
+ free(buf);
+ xx = rpmdsMerge(&fc->provides, ds);
+ ds = rpmdsFree(ds);
+ }
+ }
+
+ if (!fc->skipReq) {
+ ds = rpmdsNew(pkg->header, RPMTAG_REQUIRENAME, 0);
+ xx = rpmdsMerge(&fc->requires, ds);
+ ds = rpmdsFree(ds);
+ xx = headerDel(pkg->header, RPMTAG_REQUIRENAME);
+ xx = headerDel(pkg->header, RPMTAG_REQUIREVERSION);
+ xx = headerDel(pkg->header, RPMTAG_REQUIREFLAGS);
+
+ /* Add config dependency, Requires: config(N) = EVR */
+ if (genConfigDeps) {
+ rasprintf(&buf, "config(%s)", rpmdsN(pkg->ds));
+ ds = rpmdsSingle(RPMTAG_REQUIRENAME, buf, rpmdsEVR(pkg->ds),
+ (RPMSENSE_EQUAL|RPMSENSE_CONFIG));
+ free(buf);
+ xx = rpmdsMerge(&fc->requires, ds);
+ ds = rpmdsFree(ds);
+ }
+ }
+
+ /* Build file class dictionary. */
+ rc = rpmfcClassify(fc, av, fmode);
+ if ( rc != RPMRC_OK )
+ goto exit;
+
+ /* Build file/package dependency dictionary. */
+ rc = rpmfcApply(fc);
+ if ( rc != RPMRC_OK )
+ goto exit;
+
+ /* Add per-file colors(#files) */
+ if (rpmtdFromArgi(&td, RPMTAG_FILECOLORS, fc->fcolor)) {
+ rpm_color_t *fcolor;
+ assert(rpmtdType(&td) == RPM_INT32_TYPE);
+ /* XXX Make sure only primary (i.e. Elf32/Elf64) colors are added. */
+ while ((fcolor = rpmtdNextUint32(&td))) {
+ *fcolor &= 0x0f;
+ }
+ headerPut(pkg->header, &td, HEADERPUT_DEFAULT);
+ }
+
+ /* Add classes(#classes) */
+ if (rpmtdFromArgv(&td, RPMTAG_CLASSDICT, fc->cdict)) {
+ headerPut(pkg->header, &td, HEADERPUT_DEFAULT);
+ }
+
+ /* Add per-file classes(#files) */
+ if (rpmtdFromArgi(&td, RPMTAG_FILECLASS, fc->fcdictx)) {
+ assert(rpmtdType(&td) == RPM_INT32_TYPE);
+ headerPut(pkg->header, &td, HEADERPUT_DEFAULT);
+ }
+
+ /* Add Provides: */
+ if (fc->provides != NULL && rpmdsCount(fc->provides) > 0 && !fc->skipProv) {
+ rpmds pi = rpmdsInit(fc->provides);
+ while (rpmdsNext(pi) >= 0) {
+ rpmsenseFlags flags = rpmdsFlags(pi);
+
+ headerPutString(pkg->header, RPMTAG_PROVIDENAME, rpmdsN(pi));
+ headerPutString(pkg->header, RPMTAG_PROVIDEVERSION, rpmdsEVR(pi));
+ headerPutUint32(pkg->header, RPMTAG_PROVIDEFLAGS, &flags, 1);
+ }
+ }
+
+ /* Add Requires: */
+ if (fc->requires != NULL && rpmdsCount(fc->requires) > 0 && !fc->skipReq) {
+ rpmds pi = rpmdsInit(fc->requires);
+ while (rpmdsNext(pi) >= 0) {
+ rpmsenseFlags flags = rpmdsFlags(pi);
+
+ headerPutString(pkg->header, RPMTAG_REQUIRENAME, rpmdsN(pi));
+ headerPutString(pkg->header, RPMTAG_REQUIREVERSION, rpmdsEVR(pi));
+ headerPutUint32(pkg->header, RPMTAG_REQUIREFLAGS, &flags, 1);
+ }
+ }
+
+ /* Add dependency dictionary(#dependencies) */
+ if (rpmtdFromArgi(&td, RPMTAG_DEPENDSDICT, fc->ddictx)) {
+ assert(rpmtdType(&td) == RPM_INT32_TYPE);
+ headerPut(pkg->header, &td, HEADERPUT_DEFAULT);
+ }
+
+ /* Add per-file dependency (start,number) pairs (#files) */
+ if (rpmtdFromArgi(&td, RPMTAG_FILEDEPENDSX, fc->fddictx)) {
+ assert(rpmtdType(&td) == RPM_INT32_TYPE);
+ headerPut(pkg->header, &td, HEADERPUT_DEFAULT);
+ }
+
+ if (rpmtdFromArgi(&td, RPMTAG_FILEDEPENDSN, fc->fddictn)) {
+ assert(rpmtdType(&td) == RPM_INT32_TYPE);
+ headerPut(pkg->header, &td, HEADERPUT_DEFAULT);
+ }
+
+ printDeps(pkg->header);
+
+if (fc != NULL && _rpmfc_debug) {
+char *msg = NULL;
+rasprintf(&msg, "final: files %d cdict[%d] %d%% ddictx[%d]", fc->nfiles, argvCount(fc->cdict), ((100 * fc->fknown)/fc->nfiles), argiCount(fc->ddictx));
+rpmfcPrint(msg, fc, NULL);
+free(msg);
+}
+exit:
+ /* Clean up. */
+ fmode = _free(fmode);
+ fc = rpmfcFree(fc);
+ av = argvFree(av);
+
+ return rc;
+}
diff --git a/build/rpmfc.h b/build/rpmfc.h
new file mode 100644
index 0000000..f3d3e66
--- /dev/null
+++ b/build/rpmfc.h
@@ -0,0 +1,113 @@
+#ifndef _H_RPMFC_
+#define _H_RPMFC_
+
+/** \ingroup rpmfc rpmbuild
+ * \file build/rpmfc.h
+ * Structures and methods for build-time file classification.
+ */
+
+#include <rpm/rpmtypes.h>
+#include <rpm/argv.h> /* for ARGV_t */
+#include <rpm/rpmspec.h> /* for Package */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int _rpmfc_debug;
+
+/** \ingroup rpmfc
+ */
+typedef struct rpmfc_s * rpmfc;
+
+/** \ingroup rpmfc
+ */
+enum FCOLOR_e {
+ RPMFC_BLACK = 0,
+ RPMFC_ELF32 = (1 << 0),
+ RPMFC_ELF64 = (1 << 1),
+ RPMFC_ELFMIPSN32 = (1 << 2),
+#define RPMFC_ELF (RPMFC_ELF32|RPMFC_ELF64|RPMFC_ELFMIPSN32)
+ /* (1 << 3) leaks into package headers, reserved */
+
+ RPMFC_WHITE = (1 << 29),
+ RPMFC_INCLUDE = (1 << 30),
+ RPMFC_ERROR = (1 << 31)
+};
+
+/** \ingroup rpmfc
+ */
+typedef rpmFlags FCOLOR_t;
+
+/** \ingroup rpmfc
+ */
+typedef const struct rpmfcTokens_s * rpmfcToken;
+
+/** \ingroup rpmfc
+ * Print results of file classification.
+ * @todo Remove debugging routine.
+ * @param msg message prefix (NULL for none)
+ * @param fc file classifier
+ * @param fp output file handle (NULL for stderr)
+ */
+void rpmfcPrint(const char * msg, rpmfc fc, FILE * fp);
+
+/** \ingroup rpmfc
+ * Destroy a file classifier.
+ * @param fc file classifier
+ * @return NULL always
+ */
+rpmfc rpmfcFree(rpmfc fc);
+
+/** \ingroup rpmfc
+ * Create a file classifier.
+ * @param rootDir (build) root directory
+ * @param flags (unused)
+ * @return new file classifier
+ */
+rpmfc rpmfcCreate(const char *rootDir, rpmFlags flags);
+
+/** \ingroup rpmfc
+ * @deprecated
+ * Create a file classifier.
+ * @return new file classifier
+ */
+RPM_GNUC_DEPRECATED
+rpmfc rpmfcNew(void);
+
+
+/** \ingroup rpmfc
+ * Build file class dictionary and mappings.
+ * @param fc file classifier
+ * @param argv files to classify
+ * @param fmode files mode_t array (or NULL)
+ * @return RPMRC_OK on success
+ */
+rpmRC rpmfcClassify(rpmfc fc, ARGV_t argv, rpm_mode_t * fmode);
+
+/** \ingroup rpmfc
+ * Build file/package dependency dictionary and mappings.
+ * @param fc file classifier
+ * @return RPMRC_OK on success
+ */
+rpmRC rpmfcApply(rpmfc fc);
+
+/** \ingroup rpmfc
+ * Retrieve file classification provides
+ * @param fc file classifier
+ * @return rpmds dependency set of fc provides
+ */
+rpmds rpmfcProvides(rpmfc fc);
+
+/** \ingroup rpmfc
+ * Retrieve file classification requires
+ * @param fc file classifier
+ * @return rpmds dependency set of fc requires
+ */
+rpmds rpmfcRequires(rpmfc fc);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _H_RPMFC_ */
diff --git a/build/rpmspec.h b/build/rpmspec.h
new file mode 100644
index 0000000..195fc72
--- /dev/null
+++ b/build/rpmspec.h
@@ -0,0 +1,88 @@
+#ifndef _H_SPEC_
+#define _H_SPEC_
+
+/** \ingroup rpmbuild
+ * \file build/rpmspec.h
+ * The rpmSpec and Package data structures used during build.
+ */
+
+#include <rpm/rpmstring.h> /* StringBuf */
+#include <rpm/rpmcli.h> /* for QVA_t */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** \ingroup rpmbuild
+ */
+typedef struct Package_s * rpmSpecPkg;
+typedef struct Source * rpmSpecSrc;
+typedef struct rpmSpecIter_s * rpmSpecPkgIter;
+typedef struct rpmSpecIter_s * rpmSpecSrcIter;
+
+enum rpmSourceFlags_e {
+ RPMBUILD_ISSOURCE = (1 << 0),
+ RPMBUILD_ISPATCH = (1 << 1),
+ RPMBUILD_ISICON = (1 << 2),
+ RPMBUILD_ISNO = (1 << 3),
+};
+
+typedef rpmFlags rpmSourceFlags;
+
+#define RPMBUILD_DEFAULT_LANG "C"
+
+enum rpmSpecFlags_e {
+ RPMSPEC_NONE = 0,
+ RPMSPEC_ANYARCH = (1 << 0),
+ RPMSPEC_FORCE = (1 << 1),
+ RPMSPEC_NOLANG = (1 << 2),
+};
+
+typedef rpmFlags rpmSpecFlags;
+
+/** \ingroup rpmbuild
+ * Destroy Spec structure.
+ * @param spec spec file control structure
+ * @return NULL always
+ */
+rpmSpec rpmSpecFree(rpmSpec spec);
+
+/* Iterator for spec packages */
+rpmSpecPkgIter rpmSpecPkgIterInit(rpmSpec spec);
+rpmSpecPkg rpmSpecPkgIterNext(rpmSpecPkgIter iter);
+rpmSpecPkgIter rpmSpecPkgIterFree(rpmSpecPkgIter iter);
+
+/* Getters for spec package attributes */
+Header rpmSpecPkgHeader(rpmSpecPkg pkg);
+
+/* Iterator for spec sources */
+rpmSpecSrcIter rpmSpecSrcIterInit(rpmSpec spec);
+rpmSpecSrc rpmSpecSrcIterNext(rpmSpecSrcIter iter);
+rpmSpecSrcIter rpmSpecSrcIterFree(rpmSpecSrcIter iter);
+
+/* Getters for spec source attributes */
+rpmSourceFlags rpmSpecSrcFlags(rpmSpecSrc src);
+int rpmSpecSrcNum(rpmSpecSrc src);
+const char * rpmSpecSrcFilename(rpmSpecSrc src, int full);
+
+/*
+ * Retrieve parsed spec script section (RPMBUILD_PREP, RPMBUILD_BUILD etc).
+ * As a special case, RPMBUILD_NONE as section returns the entire spec in
+ * preprocessed (macros expanded etc) format.
+ */
+const char * rpmSpecGetSection(rpmSpec spec, int section);
+
+/** \ingroup rpmbuild
+ * Function to query spec file(s).
+ * @param ts transaction set
+ * @param qva parsed query/verify options
+ * @param arg query argument
+ * @return 0 on success, else no. of failures
+ */
+int rpmspecQuery(rpmts ts, QVA_t qva, const char * arg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _H_SPEC_ */
diff --git a/build/spec.c b/build/spec.c
new file mode 100644
index 0000000..a4a321f
--- /dev/null
+++ b/build/spec.c
@@ -0,0 +1,428 @@
+/** \ingroup rpmbuild
+ * \file build/spec.c
+ * Handle spec data structure.
+ */
+
+#include "system.h"
+
+#include <rpm/header.h>
+#include <rpm/rpmds.h>
+#include <rpm/rpmfi.h>
+#include <rpm/rpmts.h>
+#include <rpm/rpmlog.h>
+#include <rpm/rpmfileutil.h>
+
+#include "rpmio/rpmlua.h"
+#include "build/rpmbuild_internal.h"
+
+#include "debug.h"
+
+#define SKIPSPACE(s) { while (*(s) && risspace(*(s))) (s)++; }
+
+/**
+ * @param p trigger entry chain
+ * @return NULL always
+ */
+static inline
+struct TriggerFileEntry * freeTriggerFiles(struct TriggerFileEntry * p)
+{
+ struct TriggerFileEntry *o, *q = p;
+
+ while (q != NULL) {
+ o = q;
+ q = q->next;
+ o->fileName = _free(o->fileName);
+ o->script = _free(o->script);
+ o->prog = _free(o->prog);
+ o = _free(o);
+ }
+ return NULL;
+}
+
+/**
+ * Destroy source component chain.
+ * @param s source component chain
+ * @return NULL always
+ */
+static inline
+struct Source * freeSources(struct Source * s)
+{
+ struct Source *r, *t = s;
+
+ while (t != NULL) {
+ r = t;
+ t = t->next;
+ r->fullSource = _free(r->fullSource);
+ r = _free(r);
+ }
+ return NULL;
+}
+
+rpmRC lookupPackage(rpmSpec spec, const char *name, int flag,Package *pkg)
+{
+ const char *pname;
+ char *fullName = NULL;
+ Package p;
+
+ /* "main" package */
+ if (name == NULL) {
+ if (pkg)
+ *pkg = spec->packages;
+ return RPMRC_OK;
+ }
+
+ /* Construct package name */
+ if (flag == PART_SUBNAME) {
+ pname = headerGetString(spec->packages->header, RPMTAG_NAME);
+ rasprintf(&fullName, "%s-%s", pname, name);
+ } else {
+ fullName = xstrdup(name);
+ }
+
+ /* Locate package with fullName */
+ for (p = spec->packages; p != NULL; p = p->next) {
+ pname = headerGetString(p->header, RPMTAG_NAME);
+ if (pname && (rstreq(fullName, pname))) {
+ break;
+ }
+ }
+ free(fullName);
+
+ if (pkg)
+ *pkg = p;
+ return ((p == NULL) ? RPMRC_FAIL : RPMRC_OK);
+}
+
+Package newPackage(rpmSpec spec)
+{
+ Package p = xcalloc(1, sizeof(*p));
+ p->header = headerNew();
+ p->autoProv = 1;
+ p->autoReq = 1;
+ p->fileList = NULL;
+ p->fileFile = NULL;
+ p->policyList = NULL;
+
+ if (spec->packages == NULL) {
+ spec->packages = p;
+ } else {
+ Package pp;
+ /* Always add package to end of list */
+ for (pp = spec->packages; pp->next != NULL; pp = pp->next)
+ {};
+ pp->next = p;
+ }
+ p->next = NULL;
+
+ return p;
+}
+
+static Package freePackage(Package pkg)
+{
+ if (pkg == NULL) return NULL;
+
+ pkg->preInFile = _free(pkg->preInFile);
+ pkg->postInFile = _free(pkg->postInFile);
+ pkg->preUnFile = _free(pkg->preUnFile);
+ pkg->postUnFile = _free(pkg->postUnFile);
+ pkg->verifyFile = _free(pkg->verifyFile);
+
+ pkg->header = headerFree(pkg->header);
+ pkg->ds = rpmdsFree(pkg->ds);
+ pkg->fileList = argvFree(pkg->fileList);
+ pkg->fileFile = argvFree(pkg->fileFile);
+ pkg->policyList = argvFree(pkg->policyList);
+ if (pkg->cpioList) {
+ rpmfi fi = pkg->cpioList;
+ pkg->cpioList = NULL;
+ fi = rpmfiFree(fi);
+ }
+
+ pkg->specialDoc = freeStringBuf(pkg->specialDoc);
+ pkg->specialDocDir = _free(pkg->specialDocDir);
+ pkg->icon = freeSources(pkg->icon);
+ pkg->triggerFiles = freeTriggerFiles(pkg->triggerFiles);
+
+ pkg = _free(pkg);
+ return NULL;
+}
+
+static Package freePackages(Package packages)
+{
+ Package p;
+
+ while ((p = packages) != NULL) {
+ packages = p->next;
+ p->next = NULL;
+ p = freePackage(p);
+ }
+ return NULL;
+}
+
+rpmSpec newSpec(void)
+{
+ rpmSpec spec = xcalloc(1, sizeof(*spec));
+
+ spec->specFile = NULL;
+
+ spec->fileStack = NULL;
+ spec->lbuf[0] = '\0';
+ spec->line = spec->lbuf;
+ spec->nextline = NULL;
+ spec->nextpeekc = '\0';
+ spec->lineNum = 0;
+ spec->readStack = xcalloc(1, sizeof(*spec->readStack));
+ spec->readStack->next = NULL;
+ spec->readStack->reading = 1;
+
+ spec->rootDir = NULL;
+ spec->prep = NULL;
+ spec->build = NULL;
+ spec->install = NULL;
+ spec->check = NULL;
+ spec->clean = NULL;
+ spec->parsed = NULL;
+
+ spec->sources = NULL;
+ spec->packages = NULL;
+ spec->noSource = 0;
+ spec->numSources = 0;
+
+ spec->sourceRpmName = NULL;
+ spec->sourcePkgId = NULL;
+ spec->sourceHeader = NULL;
+ spec->sourceCpioList = NULL;
+
+ spec->buildRoot = NULL;
+ spec->buildSubdir = NULL;
+
+ spec->buildRestrictions = headerNew();
+ spec->BANames = NULL;
+ spec->BACount = 0;
+ spec->recursing = 0;
+ spec->BASpecs = NULL;
+
+ spec->flags = RPMSPEC_NONE;
+
+ spec->macros = rpmGlobalMacroContext;
+
+#ifdef WITH_LUA
+ {
+ /* make sure patches and sources tables always exist */
+ rpmlua lua = NULL; /* global state */
+ rpmluaPushTable(lua, "patches");
+ rpmluaPushTable(lua, "sources");
+ rpmluaPop(lua);
+ rpmluaPop(lua);
+ }
+#endif
+ return spec;
+}
+
+rpmSpec rpmSpecFree(rpmSpec spec)
+{
+
+ if (spec == NULL) return NULL;
+
+ spec->prep = freeStringBuf(spec->prep);
+ spec->build = freeStringBuf(spec->build);
+ spec->install = freeStringBuf(spec->install);
+ spec->check = freeStringBuf(spec->check);
+ spec->clean = freeStringBuf(spec->clean);
+ spec->parsed = freeStringBuf(spec->parsed);
+
+ spec->buildRoot = _free(spec->buildRoot);
+ spec->buildSubdir = _free(spec->buildSubdir);
+ spec->specFile = _free(spec->specFile);
+
+ closeSpec(spec);
+
+ while (spec->readStack) {
+ struct ReadLevelEntry *rl = spec->readStack;
+ spec->readStack = rl->next;
+ rl->next = NULL;
+ free(rl);
+ }
+
+ spec->sourceRpmName = _free(spec->sourceRpmName);
+ spec->sourcePkgId = _free(spec->sourcePkgId);
+ spec->sourceHeader = headerFree(spec->sourceHeader);
+
+ if (spec->sourceCpioList) {
+ rpmfi fi = spec->sourceCpioList;
+ spec->sourceCpioList = NULL;
+ fi = rpmfiFree(fi);
+ }
+
+ spec->buildRestrictions = headerFree(spec->buildRestrictions);
+
+ if (!spec->recursing) {
+ if (spec->BASpecs != NULL)
+ while (spec->BACount--) {
+ spec->BASpecs[spec->BACount] =
+ rpmSpecFree(spec->BASpecs[spec->BACount]);
+ }
+ spec->BASpecs = _free(spec->BASpecs);
+ }
+ spec->BANames = _free(spec->BANames);
+
+#ifdef WITH_LUA
+ rpmlua lua = NULL; /* global state */
+ rpmluaDelVar(lua, "patches");
+ rpmluaDelVar(lua, "sources");
+#endif
+
+ spec->sources = freeSources(spec->sources);
+ spec->packages = freePackages(spec->packages);
+
+ spec = _free(spec);
+
+ return spec;
+}
+
+Header rpmSpecSourceHeader(rpmSpec spec)
+{
+ return spec->sourceHeader;
+}
+
+rpmds rpmSpecDS(rpmSpec spec, rpmTagVal tag)
+{
+ return (spec != NULL) ? rpmdsNew(spec->buildRestrictions, tag, 0) : NULL;
+}
+
+rpmps rpmSpecCheckDeps(rpmts ts, rpmSpec spec)
+{
+ rpmps probs = NULL;
+
+ rpmtsEmpty(ts);
+
+ rpmtsAddInstallElement(ts, rpmSpecSourceHeader(spec), NULL, 0, NULL);
+ rpmtsCheck(ts);
+ probs = rpmtsProblems(ts);
+
+ rpmtsEmpty(ts);
+ return probs;
+}
+
+struct rpmSpecIter_s {
+ void *next;
+};
+
+#define SPEC_LISTITER_INIT(_itertype, _iteritem) \
+ _itertype iter = NULL; \
+ if (spec) { \
+ iter = xcalloc(1, sizeof(*iter)); \
+ iter->next = spec->_iteritem; \
+ } \
+ return iter
+
+#define SPEC_LISTITER_NEXT(_valuetype) \
+ _valuetype item = NULL; \
+ if (iter) { \
+ item = iter->next; \
+ iter->next = (item) ? item->next : NULL; \
+ } \
+ return item
+
+#define SPEC_LISTITER_FREE() \
+ free(iter); \
+ return NULL
+
+
+rpmSpecPkgIter rpmSpecPkgIterInit(rpmSpec spec)
+{
+ SPEC_LISTITER_INIT(rpmSpecPkgIter, packages);
+}
+
+rpmSpecPkgIter rpmSpecPkgIterFree(rpmSpecPkgIter iter)
+{
+ SPEC_LISTITER_FREE();
+}
+
+rpmSpecPkg rpmSpecPkgIterNext(rpmSpecPkgIter iter)
+{
+ SPEC_LISTITER_NEXT(rpmSpecPkg);
+}
+
+Header rpmSpecPkgHeader(rpmSpecPkg pkg)
+{
+ return (pkg != NULL) ? pkg->header : NULL;
+}
+
+rpmSpecSrcIter rpmSpecSrcIterInit(rpmSpec spec)
+{
+ SPEC_LISTITER_INIT(rpmSpecSrcIter, sources);
+}
+
+rpmSpecSrcIter rpmSpecSrcIterFree(rpmSpecSrcIter iter)
+{
+ SPEC_LISTITER_FREE();
+}
+
+rpmSpecSrc rpmSpecSrcIterNext(rpmSpecSrcIter iter)
+{
+ SPEC_LISTITER_NEXT(rpmSpecSrc);
+}
+
+rpmSourceFlags rpmSpecSrcFlags(rpmSpecSrc src)
+{
+ return (src != NULL) ? src->flags : 0;
+}
+
+int rpmSpecSrcNum(rpmSpecSrc src)
+{
+ return (src != NULL) ? src->num : -1;
+}
+
+const char * rpmSpecSrcFilename(rpmSpecSrc src, int full)
+{
+ const char *source = NULL;
+ if (src) {
+ source = full ? src->fullSource : src->source;
+ }
+ return source;
+}
+
+const char * rpmSpecGetSection(rpmSpec spec, int section)
+{
+ if (spec) {
+ switch (section) {
+ case RPMBUILD_NONE: return getStringBuf(spec->parsed);
+ case RPMBUILD_PREP: return getStringBuf(spec->prep);
+ case RPMBUILD_BUILD: return getStringBuf(spec->build);
+ case RPMBUILD_INSTALL: return getStringBuf(spec->install);
+ case RPMBUILD_CHECK: return getStringBuf(spec->check);
+ case RPMBUILD_CLEAN: return getStringBuf(spec->clean);
+ }
+ }
+ return NULL;
+}
+
+int rpmspecQuery(rpmts ts, QVA_t qva, const char * arg)
+{
+ rpmSpec spec = NULL;
+ int res = 1;
+ int xx;
+
+ if (qva->qva_showPackage == NULL)
+ goto exit;
+
+ spec = rpmSpecParse(arg, (RPMSPEC_ANYARCH|RPMSPEC_FORCE), NULL);
+ if (spec == NULL) {
+ rpmlog(RPMLOG_ERR,
+ _("query of specfile %s failed, can't parse\n"), arg);
+ goto exit;
+ }
+
+ res = 0;
+ if (qva->qva_source == RPMQV_SPECRPMS) {
+ for (Package pkg = spec->packages; pkg != NULL; pkg = pkg->next)
+ xx = qva->qva_showPackage(qva, ts, pkg->header);
+ } else {
+ xx = qva->qva_showPackage(qva, ts, spec->sourceHeader);
+ }
+
+exit:
+ spec = rpmSpecFree(spec);
+ return res;
+}