diff options
author | Jinkun Jang <jinkun.jang@samsung.com> | 2013-03-12 15:17:20 +0900 |
---|---|---|
committer | Jinkun Jang <jinkun.jang@samsung.com> | 2013-03-12 15:17:20 +0900 |
commit | 7df2385c2f6c93f96e00bc87f2086066cae89ecc (patch) | |
tree | 79d5c20a494622eb084de831a2a51530cd421e33 /build | |
parent | b7a3bffb8e0341b7e4ef69def268bca3a7f279ff (diff) | |
download | rpm-7df2385c2f6c93f96e00bc87f2086066cae89ecc.tar.gz rpm-7df2385c2f6c93f96e00bc87f2086066cae89ecc.tar.bz2 rpm-7df2385c2f6c93f96e00bc87f2086066cae89ecc.zip |
Tizen 2.1 basesubmit/tizen_2.2/20130710.072219submit/tizen_2.1/20130423.104200accepted/tizen_2.1/20130423.1513382.2_release2.2.1_release2.1b_releasetizen_2.2tizen_2.1
Diffstat (limited to 'build')
-rw-r--r-- | build/Makefile.am | 25 | ||||
-rw-r--r-- | build/Makefile.in | 678 | ||||
-rw-r--r-- | build/build.c | 304 | ||||
-rw-r--r-- | build/expression.c | 692 | ||||
-rw-r--r-- | build/files.c | 2171 | ||||
-rw-r--r-- | build/misc.c | 103 | ||||
-rw-r--r-- | build/pack.c | 822 | ||||
-rw-r--r-- | build/parseBuildInstallClean.c | 64 | ||||
-rw-r--r-- | build/parseChangelog.c | 245 | ||||
-rw-r--r-- | build/parseDescription.c | 113 | ||||
-rw-r--r-- | build/parseFiles.c | 99 | ||||
-rw-r--r-- | build/parsePolicies.c | 89 | ||||
-rw-r--r-- | build/parsePreamble.c | 1090 | ||||
-rw-r--r-- | build/parsePrep.c | 546 | ||||
-rw-r--r-- | build/parseReqs.c | 189 | ||||
-rw-r--r-- | build/parseScript.c | 389 | ||||
-rw-r--r-- | build/parseSpec.c | 698 | ||||
-rw-r--r-- | build/policies.c | 316 | ||||
-rw-r--r-- | build/reqprov.c | 123 | ||||
-rw-r--r-- | build/rpmbuild.h | 112 | ||||
-rw-r--r-- | build/rpmbuild_internal.h | 401 | ||||
-rw-r--r-- | build/rpmbuild_misc.h | 94 | ||||
-rw-r--r-- | build/rpmfc.c | 1405 | ||||
-rw-r--r-- | build/rpmfc.h | 113 | ||||
-rw-r--r-- | build/rpmspec.h | 88 | ||||
-rw-r--r-- | build/spec.c | 428 |
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, ¯o, 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; +} |