merged in Marc's new build code
authorewt <devnull@localhost>
Mon, 12 Jan 1998 21:31:29 +0000 (21:31 +0000)
committerewt <devnull@localhost>
Mon, 12 Jan 1998 21:31:29 +0000 (21:31 +0000)
CVS patchset: 1956
CVS date: 1998/01/12 21:31:29

49 files changed:
CHANGES
Makefile.in
Makefile.inc.in
build.c [new file with mode: 0644]
build.h [new file with mode: 0644]
build/Makefile.in
build/TODO [new file with mode: 0644]
build/build.c
build/build.h
build/files.c
build/files.h
build/macro.c
build/macro.h
build/misc.c [new file with mode: 0644]
build/misc.h [new file with mode: 0644]
build/myftw.c
build/myftw.h
build/names.c
build/names.h
build/pack.c
build/pack.h
build/package.c [new file with mode: 0644]
build/package.h [new file with mode: 0644]
build/parse.h [new file with mode: 0644]
build/parseBuildInstallClean.c [new file with mode: 0644]
build/parseChangelog.c [new file with mode: 0644]
build/parseDescription.c [new file with mode: 0644]
build/parseFiles.c [new file with mode: 0644]
build/parsePreamble.c [new file with mode: 0644]
build/parsePrep.c [new file with mode: 0644]
build/parseReqs.c [new file with mode: 0644]
build/parseScript.c [new file with mode: 0644]
build/parseSpec.c [new file with mode: 0644]
build/parseTrigger.c [moved from build/trigger.c with 72% similarity]
build/part.c [new file with mode: 0644]
build/part.h [new file with mode: 0644]
build/read.c [new file with mode: 0644]
build/read.h [new file with mode: 0644]
build/reqprov.c
build/reqprov.h
build/spec.c
build/spec.h
build/specP.h [deleted file]
build/trigger.h [deleted file]
build/vspec.c [deleted file]
lib/Makefile.in
lib/misc.c
rpm.c
rpm.spec

diff --git a/CHANGES b/CHANGES
index 68a6f77..22dcc63 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,5 +1,36 @@
 2.4.11 -> 2.5:
        - added many more i18n strings
+        - merged in large rewrite of build code, which includes the following
+         changes:
+           - all scripts can now take -p <prog>
+           - all scripts can now take -p "<prog> <arg>..."
+           - more strict about %package placement and use of main package 
+             preamble
+           - %attr(mode, user, group [, dirmode])
+           - %defattr()
+           - %defverify()
+           - Root: is depricated.  Use BuildRoot: instead
+           - "nosrc" sources arn't deleted when sources are removed
+           - New tag DocDir: <dir> to set alternate doc dir (default is 
+              /usr/doc)
+           - %doc should work as expected with buildroot, prefix, DocDir:
+           - all file processing is done before any packaging
+           - should work with modern patch programs (later than 2.1)
+           - %patch -z <arg> same as %patch -b <arg>
+           - %define only defines a macro if it starts a line
+           - script prog entries are now arrays (ie with arguments)
+           - standard macros: tag names, buildarch, buildos, buildarch_lc, 
+              buildos_lc, PATCHn, SOURCEn, PATCHURLn, SOURCEURLn, sourcedir, 
+              builddir, optflags
+           - deprecate require_distribution, require_icon, require_vendor
+           - add RPMTAG_FILEDEVICES and RPMTAG_FILEINODES to header
+           - export variables in scripts
+           - fixed tag:field bug
+           - %lang() in %files
+           - %description -l <lang>
+           - Summary(<lang>):
+           - fixed read_line() termination usage
+       - Makefile changes to shorten compiler lines
 
 2.4.11 -> 2.4.12:
        - intialize reserved portion of lead when writing packages
index 2e0abcc..ecdb4fd 100644 (file)
@@ -33,10 +33,11 @@ ETCDIR=$(ROOT)/etc
 SUBDIRS = popt @MISCDIR@ lib build tools @PO@
 INSTSUBDIRS = lib @PO@
 OTHERSUBDIRS = docs autodeps
-OBJS = rpm.o query.o install.o verify.o checksig.o ftp.o url.o @GETTEXTSTUB@
+OBJS = rpm.o query.o install.o verify.o checksig.o ftp.o url.o build.o \
+       @GETTEXTSTUB@
 PROGS = @RPM@ rpm2cpio
 LIBS = @LIBS@ @LIBINTL@ @LIBDL@
-LOADLIBES = -lbuild popt/libpopt.a $(topdir)/lib/librpm.a -L$(topdir)/misc @LIBMISC@
+LOADLIBES = -lrpmbuild -lpopt -lrpm @LIBMISC@
 
 SOURCES = $(subst .o,.c,$(OBJS))
 
@@ -48,11 +49,11 @@ endif
 
 all: $(TARGET)
 
-rpm: lib/librpm.a build/libbuild.a @MISCPATH@ $(OBJS) 
-       $(CC) -o rpm -static $(LDFLAGS) $(OBJS) $(LOADLIBES) $(LIBS) \
+rpm: lib/librpm.a build/librpmbuild.a @MISCPATH@ $(OBJS) 
+       $(CC) -o rpm -static $(OBJS) $(LDFLAGS) $(LOADLIBES) $(LIBS) \
                $(LIBEFENCE)
 
-rpm.shared: lib/librpm.a build/libbuild.a $(OBJS)
+rpm.shared: lib/librpm.a build/rpmlibbuild.a $(OBJS)
        $(CC) -o rpm.shared $(LDFLAGS) $(OBJS) $(LOADLIBES) $(LIBS) \
                $(LIBEFENCE)
 
@@ -68,6 +69,8 @@ rpm2cpio: lib/librpm.a rpm2cpio.o
 rpm.o: rpm.c query.h install.h lib/rpmlib.h Makefile
        $(CC) $(CFLAGS) -DRPMNLSPACKAGE=\"$(RPMNLSPACKAGE)\" \
                -DRPMNLSDIR=\"$(RPMNLSDIR)\" \
+               -DVERSION=\"$(VERSION)\"  \
+               -DLIBRPMALIAS_FILENAME="\"$(LIBRPMALIAS_FILENAME)"\" \
                -c $(srcdir)/rpm.c
 
 # these rules should be in here, but they drive me batty
index 10c4cb8..fcc42e0 100644 (file)
@@ -12,12 +12,8 @@ LIBRPMALIAS_FILENAME=$(libdir)/rpmpopt
 RPMNLSDIR=@datadir@/locale
 RPMNLSPACKAGE=rpm
 CFLAGS = @CFLAGS@ @INCPATH@ $(WARNINGS) $(OPTS) -I$(topdir) \
-       -I$(topdir)/lib -I$(topdir)/misc -Wall -Wstrict-prototypes \
-       -DLIBRPMRC_FILENAME="\"$(LIBRPMRC_FILENAME)"\" \
-       -DLIBRPMALIAS_FILENAME="\"$(LIBRPMALIAS_FILENAME)"\" \
-       -DVERSION=\"$(VERSION)\" 
-LDFLAGS = @LDFLAGS@ -Llib -Lbuild -Lmisc -L$(topdir)/lib \
-               -L$(topdir)/build -L$(topdir)/misc
-VERSION = 2.4.12
+       -I$(topdir)/lib -I$(topdir)/misc -Wall -Wstrict-prototypes 
+LDFLAGS = @LDFLAGS@ -L$(topdir)/lib -L$(topdir)/build -L$(topdir)/misc
+VERSION = 2.4.99
 CC = @CC@
 
diff --git a/build.c b/build.c
new file mode 100644 (file)
index 0000000..24c1d80
--- /dev/null
+++ b/build.c
@@ -0,0 +1,132 @@
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "build.h"
+#include "intl.h"
+#include "lib/rpmlib.h"
+#include "build/build.h"
+#include "build/parse.h"
+#include "build/spec.h"
+
+int build(char *arg, int buildAmount, char *passPhrase,
+                char *buildRoot, int fromTarball, int test, char *cookie) {
+    FILE *f;
+    char * specfile;
+    int res = 0;
+    struct stat statbuf;
+    char * specDir;
+    char * tmpSpecFile;
+    char * cmd;
+    char * s;
+    int count, fd;
+    char buf[BUFSIZ];
+    Spec spec = NULL;
+
+    if (fromTarball) {
+       specDir = rpmGetVar(RPMVAR_SPECDIR);
+       tmpSpecFile = alloca(BUFSIZ);
+       sprintf(tmpSpecFile, "%s/rpm-spec-file-%d", specDir, (int) getpid());
+
+       cmd = alloca(strlen(arg) + 50 + strlen(tmpSpecFile));
+       sprintf(cmd, "gunzip < %s | tar xOvf - \\*.spec 2>&1 > %s", arg,
+                       tmpSpecFile);
+       if (!(f = popen(cmd, "r"))) {
+           fprintf(stderr, _("Failed to open tar pipe: %s\n"), 
+                       strerror(errno));
+           return 1;
+       }
+       if (!fgets(buf, sizeof(buf) - 1, f)) {
+           fprintf(stderr, _("Failed to read spec file from %s\n"), arg);
+           unlink(tmpSpecFile);
+           return 1;
+       }
+       pclose(f);
+
+       cmd = specfile = buf;
+       while (*cmd) {
+           if (*cmd == '/') specfile = cmd + 1;
+           cmd++;
+       }
+
+       cmd = specfile;
+
+       /* remove trailing \n */
+       specfile = cmd + strlen(cmd) - 1;
+       *specfile = '\0';
+
+       specfile = alloca(strlen(specDir) + strlen(cmd) + 5);
+       sprintf(specfile, "%s/%s", specDir, cmd);
+       
+       if (rename(tmpSpecFile, specfile)) {
+           fprintf(stderr, _("Failed to rename %s to %s: %s\n"),
+                   tmpSpecFile, specfile, strerror(errno));
+           unlink(tmpSpecFile);
+           return 1;
+       }
+
+       /* Make the directory which contains the tarball the source 
+          directory for this run */
+
+       if (*arg != '/') {
+           getcwd(buf, BUFSIZ);
+           strcat(buf, "/");
+           strcat(buf, arg);
+       } else 
+           strcpy(buf, arg);
+
+       cmd = buf + strlen(buf) - 1;
+       while (*cmd != '/') cmd--;
+       *cmd = '\0';
+
+       rpmSetVar(RPMVAR_SOURCEDIR, buf);
+    } else if (arg[0] == '/') {
+       specfile = arg;
+    } else {
+       specfile = alloca(BUFSIZ);
+       getcwd(specfile, BUFSIZ);
+       strcat(specfile, "/");
+       strcat(specfile, arg);
+    }
+
+    stat(specfile, &statbuf);
+    if (! S_ISREG(statbuf.st_mode)) {
+       fprintf(stderr, _("File is not a regular file: %s\n"), specfile);
+       return 1;
+    }
+    
+    if (!(fd = open(specfile, O_RDONLY))) {
+       fprintf(stderr, _("Unable to open spec file: %s\n"), specfile);
+       return 1;
+    }
+    count = read(fd, buf, sizeof(buf));
+    close(fd);
+    s = buf;
+    while(count--) {
+       if (! (isprint(*s) || isspace(*s))) {
+           fprintf(stderr, _("File contains non-printable characters(%c): %s\n"), *s,
+                   specfile);
+           return 1;
+       }
+       s++;
+    }
+
+    if (parseSpec(&spec, specfile, buildRoot, 0, passPhrase, cookie)) {
+       return 1;
+    }
+
+    if (buildSpec(spec, buildAmount, test)) {
+       freeSpec(spec);
+       return 1;
+    }
+    
+    if (fromTarball) unlink(specfile);
+
+    freeSpec(spec);
+    
+    return res;
+}
diff --git a/build.h b/build.h
new file mode 100644 (file)
index 0000000..0cc4e3c
--- /dev/null
+++ b/build.h
@@ -0,0 +1,7 @@
+#ifndef H_BUILD
+#define H_BUILD
+
+int build(char *arg, int buildAmount, char *passPhrase,
+                char *buildRoot, int fromTarball, int test, char *cookie);
+
+#endif
index 947268d..29fdb07 100644 (file)
@@ -1,10 +1,16 @@
 srcdir = @srcdir@
 VPATH = $(srcdir)
 
-LIBOBJECTS = spec.o vspec.o build.o files.o names.o pack.o myftw.o \
-               reqprov.o trigger.o macro.o
-LIBBUILD = libbuild.a
-LOADLIBES = -lrpm $(LIBEFENCE)
+LIBOBJECTS = \
+misc.o              parsePreamble.o     part.o \
+package.o           parseDescription.o  parsePrep.o         read.o \
+parseFiles.o        parseReqs.o         reqprov.o \
+parseChangelog.o    parseSpec.o         spec.o \
+parseScript.o       parseBuildInstallClean.o  build.o \
+files.o myftw.o names.o pack.o macro.o
+
+LIBBUILD = librpmbuild.a
+LOADLIBES = -lrpm -lz ../popt/libpopt.a $(LIBEFENCE)
 
 # -----------------------------------------------------------------------
 
@@ -26,6 +32,9 @@ $(LIBBUILD): $(LIBOBJECTS)
 
 $(PROGS): $(LIBOBJECTS)
 
+pack.o:
+       $(CC) $(CFLAGS) -DVERSION=\"$(VERSION)\" -o $@ -c $<
+
 clean:
        rm -f *.a *.o *~ $(PROGS)
 
diff --git a/build/TODO b/build/TODO
new file mode 100644 (file)
index 0000000..14d0f4b
--- /dev/null
@@ -0,0 +1,44 @@
+> Most of my spec files have empty Distribution: and Vendor: tag because I
+> really don't care.  Now, empty tags are frowned upon.  So, I try to comment
+> out the lines and get a funny error message:
+> 
+>      Distribution field must be present in package: solaris2.6
+>      Vendor field must be present in package: solaris2.6
+> 
+> This happens to be my value of 'Build Os' which gets into the error message!
+
+posix %attr
+
+Add Shell: rpmrc entry
+
+some /foo/*/*.foo entries don't get matches
+
+detect multiple summary/description entries
+
+verify langs
+
+=====================================================================
+i18n strings
+
+triggers
+
+custom find-requires, find-provides
+
+messages (normal, verbose, debug)
+
+deprecate require_distribution, require_icon, require_vendor
+deprecate RPMVAR_ROOT, and the RPMVAR_REQUIRE* stuff above
+
+=======================================================================
+* i18n
+
+headerGetRawEntry() -> i18n string
+headerAddI18nString(Header, tag, string, lang)
+
+Summary(...):
+%description -l ...
+
+%lang(...) <file>
+
+"intl.h"
+_("...")
index d432a0f..0877379 100644 (file)
-/* RPM - Copyright (C) 1995 Red Hat Software
- * 
- * build.c - routines for preparing and building the sources
- */
-
-#include "miscfn.h"
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-#include <sys/stat.h>
+#include <sys/types.h>
 #include <sys/wait.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <string.h>
-#include <limits.h>
+#include <sys/stat.h>
+#include <malloc.h>
 
-#include "build.h"
-#include "files.h"
-#include "header.h"
+#include "misc.h"
 #include "spec.h"
-#include "specP.h"
+#include "build.h"
+#include "lib/misc.h"
+#include "lib/messages.h"
 #include "rpmlib.h"
-#include "messages.h"
-#include "stringbuf.h"
-#include "misc.h"
 #include "pack.h"
-#include "popt/popt.h"
-#include "config.h"
-
-#include "names.h"
-
-struct Script {
-    char *name;
-    FILE *file;
-};
-
-struct Script *openScript(Spec spec, int builddir, char *name);
-void writeScript(struct Script *script, char *s);
-int execScript(struct Script *script);
-void freeScript(struct Script *script, int test);
-int execPart(Spec s, char *sb, char *name, int builddir, int test);
-static int doSetupMacro(Spec spec, StringBuf sb, char *line);
-static int doPatchMacro(Spec spec, StringBuf sb, char *line);
-static char *do_untar(Spec spec, int c, int quietly);
-static char *do_patch(Spec spec, int c, int strip, char *dashb,
-                     int reverse, int removeEmpties);
-int isCompressed(char *file);
-static void doSweep(Spec s);
-static int doRmSource(Spec s);
+#include "files.h"
 
-char build_subdir[1024];
+static void doRmSource(Spec spec);
+static int writeVars(Spec spec, FILE *f);
 
-struct Script *openScript(Spec spec, int builddir, char *name)
+int buildSpec(Spec spec, int what, int test)
 {
-    struct Script *script = malloc(sizeof(struct Script));
-    struct PackageRec *main_package = spec->packages;
-    char *s, * arch, * os;
-    int fd;
-    int_32 foo;
-
-    rpmGetArchInfo(&arch, NULL);
-    rpmGetOsInfo(&os, NULL);
-
-    if (! main_package) {
-       rpmError(RPMERR_INTERNAL, "Empty main package");
-       exit(RPMERR_INTERNAL);
-    }
-    
-    if (makeTempFile(NULL, &script->name, &fd))
-       exit(1);
-    script->file = fdopen(fd, "w");
-
-    /* Prepare the script */
-    fprintf(script->file,
-           "# Script generated by rpm\n\n");
+    int x, rc;
 
-    fprintf(script->file, "RPM_SOURCE_DIR=\"%s\"\n", rpmGetVar(RPMVAR_SOURCEDIR));
-    fprintf(script->file, "RPM_BUILD_DIR=\"%s\"\n", rpmGetVar(RPMVAR_BUILDDIR));
-    fprintf(script->file, "RPM_DOC_DIR=\"%s\"\n", rpmGetVar(RPMVAR_DEFAULTDOCDIR));
-    fprintf(script->file, "RPM_OPT_FLAGS=\"%s\"\n", rpmGetVar(RPMVAR_OPTFLAGS));
-    fprintf(script->file, "RPM_ARCH=\"%s\"\n", arch);
-    fprintf(script->file, "RPM_OS=\"%s\"\n", os);
-    if (rpmGetVar(RPMVAR_ROOT)) {
-       fprintf(script->file, "RPM_ROOT_DIR=\"%s\"\n", rpmGetVar(RPMVAR_ROOT));
-    } else {
-       fprintf(script->file, "RPM_ROOT_DIR=\"\"\n");
-    }
-    if (rpmGetVar(RPMVAR_BUILDROOT)) {
-       fprintf(script->file, "RPM_BUILD_ROOT=\"%s\"\n",
-               rpmGetVar(RPMVAR_BUILDROOT));
-    } else {
-       fprintf(script->file, "RPM_BUILD_ROOT=\"\"\n");
-    }
-
-    fprintf(script->file, "RPM_PACKAGE_NAME=\"%s\"\n", spec->name);
-    headerGetEntry(main_package->header, RPMTAG_VERSION, &foo, (void **)&s, &foo);
-    fprintf(script->file, "RPM_PACKAGE_VERSION=\"%s\"\n", s);
-    headerGetEntry(main_package->header, RPMTAG_RELEASE, &foo, (void **)&s, &foo);
-    fprintf(script->file, "RPM_PACKAGE_RELEASE=\"%s\"\n", s);
-
-    if (rpmIsVerbose()) {
-       fprintf(script->file, "set -x\n\n");
+    if (!spec->inBuildArchitectures && spec->buildArchitectureCount) {
+       /* When iterating over buildArchitectures, do the source    */
+       /* packaging on the first run, and skip RMSOURCE altogether */
+       x = 0;
+       while (x < spec->buildArchitectureCount) {
+           if ((rc = buildSpec(spec->buildArchitectureSpecs[x],
+                               (what & ~RPMBUILD_RMSOURCE) |
+                               (x ? 0 : (what & RPMBUILD_PACKAGESOURCE)),
+                               test))) {
+               return rc;
+           }
+           x++;
+       }
     } else {
-       fprintf(script->file, "exec > /dev/null\n\n");
-    }
-
-    /* Set the umask to a known value */
-    fprintf(script->file, "umask 022\n");
-
-    fprintf(script->file, "\necho Executing: %s\n", name);
-    fprintf(script->file, "cd %s\n\n", rpmGetVar(RPMVAR_BUILDDIR));
-    if (builddir) {
-       /* Need to cd to the actual build directory. */
-       /* Note that this means we have to parse the */
-       /* %prep section even if we aren't using it. */
-       fprintf(script->file, "cd %s\n\n", build_subdir);
-    }
-
-    /* We do a litte sanity check here just to make sure we do not wipe */
-    /* the system by accident...                                        */
-    if (rpmGetVar(RPMVAR_BUILDROOT)) {
-       fprintf(script->file, "if [ -z \"$RPM_BUILD_ROOT\" -o \"$RPM_BUILD_ROOT\" = \"/\" ]; then\n");
-       fprintf(script->file, "  echo\n");
-       fprintf(script->file, "  echo 'Warning: Spec contains BuildRoot: tag that is either empty or is set to \"/\"'\n");
-       fprintf(script->file, "  echo\n");
-       fprintf(script->file, "  exit 1\n");
-       fprintf(script->file, "fi\n");
-    }
-    
-    return script;
-}
-
-void writeScript(struct Script *script, char *s)
-{
-    fprintf(script->file, "%s", s);
-}
-
-int execScript(struct Script *script)
-{
-    int pid;
-    int status;
-    
-    writeScript(script, "\nexit 0;\n");
-    fclose(script->file);
-    script->file = NULL;
-    chmod(script->name, 0600);
-
-    if (!(pid = fork())) {
-       execl("/bin/sh", "/bin/sh", "-e", script->name, script->name, NULL);
-       rpmError(RPMERR_SCRIPT, "Exec failed");
-       _exit(RPMERR_SCRIPT);
-    }
-    wait(&status);
-    if (! WIFEXITED(status) || WEXITSTATUS(status)) {
-       rpmError(RPMERR_SCRIPT, "Bad exit status");
-       exit(RPMERR_SCRIPT);
-    }
-    return 0;
-}
-
-void freeScript(struct Script *script, int test)
-{
-    if (script->file)
-       fclose(script->file);
-    if (! test)
-       unlink(script->name);
-    free(script->name);
-    free(script);
-}
-
-int execPart(Spec s, char *sb, char *name, int builddir, int test)
-{
-    struct Script *script;
-
-    rpmMessage(RPMMESS_DEBUG, "RUNNING: %s\n", name);
-    script = openScript(s, builddir, name);
-    writeScript(script, sb);
-    if (!test) {
-       execScript(script);
-    }
-    freeScript(script, test);
-    return 0;
-}
-
-static void doSweep(Spec s)
-{
-    char buf[1024];
-
-    if (strcmp(build_subdir, ".")) {
-        struct Script *script;
-        script = openScript(s, 0, "sweep");
-        sprintf(buf, "rm -rf %s\n", build_subdir);
-        writeScript(script, buf);
-        execScript(script);
-        freeScript(script, 0);
-    }
-}
-
-static int doRmSource(Spec s)
-{
-    char filename[1024];
-    struct sources *source;
-    struct PackageRec *package;
-
-    /* spec file */
-    sprintf(filename, "%s%s", rpmGetVar(RPMVAR_SPECDIR),
-           strrchr(s->specfile, '/'));
-    unlink(filename);
-
-    /* sources and patches */
-    source = s->sources;
-    while (source) {
-       sprintf(filename, "%s/%s", rpmGetVar(RPMVAR_SOURCEDIR), source->source);
-       unlink(filename);
-       source = source->next;
-    }
-
-    /* icons */
-    package = s->packages;
-    while (package) {
-       if (package->icon) {
-           sprintf(filename, "%s/%s", rpmGetVar(RPMVAR_SOURCEDIR),
-                   package->icon);
-           unlink(filename);
+       if (what & RPMBUILD_PREP) {
+           if ((rc = doScript(spec, RPMBUILD_PREP, NULL, NULL, test))) {
+               return rc;
+           }
        }
-       package = package->next;
-    }
-    
-    return 0;
-}
-
-static int doSetupMacro(Spec spec, StringBuf sb, char *line)
-{
-    char *version;
-    int leaveDirs = 0, skipDefaultAction = 0;
-    int createDir = 0, quietly = 0;
-    char * dirName = NULL;
-    char buf[1024];
-    StringBuf before;
-    StringBuf after;
-    poptContext optCon;
-    int argc;
-    char ** argv;
-    int arg;
-    char * optArg;
-    char * chptr;
-    int rc;
-    int num;
-    struct poptOption optionsTable[] = {
-           { NULL, 'a', POPT_ARG_STRING, NULL, 'a' },
-           { NULL, 'b', POPT_ARG_STRING, NULL, 'b' },
-           { NULL, 'c', 0, &createDir, 0 },
-           { NULL, 'D', 0, &leaveDirs, 0 },
-           { NULL, 'n', POPT_ARG_STRING, &dirName, 0 },
-           { NULL, 'T', 0, &skipDefaultAction, 0 },
-           { NULL, 'q', 0, &quietly, 0 },
-           { 0, 0, 0, 0, 0 }
-    };
-
-    if ((rc = poptParseArgvString(line, &argc, &argv))) {
-       rpmError(RPMERR_BADSPEC, "Error parsing %%setup: %s",
-                       poptStrerror(rc));
-       return RPMERR_BADSPEC;
-    }
-
-    before = newStringBuf();
-    after = newStringBuf();
-
-    optCon = poptGetContext(NULL, argc, argv, optionsTable, 0);
-    while ((arg = poptGetNextOpt(optCon)) > 0) {
-       optArg = poptGetOptArg(optCon);
-
-       /* We only parse -a and -b here */
-
-       num = strtoul(optArg, &chptr, 10);
-       if ((*chptr) || (chptr == optArg) || (num == ULONG_MAX)) {
-           rpmError(RPMERR_BADSPEC, "Bad arg to %%setup %c: %s", num, optArg);
-           free(argv);
-           freeStringBuf(before);
-           freeStringBuf(after);
-           poptFreeContext(optCon);
-           return(RPMERR_BADSPEC);
+       if (what & RPMBUILD_BUILD) {
+           if ((rc = doScript(spec, RPMBUILD_BUILD, NULL, NULL, test))) {
+               return rc;
+           }
        }
-
-       chptr = do_untar(spec, num, quietly);
-       if (!chptr) return 1;
-
-       if (arg == 'a')
-           appendLineStringBuf(after, chptr);
-       else
-           appendLineStringBuf(before, chptr);
-    }
-
-    if (arg < -1) {
-       rpmError(RPMERR_BADSPEC, "Bad %%setup option %s: %s",
-               poptBadOption(optCon, POPT_BADOPTION_NOALIAS), 
-               poptStrerror(arg));
-       free(argv);
-       freeStringBuf(before);
-       freeStringBuf(after);
-       poptFreeContext(optCon);
-       return(RPMERR_BADSPEC);
-    }
-
-    if (dirName) {
-       strcpy(build_subdir, dirName);
-    } else {
-       strcpy(build_subdir, spec->name);
-       strcat(build_subdir, "-");
-       /* We should already have a version field */
-       headerGetEntry(spec->packages->header, RPMTAG_VERSION, NULL,
-                (void *) &version, NULL);
-       strcat(build_subdir, version);
-    }
-    
-    free(argv);
-    poptFreeContext(optCon);
-
-    /* cd to the build dir */
-    sprintf(buf, "cd %s", rpmGetVar(RPMVAR_BUILDDIR));
-    appendLineStringBuf(sb, buf);
-    
-    /* delete any old sources */
-    if (!leaveDirs) {
-       sprintf(buf, "rm -rf %s", build_subdir);
-       appendLineStringBuf(sb, buf);
-    }
-
-    /* if necessary, create and cd into the proper dir */
-    if (createDir) {
-       sprintf(buf, "mkdir -p %s\ncd %s", build_subdir, build_subdir);
-       appendLineStringBuf(sb, buf);
-    }
-
-    /* do the default action */
-    if (!createDir && !skipDefaultAction) {
-       chptr = do_untar(spec, 0, quietly);
-       if (!chptr) return 1;
-       appendLineStringBuf(sb, chptr);
-    }
-
-    appendStringBuf(sb, getStringBuf(before));
-    freeStringBuf(before);
-
-    if (!createDir) {
-       sprintf(buf, "cd %s", build_subdir);
-       appendLineStringBuf(sb, buf);
-    }
-
-    if (createDir && !skipDefaultAction) {
-       chptr = do_untar(spec, 0, quietly);
-       if (!chptr) return 1;
-       appendLineStringBuf(sb, chptr);
-    }
-    
-    appendStringBuf(sb, getStringBuf(after));
-    freeStringBuf(after);
-
-    /* clean up permissions etc */
-    if (!geteuid()) {
-       appendLineStringBuf(sb, "chown -R root .");
-       appendLineStringBuf(sb, "chgrp -R root .");
-    }
-
-    if (rpmGetVar(RPMVAR_FIXPERMS)) {
-       appendStringBuf(sb, "chmod -R ");
-       appendStringBuf(sb, rpmGetVar(RPMVAR_FIXPERMS));
-       appendLineStringBuf(sb, " .");
-    }
-    
-    return 0;
-}
-
-int isCompressed(char *file)
-{
-    int fd;
-    unsigned char magic[4];
-
-    fd = open(file, O_RDONLY);
-    read(fd, magic, 4);
-    close(fd);
-
-    if (((magic[0] == 0037) && (magic[1] == 0213)) ||  /* gzip */
-       ((magic[0] == 0037) && (magic[1] == 0236)) ||  /* old gzip */
-       ((magic[0] == 0037) && (magic[1] == 0036)) ||  /* pack */
-       ((magic[0] == 0037) && (magic[1] == 0240)) ||  /* SCO lzh */
-       ((magic[0] == 0037) && (magic[1] == 0235)) ||  /* compress */
-       ((magic[0] == 0120) && (magic[1] == 0113) &&
-        (magic[2] == 0003) && (magic[3] == 0004))     /* pkzip */
-       ) {
-       return 1;
-    }
-
-    return 0;
-}
-
-static char *do_untar(Spec spec, int c, int quietly)
-{
-    static char buf[1024];
-    char file[1024];
-    char *s, *taropts;
-    struct sources *sp;
-
-    s = NULL;
-    sp = spec->sources;
-    while (sp) {
-       if ((sp->ispatch == 0) && (sp->num == c)) {
-           s = sp->source;
-           break;
+       if (what & RPMBUILD_INSTALL) {
+           if ((rc = doScript(spec, RPMBUILD_INSTALL, NULL, NULL, test))) {
+               return rc;
+           }
        }
-       sp = sp->next;
-    }
-    if (! s) {
-       rpmError(RPMERR_BADSPEC, "No source number %d", c);
-       return NULL;
-    }
-
-    sprintf(file, "%s/%s", rpmGetVar(RPMVAR_SOURCEDIR), s);
-    taropts = (rpmIsVerbose() && !quietly ? "-xvvf" : "-xf");
-    
-    if (isCompressed(file)) {
-       sprintf(buf,
-               "%s -dc %s | tar %s -\n"
-               "if [ $? -ne 0 ]; then\n"
-               "  exit $?\n"
-               "fi",
-               rpmGetVar(RPMVAR_GZIPBIN), file, taropts);
-    } else {
-       sprintf(buf, "tar %s %s", taropts, file);
-    }
 
-    return buf;
-}
-
-static char *do_patch(Spec spec, int c, int strip, char *db,
-                     int reverse, int removeEmpties)
-{
-    static char buf[1024];
-    char file[1024];
-    char args[1024];
-    char *s;
-    struct sources *sp;
-
-    s = NULL;
-    sp = spec->sources;
-    while (sp) {
-       if ((sp->ispatch == 1) && (sp->num == c)) {
-           s = sp->source;
-           break;
+       if (what & RPMBUILD_PACKAGESOURCE) {
+           if ((rc = processSourceFiles(spec))) {
+               return rc;
+           }
        }
-       sp = sp->next;
-    }
-    if (! s) {
-       rpmError(RPMERR_BADSPEC, "No patch number %d", c);
-       return NULL;
-    }
-
-    sprintf(file, "%s/%s", rpmGetVar(RPMVAR_SOURCEDIR), s);
-
-    args[0] = '\0';
-    if (db) {
-#if HAVE_OLDPATCH_21 == 0
-       strcat(args, "-b ");
-#endif
-       strcat(args, "--suffix ");
-       strcat(args, db);
-    }
-    if (reverse) {
-       strcat(args, " -R");
-    }
-    if (removeEmpties) {
-       strcat(args, " -E");
-    }
-
-    if (isCompressed(file)) {
-       sprintf(buf,
-               "echo \"Patch #%d:\"\n"
-               "%s -dc %s | patch -p%d %s -s\n"
-               "if [ $? -ne 0 ]; then\n"
-               "  exit $?\n"
-               "fi",
-               c, rpmGetVar(RPMVAR_GZIPBIN), file, strip, args);
-    } else {
-       sprintf(buf,
-               "echo \"Patch #%d:\"\n"
-               "patch -p%d %s -s < %s", c, strip, args, file);
-    }
 
-    return buf;
-}
-
-static int doPatchMacro(Spec spec, StringBuf sb, char *line)
-{
-    char *opt_b;
-    int opt_P, opt_p, opt_R, opt_E;
-    char *s, *s1;
-    char buf[1024];
-    int patch_nums[1024];  /* XXX - we can only handle 1024 patches! */
-    int patch_index, x;
-
-    opt_P = opt_p = opt_R = opt_E = 0;
-    opt_b = NULL;
-    patch_index = 0;
-
-    if (! strchr(" \t\n", line[6])) {
-       /* %patchN */
-       sprintf(buf, "%%patch -P %s", line + 6);
-    } else {
-       strcpy(buf, line);
-    }
-    
-    strtok(buf, " \t\n");  /* remove %patch */
-    while ((s = strtok(NULL, " \t\n"))) {
-       if (!strcmp(s, "-P")) {
-           opt_P = 1;
-       } else if (!strcmp(s, "-R")) {
-           opt_R = 1;
-       } else if (!strcmp(s, "-E")) {
-           opt_E = 1;
-       } else if (!strcmp(s, "-b")) {
-           /* orig suffix */
-           opt_b = strtok(NULL, " \t\n");
-           if (! opt_b) {
-               rpmError(RPMERR_BADSPEC, "Need arg to %%patch -b");
-               return(RPMERR_BADSPEC);
+       if ((what & RPMBUILD_INSTALL) || (what & RPMBUILD_PACKAGEBINARY) ||
+           (what & RPMBUILD_FILECHECK)) {
+           if ((rc = processBinaryFiles(spec, what & RPMBUILD_INSTALL))) {
+               return rc;
            }
-       } else if (!strncmp(s, "-p", 2)) {
-           /* unfortunately, we must support -pX */
-           if (! strchr(" \t\n", s[2])) {
-               s = s + 2;
-           } else {
-               s = strtok(NULL, " \t\n");
-               if (! s) {
-                   rpmError(RPMERR_BADSPEC, "Need arg to %%patch -p");
-                   return(RPMERR_BADSPEC);
-               }
+       }
+
+       if (what & RPMBUILD_PACKAGESOURCE) {
+           if ((rc = packageSources(spec))) {
+               return rc;
            }
-           s1 = NULL;
-           opt_p = strtoul(s, &s1, 10);
-           if ((*s1) || (s1 == s) || (opt_p == ULONG_MAX)) {
-               rpmError(RPMERR_BADSPEC, "Bad arg to %%patch -p: %s", s);
-               return(RPMERR_BADSPEC);
+       }
+       if (what & RPMBUILD_PACKAGEBINARY) {
+           if ((rc = packageBinaries(spec))) {
+               return rc;
            }
-       } else {
-           /* Must be a patch num */
-           if (patch_index == 1024) {
-               rpmError(RPMERR_BADSPEC, "Too many patches!");
-               return(RPMERR_BADSPEC);
+       }
+       
+       if (what & RPMBUILD_CLEAN) {
+           if ((rc = doScript(spec, RPMBUILD_CLEAN, NULL, NULL, test))) {
+               return rc;
            }
-           s1 = NULL;
-           patch_nums[patch_index] = strtoul(s, &s1, 10);
-           if ((*s1) || (s1 == s) || (patch_nums[patch_index] == ULONG_MAX)) {
-               rpmError(RPMERR_BADSPEC, "Bad arg to %%patch: %s", s);
-               return(RPMERR_BADSPEC);
+       }
+       if (what & RPMBUILD_RMBUILD) {
+           if ((rc = doScript(spec, RPMBUILD_RMBUILD, NULL, NULL, test))) {
+               return rc;
            }
-           patch_index++;
        }
     }
 
-    /* All args processed */
-
-    if (! opt_P) {
-       s = do_patch(spec, 0, opt_p, opt_b, opt_R, opt_E);
-       if (! s) {
-           return 1;
-       }
-       appendLineStringBuf(sb, s);
+    if (what & RPMBUILD_RMSOURCE) {
+       doRmSource(spec);
     }
 
-    x = 0;
-    while (x < patch_index) {
-       s = do_patch(spec, patch_nums[x], opt_p, opt_b, opt_R, opt_E);
-       if (! s) {
-           return 1;
-       }
-       appendLineStringBuf(sb, s);
-       x++;
-    }
-    
     return 0;
 }
 
-static int checkSources(Spec s)
+int doScript(Spec spec, int what, char *name, StringBuf sb, int test)
 {
-    struct sources *source;
-    struct PackageRec *package;
-    char buf[1024];
-
-    /* Check that we can access all the sources */
-    source = s->sources;
-    while (source) {
-       sprintf(buf, "%s/%s", rpmGetVar(RPMVAR_SOURCEDIR), source->source);
-       if (access(buf, R_OK)) {
-           rpmError(RPMERR_BADSPEC, "missing source or patch: %s", buf);
-           return RPMERR_BADSPEC;
-       }
-       source = source->next;
+    int fd;
+    FILE *f;
+    char *scriptName;
+    int pid;
+    int status;
+    
+    switch (what) {
+      case RPMBUILD_PREP:
+       name = "%prep";
+       sb = spec->prep;
+       break;
+      case RPMBUILD_BUILD:
+       name = "%build";
+       sb = spec->build;
+       break;
+      case RPMBUILD_INSTALL:
+       name = "%install";
+       sb = spec->install;
+       break;
+      case RPMBUILD_CLEAN:
+       name = "%clean";
+       sb = spec->clean;
+       break;
+      case RPMBUILD_RMBUILD:
+       name = "--clean";
+       break;
+      case RPMBUILD_STRINGBUF:
+       break;
+    }
+    if ((what != RPMBUILD_RMBUILD) && !sb) {
+       return 0;
     }
-
-    /* ... and icons */
-    package = s->packages;
-    while (package) {
-       if (package->icon) {
-           sprintf(buf, "%s/%s", rpmGetVar(RPMVAR_SOURCEDIR), package->icon);
-           if (access(buf, R_OK)) {
-               rpmError(RPMERR_BADSPEC, "missing icon: %s", buf);
-               return RPMERR_BADSPEC;
-           }
-       }
-       package = package->next;
+    
+    if (makeTempFile(NULL, &scriptName, &fd)) {
+       rpmError(RPMERR_SCRIPT, "Unable to open temp file");
+       return RPMERR_SCRIPT;
     }
+    f = fdopen(fd, "w");
     
-    return 0;
-}
-
-int execPrep(Spec s, int really_exec, int test)
-{
-    char **lines, **lines1, *p;
-    StringBuf out;
-    int res;
-
-    if (checkSources(s)) {
-       return 1;
+    if (writeVars(spec, f)) {
+       fclose(f);
+       FREE(scriptName);
+       return RPMERR_SCRIPT;
     }
-    out = newStringBuf();
     
-    p = getStringBuf(s->prep);
-    lines = splitString(p, strlen(p), '\n');
-    lines1 = lines;
-    while (*lines) {
-       if (! strncmp(*lines, "%setup", 6)) {
-           if (doSetupMacro(s, out, *lines)) {
-               return 1;
-           }
-       } else if (! strncmp(*lines, "%patch", 6)) {
-           if (doPatchMacro(s, out, *lines)) {
-               return 1;
-           }
-       } else {
-           appendLineStringBuf(out, *lines);
-       }
-       lines++;
+    fprintf(f, rpmIsVerbose() ? "set -x\n\n" : "exec > /dev/null\n\n");
+    fprintf(f, "umask 022\n");
+    fprintf(f, "cd %s\n", rpmGetVar(RPMVAR_BUILDDIR));
+    if (what != RPMBUILD_PREP && what != RPMBUILD_RMBUILD) {
+       fprintf(f, "cd %s\n", spec->buildSubdir);
+    }
+    if (what == RPMBUILD_RMBUILD) {
+       fprintf(f, "rm -rf %s\n", spec->buildSubdir);
+    } else {
+       fprintf(f, "%s", getStringBuf(sb));
     }
+    fprintf(f, "\nexit 0\n");
+    
+    fclose(f);
+    chmod(scriptName, 0600);
 
-    freeSplitString(lines1);
-    res = 0;
-    if (really_exec) {
-       res = execPart(s, getStringBuf(out), "%prep", 0, test);
+    if (test) {
+       FREE(scriptName);
+       return 0;
     }
-    freeStringBuf(out);
-    return res;
-}
-
-int execBuild(Spec s, int test)
-{
-    return execPart(s, getStringBuf(s->build), "%build", 1, test);
-}
-
-int execInstall(Spec s, int test)
-{
-    int res;
-
-    if ((res = execPart(s, getStringBuf(s->install), "%install", 1, test))) {
-       return res;
+    
+    rpmMessage(RPMMESS_NORMAL, "Executing: %s\n", name);
+    if (!(pid = fork())) {
+       execl("/bin/sh", "/bin/sh", "-e", scriptName, scriptName, NULL);
+       rpmError(RPMERR_SCRIPT, "Exec of %s failed (%s)",
+                scriptName, name);
+       FREE(scriptName);
+       return RPMERR_SCRIPT;
     }
-    if ((res = finish_filelists(s))) {
-       return res;
+    wait(&status);
+    if (! WIFEXITED(status) || WEXITSTATUS(status)) {
+       rpmError(RPMERR_SCRIPT, "Bad exit status from %s (%s)",
+                scriptName, name);
+       FREE(scriptName);
+       return RPMERR_SCRIPT;
     }
-    return execPart(s, getStringBuf(s->doc), "special doc", 1, test);
-}
+    
+    unlink(scriptName);
+    FREE(scriptName);
 
-int execClean(Spec s)
-{
-    return execPart(s, getStringBuf(s->clean), "%clean", 1, 0);
+    return 0;
 }
 
-int verifyList(Spec s)
+static int writeVars(Spec spec, FILE *f)
 {
-    int res;
+    char *arch, *os, *s;
+    
+    rpmGetArchInfo(&arch, NULL);
+    rpmGetOsInfo(&os, NULL);
 
-    if ((res = finish_filelists(s))) {
-       return res;
-    }
-    return packageBinaries(s, NULL, PACK_NOPACKAGE);
+    fprintf(f, "export RPM_SOURCE_DIR=\"%s\"\n", rpmGetVar(RPMVAR_SOURCEDIR));
+    fprintf(f, "export RPM_BUILD_DIR=\"%s\"\n", rpmGetVar(RPMVAR_BUILDDIR));
+    fprintf(f, "export RPM_DOC_DIR=\"%s\"\n", spec->docDir);
+    fprintf(f, "export RPM_OPT_FLAGS=\"%s\"\n", rpmGetVar(RPMVAR_OPTFLAGS));
+    fprintf(f, "export RPM_ARCH=\"%s\"\n", arch);
+    fprintf(f, "export RPM_OS=\"%s\"\n", os);
+
+    if (spec->buildRoot) {
+       fprintf(f, "export RPM_BUILD_ROOT=\"%s\"\n", spec->buildRoot);
+       /* This could really be checked internally */
+       fprintf(f, "if [ -z \"$RPM_BUILD_ROOT\" -o -z \"`echo $RPM_BUILD_ROOT | sed -e 's#/##g'`\" ]; then\n");
+       fprintf(f, "  echo 'Warning: Spec contains BuildRoot: tag that is either empty or is set to \"/\"'\n");
+       fprintf(f, "  exit 1\n");
+       fprintf(f, "fi\n");
+    }
+
+    headerGetEntry(spec->packages->header, RPMTAG_NAME,
+                  NULL, (void **)&s, NULL);
+    fprintf(f, "export RPM_PACKAGE_NAME=\"%s\"\n", s);
+    headerGetEntry(spec->packages->header, RPMTAG_VERSION,
+                  NULL, (void **)&s, NULL);
+    fprintf(f, "export RPM_PACKAGE_VERSION=\"%s\"\n", s);
+    headerGetEntry(spec->packages->header, RPMTAG_RELEASE,
+                  NULL, (void **)&s, NULL);
+    fprintf(f, "export RPM_PACKAGE_RELEASE=\"%s\"\n", s);
+    
+    return 0;
 }
 
-int doBuild(Spec s, int flags, char *passPhrase)
+static void doRmSource(Spec spec)
 {
-    int test;
-
-    test = flags & RPMBUILD_TEST;
-
-    strcpy(build_subdir, ".");
-
-    if (s->buildArch) {
-       rpmSetMachine(s->buildArch, NULL);
-    }
-
-    /* We always need to parse the %prep section */
-    if (execPrep(s, (flags & RPMBUILD_PREP), test)) {
-       return 1;
-    }
-
-    if (flags & RPMBUILD_LIST)
-       return verifyList(s);
-
-    if (flags & RPMBUILD_BUILD) {
-       if (execBuild(s, test)) {
-           return 1;
-       }
-    }
-
-    if (flags & RPMBUILD_INSTALL) {
-       if (execInstall(s, test)) {
-           return 1;
-       }
-    }
-
-    if (test) {
-       return 0;
-    }
+    struct Source *p;
+    char buf[BUFSIZ];
     
-    markBuildTime();
-    
-    if (flags & RPMBUILD_BINARY) {
-       if (packageBinaries(s, passPhrase, PACK_PACKAGE)) {
-           return 1;
-       }
-       if (execClean(s)) {
-           return 1;
-       }
-    }
+    unlink(spec->specFile);
 
-    if (flags & RPMBUILD_SOURCE) {
-       if (packageSource(s, passPhrase)) {
-           return 1;
+    p = spec->sources;
+    while (p) {
+       if (! (p->flags & RPMBUILD_ISNO)) {
+           sprintf(buf, "%s/%s", rpmGetVar(RPMVAR_SOURCEDIR), p->source);
+           unlink(buf);
        }
+       p = p->next;
     }
-
-    if (flags & RPMBUILD_SWEEP) {
-       doSweep(s);
-    }
-
-    if (flags & RPMBUILD_RMSOURCE) {
-       doRmSource(s);
-    }
-
-    return 0;
 }
index aa984a5..62c27fe 100644 (file)
@@ -1,25 +1,16 @@
-#ifndef _BUILD_H_
-#define _BUILD_H_
-
 #include "spec.h"
 
-int doBuild(Spec s, int flags, char *passPhrase);
-int execPrep(Spec s, int really_exec, int test);
-int execBuild(Spec s, int test);
-int execInstall(Spec s, int test);
-int execClean(Spec s);
-int verifyList(Spec s);
-
-extern char build_subdir[1024];
+#define RPMBUILD_PREP             (1 << 0)
+#define RPMBUILD_BUILD            (1 << 1)
+#define RPMBUILD_INSTALL          (1 << 2)
+#define RPMBUILD_CLEAN            (1 << 3)
+#define RPMBUILD_FILECHECK        (1 << 4)
+#define RPMBUILD_PACKAGESOURCE    (1 << 5)
+#define RPMBUILD_PACKAGEBINARY    (1 << 6)
+#define RPMBUILD_RMSOURCE         (1 << 7)
+#define RPMBUILD_RMBUILD          (1 << 8)
+#define RPMBUILD_STRINGBUF        (1 << 9) /* only for doScript() */
 
-#define RPMBUILD_PREP        1
-#define RPMBUILD_BUILD      (1 << 1)
-#define RPMBUILD_INSTALL    (1 << 2)
-#define RPMBUILD_BINARY     (1 << 3)
-#define RPMBUILD_SOURCE     (1 << 4)
-#define RPMBUILD_SWEEP      (1 << 5)
-#define RPMBUILD_LIST       (1 << 6)
-#define RPMBUILD_RMSOURCE   (1 << 7)
-#define RPMBUILD_TEST       (1 << 8)
+int buildSpec(Spec spec, int what, int test);
 
-#endif _BUILD_H_
+int doScript(Spec spec, int what, char *name, StringBuf sb, int test);
index 1159ff6..fe2023b 100644 (file)
-/* RPM - Copyright (C) 1995 Red Hat Software
- * 
- * prepack.c - routines for packaging
- */
-
-#include "miscfn.h"
-
-#include <stdlib.h>
-#include <unistd.h>
 #include <string.h>
-#include <sys/stat.h>
+#include <malloc.h>
+#include <stdlib.h>
+#include <glob.h>
 #include <time.h>
 
 #include "spec.h"
-#include "specP.h"
-#include "stringbuf.h"
-#include "build.h"
-#include "messages.h"
-#include "md5.h"
+#include "package.h"
+#include "rpmlib.h"
+#include "misc.h"
+#include "lib/misc.h"
 #include "myftw.h"
+#include "lib/cpio.h"
 #include "header.h"
-#include "files.h"
+#include "md5.h"
 #include "names.h"
-#include "rpmlead.h"
-#include "rpmlib.h"
-#include "misc.h"
+#include "messages.h"
 #include "macro.h"
+#include "build.h"
 
-#define BINARY_HEADER 0
-#define SOURCE_HEADER 1
-
-struct file_entry {
-    char file[1024];
-    int isdoc;
-    int conf;
-    int isspecfile;
-    int isghost;
-    int verify_flags;
-    char *uname;  /* reference -- do not free */
-    char *gname;  /* reference -- do not free */
-    struct stat statbuf;
-    struct file_entry *next;
+#define MAXDOCDIR 1024
+
+struct FileListRec {
+    char *diskName; /* get file from here       */
+    char *fileName; /* filename in cpio archive */
+    mode_t mode;
+    uid_t uid;
+    gid_t gid;
+    dev_t device;
+    ino_t inode;
+    char *uname;
+    char *gname;
+    int flags;
+    int verifyFlags;
+    int size;
+    int mtime;
+    dev_t rdev;
+    char *lang;
 };
 
-static int add_file(struct file_entry **festack, const char *name,
-                   int isdoc, int isconf, int isdir, int isghost,
-                   int verify_flags,
-                   char *Pmode, char *Uname, char *Gname, char *prefix);
-static int compare_fe(const void *ap, const void *bp);
-static int add_file_aux(const char *file, struct stat *sb, int flag);
-static int glob_error(const char *foo, int bar);
-static int glob_pattern_p (char *pattern);
-static int parseForVerify(char *buf, int *verify_flags);
-static int parseForConfig(char *buf, int *conf);
-static int parseForAttr(char *origbuf, char **currPmode,
-                       char **currUname, char **currGname);
-
-static void resetDocdir(void);
-static void addDocdir(char *dirname);
-static int isDoc(char *filename);
+struct AttrRec {
+    char *PmodeString;
+    int Pmode;
+    char *PdirmodeString;
+    int Pdirmode;
+    char *Uname;
+    char *Gname;
+};
 
-static int processFileListFailed;
+struct FileList {
+    char *buildRoot;
+    char *prefix;
 
-static void parseForDocFiles(struct PackageRec *package, char *line)
-{
-    if (! (line = strstr(line, "%doc"))) {
-       return;
-    }
+    int fileCount;
+    int totalFileSize;
+    int processingFailed;
 
-    line += 4;
-    if ((*line != ' ') && (*line != '\t')) {
-       return;
-    }
-    line += strspn(line, " \t\n");
-    if ((! *line) || (*line == '/')) {
-       return;
-    }
+    int passedSpecialDoc;
+    int isSpecialDoc;
     
-    appendLineStringBuf(package->doc, "mkdir -p $DOCDIR");
-    appendStringBuf(package->doc, "cp -pr ");
-    appendStringBuf(package->doc, line);
-    appendLineStringBuf(package->doc, " $DOCDIR");
-}
+    int isDir;
+    int inFtw;
+    int currentFlags;
+    int currentVerifyFlags;
+    struct AttrRec current;
+    struct AttrRec def;
+    int defVerifyFlags;
+    char *currentLang;
+
+    /* Hard coded limit of MAXDOCDIR docdirs.         */
+    /* If you break it you are doing something wrong. */
+    char *docDirs[MAXDOCDIR];
+    int docDirCount;
+    
+    struct FileListRec *fileList;
+    int fileListRecsAlloced;
+    int fileListRecsUsed;
+};
+
+static int processPackageFiles(Spec spec, Package pkg, int installSpecialDoc);
+static void freeFileList(struct FileListRec *fileList, int count);
+static int compareFileListRecs(const void *ap, const void *bp);
+static int isDoc(struct FileList *fl, char *fileName);
+static int processBinaryFile(Package pkg, struct FileList *fl, char *fileName);
+static int addFile(struct FileList *fl, char *name, struct stat *statp);
+static int parseForSimple(Spec spec, Package pkg, char *buf,
+                         struct FileList *fl, char **fileName);
+static int parseForVerify(char *buf, struct FileList *fl);
+static int parseForLang(char *buf, struct FileList *fl);
+static int parseForAttr(char *buf, struct FileList *fl);
+static int parseForConfig(char *buf, struct FileList *fl);
+static int myGlobPatternP(char *pattern);
+static int glob_error(const char *foo, int bar);
+static void timeCheck(int tc, Header h);
+static void genCpioListAndHeader(struct FileList *fl,
+                                struct cpioFileMapping **cpioList,
+                                int *cpioCount, Header h, int isSrc);
 
-int finish_filelists(Spec spec)
+int processSourceFiles(Spec spec)
 {
-    char buf[1024];
-    FILE *file;
-    struct PackageRec *pr = spec->packages;
-    char *s, **files, **line;
-    char *version, *release, *packageVersion, *docs, *name;
-
-    headerGetEntry(spec->packages->header, RPMTAG_VERSION, NULL,
-            (void *) &version, NULL);
-    headerGetEntry(spec->packages->header, RPMTAG_RELEASE, NULL,
-            (void *) &release, NULL);
-    
-    while (pr) {
-       if (pr->fileFile) {
-           sprintf(buf, "%s/%s/%s", rpmGetVar(RPMVAR_BUILDDIR),
-                   build_subdir, pr->fileFile);
-           rpmMessage(RPMMESS_DEBUG, "Reading file names from: %s\n", buf);
-           if ((file = fopen(buf, "r")) == NULL) {
-               rpmError(RPMERR_BADSPEC, "unable to open filelist: %s\n", buf);
-               return(RPMERR_BADSPEC);
+    struct Source *srcPtr;
+    char buf[BUFSIZ];
+    StringBuf sourceFiles;
+    int x, isSpec = 1;
+    struct FileList fl;
+    char *s, **files, **fp, *fn;
+    struct stat sb;
+    HeaderIterator hi;
+    int tag, type, count;
+    void * ptr;
+
+    sourceFiles = newStringBuf();
+    spec->sourceHeader = headerNew();
+
+    /* Only specific tags are added to the source package header */
+    hi = headerInitIterator(spec->packages->header);
+    while (headerNextIterator(hi, &tag, &type, &ptr, &count)) {
+       switch (tag) {
+         case RPMTAG_NAME:
+         case RPMTAG_VERSION:
+         case RPMTAG_RELEASE:
+         case RPMTAG_SERIAL:
+         case RPMTAG_SUMMARY:
+         case RPMTAG_DESCRIPTION:
+         case RPMTAG_PACKAGER:
+         case RPMTAG_DISTRIBUTION:
+         case RPMTAG_VENDOR:
+         case RPMTAG_COPYRIGHT:
+         case RPMTAG_GROUP:
+         case RPMTAG_OS:
+         case RPMTAG_ARCH:
+         case RPMTAG_CHANGELOGTIME:
+         case RPMTAG_CHANGELOGNAME:
+         case RPMTAG_CHANGELOGTEXT:
+         case RPMTAG_URL:
+           headerAddEntry(spec->sourceHeader, tag, type, ptr, count);
+           break;
+         default:
+           /* do not copy */
+           break;
+       }
+       if (type == RPM_STRING_ARRAY_TYPE) {
+           FREE(ptr);
+       }
+    }
+    headerFreeIterator(hi);
+
+    /* Construct the file list and source entries */
+    appendLineStringBuf(sourceFiles, spec->specFile);
+    srcPtr = spec->sources;
+    while (srcPtr) {
+       if (srcPtr->flags & RPMBUILD_ISSOURCE) {
+           headerAddOrAppendEntry(spec->sourceHeader, RPMTAG_SOURCE,
+                                  RPM_STRING_ARRAY_TYPE, &srcPtr->source, 1);
+           if (srcPtr->flags & RPMBUILD_ISNO) {
+               headerAddOrAppendEntry(spec->sourceHeader, RPMTAG_NOSOURCE,
+                                      RPM_INT32_TYPE, &srcPtr->num, 1);
            }
-           while (fgets(buf, sizeof(buf), file)) {
-               expandMacros(buf);
-               appendStringBuf(pr->filelist, buf);
+       }
+       if (srcPtr->flags & RPMBUILD_ISPATCH) {
+           headerAddOrAppendEntry(spec->sourceHeader, RPMTAG_PATCH,
+                                  RPM_STRING_ARRAY_TYPE, &srcPtr->source, 1);
+           if (srcPtr->flags & RPMBUILD_ISNO) {
+               headerAddOrAppendEntry(spec->sourceHeader, RPMTAG_NOPATCH,
+                                      RPM_INT32_TYPE, &srcPtr->num, 1);
            }
-           fclose(file);
        }
+       sprintf(buf, "%s%s/%s",
+               srcPtr->flags & RPMBUILD_ISNO ? "!" : "",
+               rpmGetVar(RPMVAR_SOURCEDIR), srcPtr->source);
+       appendLineStringBuf(sourceFiles, buf);
+       srcPtr = srcPtr->next;
+    }
+
+    spec->sourceCpioList = NULL;
+    spec->sourceCpioCount = 0;
 
-       /* parse for %doc wierdness */
-       s = getStringBuf(pr->filelist);
-       files = splitString(s, strlen(s), '\n');
-       line = files;
-       while (*line) {
-           parseForDocFiles(pr, *line);
-           line++;
+    fl.fileList = malloc((spec->numSources + 1) * sizeof(struct FileListRec));
+    fl.processingFailed = 0;
+    fl.fileListRecsUsed = 0;
+    fl.totalFileSize = 0;
+    fl.prefix = NULL;
+
+    s = getStringBuf(sourceFiles);
+    files = splitString(s, strlen(s), '\n');
+    fp = files;
+
+    /* The first source file is the spec file */
+    x = 0;
+    while (*fp) {
+       s = *fp;
+       SKIPSPACE(s);
+       if (! *s) {
+           fp++; continue;
        }
-       freeSplitString(files);
 
-       /* Handle subpackage version overrides */
-        if (!headerGetEntry(pr->header, RPMTAG_VERSION, NULL,
-                     (void *) &packageVersion, NULL)) {
-            packageVersion = version;
+       fl.fileList[x].flags = isSpec ? RPMFILE_SPECFILE : 0;
+       /* files with leading ! are no source files */
+       if (*s == '!') {
+           fl.fileList[x].flags |= RPMFILE_GHOST;
+           s++;
+       }
+       fl.fileList[x].diskName = strdup(s);
+       fn = strrchr(s, '/');
+       if (fn) {
+           fn++;
+       } else {
+           fn = s;
        }
+       fl.fileList[x].fileName = strdup(fn);
+       fl.fileList[x].verifyFlags = RPMVERIFY_ALL;
+       lstat(s, &sb);
+       fl.fileList[x].mode = sb.st_mode;
+       fl.fileList[x].uid = sb.st_uid;
+       fl.fileList[x].gid = sb.st_gid;
+       fl.fileList[x].uname = getUname(sb.st_uid);
+       fl.fileList[x].gname = getGname(sb.st_gid);
+       fl.fileList[x].size = sb.st_size;
+       fl.fileList[x].mtime = sb.st_mtime;
+       fl.fileList[x].rdev = sb.st_rdev;
+       fl.fileList[x].device = sb.st_dev;
+       fl.fileList[x].inode = sb.st_ino;
+       fl.fileList[x].lang = strdup("");
+       
+       fl.totalFileSize += sb.st_size;
+       
+       if (! (fl.fileList[x].uname && fl.fileList[x].gname)) {
+           rpmError(RPMERR_BADSPEC, "Bad owner/group: %s", s);
+           fl.processingFailed = 1;
+       }
+
+       isSpec = 0;
+       fp++;
+       x++;
+    }
+    fl.fileListRecsUsed = x;
+    FREE(files);
 
-       /* Generate the doc script */
-       appendStringBuf(spec->doc, "DOCDIR=$RPM_ROOT_DIR/$RPM_DOC_DIR/");
-       headerGetEntry(pr->header, RPMTAG_NAME, NULL, (void *) &name, NULL);
-       sprintf(buf, "%s-%s", name, packageVersion);
-       appendLineStringBuf(spec->doc, buf);
-       docs = getStringBuf(pr->doc);
-       if (*docs) {
-           appendLineStringBuf(spec->doc, "rm -rf $DOCDIR");
-           appendLineStringBuf(spec->doc, docs);
+    if (! fl.processingFailed) {
+       genCpioListAndHeader(&fl, &(spec->sourceCpioList),
+                            &(spec->sourceCpioCount), spec->sourceHeader, 1);
+    }
+
+    freeStringBuf(sourceFiles);
+    freeFileList(fl.fileList, fl.fileListRecsUsed);
+    return fl.processingFailed;
+}
+
+int processBinaryFiles(Spec spec, int installSpecialDoc)
+{
+    Package pkg;
+    int rc;
+    
+    pkg = spec->packages;
+    while (pkg) {
+       if (!pkg->fileList) {
+           pkg = pkg->next;
+           continue;
+       }
+
+       if ((rc = processPackageFiles(spec, pkg, installSpecialDoc))) {
+           return rc;
        }
 
-       pr = pr->next;
+       pkg = pkg->next;
     }
 
     return 0;
 }
 
-int process_filelist(Header header, struct PackageRec *pr,
-                    StringBuf sb, int *size, char *name,
-                    char *version, char *release, int type,
-                    char *prefix, char *specFile)
+static int processPackageFiles(Spec spec, Package pkg, int installSpecialDoc)
 {
-    char buf[1024];
-    char **files, **fp;
-    struct file_entry *fes, *fest;
-    struct file_entry **file_entry_array;
-    int isdoc, conf, isdir, verify_flags, isghost;
-    char *currPmode=NULL;      /* hold info from %attr() */
-    char *currUname=NULL;      /* hold info from %attr() */
-    char *currGname=NULL;      /* hold info from %attr() */
-    char *filename, *s;
-    char *str;
-    int count = 0;
-    int c, x;
-    glob_t glob_result;
-    int special_doc;
-    int passed_special_doc = 0;
-    int tc;
-    char *tcs;
-    int currentTime;
+    struct FileList fl;
+    char *s, **files, **fp, *fileName;
+    char buf[BUFSIZ];
+    FILE *f;
 
-    processFileListFailed = 0;
-    fes = NULL;
-    *size = 0;
+    struct AttrRec specialDocAttrRec;
+    char *specialDoc = NULL;
+    
+    pkg->cpioList = NULL;
+    pkg->cpioCount = 0;
+
+    if (pkg->fileFile) {
+       sprintf(buf, "%s/%s/%s", rpmGetVar(RPMVAR_BUILDDIR),
+               spec->buildSubdir, pkg->fileFile);
+       if ((f = fopen(buf, "r")) == NULL) {
+           rpmError(RPMERR_BADFILENAME,
+                    "Could not open %%files file: %s", pkg->fileFile);
+           return RPMERR_BADFILENAME;
+       }
+       while (fgets(buf, sizeof(buf), f)) {
+           expandMacros(&spec->macros, buf);
+           appendStringBuf(pkg->fileList, buf);
+       }
+       fclose(f);
+    }
+    
+    /* Init the file list structure */
+    
+    fl.buildRoot = spec->buildRoot ? spec->buildRoot : "";
+    if (headerGetEntry(pkg->header, RPMTAG_DEFAULTPREFIX,
+                      NULL, (void *)&fl.prefix, NULL)) {
+       fl.prefix = strdup(fl.prefix);
+    } else {
+       fl.prefix = NULL;
+    }
 
-    resetDocdir();
+    fl.fileCount = 0;
+    fl.totalFileSize = 0;
+    fl.processingFailed = 0;
 
-    str = getStringBuf(sb);
-    files = splitString(str, strlen(str), '\n');
+    fl.passedSpecialDoc = 0;
+    
+    fl.current.PmodeString = NULL;
+    fl.current.PdirmodeString = NULL;
+    fl.current.Uname = NULL;
+    fl.current.Gname = NULL;
+    fl.def.PmodeString = NULL;
+    fl.def.PdirmodeString = NULL;
+    fl.def.Uname = NULL;
+    fl.def.Gname = NULL;
+    fl.currentLang = NULL;
+
+    fl.defVerifyFlags = RPMVERIFY_ALL;
+
+    fl.docDirCount = 0;
+    fl.docDirs[fl.docDirCount++] = strdup("/usr/doc");
+    fl.docDirs[fl.docDirCount++] = strdup("/usr/man");
+    fl.docDirs[fl.docDirCount++] = strdup("/usr/info");
+    fl.docDirs[fl.docDirCount++] = strdup("/usr/X11R6/man");
+    fl.docDirs[fl.docDirCount++] = strdup(spec->docDir);
+    
+    fl.fileList = NULL;
+    fl.fileListRecsAlloced = 0;
+    fl.fileListRecsUsed = 0;
+
+    s = getStringBuf(pkg->fileList);
+    files = splitString(s, strlen(s), '\n');
     fp = files;
 
     while (*fp) {
-       strcpy(buf, *fp);  /* temp copy */
-       isdoc = 0;
-       special_doc = 0;
-       conf = 0;
-       isdir = 0;
-       isghost = 0;
-       if (currPmode) {
-           free (currPmode);
-           currPmode = NULL;
-       }
-       if (currUname) {
-           free (currUname);
-           currUname = NULL;
-       }
-       if (currGname) {
-           free (currGname);
-           currGname = NULL;
-       }
-       verify_flags = RPMVERIFY_ALL;
-       filename = NULL;
-
-       /* First preparse buf for %verify() */
-       if (parseForVerify(buf, &verify_flags)) {
-           processFileListFailed = 1;
+       s = *fp;
+       SKIPSPACE(s);
+       if (! *s) {
            fp++; continue;
        }
+       fileName = NULL;
+       strcpy(buf, s);
        
-       /* Next parse for %attr() */
-       if (parseForAttr(buf, &currPmode, &currUname, &currGname)) {
-           processFileListFailed = 1;
-           fp++; continue;
+       /* Reset for a new line in %files */
+       fl.isDir = 0;
+       fl.inFtw = 0;
+       fl.currentFlags = 0;
+       fl.currentVerifyFlags = fl.defVerifyFlags;
+       fl.current.Pmode = fl.def.Pmode;
+       fl.current.Pdirmode = fl.def.Pdirmode;
+       fl.isSpecialDoc = 0;
+       FREE(fl.current.PmodeString);
+       FREE(fl.current.PdirmodeString);
+       FREE(fl.current.Uname);
+       FREE(fl.current.Gname);
+       FREE(fl.currentLang);
+       if (fl.def.PmodeString) {
+           fl.current.PmodeString = strdup(fl.def.PmodeString);
+       }
+       if (fl.def.PdirmodeString) {
+           fl.current.PdirmodeString = strdup(fl.def.PdirmodeString);
+       }
+       if (fl.def.Uname) {
+           fl.current.Uname = strdup(fl.def.Uname);
+       }
+       if (fl.def.Gname) {
+           fl.current.Gname = strdup(fl.def.Gname);
        }
 
-       /* Then parse for %config or %config() */
-       if (parseForConfig(buf, &conf)) {
-           processFileListFailed = 1;
+       if (parseForVerify(buf, &fl)) {
            fp++; continue;
        }
-
-       s = strtok(buf, " \t\n");
-       while (s) {
-           if (!strcmp(s, "%docdir")) {
-               s = strtok(NULL, " \t\n");
-               addDocdir(s);
-               break;
-           } else if (!strcmp(s, "%doc")) {
-               isdoc = 1;
-           } else if (!strcmp(s, "%dir")) {
-               isdir = 1;
-           } else if (!strcmp(s, "%ghost")) {
-               isghost = 1;
-           } else {
-               if (filename) {
-                   /* We already got a file -- error */
-                   rpmError(RPMERR_BADSPEC,
-                         "Two files on one line: %s", filename);
-                   processFileListFailed = 1;
-               }
-               if (*s != '/') {
-                   if (isdoc) {
-                       special_doc = 1;
-                   } else {
-                       /* not in %doc, does not begin with / -- error */
-                       rpmError(RPMERR_BADSPEC,
-                             "File must begin with \"/\": %s", s);
-                       processFileListFailed = 1;
-                   }
-               } else {
-                   filename = s;
-               }
-           }
-           s = strtok(NULL, " \t\n");
+       if (parseForAttr(buf, &fl)) {
+           fp++; continue;
        }
-       if (special_doc) {
-           if (passed_special_doc) {
-               fp++;
-               continue;
-           } else {
-               if (filename || conf || isdir || isghost) {
-                   rpmError(RPMERR_BADSPEC,
-                         "Can't mix special %%doc with other forms: %s", fp);
-                   processFileListFailed = 1;
-                   fp++; continue;
-               }
-               sprintf(buf, "%s/%s-%s", rpmGetVar(RPMVAR_DEFAULTDOCDIR), 
-                       name, version);
-               filename = buf;
-               passed_special_doc = 1;
-           }
+       if (parseForConfig(buf, &fl)) {
+           fp++; continue;
        }
-       if (! filename) {
-           fp++;
-           continue;
+       if (parseForLang(buf, &fl)) {
+           fp++; continue;
+       }
+       if (parseForSimple(spec, pkg, buf, &fl, &fileName)) {
+           fp++; continue;
+       }
+       if (! fileName) {
+           fp++; continue;
        }
 
-       if (type == RPMLEAD_BINARY) {
-           /* check that file starts with leading "/" */
-           if (*filename != '/') {
-               rpmError(RPMERR_BADSPEC,
-                     "File needs leading \"/\": %s", filename);
-               processFileListFailed = 1;
-               fp++; continue;
+       if (fl.isSpecialDoc) {
+           /* Save this stuff for last */
+           specialDoc = strdup(fileName);
+           specialDocAttrRec = fl.current;
+           if (specialDocAttrRec.PmodeString) {
+               specialDocAttrRec.PmodeString =
+                   strdup(specialDocAttrRec.PmodeString);
            }
-
-           if (glob_pattern_p(filename)) {
-               char fullname[1024];
-
-               if (rpmGetVar(RPMVAR_ROOT)) {
-                   sprintf(fullname, "%s%s", rpmGetVar(RPMVAR_ROOT), filename);
-               } else {
-                   strcpy(fullname, filename);
-               }
-
-               if (glob(fullname, 0, glob_error, &glob_result) ||
-                   (glob_result.gl_pathc < 1)) {
-                   rpmError(RPMERR_BADSPEC, "No matches: %s", fullname);
-                   processFileListFailed = 1;
-                   globfree(&glob_result);
-                   fp++; continue;
-               }
-               
-               c = 0;
-               x = 0;
-               while (x < glob_result.gl_pathc) {
-                   int offset = strlen(rpmGetVar(RPMVAR_ROOT) ? : "");
-                   c += add_file(&fes, &(glob_result.gl_pathv[x][offset]),
-                                 isdoc, conf, isdir, isghost, verify_flags,
-                                 currPmode, currUname, currGname, prefix);
-                   x++;
-               }
-               globfree(&glob_result);
-           } else {
-               c = add_file(&fes, filename, isdoc, conf, isdir, isghost,
-                            verify_flags, currPmode, currUname,
-                            currGname, prefix);
+           if (specialDocAttrRec.PdirmodeString) {
+               specialDocAttrRec.PdirmodeString =
+                   strdup(specialDocAttrRec.PdirmodeString);
            }
-       } else {
-           /* Source package are the simple case */
-           fest = malloc(sizeof(struct file_entry));
-           fest->isdoc = 0;
-           fest->conf = 0;
-           fest->isghost = 0;
-           if (!strcmp(filename, specFile)) {
-               fest->isspecfile = 1;
-           } else {
-               fest->isspecfile = 0;
+           if (specialDocAttrRec.Uname) {
+               specialDocAttrRec.Uname = strdup(specialDocAttrRec.Uname);
            }
-           fest->verify_flags = 0;  /* XXX - something else? */
-           stat(filename, &fest->statbuf);
-           fest->uname = getUname(fest->statbuf.st_uid);
-           fest->gname = getGname(fest->statbuf.st_gid);
-           if (! (fest->uname && fest->gname)) {
-               rpmError(RPMERR_BADSPEC, "Bad owner/group: %s", filename);
-               fest->uname = "";
-               fest->gname = "";
-               processFileListFailed = 1;
+           if (specialDocAttrRec.Gname) {
+               specialDocAttrRec.Gname = strdup(specialDocAttrRec.Gname);
            }
-           strcpy(fest->file, filename);
-           fest->next = fes;
-           fes = fest;
-           c = 1;
-       }
-           
-       if (! c) {
-           rpmError(RPMERR_BADSPEC, "File not found: %s", filename);
-           processFileListFailed = 1;
+       } else {
+           processBinaryFile(pkg, &fl, fileName);
        }
-       count += c;
        
        fp++;
     }
 
-    /* If there are no files, don't add anything to the header */
-    if (count) {
-       char ** fileList;
-       char ** fileMD5List;
-       char ** fileLinktoList;
-       int_32 * fileSizeList;
-       int_32 * fileUIDList;
-       int_32 * fileGIDList;
-       char ** fileUnameList;
-       char ** fileGnameList;
-       int_32 * fileMtimesList;
-       int_32 * fileFlagsList;
-       int_16 * fileModesList;
-       int_16 * fileRDevsList;
-       int_32 * fileVerifyFlagsList;
-
-       fileList = malloc(sizeof(char *) * count);
-       fileLinktoList = malloc(sizeof(char *) * count);
-       fileMD5List = malloc(sizeof(char *) * count);
-       fileSizeList = malloc(sizeof(int_32) * count);
-       fileUIDList = malloc(sizeof(int_32) * count);
-       fileGIDList = malloc(sizeof(int_32) * count);
-       fileUnameList = malloc(sizeof(char *) * count);
-       fileGnameList = malloc(sizeof(char *) * count);
-       fileMtimesList = malloc(sizeof(int_32) * count);
-       fileFlagsList = malloc(sizeof(int_32) * count);
-       fileModesList = malloc(sizeof(int_16) * count);
-       fileRDevsList = malloc(sizeof(int_16) * count);
-       fileVerifyFlagsList = malloc(sizeof(int_32) * count);
-
-       /* Build a reverse sorted file array.  */
-       /* This makes uninstalls a lot easier. */
-       file_entry_array = malloc(sizeof(struct file_entry *) * count);
-       c = 0;
-       fest = fes;
-       while (fest) {
-           file_entry_array[c++] = fest;
-           fest = fest->next;
-       }
-       qsort(file_entry_array, count, sizeof(struct file_entry *),
-             compare_fe);
-               
-       /* Do timecheck */
-       tc = 0;
-       currentTime = time(NULL);
-       if ((tcs = rpmGetVar(RPMVAR_TIMECHECK))) {
-           tc = strtoul(tcs, NULL, 10);
+    /* Now process special doc, if there is one */
+    if (specialDoc) {
+       if (installSpecialDoc) {
+           doScript(spec, RPMBUILD_STRINGBUF, "%doc", pkg->specialDoc, 0);
        }
+       fl.current = specialDocAttrRec;
+       processBinaryFile(pkg, &fl, specialDoc);
+       FREE(specialDoc);
+       FREE(specialDocAttrRec.PmodeString);
+       FREE(specialDocAttrRec.PdirmodeString);
+       FREE(specialDocAttrRec.Uname);
+       FREE(specialDocAttrRec.Gname);
+    }
     
-       c = 0;
-       while (c < count) {
-           fest = file_entry_array[c];
-           if (type == RPMLEAD_BINARY) {
-               x = strlen(fest->file) - 1;
-               if (x && fest->file[x] == '/') {
-                   fest->file[x] = '\0';
-               }
-               fileList[c] = fest->file;
-           } else {
-               fileList[c] = strrchr(fest->file, '/') + 1;
-           }
-           if ((c > 0) && !strcmp(fileList[c], fileList[c-1])) {
-               rpmError(RPMERR_BADSPEC, "File listed twice: %s", fileList[c]);
-               processFileListFailed = 1;
-           }
-           
-           fileUnameList[c] = fest->uname;
-           fileGnameList[c] = fest->gname;
-           *size += fest->statbuf.st_size;
-           if (S_ISREG(fest->statbuf.st_mode)) {
-               if ((type == RPMLEAD_BINARY) &&
-                   rpmGetVar(RPMVAR_ROOT)) {
-                   sprintf(buf, "%s%s", rpmGetVar(RPMVAR_ROOT), fest->file);
-               } else {
-                   strcpy(buf, fest->file);
-               }
-               mdfile(buf, buf);
-               fileMD5List[c] = strdup(buf);
-               rpmMessage(RPMMESS_DEBUG, "md5(%s) = %s\n", fest->file, buf);
-           } else {
-               /* This is stupid */
-               fileMD5List[c] = strdup("");
-           }
-           fileSizeList[c] = fest->statbuf.st_size;
-           fileUIDList[c] = fest->statbuf.st_uid;
-           fileGIDList[c] = fest->statbuf.st_gid;
-           fileMtimesList[c] = fest->statbuf.st_mtime;
-
-           /* Do timecheck */
-           if (tc && (type == RPMLEAD_BINARY)) {
-               if (currentTime - fest->statbuf.st_mtime > tc) {
-                   rpmMessage(RPMMESS_WARNING, "TIMECHECK failure: %s\n",
-                           fest->file);
-               }
-           }
-    
-           fileFlagsList[c] = 0;
-           if (isDoc(fest->file))
-               fileFlagsList[c] |= RPMFILE_DOC;
-           if (fest->isdoc) 
-               fileFlagsList[c] |= RPMFILE_DOC;
-           if (fest->isghost) 
-               fileFlagsList[c] |= RPMFILE_GHOST;
-           if (fest->conf && !(fest->statbuf.st_mode & S_IFDIR))
-               fileFlagsList[c] |= fest->conf;
-           if (fest->isspecfile)
-               fileFlagsList[c] |= RPMFILE_SPECFILE;
-
-           fileModesList[c] = fest->statbuf.st_mode;
-           fileRDevsList[c] = fest->statbuf.st_rdev;
-           if (! fest->isghost) {
-               fileVerifyFlagsList[c] = fest->verify_flags;
-           } else {
-               fileVerifyFlagsList[c] = fest->verify_flags &
-                   ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE |
-                     RPMVERIFY_LINKTO | RPMVERIFY_MTIME);
-           }
+    FREE(files);
 
-           if (S_ISLNK(fest->statbuf.st_mode)) {
-               if (rpmGetVar(RPMVAR_ROOT)) {
-                   sprintf(buf, "%s%s", rpmGetVar(RPMVAR_ROOT), fest->file);
-               } else {
-                   strcpy(buf, fest->file);
-               }
-               buf[readlink(buf, buf, 1024)] = '\0';
-               fileLinktoList[c] = strdup(buf);
-           } else {
-               /* This is stupid */
-               fileLinktoList[c] = strdup("");
-           }
-           c++;
-       }
-
-       /* Add the header entries */
-       c = count;
-       headerAddEntry(header, RPMTAG_FILENAMES, RPM_STRING_ARRAY_TYPE, fileList, c);
-       headerAddEntry(header, RPMTAG_FILELINKTOS, RPM_STRING_ARRAY_TYPE,
-                fileLinktoList, c);
-       headerAddEntry(header, RPMTAG_FILEMD5S, RPM_STRING_ARRAY_TYPE, fileMD5List, c);
-       headerAddEntry(header, RPMTAG_FILESIZES, RPM_INT32_TYPE, fileSizeList, c);
-       headerAddEntry(header, RPMTAG_FILEUIDS, RPM_INT32_TYPE, fileUIDList, c);
-       headerAddEntry(header, RPMTAG_FILEGIDS, RPM_INT32_TYPE, fileGIDList, c);
-       headerAddEntry(header, RPMTAG_FILEUSERNAME, RPM_STRING_ARRAY_TYPE,
-                fileUnameList, c);
-       headerAddEntry(header, RPMTAG_FILEGROUPNAME, RPM_STRING_ARRAY_TYPE,
-                fileGnameList, c);
-       headerAddEntry(header, RPMTAG_FILEMTIMES, RPM_INT32_TYPE, fileMtimesList, c);
-       headerAddEntry(header, RPMTAG_FILEFLAGS, RPM_INT32_TYPE, fileFlagsList, c);
-       headerAddEntry(header, RPMTAG_FILEMODES, RPM_INT16_TYPE, fileModesList, c);
-       headerAddEntry(header, RPMTAG_FILERDEVS, RPM_INT16_TYPE, fileRDevsList, c);
-       headerAddEntry(header, RPMTAG_FILEVERIFYFLAGS, RPM_INT32_TYPE,
-                fileVerifyFlagsList, c);
-       
-       /* Free the allocated strings */
-       c = count;
-       while (c--) {
-           free(fileMD5List[c]);
-           free(fileLinktoList[c]);
-       }
-
-       /* Free all those lists */
-       free(fileList);
-       free(fileLinktoList);
-       free(fileMD5List);
-       free(fileSizeList);
-       free(fileUIDList);
-       free(fileGIDList);
-       free(fileUnameList);
-       free(fileGnameList);
-       free(fileMtimesList);
-       free(fileFlagsList);
-       free(fileModesList);
-       free(fileRDevsList);
-       free(fileVerifyFlagsList);
-       
-       /* Free the file entry array */
-       free(file_entry_array);
-       
-       /* Free the file entry stack */
-       fest = fes;
-       while (fest) {
-           fes = fest->next;
-           free(fest);
-           fest = fes;
+    if (! fl.processingFailed) {
+       genCpioListAndHeader(&fl, &(pkg->cpioList), &(pkg->cpioCount),
+                            pkg->header, 0);
+
+       if (spec->timeCheck) {
+           timeCheck(spec->timeCheck, pkg->header);
        }
     }
     
-    freeSplitString(files);
-    return processFileListFailed;
+    /* Clean up */
+    FREE(fl.prefix);
+    FREE(fl.current.PmodeString);
+    FREE(fl.current.PdirmodeString);
+    FREE(fl.current.Uname);
+    FREE(fl.current.Gname);
+    FREE(fl.def.PmodeString);
+    FREE(fl.def.PdirmodeString);
+    FREE(fl.def.Uname);
+    FREE(fl.def.Gname);
+    FREE(fl.currentLang);
+    freeFileList(fl.fileList, fl.fileListRecsUsed);
+    while (fl.docDirCount--) {
+        FREE(fl.docDirs[fl.docDirCount]);
+    }
+    return fl.processingFailed;
 }
 
-/*************************************************************/
-/*                                                           */
-/* misc                                                      */
-/*                                                           */
-/*************************************************************/
-
-static int compare_fe(const void *ap, const void *bp)
+static void timeCheck(int tc, Header h)
 {
-    char *a, *b;
+    int *mtime;
+    char **file;
+    int count, x, currentTime;
 
-    a = (*(struct file_entry **)ap)->file;
-    b = (*(struct file_entry **)bp)->file;
+    headerGetEntry(h, RPMTAG_FILENAMES, NULL, (void **) &file, &count);
+    headerGetEntry(h, RPMTAG_FILEMTIMES, NULL, (void **) &mtime, NULL);
 
-    return strcmp(a, b);
+    currentTime = time(NULL);
+    
+    x = 0;
+    while (x < count) {
+       if (currentTime - mtime[x] > tc) {
+           rpmMessage(RPMMESS_WARNING, "TIMECHECK failure: %s\n", file[x]);
+       }
+       x++;
+    }
 }
 
-/*************************************************************/
-/*                                                           */
-/* Doc dir stuff                                             */
-/*                                                           */
-/*************************************************************/
-
-/* XXX hard coded limit -- only 1024 %docdir allowed */
-static char *docdirs[1024];
-static int docdir_count;
+static void genCpioListAndHeader(struct FileList *fl,
+                                struct cpioFileMapping **cpioList,
+                                int *cpioCount, Header h, int isSrc)
+{
+    int skipLen = 0;
+    struct FileListRec *p;
+    int count;
+    struct cpioFileMapping *cpioListPtr;
+    char *s, buf[BUFSIZ];
+    
+    /* Sort the big list */
+    qsort(fl->fileList, fl->fileListRecsUsed,
+         sizeof(*(fl->fileList)), compareFileListRecs);
+    
+    /* Generate the cpio list and the header */
+    if (! isSrc) {
+       if (fl->prefix) {
+           skipLen = 1 + strlen(fl->prefix);
+       } else {
+           skipLen = 1;
+       }
+    }
+    *cpioList = malloc(sizeof(**cpioList) * fl->fileListRecsUsed);
+    *cpioCount = 0;
+    cpioListPtr = *cpioList;
+    p = fl->fileList;
+    count = fl->fileListRecsUsed;
+    while (count) {
+       if ((count > 1) && !strcmp(p->fileName, p[1].fileName)) {
+           rpmError(RPMERR_BADSPEC, "File listed twice: %s", p->fileName);
+           fl->processingFailed = 1;
+       }
+       
+       /* Make the cpio list */
+       if (! (p->flags & RPMFILE_GHOST)) {
+           cpioListPtr->fsPath = strdup(p->diskName);
+           cpioListPtr->archivePath = strdup(p->fileName + skipLen);
+           cpioListPtr->finalMode = p->mode;
+           cpioListPtr->finalUid = p->uid;
+           cpioListPtr->finalGid = p->gid;
+           cpioListPtr->mapFlags = CPIO_MAP_PATH | CPIO_MAP_MODE |
+               CPIO_MAP_UID | CPIO_MAP_GID;
+           cpioListPtr++;
+           (*cpioCount)++;
+       }
+       
+       /* Make the header */
+       headerAddOrAppendEntry(h, RPMTAG_FILENAMES, RPM_STRING_ARRAY_TYPE,
+                              &(p->fileName), 1);
+       headerAddOrAppendEntry(h, RPMTAG_FILESIZES, RPM_INT32_TYPE,
+                              &(p->size), 1);
+       headerAddOrAppendEntry(h, RPMTAG_FILEUSERNAME, RPM_STRING_ARRAY_TYPE,
+                              &(p->uname), 1);
+       headerAddOrAppendEntry(h, RPMTAG_FILEGROUPNAME, RPM_STRING_ARRAY_TYPE,
+                              &(p->gname), 1);
+       headerAddOrAppendEntry(h, RPMTAG_FILEMTIMES, RPM_INT32_TYPE,
+                              &(p->mtime), 1);
+       headerAddOrAppendEntry(h, RPMTAG_FILEMODES, RPM_INT16_TYPE,
+                              &(p->mode), 1);
+       headerAddOrAppendEntry(h, RPMTAG_FILERDEVS, RPM_INT16_TYPE,
+                              &(p->rdev), 1);
+       headerAddOrAppendEntry(h, RPMTAG_FILEDEVICES, RPM_INT32_TYPE,
+                              &(p->device), 1);
+       headerAddOrAppendEntry(h, RPMTAG_FILEINODES, RPM_INT32_TYPE,
+                              &(p->inode), 1);
+       headerAddOrAppendEntry(h, RPMTAG_FILELANGS, RPM_STRING_ARRAY_TYPE,
+                              &(p->lang), 1);
+       
+       /* We used to add these, but they should not be needed */
+       /* headerAddOrAppendEntry(h, RPMTAG_FILEUIDS,
+        *                 RPM_INT32_TYPE, &(p->uid), 1);
+        * headerAddOrAppendEntry(h, RPMTAG_FILEGIDS,
+        *                 RPM_INT32_TYPE, &(p->gid), 1);
+        */
+       
+       buf[0] = '\0';
+       s = buf;
+       if (S_ISREG(p->mode)) {
+           mdfile(p->diskName, buf);
+       }
+       headerAddOrAppendEntry(h, RPMTAG_FILEMD5S, RPM_STRING_ARRAY_TYPE,
+                              &s, 1);
+       
+       buf[0] = '\0';
+       s = buf;
+       if (S_ISLNK(p->mode)) {
+           buf[readlink(p->diskName, buf, BUFSIZ)] = '\0';
+       }
+       headerAddOrAppendEntry(h, RPMTAG_FILELINKTOS, RPM_STRING_ARRAY_TYPE,
+                              &s, 1);
+       
+       if (p->flags & RPMFILE_GHOST) {
+           p->verifyFlags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE |
+                               RPMVERIFY_LINKTO | RPMVERIFY_MTIME);
+       }
+       headerAddOrAppendEntry(h, RPMTAG_FILEVERIFYFLAGS, RPM_INT32_TYPE,
+                              &(p->verifyFlags), 1);
+       
+       if (!isSrc && isDoc(fl, p->fileName)) {
+           p->flags |= RPMFILE_DOC;
+       }
+       if (p->mode & S_IFDIR) {
+           p->flags &= ~RPMFILE_CONFIG;
+           p->flags &= ~RPMFILE_DOC;
+       }
+       headerAddOrAppendEntry(h, RPMTAG_FILEFLAGS, RPM_INT32_TYPE,
+                              &(p->flags), 1);
+       
+       p++;
+       count--;
+    }
+    headerAddEntry(h, RPMTAG_SIZE, RPM_INT32_TYPE,
+                  &(fl->totalFileSize), 1);
+}
 
-static void resetDocdir(void)
+static void freeFileList(struct FileListRec *fileList, int count)
 {
-    while (docdir_count--) {
-        free(docdirs[docdir_count]);
-    }
-    docdir_count = 0;
-    docdirs[docdir_count++] = strdup("/usr/doc");
-    docdirs[docdir_count++] = strdup("/usr/man");
-    docdirs[docdir_count++] = strdup("/usr/info");
-    docdirs[docdir_count++] = strdup("/usr/X11R6/man");
+    while (count--) {
+       FREE(fileList[count].diskName);
+       FREE(fileList[count].fileName);
+       FREE(fileList[count].lang);
+    }
+    FREE(fileList);
 }
 
-static void addDocdir(char *dirname)
+void freeCpioList(struct cpioFileMapping *cpioList, int cpioCount)
 {
-    if (docdir_count == 1024) {
-       fprintf(stderr, "RPMERR_INTERNAL: Hit limit in addDocdir()\n");
-       exit(RPMERR_INTERNAL);
+    struct cpioFileMapping *p = cpioList;
+
+    while (cpioCount--) {
+       rpmMessage(RPMMESS_DEBUG, "archive = %s, fs = %s\n",
+                  p->archivePath, p->fsPath);
+       FREE(p->archivePath);
+       FREE(p->fsPath);
+       p++;
     }
-    docdirs[docdir_count++] = strdup(dirname);
+    FREE(cpioList);
+}
+
+static int compareFileListRecs(const void *ap, const void *bp)
+{
+    char *a, *b;
+
+    a = ((struct FileListRec *)ap)->fileName;
+    b = ((struct FileListRec *)bp)->fileName;
+
+    return strcmp(a, b);
 }
 
-static int isDoc(char *filename)
+static int isDoc(struct FileList *fl, char *fileName)
 {
-    int x = 0;
+    int x = fl->docDirCount;
 
-    while (x < docdir_count) {
-        if (strstr(filename, docdirs[x]) == filename) {
+    while (x--) {
+        if (strstr(fileName, fl->docDirs[x]) == fileName) {
            return 1;
         }
-       x++;
     }
     return 0;
 }
 
-/*************************************************************/
-/*                                                           */
-/* File stating / tree walk                                  */
-/*                                                           */
-/*************************************************************/
-
-/* Need a bunch of globals to keep track of things in ftw() */
-static int Gisdoc;
-static int Gisghost;
-static int Gconf;
-static int Gverify_flags;
-static int Gcount;
-static char *GPmode;
-static char *GUname;
-static char *GGname;
-static struct file_entry **Gfestack;
-static char *Gprefix;
-
-static int add_file(struct file_entry **festack, const char *name,
-                   int isdoc, int conf, int isdir, int isghost,
-                   int verify_flags,
-                   char *Pmode, char *Uname, char *Gname, char *prefix)
+static int processBinaryFile(Package pkg, struct FileList *fl, char *fileName)
+{
+    char fullname[BUFSIZ];
+    glob_t glob_result;
+    int x, offset, rc = 0;
+    
+    /* check that file starts with leading "/" */
+    if (*fileName != '/') {
+       rpmError(RPMERR_BADSPEC, "File needs leading \"/\": %s", *fileName);
+       fl->processingFailed = 1;
+       return 1;
+    }
+    
+    if (myGlobPatternP(fileName)) {
+       if (fl->buildRoot) {
+           sprintf(fullname, "%s%s", fl->buildRoot, fileName);
+           offset = strlen(fl->buildRoot);
+       } else {
+           strcpy(fullname, fileName);
+           offset = 0;
+       }
+       
+       if (glob(fullname, 0, glob_error, &glob_result) ||
+           (glob_result.gl_pathc < 1)) {
+           rpmError(RPMERR_BADSPEC, "File not found: %s", fullname);
+           fl->processingFailed = 1;
+           globfree(&glob_result);
+           return 1;
+       }
+       
+       x = 0;
+       while (x < glob_result.gl_pathc) {
+           rc = addFile(fl, &(glob_result.gl_pathv[x][offset]), NULL);
+           x++;
+       }
+       globfree(&glob_result);
+    } else {
+       rc = addFile(fl, fileName, NULL);
+    }
+
+    return rc;
+}
+
+static int addFile(struct FileList *fl, char *name, struct stat *statp)
 {
-    struct file_entry *p;
-    char *copyTo, copied;
-    const char *prefixTest, *prefixPtr;
-    const char *copyFrom;
-    char fullname[1024];
-    int mode;
-
-    /* Set these up for ftw() */
-    Gfestack = festack;
-    Gisdoc = isdoc;
-    Gconf = conf;
-    Gisghost = isghost;
-    Gverify_flags = verify_flags;
-    GPmode = Pmode;
-    GUname = Uname;
-    GGname = Gname;
-    Gprefix = prefix;
-
-    p = malloc(sizeof(struct file_entry));
-
-    copyTo = p->file;
+    char fileName[BUFSIZ];
+    char diskName[BUFSIZ];
+    char *copyTo, *copyFrom, copied;
+    char *prefixTest, *prefixPtr;
+    struct stat statbuf;
+    int_16 fileMode;
+    int fileUid, fileGid;
+    char *fileUname, *fileGname;
+    
+    /* Copy to fileName, eliminate duplicate "/" and trailing "/" */
+    copyTo = fileName;
     copied = '\0';
     copyFrom = name;
     while (*copyFrom) {
@@ -655,178 +707,325 @@ static int add_file(struct file_entry **festack, const char *name,
        copyFrom++;
     }
     *copyTo = '\0';
+    copyTo--;
+    if ((copyTo != fileName) && (*copyTo == '/')) {
+       *copyTo = '\0';
+    }
 
-    p->isdoc = isdoc;
-    p->conf = conf;
-    p->isghost = isghost;
-    p->isspecfile = 0;  /* source packages are done by hand */
-    p->verify_flags = verify_flags;
-    if (rpmGetVar(RPMVAR_ROOT)) {
-       sprintf(fullname, "%s%s", rpmGetVar(RPMVAR_ROOT), name);
+    if (fl->inFtw) {
+       /* Any buildRoot is already prepended */
+       strcpy(diskName, fileName);
+       if (fl->buildRoot) {
+           strcpy(fileName, diskName + strlen(fl->buildRoot));
+       }
     } else {
-       strcpy(fullname, name);
+       if (fl->buildRoot) {
+           sprintf(diskName, "%s%s", fl->buildRoot, fileName);
+       } else {
+           strcpy(diskName, fileName);
+       }
     }
 
     /* If we are using a prefix, validate the file */
-    if (prefix) {
-       prefixTest = name;
-       prefixPtr = prefix;
-       while (*prefixTest == '/') {
-           prefixTest++;  /* Skip leading "/" */
-       }
+    if (!fl->inFtw && fl->prefix) {
+       prefixTest = fileName;
+       prefixPtr = fl->prefix;
+
        while (*prefixPtr && *prefixTest && (*prefixTest == *prefixPtr)) {
            prefixPtr++;
            prefixTest++;
        }
-       if (*prefixPtr) {
+       if (*prefixPtr || (*prefixTest && *prefixTest != '/')) {
            rpmError(RPMERR_BADSPEC, "File doesn't match prefix (%s): %s",
-                 prefix, name);
-           return 0;
+                    fl->prefix, fileName);
+           fl->processingFailed = 1;
+           return RPMERR_BADSPEC;
        }
     }
 
-    /* OK, finally stat() the file */
-    if (lstat(fullname, &p->statbuf)) {
-       return 0;
-    }
-
-    /*
-     * If %attr() was specified, then use those values instead of
-     * what lstat() returned.
-     */
-    if (Pmode && strcmp(Pmode, "-")) {
-       sscanf(Pmode, "%o", &mode);
-       mode |= p->statbuf.st_mode & S_IFMT;
-       p->statbuf.st_mode = (unsigned short)mode;
+    if (! statp) {
+       statp = &statbuf;
+       if (lstat(diskName, statp)) {
+           rpmError(RPMERR_BADSPEC, "File not found: %s", diskName);
+           fl->processingFailed = 1;
+           return RPMERR_BADSPEC;
+       }
     }
 
-    if (Uname && strcmp(Uname, "-")) {
-       p->uname = getUnameS(Uname);
-    } else {
-       p->uname = getUname(p->statbuf.st_uid);
-    }
-    
-    if (Gname && strcmp(Gname, "-")) {
-       p->gname = getGnameS(Gname);
-    } else {
-       p->gname = getGname(p->statbuf.st_gid);
-    }
-    
-    if (! (p->uname && p->gname)) {
-       rpmError(RPMERR_BADSPEC, "Bad owner/group: %s\n", fullname);
-       p->uname = "";
-       p->gname = "";
-       return 0;
-    }
-    
-    if ((! isdir) && S_ISDIR(p->statbuf.st_mode)) {
-       /* This means we need to descend with ftw() */
-       Gcount = 0;
-       
+    if ((! fl->isDir) && S_ISDIR(statp->st_mode)) {
        /* We use our own ftw() call, because ftw() uses stat()    */
        /* instead of lstat(), which causes it to follow symlinks! */
-       myftw(fullname, add_file_aux, 16);
+       /* It also has better callback support.                    */
        
-       free(p);
-
-       return Gcount;
+       fl->inFtw = 1;  /* Flag to indicate file has buildRoot prefixed */
+       fl->isDir = 1;  /* Keep it from following myftw() again         */
+       myftw(diskName, 16, addFile, fl);
+       fl->isDir = 0;
+       fl->inFtw = 0;
     } else {
-       /* Link it in */
-       p->next = *festack;
-       *festack = p;
+       fileMode = statp->st_mode;
+       fileUid = statp->st_uid;
+       fileGid = statp->st_gid;
+       
+       /* %attr ? */
+       if (S_ISDIR(fileMode) && fl->current.PdirmodeString) {
+           if (fl->current.PdirmodeString[0] != '-') {
+               fileMode &= S_IFMT;
+               fileMode |= fl->current.Pdirmode;
+           }
+       } else {
+           if (fl->current.PmodeString) {
+               fileMode &= S_IFMT;
+               fileMode |= fl->current.Pmode;
+           }
+       }
+       if (fl->current.Uname) {
+           fileUname = getUnameS(fl->current.Uname);
+       } else {
+           fileUname = getUname(fileUid);
+       }
+       if (fl->current.Gname) {
+           fileGname = getGnameS(fl->current.Gname);
+       } else {
+           fileGname = getGname(fileGid);
+       }
+       
+       if (! (fileUname && fileGname)) {
+           rpmError(RPMERR_BADSPEC, "Bad owner/group: %s\n", diskName);
+           fl->processingFailed = 1;
+           return RPMERR_BADSPEC;
+       }
+    
+       rpmMessage(RPMMESS_DEBUG, "File %d: %s\n", fl->fileCount, fileName);
+
+       /* Add to the file list */
+       if (fl->fileListRecsUsed == fl->fileListRecsAlloced) {
+           fl->fileListRecsAlloced += 128;
+           fl->fileList = realloc(fl->fileList,
+                                  fl->fileListRecsAlloced *
+                                  sizeof(*(fl->fileList)));
+       }
+           
+       fl->fileList[fl->fileListRecsUsed].fileName = strdup(fileName);
+       fl->fileList[fl->fileListRecsUsed].diskName = strdup(diskName);
+       fl->fileList[fl->fileListRecsUsed].mode = fileMode;
+       fl->fileList[fl->fileListRecsUsed].uid = fileUid;
+       fl->fileList[fl->fileListRecsUsed].gid = fileGid;
+       fl->fileList[fl->fileListRecsUsed].uname = fileUname;
+       fl->fileList[fl->fileListRecsUsed].gname = fileGname;
+
+       fl->fileList[fl->fileListRecsUsed].lang =
+           strdup(fl->currentLang ? fl->currentLang : "");
+       
+       fl->fileList[fl->fileListRecsUsed].flags = fl->currentFlags;
+       fl->fileList[fl->fileListRecsUsed].verifyFlags =
+           fl->currentVerifyFlags;
+       fl->fileList[fl->fileListRecsUsed].size = statp->st_size;
+       fl->fileList[fl->fileListRecsUsed].mtime = statp->st_mtime;
+       fl->fileList[fl->fileListRecsUsed].rdev = statp->st_rdev;
+       fl->fileList[fl->fileListRecsUsed].device = statp->st_dev;
+       fl->fileList[fl->fileListRecsUsed].inode = statp->st_ino;
+       fl->fileListRecsUsed++;
+
+       fl->totalFileSize += statp->st_size;
+       fl->fileCount++;
+    }
 
-       rpmMessage(RPMMESS_DEBUG, "ADDING: %s\n", name);
+    return 0;
+}
 
-       /* return number of entries added */
-       return 1;
+static int parseForSimple(Spec spec, Package pkg, char *buf,
+                         struct FileList *fl, char **fileName)
+{
+    char *s;
+    int res, specialDoc = 0;
+    char *name, *version;
+    char specialDocBuf[BUFSIZ];
+
+    specialDocBuf[0] = '\0';
+    *fileName = NULL;
+    res = 0;
+    s = strtok(buf, " \t\n");
+    while (s) {
+       if (!strcmp(s, "%docdir")) {
+           s = strtok(NULL, " \t\n");
+           if (fl->docDirCount == MAXDOCDIR) {
+               rpmError(RPMERR_INTERNAL, "Hit limit for %%docdir");
+               fl->processingFailed = 1;
+               res = 1;
+           }
+           fl->docDirs[fl->docDirCount++] = strdup(s);
+           if (strtok(NULL, " \t\n")) {
+               rpmError(RPMERR_INTERNAL, "Only one arg for %%docdir");
+               fl->processingFailed = 1;
+               res = 1;
+           }
+           break;
+       } else if (!strcmp(s, "%doc")) {
+           fl->currentFlags |= RPMFILE_DOC;
+       } else if (!strcmp(s, "%ghost")) {
+           fl->currentFlags |= RPMFILE_GHOST;
+       } else if (!strcmp(s, "%dir")) {
+           fl->isDir = 1;
+       } else {
+           if (*fileName) {
+               /* We already got a file -- error */
+               rpmError(RPMERR_BADSPEC,
+                        "Two files on one line: %s", *fileName);
+               fl->processingFailed = 1;
+               res = 1;
+           }
+           if (*s != '/') {
+               if (fl->currentFlags & RPMFILE_DOC) {
+                   specialDoc = 1;
+                   strcat(specialDocBuf, " ");
+                   strcat(specialDocBuf, s);
+               } else {
+                   /* not in %doc, does not begin with / -- error */
+                   rpmError(RPMERR_BADSPEC,
+                            "File must begin with \"/\": %s", s);
+                   fl->processingFailed = 1;
+                   res = 1;
+               }
+           } else {
+               *fileName = s;
+           }
+       }
+       s = strtok(NULL, " \t\n");
+    }
+
+    if (specialDoc) {
+       if (*fileName || (fl->currentFlags & ~(RPMFILE_DOC))) {
+           rpmError(RPMERR_BADSPEC,
+                    "Can't mix special %%doc with other forms: %s",
+                    *fileName);
+           fl->processingFailed = 1;
+           res = 1;
+       } else {
+           headerGetEntry(pkg->header, RPMTAG_NAME, NULL,
+                          (void *) &name, NULL);
+           headerGetEntry(pkg->header, RPMTAG_VERSION, NULL,
+                          (void *) &version, NULL);
+           sprintf(buf, "%s/%s-%s", spec->docDir, name, version);
+
+           if (! fl->passedSpecialDoc) {
+               pkg->specialDoc = newStringBuf();
+               appendStringBuf(pkg->specialDoc,
+                               "export DOCDIR=$RPM_BUILD_ROOT");
+               appendLineStringBuf(pkg->specialDoc, buf);
+               appendLineStringBuf(pkg->specialDoc, "rm -rf $DOCDIR");
+               appendLineStringBuf(pkg->specialDoc, "mkdir -p $DOCDIR");
+
+               *fileName = buf;
+               fl->passedSpecialDoc = 1;
+               fl->isSpecialDoc = 1;
+           }
+
+           appendStringBuf(pkg->specialDoc, "cp -pr ");
+           appendStringBuf(pkg->specialDoc, specialDocBuf);
+           appendLineStringBuf(pkg->specialDoc, " $DOCDIR");
+       }
     }
+
+    return res;
 }
 
-static int add_file_aux(const char *file, struct stat *sb, int flag)
+static int parseForVerify(char *buf, struct FileList *fl)
 {
-    const char *name = file;
+    char *p, *start, *end, *name;
+    char ourbuf[BUFSIZ];
+    int not, verifyFlags;
+    int *resultVerify;
 
-    if (rpmGetVar(RPMVAR_ROOT)) {
-       name += strlen(rpmGetVar(RPMVAR_ROOT));
+    if (!(p = start = strstr(buf, "%verify"))) {
+       if (!(p = start = strstr(buf, "%defverify"))) {
+           return 0;
+       }
+       name = "%defverify";
+       resultVerify = &(fl->defVerifyFlags);
+       p += 10;
+    } else {
+       name = "%verify";
+       resultVerify = &(fl->currentVerifyFlags);
+       p += 7;
     }
 
-    /* The 1 will cause add_file() to *not* descend */
-    /* directories -- ftw() is already doing it!    */
-    Gcount += add_file(Gfestack, name, Gisdoc, Gconf, 1, Gisghost,
-                      Gverify_flags,
-                      GPmode, GUname, GGname, Gprefix);
+    SKIPSPACE(p);
 
-    return 0; /* for ftw() */
-}
+    if (*p != '(') {
+       rpmError(RPMERR_BADSPEC, "Bad %s() syntax: %s", name, buf);
+       fl->processingFailed = 1;
+       return RPMERR_BADSPEC;
+    }
+    p++;
 
-/*************************************************************/
-/*                                                           */
-/* globbing                                                  */
-/*                                                           */
-/*************************************************************/
+    end = p;
+    while (*end && *end != ')') {
+       end++;
+    }
 
-/* glob_pattern_p() taken from bash
- * Copyright (C) 1985, 1988, 1989 Free Software Foundation, Inc.
- */
+    if (! *end) {
+       rpmError(RPMERR_BADSPEC, "Bad %s() syntax: %s", name, buf);
+       fl->processingFailed = 1;
+       return RPMERR_BADSPEC;
+    }
 
-/* Return nonzero if PATTERN has any special globbing chars in it.  */
-static int glob_pattern_p (char *pattern)
-{
-    register char *p = pattern;
-    register char c;
-    int open = 0;
-  
-    while ((c = *p++) != '\0')
-       switch (c) {
-       case '?':
-       case '*':
-           return (1);
-       case '[':      /* Only accept an open brace if there is a close */
-           open++;    /* brace to match it.  Bracket expressions must be */
-           continue;  /* complete, according to Posix.2 */
-       case ']':
-           if (open)
-               return (1);
-           continue;      
-       case '\\':
-           if (*p++ == '\0')
-               return (0);
+    strncpy(ourbuf, p, end-p);
+    ourbuf[end-p] = '\0';
+    while (start <= end) {
+       *start++ = ' ';
+    }
+
+    p = strtok(ourbuf, ", \n\t");
+    not = 0;
+    verifyFlags = RPMVERIFY_NONE;
+    while (p) {
+       if (!strcmp(p, "not")) {
+           not = 1;
+       } else if (!strcmp(p, "md5")) {
+           verifyFlags |= RPMVERIFY_MD5;
+       } else if (!strcmp(p, "size")) {
+           verifyFlags |= RPMVERIFY_FILESIZE;
+       } else if (!strcmp(p, "link")) {
+           verifyFlags |= RPMVERIFY_LINKTO;
+       } else if (!strcmp(p, "user")) {
+           verifyFlags |= RPMVERIFY_USER;
+       } else if (!strcmp(p, "group")) {
+           verifyFlags |= RPMVERIFY_GROUP;
+       } else if (!strcmp(p, "mtime")) {
+           verifyFlags |= RPMVERIFY_MTIME;
+       } else if (!strcmp(p, "mode")) {
+           verifyFlags |= RPMVERIFY_MODE;
+       } else if (!strcmp(p, "rdev")) {
+           verifyFlags |= RPMVERIFY_RDEV;
+       } else {
+           rpmError(RPMERR_BADSPEC, "Invalid %s token: %s", name, p);
+           fl->processingFailed = 1;
+           return RPMERR_BADSPEC;
        }
+       p = strtok(NULL, ", \n\t");
+    }
 
-    return (0);
-}
+    *resultVerify = not ? ~(verifyFlags) : verifyFlags;
 
-static int glob_error(const char *foo, int bar)
-{
-    return 1;
+    return 0;
 }
 
-/*************************************************************/
-/*                                                           */
-/* %attr parsing                                             */
-/*                                                           */
-/*************************************************************/
-
-static int parseForAttr(char *buf, char **currPmode,
-                       char **currUname, char **currGname)
+static int parseForLang(char *buf, struct FileList *fl)
 {
     char *p, *start, *end;
     char ourbuf[1024];
-    int mode, x;
 
-    if (!(p = start = strstr(buf, "%attr"))) {
+    if (!(p = start = strstr(buf, "%lang"))) {
        return 0;
     }
 
-    *currPmode = *currUname = *currGname = NULL;
-
     p += 5;
-    while (*p && (*p == ' ' || *p == '\t')) {
-       p++;
-    }
+    SKIPSPACE(p);
 
     if (*p != '(') {
-       rpmError(RPMERR_BADSPEC, "Bad %%attr() syntax: %s", buf);
+       rpmError(RPMERR_BADSPEC, "Bad %%lang() syntax: %s", buf);
+       fl->processingFailed = 1;
        return RPMERR_BADSPEC;
     }
     p++;
@@ -837,68 +1036,66 @@ static int parseForAttr(char *buf, char **currPmode,
     }
 
     if (! *end) {
-       rpmError(RPMERR_BADSPEC, "Bad %%attr() syntax: %s", buf);
+       rpmError(RPMERR_BADSPEC, "Bad %%lang() syntax: %s", buf);
+       fl->processingFailed = 1;
        return RPMERR_BADSPEC;
     }
 
     strncpy(ourbuf, p, end-p);
     ourbuf[end-p] = '\0';
+    while (start <= end) {
+       *start++ = ' ';
+    }
 
-    *currPmode = strtok(ourbuf, ", \n\t");
-    *currUname = strtok(NULL, ", \n\t");
-    *currGname = strtok(NULL, ", \n\t");
-
-    if (! (*currPmode && *currUname && *currGname)) {
-       rpmError(RPMERR_BADSPEC, "Bad %%attr() syntax: %s", buf);
-       *currPmode = *currUname = *currGname = NULL;
+    p = strtok(ourbuf, ", \n\t");
+    if (!p) {
+       rpmError(RPMERR_BADSPEC, "Bad %%lang() syntax: %s", buf);
+       fl->processingFailed = 1;
        return RPMERR_BADSPEC;
     }
-
-    /* Do a quick test on the mode argument */
-    if (strcmp(*currPmode, "-")) {
-       x = sscanf(*currPmode, "%o", &mode);
-       if ((x == 0) || (mode >> 12)) {
-           rpmError(RPMERR_BADSPEC, "Bad %%attr() mode spec: %s", buf);
-           *currPmode = *currUname = *currGname = NULL;
-           return RPMERR_BADSPEC;
-       }
+    if (strlen(p) != 2) {
+       rpmError(RPMERR_BADSPEC, "%%lang() entries are 2 characters: %s", buf);
+       fl->processingFailed = 1;
+       return RPMERR_BADSPEC;
     }
-    
-    *currPmode = strdup(*currPmode);
-    *currUname = strdup(*currUname);
-    *currGname = strdup(*currGname);
-    
-    /* Set everything we just parsed to blank spaces */
-    while (start <= end) {
-       *start++ = ' ';
+    if (strtok(NULL, ", \n\t")) {
+       rpmError(RPMERR_BADSPEC, "Only one entry in %%lang(): %s", buf);
+       fl->processingFailed = 1;
+       return RPMERR_BADSPEC;
     }
-
+    fl->currentLang = strdup(p);
+    
     return 0;
 }
 
-/*************************************************************/
-/*                                                           */
-/* %verify parsing                                           */
-/*                                                           */
-/*************************************************************/
-
-static int parseForVerify(char *buf, int *verify_flags)
+static int parseForAttr(char *buf, struct FileList *fl)
 {
-    char *p, *start, *end;
+    char *p, *s, *start, *end, *name;
     char ourbuf[1024];
-    int not;
+    int x, defattr = 0;
+    struct AttrRec *resultAttr;
 
-    if (!(p = start = strstr(buf, "%verify"))) {
-       return 0;
+    if (!(p = start = strstr(buf, "%attr"))) {
+       if (!(p = start = strstr(buf, "%defattr"))) {
+           return 0;
+       }
+       defattr = 1;
+       name = "%defattr";
+       resultAttr = &(fl->def);
+       p += 8;
+    } else {
+       name = "%attr";
+       resultAttr = &(fl->current);
+       p += 5;
     }
 
-    p += 7;
-    while (*p && (*p == ' ' || *p == '\t')) {
-       p++;
-    }
+    resultAttr->PmodeString = resultAttr->Uname = resultAttr->Gname = NULL;
+
+    SKIPSPACE(p);
 
     if (*p != '(') {
-       rpmError(RPMERR_BADSPEC, "Bad %%verify() syntax: %s", buf);
+       rpmError(RPMERR_BADSPEC, "Bad %s() syntax: %s", name, buf);
+       fl->processingFailed = 1;
        return RPMERR_BADSPEC;
     }
     p++;
@@ -909,53 +1106,92 @@ static int parseForVerify(char *buf, int *verify_flags)
     }
 
     if (! *end) {
-       rpmError(RPMERR_BADSPEC, "Bad %%verify() syntax: %s", buf);
+       rpmError(RPMERR_BADSPEC, "Bad %s() syntax: %s", name, buf);
+       fl->processingFailed = 1;
        return RPMERR_BADSPEC;
     }
 
+    if (defattr) {
+       s = end;
+       s++;
+       SKIPSPACE(s);
+       if (*s) {
+           rpmError(RPMERR_BADSPEC,
+                    "No files after %%defattr(): %s", buf);
+           fl->processingFailed = 1;
+           return RPMERR_BADSPEC;
+       }
+    }
+
     strncpy(ourbuf, p, end-p);
     ourbuf[end-p] = '\0';
-    while (start <= end) {
-       *start++ = ' ';
+
+    resultAttr->PmodeString = strtok(ourbuf, ", \n\t");
+    resultAttr->Uname = strtok(NULL, ", \n\t");
+    resultAttr->Gname = strtok(NULL, ", \n\t");
+    resultAttr->PdirmodeString = strtok(NULL, ", \n\t");
+
+    if (! (resultAttr->PmodeString &&
+          resultAttr->Uname && resultAttr->Gname)) {
+       rpmError(RPMERR_BADSPEC, "Bad %s() syntax: %s", name, buf);
+       resultAttr->PmodeString = resultAttr->Uname = resultAttr->Gname = NULL;
+       fl->processingFailed = 1;
+       return RPMERR_BADSPEC;
     }
 
-    p = strtok(ourbuf, ", \n\t");
-    not = 0;
-    *verify_flags = RPMVERIFY_NONE;
-    while (p) {
-       if (!strcmp(p, "not")) {
-           not = 1;
-       } else if (!strcmp(p, "md5")) {
-           *verify_flags |= RPMVERIFY_MD5;
-       } else if (!strcmp(p, "size")) {
-           *verify_flags |= RPMVERIFY_FILESIZE;
-       } else if (!strcmp(p, "link")) {
-           *verify_flags |= RPMVERIFY_LINKTO;
-       } else if (!strcmp(p, "user")) {
-           *verify_flags |= RPMVERIFY_USER;
-       } else if (!strcmp(p, "group")) {
-           *verify_flags |= RPMVERIFY_GROUP;
-       } else if (!strcmp(p, "mtime")) {
-           *verify_flags |= RPMVERIFY_MTIME;
-       } else if (!strcmp(p, "mode")) {
-           *verify_flags |= RPMVERIFY_MODE;
-       } else if (!strcmp(p, "rdev")) {
-           *verify_flags |= RPMVERIFY_RDEV;
-       } else {
-           rpmError(RPMERR_BADSPEC, "Invalid %%verify token: %s", p);
+    /* Do a quick test on the mode argument and adjust for "-" */
+    if (!strcmp(resultAttr->PmodeString, "-")) {
+       resultAttr->PmodeString = NULL;
+    } else {
+       x = sscanf(resultAttr->PmodeString, "%o", &(resultAttr->Pmode));
+       if ((x == 0) || (resultAttr->Pmode >> 12)) {
+           rpmError(RPMERR_BADSPEC, "Bad %s() mode spec: %s", name, buf);
+           resultAttr->PmodeString = resultAttr->Uname =
+               resultAttr->Gname = NULL;
+           fl->processingFailed = 1;
            return RPMERR_BADSPEC;
        }
-       p = strtok(NULL, ", \n\t");
+       resultAttr->PmodeString = strdup(resultAttr->PmodeString);
     }
-
-    if (not) {
-       *verify_flags = ~(*verify_flags);
+    if (resultAttr->PdirmodeString) {
+       /* The processing here is slightly different to maintain */
+       /* compatibility with old spec files.                    */
+       if (!strcmp(resultAttr->PdirmodeString, "-")) {
+           resultAttr->PdirmodeString = strdup(resultAttr->PdirmodeString);
+       } else {
+           x = sscanf(resultAttr->PdirmodeString, "%o",
+                      &(resultAttr->Pdirmode));
+           if ((x == 0) || (resultAttr->Pdirmode >> 12)) {
+               rpmError(RPMERR_BADSPEC,
+                        "Bad %s() dirmode spec: %s", name, buf);
+               resultAttr->PmodeString = resultAttr->Uname =
+                   resultAttr->Gname = resultAttr->PdirmodeString = NULL;
+               fl->processingFailed = 1;
+               return RPMERR_BADSPEC;
+           }
+           resultAttr->PdirmodeString = strdup(resultAttr->PdirmodeString);
+       }
+    }
+    if (!strcmp(resultAttr->Uname, "-")) {
+       resultAttr->Uname = NULL;
+    } else {
+       resultAttr->Uname = strdup(resultAttr->Uname);
+    }
+    if (!strcmp(resultAttr->Gname, "-")) {
+       resultAttr->Gname = NULL;
+    } else {
+       resultAttr->Gname = strdup(resultAttr->Gname);
+    }
+    
+    /* Set everything we just parsed to blank spaces */
+    while (start <= end) {
+       *start++ = ' ';
     }
 
     return 0;
 }
 
-static int parseForConfig(char *buf, int *conf)
+static int parseForConfig(char *buf, struct FileList *fl)
 {
     char *p, *start, *end;
     char ourbuf[1024];
@@ -963,12 +1199,10 @@ static int parseForConfig(char *buf, int *conf)
     if (!(p = start = strstr(buf, "%config"))) {
        return 0;
     }
-    *conf = RPMFILE_CONFIG;
+    fl->currentFlags = RPMFILE_CONFIG;
 
     p += 7;
-    while (*p && (*p == ' ' || *p == '\t')) {
-       p++;
-    }
+    SKIPSPACE(p);
 
     if (*p != '(') {
        while (start < p) {
@@ -985,6 +1219,7 @@ static int parseForConfig(char *buf, int *conf)
 
     if (! *end) {
        rpmError(RPMERR_BADSPEC, "Bad %%config() syntax: %s", buf);
+       fl->processingFailed = 1;
        return RPMERR_BADSPEC;
     }
 
@@ -997,11 +1232,12 @@ static int parseForConfig(char *buf, int *conf)
     p = strtok(ourbuf, ", \n\t");
     while (p) {
        if (!strcmp(p, "missingok")) {
-           *conf |= RPMFILE_MISSINGOK;
+           fl->currentFlags |= RPMFILE_MISSINGOK;
        } else if (!strcmp(p, "noreplace")) {
-           *conf |= RPMFILE_NOREPLACE;
+           fl->currentFlags |= RPMFILE_NOREPLACE;
        } else {
            rpmError(RPMERR_BADSPEC, "Invalid %%config token: %s", p);
+           fl->processingFailed = 1;
            return RPMERR_BADSPEC;
        }
        p = strtok(NULL, ", \n\t");
@@ -1009,3 +1245,39 @@ static int parseForConfig(char *buf, int *conf)
 
     return 0;
 }
+
+/* glob_pattern_p() taken from bash
+ * Copyright (C) 1985, 1988, 1989 Free Software Foundation, Inc.
+ *
+ * Return nonzero if PATTERN has any special globbing chars in it.
+ */
+static int myGlobPatternP (char *pattern)
+{
+    register char *p = pattern;
+    register char c;
+    int open = 0;
+  
+    while ((c = *p++) != '\0')
+       switch (c) {
+       case '?':
+       case '*':
+           return (1);
+       case '[':      /* Only accept an open brace if there is a close */
+           open++;    /* brace to match it.  Bracket expressions must be */
+           continue;  /* complete, according to Posix.2 */
+       case ']':
+           if (open)
+               return (1);
+           continue;      
+       case '\\':
+           if (*p++ == '\0')
+               return (0);
+       }
+
+    return (0);
+}
+
+static int glob_error(const char *foo, int bar)
+{
+    return 1;
+}
index 235e05a..61239e2 100644 (file)
@@ -1,14 +1,8 @@
-#ifndef _FILES_H_
-#define _FILES_H_
-
 #include "spec.h"
-#include "specP.h"
-#include "stringbuf.h"
-
-int finish_filelists(Spec spec);
+#include "package.h"
+#include "lib/cpio.h"
 
-int process_filelist(Header header, struct PackageRec *pr, StringBuf sb,
-                    int *size, char *name, char *version,
-                    char *release, int type, char *prefix, char *specFile);
 
-#endif _FILES_H_
+int processBinaryFiles(Spec spec, int installSpecialDoc);
+int processSourceFiles(Spec spec);
+void freeCpioList(struct cpioFileMapping *cpioList, int cpioCount);
index 19a4fd8..1731916 100644 (file)
@@ -4,48 +4,42 @@
 #include <stdlib.h>
 #include <string.h>
 #include <ctype.h>
+
 #include "macro.h"
+#include "misc.h"
 
-#ifdef DEBUG
+#ifdef DEBUG_MACROS
 #include <stdio.h>
 #define rpmError fprintf
 #define RPMERR_BADSPEC stderr
 static void dumpTable(void);
 #else
-#include "rpmlib.h"
+#include "lib/rpmlib.h"
 #endif
 
-static void expandMacroTable(void);
+static void expandMacroTable(struct MacroContext *mc);
 static int compareMacros(const void *ap, const void *bp);
-static struct macroEntry *findEntry(char *name);
-static int handleDefine(char *buf);
+static struct MacroEntry *findEntry(struct MacroContext *mc, char *name);
+static int handleDefine(struct MacroContext *mc, char *buf);
 static int parseMacro(char *p, char **macro, char **next);
 
-/* This should be a hash table, but I doubt anyone will ever notice */
+/* This should be a hash table, but I doubt anyone would ever */
+/* notice the increase is speed.                              */
 
 #define MACRO_CHUNK_SIZE 16
 
-struct macroEntry {
-    char *name;
-    char *expansion;
-};
-
-static struct macroEntry *macroTable = NULL;
-static int macrosAllocated = 0;
-static int firstFree = 0;
-
 /*************************************************************************/
 /*                                                                       */
 /* Parsing routines                                                      */
 /*                                                                       */
 /*************************************************************************/
 
-int expandMacros(char *buf)
+int expandMacros(struct MacroContext *mc, char *buf)
 {
     char bufA[1024];
     char *copyTo, *copyFrom;
     char *name, *rest, *first;
-    struct macroEntry *p;
+    struct MacroEntry *p;
     
     if (! buf) {
        return 0;
@@ -57,6 +51,7 @@ int expandMacros(char *buf)
        first++;
     }
     if (*first == '#') {
+       buf[0] = '\0';
        return 0;
     }
     
@@ -70,8 +65,8 @@ int expandMacros(char *buf)
            if (parseMacro(copyFrom+1, &name, &rest)) {
                return 1;
            }
-           if (!strcmp(name, "define")) {
-               if (handleDefine(rest)) {
+           if (copyFrom == buf && !strcmp(name, "define")) {
+               if (handleDefine(mc, rest)) {
                    return 1;
                }
                /* result is empty */
@@ -83,7 +78,7 @@ int expandMacros(char *buf)
                copyFrom = rest;
            } else {
                /* a real live macro! */
-               p = findEntry(name);
+               p = findEntry(mc, name);
                if (! p) {
                    /* undefined - just leave it */
                    *copyTo++ = '%';
@@ -116,7 +111,7 @@ static int parseMacro(char *p, char **macro, char **next)
 
     if (! p) {
        /* empty macro name */
-       rpmError(RPMERR_BADSPEC, "Empty macro name\n");
+       rpmError(RPMERR_BADSPEC, "Empty macro name");
        return 2;
     }
     
@@ -124,14 +119,14 @@ static int parseMacro(char *p, char **macro, char **next)
        *next = strchr(p, '}');
        if (! *next) {
            /* unterminated */
-           rpmError(RPMERR_BADSPEC, "Unterminated {: %s\n", p);
+           rpmError(RPMERR_BADSPEC, "Unterminated {: %s", p);
            return 1;
        }
        **next = '\0';
        *macro = strtok(p+1, " \n\t");
        if (! *macro) {
            /* empty macro name */
-           rpmError(RPMERR_BADSPEC, "Empty macro name\n");
+           rpmError(RPMERR_BADSPEC, "Empty macro name");
            return 2;
        }
        (*next)++;
@@ -146,7 +141,7 @@ static int parseMacro(char *p, char **macro, char **next)
 
     if (isspace(*p) || ! *p) {
        /* illegal % syntax */
-       rpmError(RPMERR_BADSPEC, "Illegal %% syntax: %s\n", p);
+       rpmError(RPMERR_BADSPEC, "Illegal %% syntax: %s", p);
        return 3;
     }
 
@@ -165,7 +160,7 @@ static int parseMacro(char *p, char **macro, char **next)
     return 0;
 }
 
-static int handleDefine(char *buf)
+static int handleDefine(struct MacroContext *mc, char *buf)
 {
     char *last, *name, *expansion;
 
@@ -177,7 +172,7 @@ static int handleDefine(char *buf)
     }
     if (! *name) {
        /* missing macro name */
-       rpmError(RPMERR_BADSPEC, "Unfinished %%define\n");
+       rpmError(RPMERR_BADSPEC, "Unfinished %%define");
        return 1;
     }
     expansion = name;
@@ -201,106 +196,115 @@ static int handleDefine(char *buf)
        }
     }
 
-    expandMacros(expansion);
-    addMacro(name, expansion);
+    expandMacros(mc, expansion);
+    addMacro(mc, name, expansion);
 
     return 0;
 }
 
-#ifdef DEBUG
-static void dumpTable()
-{
-    int i;
-    
-    for (i = 0; i < firstFree; i++) {
-       printf("%s->%s.\n", macroTable[i].name,
-              macroTable[i].expansion);
-    }
-}
-
-void main(void)
-{
-    char buf[1024];
-    int x;
-
-    while(gets(buf)) {
-       x = expandMacros(buf);
-       printf("%d->%s<-\n", x, buf);
-    }
-}
-#endif
-
 /*************************************************************************/
 /*                                                                       */
 /* Table handling routines                                               */
 /*                                                                       */
 /*************************************************************************/
 
-void resetMacros(void)
+void initMacros(struct MacroContext *mc)
+{
+    mc->macrosAllocated = 0;
+    mc->firstFree = 0;
+    mc->macroTable = NULL;
+    expandMacroTable(mc);
+
+}
+
+void freeMacros(struct MacroContext *mc)
 {
     int i;
     
-    if (! macrosAllocated) {
-       expandMacroTable();
-       return;
-    }
-
-    for (i = 0; i < firstFree; i++) {
-       free(macroTable[i].name);
-       free(macroTable[i].expansion);
+    for (i = 0; i < mc->firstFree; i++) {
+       FREE(mc->macroTable[i].name);
+       FREE(mc->macroTable[i].expansion);
     }
-    firstFree = 0;
+    FREE(mc->macroTable);
 }
 
-void addMacro(char *name, char *expansion)
+void addMacro(struct MacroContext *mc, char *name, char *expansion)
 {
-    struct macroEntry *p;
+    struct MacroEntry *p;
 
-    p = findEntry(name);
+    p = findEntry(mc, name);
     if (p) {
        free(p->expansion);
        p->expansion = strdup(expansion);
        return;
     }
     
-    if (firstFree == macrosAllocated) {
-       expandMacroTable();
+    if (mc->firstFree == mc->macrosAllocated) {
+       expandMacroTable(mc);
     }
 
-    p = macroTable + firstFree++;
+    p = mc->macroTable + mc->firstFree++;
     p->name = strdup(name);
     p->expansion = strdup(expansion);
 
-    qsort(macroTable, firstFree, sizeof(*macroTable), compareMacros);
+    qsort(mc->macroTable, mc->firstFree, sizeof(*(mc->macroTable)),
+         compareMacros);
 }
 
-static struct macroEntry *findEntry(char *name)
+static struct MacroEntry *findEntry(struct MacroContext *mc, char *name)
 {
-    struct macroEntry key;
+    struct MacroEntry key;
 
-    if (! firstFree) {
+    if (! mc->firstFree) {
        return NULL;
     }
     
     key.name = name;
-    return bsearch(&key, macroTable, firstFree,
-                  sizeof(*macroTable), compareMacros);
+    return bsearch(&key, mc->macroTable, mc->firstFree,
+                  sizeof(*(mc->macroTable)), compareMacros);
 }
 
 static int compareMacros(const void *ap, const void *bp)
 {
-    return strcmp(((struct macroEntry *)ap)->name,
-                 ((struct macroEntry *)bp)->name);
+    return strcmp(((struct MacroEntry *)ap)->name,
+                 ((struct MacroEntry *)bp)->name);
 }
 
-static void expandMacroTable()
+static void expandMacroTable(struct MacroContext *mc)
 {
-    macrosAllocated += MACRO_CHUNK_SIZE;
-    if (! macrosAllocated) {
-       macroTable = malloc(sizeof(*macroTable) * macrosAllocated);
-       firstFree = 0;
+    mc->macrosAllocated += MACRO_CHUNK_SIZE;
+    if (! mc->macrosAllocated) {
+       mc->macroTable = malloc(sizeof(*(mc->macroTable)) *
+                               mc->macrosAllocated);
+       mc->firstFree = 0;
     } else {
-       macroTable = realloc(macroTable,
-                            sizeof(*macroTable) * macrosAllocated);
+       mc->macroTable = realloc(mc->macroTable, sizeof(*(mc->macroTable)) *
+                                mc->macrosAllocated);
+    }
+}
+
+/***********************************************************************/
+
+#ifdef DEBUG_MACROS
+static void dumpTable()
+{
+    int i;
+    
+    for (i = 0; i < firstFree; i++) {
+       printf("%s->%s.\n", macroTable[i].name,
+              macroTable[i].expansion);
+    }
+}
+
+void main(void)
+{
+    char buf[1024];
+    int x;
+
+    while(gets(buf)) {
+       x = expandMacros(buf);
+       printf("%d->%s<-\n", x, buf);
     }
 }
+#endif
+
index ad9b352..8d3b8d0 100644 (file)
@@ -1,8 +1,25 @@
+#ifndef _MACRO_H_
+#define _MACRO_H_
+
 /* macro.h - %macro handling */
 
-void resetMacros(void);
+struct MacroEntry {
+    char *name;
+    char *expansion;
+};
+
+struct MacroContext {
+    struct MacroEntry *macroTable;
+    int macrosAllocated;
+    int firstFree;
+};
 
-void addMacro(char *name, char *expansion);
+void initMacros(struct MacroContext *mc);
+void freeMacros(struct MacroContext *mc);
+
+void addMacro(struct MacroContext *mc, char *name, char *expansion);
 
 /* Expand all macros in buf, in place */
-int expandMacros(char *buf);
+int expandMacros(struct MacroContext *mc, char *buf);
+
+#endif
diff --git a/build/misc.c b/build/misc.c
new file mode 100644 (file)
index 0000000..7437f3d
--- /dev/null
@@ -0,0 +1,202 @@
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <signal.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <errno.h>
+
+#include "misc.h"
+#include "spec.h"
+#include "rpmlib.h"
+#include "header.h"
+#include "popt/popt.h"
+
+void addOrAppendListEntry(Header h, int_32 tag, char *line)
+{
+    int argc;
+    char **argv;
+
+    poptParseArgvString(line, &argc, &argv);
+    if (argc) {
+       headerAddOrAppendEntry(h, tag, RPM_STRING_ARRAY_TYPE, argv, argc);
+    }
+    FREE(argv);
+}
+
+/* Parse a simple part line that only take -n <pkg> or <pkg> */
+/* <pkg> is return in name as a pointer into a static buffer */
+
+int parseSimplePart(char *line, char **name, int *flag)
+{
+    char *tok;
+    char linebuf[BUFSIZ];
+    static char buf[BUFSIZ];
+
+    strcpy(linebuf, line);
+
+    /* Throw away the first token (the %xxxx) */
+    strtok(linebuf, " \t\n");
+    
+    if (!(tok = strtok(NULL, " \t\n"))) {
+       *name = NULL;
+       return 0;
+    }
+    
+    if (!strcmp(tok, "-n")) {
+       if (!(tok = strtok(NULL, " \t\n"))) {
+           return 1;
+       }
+       *flag = PART_NAME;
+    } else {
+       *flag = PART_SUBNAME;
+    }
+    strcpy(buf, tok);
+    *name = buf;
+
+    return (strtok(NULL, " \t\n")) ? 1 : 0;
+}
+
+int parseYesNo(char *s)
+{
+    if (!s || (s[0] == 'n' || s[0] == 'N') ||
+       !strcasecmp(s, "false") ||
+       !strcasecmp(s, "off") ||
+       !strcmp(s, "0")) {
+       return 0;
+    }
+
+    return 1;
+}
+
+char *findLastChar(char *s)
+{
+    char *res = s;
+
+    while (*s) {
+       if (! isspace(*s)) {
+           res = s;
+       }
+       s++;
+    }
+
+    return res;
+}
+
+int parseNum(char *line, int *res)
+{
+    char *s1;
+    
+    s1 = NULL;
+    *res = strtoul(line, &s1, 10);
+    if ((*s1) || (s1 == line) || (*res == ULONG_MAX)) {
+       return 1;
+    }
+
+    return 0;
+}
+
+StringBuf getOutputFrom(char *dir, char *argv[],
+                       char *writePtr, int writeBytesLeft,
+                       int failNonZero)
+{
+    int progPID;
+    int progDead;
+    int toProg[2];
+    int fromProg[2];
+    int status;
+    void *oldhandler;
+    int bytesWritten;
+    StringBuf readBuff;
+    int bytes;
+    unsigned char buf[8193];
+
+    oldhandler = signal(SIGPIPE, SIG_IGN);
+
+    pipe(toProg);
+    pipe(fromProg);
+    
+    if (!(progPID = fork())) {
+       close(toProg[1]);
+       close(fromProg[0]);
+       
+       dup2(toProg[0], 0);   /* Make stdin the in pipe */
+       dup2(fromProg[1], 1); /* Make stdout the out pipe */
+
+       close(toProg[0]);
+       close(fromProg[1]);
+
+       if (dir) {
+           chdir(dir);
+       }
+       
+       execvp(argv[0], argv);
+       rpmError(RPMERR_EXEC, "Couldn't exec %s", argv[0]);
+       _exit(RPMERR_EXEC);
+    }
+    if (progPID < 0) {
+       rpmError(RPMERR_FORK, "Couldn't fork %s", argv[0]);
+       return NULL;
+    }
+
+    close(toProg[0]);
+    close(fromProg[1]);
+
+    /* Do not block reading or writing from/to prog. */
+    fcntl(fromProg[0], F_SETFL, O_NONBLOCK);
+    fcntl(toProg[1], F_SETFL, O_NONBLOCK);
+    
+    readBuff = newStringBuf();
+
+    progDead = 0;
+    do {
+       if (waitpid(progPID, &status, WNOHANG)) {
+           progDead = 1;
+       }
+
+       /* Write some stuff to the process if possible */
+        if (writeBytesLeft) {
+           if ((bytesWritten =
+                 write(toProg[1], writePtr,
+                   (1024<writeBytesLeft) ? 1024 : writeBytesLeft)) < 0) {
+               if (errno != EAGAIN) {
+                   perror("getOutputFrom()");
+                   exit(1);
+               }
+               bytesWritten = 0;
+           }
+           writeBytesLeft -= bytesWritten;
+           writePtr += bytesWritten;
+       } else {
+           close(toProg[1]);
+       }
+       
+       /* Read any data from prog */
+       bytes = read(fromProg[0], buf, sizeof(buf)-1);
+       while (bytes > 0) {
+           buf[bytes] = '\0';
+           appendStringBuf(readBuff, buf);
+           bytes = read(fromProg[0], buf, sizeof(buf)-1);
+       }
+
+       /* terminate when prog dies */
+    } while (!progDead);
+
+    close(toProg[1]);
+    close(fromProg[0]);
+    signal(SIGPIPE, oldhandler);
+
+    if (writeBytesLeft) {
+       rpmError(RPMERR_EXEC, "failed to write all data to %s", argv[0]);
+       return NULL;
+    }
+    waitpid(progPID, &status, 0);
+    if (failNonZero && (!WIFEXITED(status) || WEXITSTATUS(status))) {
+       rpmError(RPMERR_EXEC, "%s failed", argv[0]);
+       return NULL;
+    }
+
+    return readBuff;
+}
diff --git a/build/misc.h b/build/misc.h
new file mode 100644 (file)
index 0000000..1ac9415
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef _MISC_H_
+#define _MISC_H_
+
+#include "spec.h"
+#include "ctype.h"
+
+#define FREE(x) { if (x) free(x); x = NULL; }
+
+#define SKIPSPACE(s) { while (*(s) && isspace(*(s))) (s)++; }
+
+#define SKIPNONSPACE(s) { while (*(s) && !isspace(*(s))) (s)++; }
+
+#define SKIPTONEWLINE(s) { while (*s && *s != '\n') s++; }
+
+#define PART_SUBNAME  0
+#define PART_NAME     1
+
+void addOrAppendListEntry(Header h, int_32 tag, char *line);
+int parseSimplePart(char *line, char **name, int *flag);
+char *findLastChar(char *s);
+int parseYesNo(char *s);
+int parseNum(char *line, int *res);
+StringBuf getOutputFrom(char *dir, char *argv[],
+                       char *writePtr, int writeBytesLeft,
+                       int failNonZero);
+
+#endif
index 28173b5..c7a69e8 100644 (file)
@@ -56,9 +56,8 @@ Cambridge, MA 02139, USA.  */
 static int
 myftw_dir (DIR **dirs, int level, int descriptors,
           char *dir, size_t len, 
-          int (*func) (const char *file,
-                       struct stat *status,
-                       int flag))
+          int (*func) (void *fl, char *name, struct stat *statp),
+          void *fl)
 {
   int got;
   struct dirent *entry;
@@ -127,13 +126,13 @@ myftw_dir (DIR **dirs, int level, int descriptors,
       else
        flag = MYFTW_F;
 
-      retval = (*func) (dir, &s, flag);
+      retval = (*func) (fl, dir, &s);
 
       if (flag == MYFTW_D)
        {
          if (retval == 0)
            retval = myftw_dir (dirs, newlev, descriptors, dir,
-                               d_namlen + len, func);
+                               d_namlen + len, func, fl);
          if (dirs[newlev] != NULL)
            {
              int save;
@@ -175,10 +174,9 @@ myftw_dir (DIR **dirs, int level, int descriptors,
 
 
 int myftw (const char *dir,
-          int (*func) (const char *file,
-                       struct stat *status,
-                       int flag),
-          int descriptors)
+          int descriptors,
+          int (*func) (void *fl, char *name, struct stat *statp),
+          void *fl)
 {
   DIR **dirs;
   size_t len;
@@ -223,12 +221,12 @@ int myftw (const char *dir,
   len = strlen (dir);
   memcpy ((void *) buf, (void *) dir, len + 1);
 
-  retval = (*func) (buf, &s, flag);
+  retval = (*func) (fl, buf, &s);
 
   if (flag == MYFTW_D)
     {
       if (retval == 0)
-       retval = myftw_dir (dirs, 0, descriptors, buf, len, func);
+       retval = myftw_dir (dirs, 0, descriptors, buf, len, func, fl);
       if (dirs[0] != NULL)
        {
          int save;
index fd82d76..05bec77 100644 (file)
@@ -3,6 +3,8 @@
 #ifndef _MYFTW_H_
 #define _MYFTW_H_
 
+#include <sys/stat.h>
+
 /* The FLAG argument to the user function passed to ftw.  */
 #define MYFTW_F                0               /* Regular file.  */
 #define MYFTW_D                1               /* Directory.  */
@@ -10,9 +12,8 @@
 #define MYFTW_NS       3               /* Unstatable file.  */
 
 int myftw (const char *dir,
-          int (*func) (const char *file,
-                       struct stat *status,
-                       int flag),
-          int descriptors);
+          int descriptors,
+          int (*func) (void *fl, char *name, struct stat *statp),
+          void *fl);
 
 #endif _MYFTW_H_
index 77cb8fe..c619c69 100644 (file)
@@ -22,8 +22,6 @@ static gid_t gids[1024];
 static char *gnames[1024];
 static int gid_used = 0;
     
-static time_t buildtime;
-
 /*
  * getUname() takes a uid, gets the username, and creates an entry in the
  * table to hold a string containing the user name.
@@ -162,14 +160,15 @@ char *getGnameS(char *gname)
     return gnames[x];
 }
 
-void markBuildTime(void)
-{
-    buildtime = time(NULL);
-}
-
 time_t *getBuildTime(void)
 {
-    return &buildtime;
+    static time_t buildTime = 0;
+
+    if (! buildTime) {
+       buildTime = time(NULL);
+    }
+
+    return &buildTime;
 }
 
 char *buildHost(void)
index 4e79744..d23adca 100644 (file)
@@ -11,7 +11,6 @@ char *getGname(gid_t gid);
 char *getGnameS(char *gname);
 
 char *buildHost(void);
-void markBuildTime(void);
 time_t *getBuildTime(void);
 
 #endif _NAMES_H_
index 0320246..ad149dc 100644 (file)
-/* The very final packaging steps */
-
-#include "config.h"
-#include "miscfn.h"
-
-#if HAVE_ALLOCA_H
-# include <alloca.h>
-#endif
-
-#include <stdlib.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-#include <sys/stat.h>
 #include <sys/types.h>
+#include <sys/stat.h>
 #include <sys/wait.h>
-#include <signal.h>
 #include <fcntl.h>
+#include <malloc.h>
 #include <string.h>
 #include <errno.h>
+#include <signal.h>
+#include <time.h>
 
-#include <sys/time.h>  /* For 'select()' interfaces */
-
-#include "cpio.h"
-#include "pack.h"
-#include "header.h"
 #include "spec.h"
-#include "specP.h"
-#include "signature.h"
-#include "rpmlead.h"
-#include "rpmlib.h"
+#include "header.h"
 #include "misc.h"
-#include "stringbuf.h"
+#include "reqprov.h"
 #include "names.h"
+#include "macro.h"
+
+#include "rpmlib.h"
 #include "files.h"
-#include "reqprov.h"
-#include "trigger.h"
-#include "messages.h"
-
-static int writeMagic(int fd, char *name, unsigned short type);
-static int cpio_gzip(int fd, char *tempdir, char *writePtr,
-                    int *archiveSize, char *prefix);
-static int generateRPM(char *name,       /* name-version-release         */
-                      char *filename,   /* output filename              */
-                      int type,         /* source or binary             */
-                      Header header,    /* the header                   */
-                      char *stempdir,   /* directory containing sources */
-                      char *fileList,   /* list of files for cpio       */
-                      char *passPhrase, /* PGP passphrase               */
-                      char *prefix);
-
-
-static int generateRPM(char *name,       /* name-version-release         */
-                      char *filename,   /* output filename              */
-                      int type,         /* source or binary             */
-                      Header header,    /* the header                   */
-                      char *stempdir,   /* directory containing sources */
-                      char *fileList,   /* list of files for cpio       */
-                      char *passPhrase,
-                      char *prefix)
+#include "lib/cpio.h"
+#include "lib/misc.h"
+#include "lib/signature.h"
+#include "lib/rpmlead.h"
+#include "lib/messages.h"
+
+#define RPM_MAJOR_NUMBER 3
+
+static int processScriptFiles(Spec spec, Package pkg);
+static int addFileToTag(Spec spec, char *file, Header h, int tag);
+static int writeRPM(Header header, char *fileName, int type,
+                   struct cpioFileMapping *cpioList, int cpioCount,
+                   char *passPhrase, char **cookie);
+static int cpio_gzip(int fd, struct cpioFileMapping *cpioList,
+                    int cpioCount, int *archiveSize);
+
+int packageSources(Spec spec)
+{
+    char *name, *version, *release;
+    char fileName[BUFSIZ];
+    HeaderIterator iter;
+    int_32 tag, count;
+    char **ptr;
+
+    /* Add some cruft */
+    headerAddEntry(spec->sourceHeader, RPMTAG_RPMVERSION,
+                  RPM_STRING_TYPE, VERSION, 1);
+    headerAddEntry(spec->sourceHeader, RPMTAG_BUILDHOST,
+                  RPM_STRING_TYPE, buildHost(), 1);
+    headerAddEntry(spec->sourceHeader, RPMTAG_BUILDTIME,
+                  RPM_INT32_TYPE, getBuildTime(), 1);
+
+    headerGetEntry(spec->sourceHeader, RPMTAG_NAME,
+                  NULL, (void **)&name, NULL);
+    headerGetEntry(spec->sourceHeader, RPMTAG_VERSION,
+                  NULL, (void **)&version, NULL);
+    headerGetEntry(spec->sourceHeader, RPMTAG_RELEASE,
+                  NULL, (void **)&release, NULL);
+    sprintf(fileName, "%s-%s-%s.%ssrc.rpm", name, version, release,
+           spec->noSource ? "no" : "");
+    spec->sourceRpmName = strdup(fileName);
+    sprintf(fileName, "%s/%s", rpmGetVar(RPMVAR_SRPMDIR), spec->sourceRpmName);
+
+    /* Add the build restrictions */
+    iter = headerInitIterator(spec->buildRestrictions);
+    while (headerNextIterator(iter, &tag, NULL, (void **)&ptr, &count)) {
+       headerAddEntry(spec->sourceHeader, tag,
+                      RPM_STRING_ARRAY_TYPE, ptr, count);
+       FREE(ptr);
+    }
+    headerFreeIterator(iter);
+    if (spec->buildArchitectureCount) {
+       headerAddEntry(spec->sourceHeader, RPMTAG_BUILDARCHS,
+                      RPM_STRING_ARRAY_TYPE,
+                      spec->buildArchitectures, spec->buildArchitectureCount);
+    }
+
+    FREE(spec->cookie);
+    
+    return writeRPM(spec->sourceHeader, fileName, RPMLEAD_SOURCE,
+                   spec->sourceCpioList, spec->sourceCpioCount,
+                   spec->passPhrase, &(spec->cookie));
+}
+
+int packageBinaries(Spec spec)
+{
+    int rc;
+    char *binFormat, *binRpm, *errorString;
+    char *name, fileName[BUFSIZ];
+    Package pkg;
+
+    pkg = spec->packages;
+    while (pkg) {
+       if (!pkg->fileList) {
+           pkg = pkg->next;
+           continue;
+       }
+
+       if ((rc = processScriptFiles(spec, pkg))) {
+           return rc;
+       }
+       
+       generateAutoReqProv(spec, pkg, pkg->cpioList, pkg->cpioCount);
+       printReqs(spec, pkg);
+       
+       if (spec->cookie) {
+           headerAddEntry(pkg->header, RPMTAG_COOKIE,
+                          RPM_STRING_TYPE, spec->cookie, 1);
+       }
+       
+       headerAddEntry(pkg->header, RPMTAG_RPMVERSION,
+                      RPM_STRING_TYPE, VERSION, 1);
+       headerAddEntry(pkg->header, RPMTAG_BUILDHOST,
+                      RPM_STRING_TYPE, buildHost(), 1);
+       headerAddEntry(pkg->header, RPMTAG_BUILDTIME,
+                      RPM_INT32_TYPE, getBuildTime(), 1);
+       headerAddEntry(pkg->header, RPMTAG_SOURCERPM, RPM_STRING_TYPE,
+                      spec->sourceRpmName ?
+                      spec->sourceRpmName : "(unknown)", 1);
+       
+       binFormat = rpmGetVar(RPMVAR_RPMFILENAME);
+       binRpm = headerSprintf(pkg->header, binFormat, rpmTagTable,
+                              rpmHeaderFormats, &errorString);
+       if (!binRpm) {
+           headerGetEntry(pkg->header, RPMTAG_NAME, NULL,
+                          (void **)&name, NULL);
+           rpmError(RPMERR_BADFILENAME, "Could not generate output "
+                    "filename for package %s: %s\n", name, errorString);
+           return RPMERR_BADFILENAME;
+       }
+       sprintf(fileName, "%s/%s", rpmGetVar(RPMVAR_RPMDIR), binRpm);
+       FREE(binRpm);
+
+       if ((rc = writeRPM(pkg->header, fileName, RPMLEAD_BINARY,
+                          pkg->cpioList, pkg->cpioCount,
+                          spec->passPhrase, NULL))) {
+           return rc;
+       }
+       
+       pkg = pkg->next;
+    }
+    
+    return 0;
+}
+
+static int writeRPM(Header header, char *fileName, int type,
+                   struct cpioFileMapping *cpioList, int cpioCount,
+                   char *passPhrase, char **cookie)
 {
-    int_32 sigtype;
-    char *sigtarget;
-    int fd, ifd, count, archiveSize;
-    unsigned char buffer[8192];
+    int archiveSize, fd, ifd, rc, count, arch, os, sigtype;
+    char *sigtarget, *name, *version, *release;
+    char buf[BUFSIZ];
     Header sig;
+    struct rpmlead lead;
 
     /* Add the a bogus archive size to the Header */
     headerAddEntry(header, RPMTAG_ARCHIVESIZE, RPM_INT32_TYPE,
                   &archiveSize, 1);
 
+    /* Create and add the cookie */
+    if (cookie) {
+       sprintf(buf, "%s %d", buildHost(), (int) time(NULL));
+       *cookie = strdup(buf);
+       headerAddEntry(header, RPMTAG_COOKIE, RPM_STRING_TYPE, *cookie, 1);
+    }
+    
     /* Write the header */
-    if (makeTempFile(NULL, &sigtarget, &fd))
-       return 1;
-
+    if (makeTempFile(NULL, &sigtarget, &fd)) {
+       rpmError(RPMERR_CREATE, "Unable to open temp file");
+       return RPMERR_CREATE;
+    }
     headerWrite(fd, header, HEADER_MAGIC_YES);
-    
+            
     /* Write the archive and get the size */
-    if (cpio_gzip(fd, stempdir, fileList, &archiveSize, prefix)) {
+    if ((rc = cpio_gzip(fd, cpioList, cpioCount, &archiveSize))) {
        close(fd);
        unlink(sigtarget);
        free(sigtarget);
-       return 1;
+       return rc;
     }
 
     /* Now set the real archive size in the Header */
     headerModifyEntry(header, RPMTAG_ARCHIVESIZE,
                      RPM_INT32_TYPE, &archiveSize, 1);
-
-    /* Rewind and rewrite the Header */
     lseek(fd, 0,  SEEK_SET);
     headerWrite(fd, header, HEADER_MAGIC_YES);
-    
+
     close(fd);
-    
-    /* Now write the lead */
-    if ((fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1) {
-       fprintf(stderr, "Could not open %s\n", filename);
+
+    /* Open the output file */
+    if ((fd = open(fileName, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1) {
+       rpmError(RPMERR_CREATE, "Could not open %s\n", fileName);
        unlink(sigtarget);
        free(sigtarget);
-       unlink(filename);
-       return 1;
+       return RPMERR_CREATE;
     }
-    if (writeMagic(fd, name, type)) {
+
+    /* Now write the lead */
+    headerGetEntry(header, RPMTAG_NAME, NULL, (void **)&name, NULL);
+    headerGetEntry(header, RPMTAG_VERSION, NULL, (void **)&version, NULL);
+    headerGetEntry(header, RPMTAG_RELEASE, NULL, (void **)&release, NULL);
+    sprintf(buf, "%s-%s-%s", name, version, release);
+    rpmGetArchInfo(NULL, &arch);
+    rpmGetOsInfo(NULL, &os);
+    lead.major = RPM_MAJOR_NUMBER;
+    lead.minor = 0;
+    lead.type = type;
+    lead.archnum = arch;
+    lead.osnum = os;
+    lead.signature_type = RPMSIG_HEADERSIG;  /* New-style signature */
+    strncpy(lead.name, buf, sizeof(lead.name));
+    if (writeLead(fd, &lead)) {
+       rpmError(RPMERR_NOSPACE, "Unable to write package: %s",
+                strerror(errno));
        close(fd);
        unlink(sigtarget);
        free(sigtarget);
-       unlink(filename);
-       return 1;
+       unlink(fileName);
+       return rc;
     }
 
     /* Generate the signature */
     sigtype = rpmLookupSignatureType();
-    rpmMessage(RPMMESS_VERBOSE, "Generating signature: %d\n", sigtype);
     fflush(stdout);
     sig = rpmNewSignature();
     rpmAddSignature(sig, sigtarget, RPMSIGTAG_SIZE, passPhrase);
     rpmAddSignature(sig, sigtarget, RPMSIGTAG_MD5, passPhrase);
-    if (sigtype>0) {
+    if (sigtype > 0) {
+       rpmMessage(RPMMESS_NORMAL, "Generating signature: %d\n", sigtype);
        rpmAddSignature(sig, sigtarget, sigtype, passPhrase);
     }
-    if (rpmWriteSignature(fd, sig)) {
+    if ((rc = rpmWriteSignature(fd, sig))) {
        close(fd);
        unlink(sigtarget);
        free(sigtarget);
-       unlink(filename);
+       unlink(fileName);
        rpmFreeSignature(sig);
-       return 1;
+       return rc;
     }
     rpmFreeSignature(sig);
-
+       
     /* Append the header and archive */
     ifd = open(sigtarget, O_RDONLY);
-    while ((count = read(ifd, buffer, sizeof(buffer))) > 0) {
-        if (count == -1) {
-           perror("Couldn't read sigtarget");
+    while ((count = read(ifd, buf, sizeof(buf))) > 0) {
+       if (count == -1) {
+           rpmError(RPMERR_READERROR, "Unable to read sigtarget: %s",
+                    strerror(errno));
            close(ifd);
            close(fd);
            unlink(sigtarget);
            free(sigtarget);
-           unlink(filename);
-           return 1;
-        }
-        if (write(fd, buffer, count) < 0) {
-           perror("Couldn't write package");
+           unlink(fileName);
+           return RPMERR_READERROR;
+       }
+       if (write(fd, buf, count) < 0) {
+           rpmError(RPMERR_NOSPACE, "Unable to write package: %s",
+                    strerror(errno));
            close(ifd);
            close(fd);
            unlink(sigtarget);
            free(sigtarget);
-           unlink(filename);
-           return 1;
-        }
+           unlink(fileName);
+           return RPMERR_NOSPACE;
+       }
     }
     close(ifd);
     close(fd);
     unlink(sigtarget);
     free(sigtarget);
 
-    rpmMessage(RPMMESS_VERBOSE, "Wrote: %s\n", filename);
-    
-    return 0;
-}
-
-static int writeMagic(int fd, char *name,
-                     unsigned short type)
-{
-    struct rpmlead lead;
-    int arch, os;
-
-    rpmGetArchInfo(NULL, &arch);
-    rpmGetOsInfo(NULL, &os);
-
-    /* There are the Major and Minor numbers */
-    lead.major = 3;
-    lead.minor = 0;
-    lead.type = type;
-    lead.archnum = arch;
-    lead.osnum = os;
-    lead.signature_type = RPMSIG_HEADERSIG;  /* New-style signature */
-    strncpy(lead.name, name, sizeof(lead.name));
-    memset(lead.reserved, 0, sizeof(lead.reserved));
-
-    writeLead(fd, &lead);
+    rpmMessage(RPMMESS_NORMAL, "Wrote: %s\n", fileName);
 
     return 0;
 }
 
-static int cpio_gzip(int fd, char *tempdir, char *writePtr,
-                    int *archiveSize, char *prefix)
+static int cpio_gzip(int fd, struct cpioFileMapping *cpioList,
+                    int cpioCount, int *archiveSize)
 {
-    int gzipPID;
-    int toGzip[2];
-    int rc;
-    char * gzipbin;
-    char * writebuf;
-    char * chptr;
-    int numMappings;
-    struct cpioFileMapping * cpioList;
-    int status;
+    char *gzipbin;
     void *oldhandler;
-    char savecwd[1024];
-    char * failedFile;
+    int rc, gzipPID, toGzip[2], status;
+    char *failedFile;
 
     gzipbin = rpmGetVar(RPMVAR_GZIPBIN);
-
-    numMappings = 0;
-    chptr = writePtr;
-    while (chptr && *chptr) {
-       numMappings++;
-       chptr = strchr(chptr, '\n');
-       if (chptr) *chptr++ = '\n';
-    }
-
-    writebuf = alloca(strlen(writePtr) + 1);
-    strcpy(writebuf, writePtr);
-
-    cpioList = alloca(sizeof(*cpioList) * numMappings);
-    chptr = writebuf;
-    numMappings = 0;
-    while (chptr && *chptr) {
-       cpioList[numMappings].fsPath = chptr;
-       cpioList[numMappings].mapFlags = tempdir ? CPIO_FOLLOW_SYMLINKS : 0;
-
-       chptr = strchr(chptr, '\n');
-       if (chptr) *chptr++ = '\0';
-
-       /* hack */
-       if (!strlen(cpioList[numMappings].fsPath)) {
-           cpioList[numMappings].fsPath = ".";
-       }
-
-       numMappings++;
-    }
     oldhandler = signal(SIGPIPE, SIG_IGN);
 
-    pipe(toGzip);
-    
     /* GZIP */
+    pipe(toGzip);
     if (!(gzipPID = fork())) {
        close(toGzip[1]);
        
@@ -248,520 +295,105 @@ static int cpio_gzip(int fd, char *tempdir, char *writePtr,
        rpmError(RPMERR_EXEC, "Couldn't exec gzip");
        _exit(RPMERR_EXEC);
     }
+    close(toGzip[0]);
     if (gzipPID < 0) {
+       close(toGzip[1]);
        rpmError(RPMERR_FORK, "Couldn't fork gzip");
        return RPMERR_FORK;
     }
 
-    close(toGzip[0]);
-
-    getcwd(savecwd, 1024);
-    
-    if (tempdir) {
-       chdir(tempdir);
-    } else if (rpmGetVar(RPMVAR_ROOT)) {
-       if (chdir(rpmGetVar(RPMVAR_ROOT))) {
-           rpmError(RPMERR_EXEC, "Couldn't chdir to %s",
-                 rpmGetVar(RPMVAR_ROOT));
-           exit(RPMERR_EXEC);
-       }
-    } else {
-       /* This is important! */
-       chdir("/");
-    }
-    if (prefix) {
-       if (chdir(prefix)) {
-           rpmError(RPMERR_EXEC, "Couldn't chdir to %s", prefix);
-           _exit(RPMERR_EXEC);
-       }
-    }
-
-    rc = cpioBuildArchive(toGzip[1], cpioList, numMappings, NULL, NULL, 
+    rc = cpioBuildArchive(fd, cpioList, cpioCount, NULL, NULL,
                          archiveSize, &failedFile);
 
-    chdir(savecwd);
-
-    close(toGzip[1]); /* Terminates the gzip process */
-    
+    close(toGzip[1]);
     signal(SIGPIPE, oldhandler);
-
     waitpid(gzipPID, &status, 0);
     if (!WIFEXITED(status) || WEXITSTATUS(status)) {
-       rpmError(RPMERR_GZIP, "gzip failed");
+       rpmError(RPMERR_GZIP, "Execution of gzip failed");
        return 1;
     }
 
     if (rc) {
        if (rc & CPIO_CHECK_ERRNO)
-           rpmError(RPMERR_CPIO, "cpio failed on file %s: %d: %s", failedFile, 
-                       rc, strerror(errno));
+           rpmError(RPMERR_CPIO, "cpio failed on file %s: %s",
+                    failedFile, strerror(errno));
        else
-           rpmError(RPMERR_CPIO, "cpio failed on file %s: %d", failedFile, rc);
+           rpmError(RPMERR_CPIO, "cpio failed on file %s: %d",
+                    failedFile, rc);
        return 1;
     }
 
     return 0;
 }
 
-
-int packageBinaries(Spec s, char *passPhrase, int doPackage)
+static int addFileToTag(Spec spec, char *file, Header h, int tag)
 {
-    char name[1024];
-    char *nametmp;
-    char filename[1024];
-    char sourcerpm[1024];
-    char * binrpm;
-    char *icon;
-    int iconFD;
-    struct stat statbuf;
-    struct PackageRec *pr;
-    Header outHeader;
-    HeaderIterator headerIter;
-    int_32 tag, type, c;
-    void *ptr;
-    char *version;
-    char *release;
-    char *vendor;
-    char *dist;
-    char *packager;
-    char *packageVersion, *packageRelease;
-    char *prefix, *prefixSave;
-    char * arch, * os;
-    char * binformat, * errorString;
-    int prefixLen;
-    int size;
-    StringBuf cpioFileList;
-    char **farray, *file;
-    int count;
-    int *fflagarray;
-    
-    if (!headerGetEntry(s->packages->header, RPMTAG_VERSION, NULL,
-                 (void *) &version, NULL)) {
-       rpmError(RPMERR_BADSPEC, "No version field");
-       return RPMERR_BADSPEC;
-    }
-    if (!headerGetEntry(s->packages->header, RPMTAG_RELEASE, NULL,
-                 (void *) &release, NULL)) {
-       rpmError(RPMERR_BADSPEC, "No release field");
-       return RPMERR_BADSPEC;
-    }
-    /* after the headerGetEntry() these are just pointers into the   */
-    /* header structure, which can be moved around by headerAddEntry */
-    version = strdup(version);
-    release = strdup(release);
-
-    sprintf(sourcerpm, "%s-%s-%s.%ssrc.rpm", s->name, version, release,
-           (s->numNoPatch + s->numNoSource) ? "no" : "");
-
-    vendor = NULL;
-    if (!headerIsEntry(s->packages->header, RPMTAG_VENDOR)) {
-       vendor = rpmGetVar(RPMVAR_VENDOR);
-    }
-    dist = NULL;
-    if (!headerIsEntry(s->packages->header, RPMTAG_DISTRIBUTION)) {
-       dist = rpmGetVar(RPMVAR_DISTRIBUTION);
+    StringBuf sb;
+    char *s;
+    char buf[BUFSIZ];
+    FILE *f;
+
+    sb = newStringBuf();
+    if (headerGetEntry(h, tag, NULL, (void **)&s, NULL)) {
+       appendLineStringBuf(sb, s);
+       headerRemoveEntry(h, tag);
+    }
+    sprintf(buf, "%s/%s/%s", rpmGetVar(RPMVAR_BUILDDIR),
+           spec->buildSubdir, file);
+    if ((f = fopen(buf, "r")) == NULL) {
+       freeStringBuf(sb);
+       return 1;
     }
-    packager = NULL;
-    if (!headerIsEntry(s->packages->header, RPMTAG_PACKAGER)) {
-       packager = rpmGetVar(RPMVAR_PACKAGER);
-    }
-
-    /* Look through for each package */
-    pr = s->packages;
-    while (pr) {
-       /* A file count of -1 means no package */
-       if (pr->files == -1) {
-           pr = pr->next;
-           continue;
-       }
-
-       /* Handle subpackage version/release overrides */
-       if (!headerGetEntry(pr->header, RPMTAG_VERSION, NULL,
-                     (void *) &packageVersion, NULL)) {
-           packageVersion = version;
-       }
-       if (!headerGetEntry(pr->header, RPMTAG_RELEASE, NULL,
-                     (void *) &packageRelease, NULL)) {
-           packageRelease = release;
-       }
-        /* after the headerGetEntry() these are just pointers into the   */
-        /* header structure, which can be moved around by headerAddEntry */
-        packageVersion = strdup(packageVersion);
-        packageRelease = strdup(packageRelease);
-       
-       /* Figure out the name of this package */
-       if (!headerGetEntry(pr->header, RPMTAG_NAME, NULL, (void *)&nametmp, NULL)) {
-           rpmError(RPMERR_INTERNAL, "Package has no name!");
-           return RPMERR_INTERNAL;
-       }
-       sprintf(name, "%s-%s-%s", nametmp, packageVersion, packageRelease);
-
-       if (doPackage == PACK_PACKAGE) {
-           rpmMessage(RPMMESS_VERBOSE, "Binary Packaging: %s\n", name);
-       } else {
-           rpmMessage(RPMMESS_VERBOSE, "File List Check: %s\n", name);
-       }
-       
-       /**** Generate the Header ****/
-       
-       /* Here's the plan: copy the package's header,  */
-       /* then add entries from the primary header     */
-       /* that don't already exist.                    */
-       outHeader = headerCopy(pr->header);
-       headerIter = headerInitIterator(s->packages->header);
-       while (headerNextIterator(headerIter, &tag, &type, &ptr, &c)) {
-           /* Some tags we don't copy */
-           switch (tag) {
-             case RPMTAG_PREIN:
-             case RPMTAG_POSTIN:
-             case RPMTAG_PREUN:
-             case RPMTAG_POSTUN:
-             case RPMTAG_PREINPROG:
-             case RPMTAG_POSTINPROG:
-             case RPMTAG_PREUNPROG:
-             case RPMTAG_POSTUNPROG:
-             case RPMTAG_VERIFYSCRIPT:
-                 continue;
-                 break;  /* Shouldn't need this */
-             default:
-                 if (! headerIsEntry(outHeader, tag)) {
-                     headerAddEntry(outHeader, tag, type, ptr, c);
-                 }
-           }
-       }
-       headerFreeIterator(headerIter);
-
-       headerRemoveEntry(outHeader, RPMTAG_BUILDARCHS);
-
-       rpmGetArchInfo(&arch, NULL);
-       rpmGetOsInfo(&os, NULL);
-       
-       /* Add some final entries to the header */
-       headerAddEntry(outHeader, RPMTAG_OS, RPM_STRING_TYPE, os, 1);
-       headerAddEntry(outHeader, RPMTAG_ARCH, RPM_STRING_TYPE, arch, 1);
-       headerAddEntry(outHeader, RPMTAG_BUILDTIME, RPM_INT32_TYPE, getBuildTime(), 1);
-       headerAddEntry(outHeader, RPMTAG_BUILDHOST, RPM_STRING_TYPE, buildHost(), 1);
-       headerAddEntry(outHeader, RPMTAG_SOURCERPM, RPM_STRING_TYPE, sourcerpm, 1);
-       headerAddEntry(outHeader, RPMTAG_RPMVERSION, RPM_STRING_TYPE, VERSION, 1);
-       if (pr->icon) {
-           sprintf(filename, "%s/%s", rpmGetVar(RPMVAR_SOURCEDIR), pr->icon);
-           stat(filename, &statbuf);
-           icon = malloc(statbuf.st_size);
-           iconFD = open(filename, O_RDONLY, 0644);
-           read(iconFD, icon, statbuf.st_size);
-           close(iconFD);
-           if (! strncmp(icon, "GIF", 3)) {
-               headerAddEntry(outHeader, RPMTAG_GIF, RPM_BIN_TYPE,
-                        icon, statbuf.st_size);
-           } else if (! strncmp(icon, "/* XPM", 6)) {
-               headerAddEntry(outHeader, RPMTAG_XPM, RPM_BIN_TYPE,
-                        icon, statbuf.st_size);
-           } else {
-              rpmError(RPMERR_BADSPEC, "Unknown icon type");
-              return 1;
-           }
-           free(icon);
-       }
-       if (vendor) {
-           headerAddEntry(outHeader, RPMTAG_VENDOR, RPM_STRING_TYPE, vendor, 1);
-       }
-       if (dist) {
-           headerAddEntry(outHeader, RPMTAG_DISTRIBUTION, RPM_STRING_TYPE, dist, 1);
-       }
-       if (packager) {
-           headerAddEntry(outHeader, RPMTAG_PACKAGER, RPM_STRING_TYPE, packager, 1);
-       }
-       
-       /**** Process the file list ****/
-
-       prefixSave = prefix = NULL;
-       prefixLen = 0;
-       if (headerGetEntry(outHeader, RPMTAG_DEFAULTPREFIX,
-                    NULL, (void **)&prefix, NULL)) {
-            /* after the headerGetEntry() this is just pointers into the     */
-            /* header structure, which can be moved around by headerAddEntry */
-           prefixSave = prefix = strdup(prefix);
-           while (*prefix && (*prefix == '/')) {
-               prefix++;
-           }
-           if (! *prefix) {
-               prefix = NULL;
-               prefixLen = 0;
-           } else {
-               prefixLen = strlen(prefix);
-               rpmMessage(RPMMESS_VERBOSE, "Package Prefix = %s\n", prefix);
-           }
-       }
-       
-       if (process_filelist(outHeader, pr, pr->filelist, &size, nametmp,
-                            packageVersion, packageRelease, RPMLEAD_BINARY,
-                            prefix, NULL)) {
-           return 1;
-       }
-
-       if (!headerGetEntry(outHeader, RPMTAG_FILENAMES, NULL,
-                           (void **) &farray, &count)) {
-           /* count may already be 0, but this is safer */
-           count = 0;
-       }
-       headerGetEntry(outHeader, RPMTAG_FILEFLAGS, NULL,
-                      (void **) &fflagarray, NULL);
-
-       cpioFileList = newStringBuf();
-       while (count--) {
-           file = *farray++;
-           while (*file == '/') {
-               file++;  /* Skip leading "/" */
-           }
-           if (prefix) {
-               if (strncmp(prefix, file, prefixLen)) {
-                   rpmError(RPMERR_BADSPEC, "File doesn't match prefix (%s): %s",
-                         prefix, file);
-                   return 1;
-               }
-               file += prefixLen;
-               if (*file) {
-                   file++;  /* 1 for "/" */
-               }
-           }
-
-           if (! (*fflagarray & RPMFILE_GHOST)) {
-               appendLineStringBuf(cpioFileList, file);
-           }
-           fflagarray++;
-       }
-       
-       /* Generate any automatic require/provide entries */
-       /* Then add the whole thing to the header         */
-       if (s->autoReqProv) {
-           generateAutoReqProv(outHeader, pr);
-       }
-       processReqProv(outHeader, pr);
-
-       /* Generate the any trigger entries */
-       generateTriggerEntries(outHeader, pr);
-       
-       /* And add the final Header entry */
-       headerAddEntry(outHeader, RPMTAG_SIZE, RPM_INT32_TYPE, &size, 1);
-
-       /**** Make the RPM ****/
-
-       /* Make the output RPM filename */
-       if (doPackage == PACK_PACKAGE) {
-           binformat = rpmGetVar(RPMVAR_RPMFILENAME);
-           binrpm = headerSprintf(outHeader, binformat, rpmTagTable,
-                                  rpmHeaderFormats, &errorString);
-
-           if (!binrpm) {
-               rpmError(RPMERR_BADFILENAME, "could not generate output "
-                        "filename for package %s: %s\n", name, binrpm);
-           }
-
-           sprintf(filename, "%s/%s", rpmGetVar(RPMVAR_RPMDIR), binrpm);
-           free(binrpm);
-
-           if (generateRPM(name, filename, RPMLEAD_BINARY, outHeader, NULL,
-                           getStringBuf(cpioFileList), passPhrase, prefix)) {
-               /* Build failed */
-               return 1;
-           }
-       }
-
-       freeStringBuf(cpioFileList);
-       headerFree(outHeader);
-       
-        if (prefixSave)
-           free(prefixSave);
-        free(packageVersion);
-        free(packageRelease);
-       
-       pr = pr->next;
+    while (fgets(buf, sizeof(buf), f)) {
+       expandMacros(&spec->macros, buf);
+       appendStringBuf(sb, buf);
     }
+    fclose(f);
+    
+    headerAddEntry(h, tag, RPM_STRING_TYPE, getStringBuf(sb), 1);
 
-    free(version);
-    free(release);
-   
+    freeStringBuf(sb);
     return 0;
 }
 
-/**************** SOURCE PACKAGING ************************/
-
-int packageSource(Spec s, char *passPhrase)
+static int processScriptFiles(Spec spec, Package pkg)
 {
-    struct sources *source;
-    struct PackageRec *package;
-    char *tempdir;
-    char src[1024], dest[1024], fullname[1024], filename[1024], specFile[1024];
-    char *version;
-    char *release;
-    char *vendor;
-    char *dist;
-    char *p;
-    Header outHeader;
-    StringBuf filelist;
-    StringBuf cpioFileList;
-    int size;
-    char **sources;
-    char **patches;
-    char * arch, * os;
-    int scount, pcount;
-    int skipi;
-    int_32 *skip;
-
-    /**** Create links for all the sources ****/
-    
-    tempdir = tempnam(rpmGetVar(RPMVAR_TMPPATH), "rpmbuild");
-    mkdir(tempdir, 0700);
-
-    filelist = newStringBuf();     /* List in the header */
-    cpioFileList = newStringBuf(); /* List for cpio      */
-
-    sources = malloc((s->numSources+1) * sizeof(char *));
-    patches = malloc((s->numPatches+1) * sizeof(char *));
-    
-    /* Link in the spec file and all the sources */
-    p = strrchr(s->specfile, '/');
-    sprintf(dest, "%s%s", tempdir, p);
-    strcpy(specFile, dest);
-    symlink(s->specfile, dest);
-    appendLineStringBuf(filelist, dest);
-    appendLineStringBuf(cpioFileList, p+1);
-    source = s->sources;
-    scount = 0;
-    pcount = 0;
-    while (source) {
-       if (source->ispatch) {
-           skipi = s->numNoPatch - 1;
-           skip = s->noPatch;
-       } else {
-           skipi = s->numNoSource - 1;
-           skip = s->noSource;
-       }
-       while (skipi >= 0) {
-           if (skip[skipi] == source->num) {
-               break;
-           }
-           skipi--;
-       }
-       sprintf(src, "%s/%s", rpmGetVar(RPMVAR_SOURCEDIR), source->source);
-       sprintf(dest, "%s/%s", tempdir, source->source);
-       if (skipi < 0) {
-           symlink(src, dest);
-           appendLineStringBuf(cpioFileList, source->source);
-       } else {
-           rpmMessage(RPMMESS_VERBOSE, "Skipping source/patch (%d): %s\n",
-                   source->num, source->source);
-       }
-       appendLineStringBuf(filelist, src);
-       if (source->ispatch) {
-           patches[pcount++] = source->fullSource;
-       } else {
-           sources[scount++] = source->fullSource;
+    if (pkg->preInFile) {
+       if (addFileToTag(spec, pkg->preInFile, pkg->header, RPMTAG_PREIN)) {
+           rpmError(RPMERR_BADFILENAME,
+                    "Could not open PreIn file: %s", pkg->preInFile);
+           return RPMERR_BADFILENAME;
        }
-       source = source->next;
     }
-    /* ... and icons */
-    package = s->packages;
-    while (package) {
-       if (package->icon) {
-           sprintf(src, "%s/%s", rpmGetVar(RPMVAR_SOURCEDIR), package->icon);
-           sprintf(dest, "%s/%s", tempdir, package->icon);
-           appendLineStringBuf(filelist, dest);
-           appendLineStringBuf(cpioFileList, package->icon);
-           symlink(src, dest);
+    if (pkg->preUnFile) {
+       if (addFileToTag(spec, pkg->preUnFile, pkg->header, RPMTAG_PREUN)) {
+           rpmError(RPMERR_BADFILENAME,
+                    "Could not open PreUn file: %s", pkg->preUnFile);
+           return RPMERR_BADFILENAME;
        }
-       package = package->next;
     }
-
-    /**** Generate the Header ****/
-    
-    if (!headerGetEntry(s->packages->header, RPMTAG_VERSION, NULL,
-                 (void *) &version, NULL)) {
-       rpmError(RPMERR_BADSPEC, "No version field");
-       return RPMERR_BADSPEC;
-    }
-    if (!headerGetEntry(s->packages->header, RPMTAG_RELEASE, NULL,
-                 (void *) &release, NULL)) {
-       rpmError(RPMERR_BADSPEC, "No release field");
-       return RPMERR_BADSPEC;
-    }
-
-    rpmGetArchInfo(&arch, NULL);
-    rpmGetOsInfo(&os, NULL);
-
-    outHeader = headerCopy(s->packages->header);
-    headerAddEntry(outHeader, RPMTAG_OS, RPM_STRING_TYPE, os, 1);
-    headerAddEntry(outHeader, RPMTAG_ARCH, RPM_STRING_TYPE, arch, 1);
-    headerAddEntry(outHeader, RPMTAG_BUILDTIME, RPM_INT32_TYPE, getBuildTime(), 1);
-    headerAddEntry(outHeader, RPMTAG_BUILDHOST, RPM_STRING_TYPE, buildHost(), 1);
-    headerAddEntry(outHeader, RPMTAG_RPMVERSION, RPM_STRING_TYPE, VERSION, 1);
-    if (scount) 
-        headerAddEntry(outHeader, RPMTAG_SOURCE, RPM_STRING_ARRAY_TYPE, sources, scount);
-    if (pcount)
-        headerAddEntry(outHeader, RPMTAG_PATCH, RPM_STRING_ARRAY_TYPE, patches, pcount);
-    if (s->numNoSource) {
-       headerAddEntry(outHeader, RPMTAG_NOSOURCE, RPM_INT32_TYPE, s->noSource,
-                s->numNoSource);
-    }
-    if (s->numNoPatch) {
-       headerAddEntry(outHeader, RPMTAG_NOPATCH, RPM_INT32_TYPE, s->noPatch,
-                s->numNoPatch);
-    }
-    if (!headerIsEntry(s->packages->header, RPMTAG_VENDOR)) {
-       if ((vendor = rpmGetVar(RPMVAR_VENDOR))) {
-           headerAddEntry(outHeader, RPMTAG_VENDOR, RPM_STRING_TYPE, vendor, 1);
+    if (pkg->postInFile) {
+       if (addFileToTag(spec, pkg->postInFile, pkg->header, RPMTAG_POSTIN)) {
+           rpmError(RPMERR_BADFILENAME,
+                    "Could not open PostIn file: %s", pkg->postInFile);
+           return RPMERR_BADFILENAME;
        }
     }
-    if (!headerIsEntry(s->packages->header, RPMTAG_DISTRIBUTION)) {
-       if ((dist = rpmGetVar(RPMVAR_DISTRIBUTION))) {
-           headerAddEntry(outHeader, RPMTAG_DISTRIBUTION, RPM_STRING_TYPE, dist, 1);
+    if (pkg->postUnFile) {
+       if (addFileToTag(spec, pkg->postUnFile, pkg->header, RPMTAG_POSTUN)) {
+           rpmError(RPMERR_BADFILENAME,
+                    "Could not open PostUn file: %s", pkg->postUnFile);
+           return RPMERR_BADFILENAME;
        }
     }
-
-    /* Process the file list */
-    if (process_filelist(outHeader, NULL, filelist, &size,
-                        s->name, version, release, RPMLEAD_SOURCE,
-                        NULL, specFile)) {
-       return 1;
-    }
-
-    /* And add the final Header entry */
-    headerAddEntry(outHeader, RPMTAG_SIZE, RPM_INT32_TYPE, &size, 1);
-
-    /**** Make the RPM ****/
-
-    sprintf(fullname, "%s-%s-%s", s->name, version, release);
-    sprintf(filename, "%s/%s.%ssrc.rpm", rpmGetVar(RPMVAR_SRPMDIR), fullname,
-           (s->numNoPatch + s->numNoSource) ? "no" : "");
-    rpmMessage(RPMMESS_VERBOSE, "Source Packaging: %s\n", fullname);
-
-    if (generateRPM(fullname, filename, RPMLEAD_SOURCE, outHeader,
-                   tempdir, getStringBuf(cpioFileList), passPhrase, NULL)) {
-       return 1;
-    }
-    
-    /**** Now clean up ****/
-
-    freeStringBuf(filelist);
-    freeStringBuf(cpioFileList);
-    
-    source = s->sources;
-    while (source) {
-       sprintf(dest, "%s/%s", tempdir, source->source);
-       unlink(dest);
-       source = source->next;
-    }
-    package = s->packages;
-    while (package) {
-       if (package->icon) {
-           sprintf(dest, "%s/%s", tempdir, package->icon);
-           unlink(dest);
+    if (pkg->verifyFile) {
+       if (addFileToTag(spec, pkg->verifyFile, pkg->header,
+                        RPMTAG_VERIFYSCRIPT)) {
+           rpmError(RPMERR_BADFILENAME,
+                    "Could not open VerifyScript file: %s", pkg->verifyFile);
+           return RPMERR_BADFILENAME;
        }
-       package = package->next;
     }
-    sprintf(dest, "%s%s", tempdir, strrchr(s->specfile, '/'));
-    unlink(dest);
-    rmdir(tempdir);
-    
+
     return 0;
 }
index d144484..51905e2 100644 (file)
@@ -1,14 +1,4 @@
-/* pack.h -- final packing steps */
-
-#ifndef _PACK_H_
-#define _PACK_H_
-
 #include "spec.h"
 
-#define PACK_PACKAGE   1
-#define PACK_NOPACKAGE 0
-
-int packageBinaries(Spec s, char *passPhrase, int doPackage);
-int packageSource(Spec s, char *passPhrase);
-
-#endif _PACK_H_
+int packageBinaries(Spec spec);
+int packageSources(Spec spec);
diff --git a/build/package.c b/build/package.c
new file mode 100644 (file)
index 0000000..9184282
--- /dev/null
@@ -0,0 +1,128 @@
+#include "spec.h"
+#include "package.h"
+#include "misc.h"
+#include "rpmlib.h"
+#include "files.h"
+
+#include <malloc.h>
+
+int lookupPackage(Spec spec, char *name, int flag, Package *pkg)
+{
+    char buf[BUFSIZ];
+    char *n, *fullName;
+    Package p;
+    
+    /* "main" package */
+    if (! name) {
+       if (pkg) {
+           *pkg = spec->packages;
+       }
+       return 0;
+    }
+
+    /* Construct package name */
+    if (flag == PART_SUBNAME) {
+       headerGetEntry(spec->packages->header, RPMTAG_NAME,
+                      NULL, (void *) &n, NULL);
+       sprintf(buf, "%s-%s", n, name);
+       fullName = buf;
+    } else {
+       fullName = name;
+    }
+
+    p = spec->packages;
+    while (p) {
+       headerGetEntry(p->header, RPMTAG_NAME, NULL, (void *) &n, NULL);
+       if (n && (! strcmp(fullName, n))) {
+           if (pkg) {
+               *pkg = p;
+           }
+           return 0;
+       }
+       p = p->next;
+    }
+
+    if (pkg) {
+       *pkg = NULL;
+    }
+    return 1;
+}
+
+Package newPackage(Spec spec)
+{
+    Package p;
+    Package pp;
+
+    p = malloc(sizeof(*p));
+
+    p->header = headerNew();
+    p->icon = NULL;
+    p->autoReqProv = 1;
+    
+#if 0    
+    p->reqProv = NULL;
+    p->triggers = NULL;
+    p->triggerScripts = NULL;
+#endif
+    
+    p->fileFile = NULL;
+    p->fileList = NULL;
+    p->next = NULL;
+
+    p->cpioList = NULL;
+    p->cpioCount = 0;
+
+    p->preInFile = NULL;
+    p->postInFile = NULL;
+    p->preUnFile = NULL;
+    p->postUnFile = NULL;
+    p->verifyFile = NULL;
+
+    p->specialDoc = NULL;
+
+    if (! spec->packages) {
+       spec->packages = p;
+    } else {
+       /* Always add package to end of list */
+       pp = spec->packages;
+       while (pp->next) {
+           pp = pp->next;
+       }
+       pp->next = p;
+    }
+
+    return p;
+}
+
+void freePackages(Spec spec)
+{
+    Package p;
+
+    while (spec->packages) {
+       p = spec->packages;
+       spec->packages = p->next;
+       freePackage(p);
+    }
+}
+
+void freePackage(Package p)
+{
+    if (! p) {
+       return;
+    }
+    
+    FREE(p->preInFile);
+    FREE(p->postInFile);
+    FREE(p->preUnFile);
+    FREE(p->postUnFile);
+    FREE(p->verifyFile);
+
+    headerFree(p->header);
+    freeStringBuf(p->fileList);
+    FREE(p->fileFile);
+    freeCpioList(p->cpioList, p->cpioCount);
+
+    freeStringBuf(p->specialDoc);
+
+    free(p);
+}
diff --git a/build/package.h b/build/package.h
new file mode 100644 (file)
index 0000000..9086386
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef _PACKAGE_H_
+#define _PACKAGE_H_
+
+int lookupPackage(Spec spec, char *name, int flag, Package *pkg);
+Package newPackage(Spec spec);
+void freePackages(Spec spec);
+void freePackage(Package p);
+
+#endif
diff --git a/build/parse.h b/build/parse.h
new file mode 100644 (file)
index 0000000..4b74828
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef _PARSE_H_
+#define _PARSE_H_
+
+int parseChangelog(Spec spec);
+int parseDescription(Spec spec);
+int parseFiles(Spec spec);
+int parsePreamble(Spec spec, int initialPackage);
+int parsePrep(Spec spec);
+int parseRequiresConflicts(Spec spec, Package pkg, char *field, int tag);
+int parseProvidesObsoletes(Spec spec, Package pkg, char *field, int tag);
+int parseScript(Spec spec, int parsePart);
+int parseBuildInstallClean(Spec spec, int parsePart);
+
+int parseSpec(Spec *specp, char *specFile, char *buildRoot,
+             int inBuildArch, char *passPhrase, char *cookie);
+
+#endif
diff --git a/build/parseBuildInstallClean.c b/build/parseBuildInstallClean.c
new file mode 100644 (file)
index 0000000..fbb9440
--- /dev/null
@@ -0,0 +1,46 @@
+#include "read.h"
+#include "part.h"
+#include "rpmlib.h"
+
+int parseBuildInstallClean(Spec spec, int parsePart)
+{
+    int nextPart;
+    StringBuf *sbp = NULL;
+    char *name = NULL;
+
+    switch (parsePart) {
+      case PART_BUILD:
+       sbp = &(spec->build);
+       name = "%build";
+       break;
+      case PART_INSTALL:
+       sbp = &(spec->install);
+       name = "%install";
+       break;
+      case PART_CLEAN:
+       sbp = &(spec->clean);
+       name = "%clean";
+       break;
+    }
+    
+    if (*sbp) {
+       rpmError(RPMERR_BADSPEC, "line %d: second %s", spec->lineNum, name);
+       return RPMERR_BADSPEC;
+    }
+    
+    *sbp = newStringBuf();
+
+    /* There are no options to %build, %install, or %clean */
+    if (readLine(spec, STRIP_NOTHING) > 0) {
+       return PART_NONE;
+    }
+    
+    while (! (nextPart = isPart(spec->line))) {
+       appendLineStringBuf(*sbp, spec->line);
+       if (readLine(spec, STRIP_NOTHING) > 0) {
+           return PART_NONE;
+       }
+    }
+
+    return nextPart;
+}
diff --git a/build/parseChangelog.c b/build/parseChangelog.c
new file mode 100644 (file)
index 0000000..7de812c
--- /dev/null
@@ -0,0 +1,211 @@
+#include <stdlib.h>
+#include <time.h>
+#include <string.h>
+#include <sys/types.h>
+
+
+#include "read.h"
+#include "part.h"
+#include "stringbuf.h"
+#include "misc.h"
+#include "header.h"
+#include "rpmlib.h"
+
+static void addChangelogEntry(Header h, int time, char *name, char *text);
+static int addChangelog(Header h, StringBuf sb);
+static int dateToTimet(const char * datestr, time_t * secs);
+    
+int parseChangelog(Spec spec)
+{
+    int nextPart, res;
+    StringBuf sb;
+
+    sb = newStringBuf();
+    
+    /* There are no options to %changelog */
+    if (readLine(spec, STRIP_NOTHING) > 0) {
+       freeStringBuf(sb);
+       return PART_NONE;
+    }
+    
+    while (! (nextPart = isPart(spec->line))) {
+       appendLineStringBuf(sb, spec->line);
+       if (readLine(spec, STRIP_NOTHING) > 0) {
+           nextPart = PART_NONE;
+           break;
+       }
+    }
+
+    res = addChangelog(spec->packages->header, sb);
+    freeStringBuf(sb);
+
+    return (res) ? res : nextPart;
+}
+
+static int addChangelog(Header h, StringBuf sb)
+{
+    char *s;
+    int i;
+    int time, lastTime = 0;
+    char *date, *name, *text, *next;
+
+    s = getStringBuf(sb);
+
+    /* skip space */
+    SKIPSPACE(s);
+
+    while (*s) {
+       if (*s != '*') {
+           rpmError(RPMERR_BADSPEC, "%%changelog entries must start with *");
+           return RPMERR_BADSPEC;
+       }
+
+       /* find end of line */
+       date = s;
+       SKIPTONEWLINE(s);
+       if (! *s) {
+           rpmError(RPMERR_BADSPEC, "incomplete %%changelog entry");
+           return RPMERR_BADSPEC;
+       }
+       *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_t *)&time)) {
+           rpmError(RPMERR_BADSPEC, "bad date in %%changelog: %s", date);
+           return RPMERR_BADSPEC;
+       }
+       if (lastTime && lastTime < time) {
+           rpmError(RPMERR_BADSPEC,
+                    "%%changelog not in decending chronological order");
+           return RPMERR_BADSPEC;
+       }
+       lastTime = time;
+
+       /* skip space to the name */
+       SKIPSPACE(s);
+       if (! *s) {
+           rpmError(RPMERR_BADSPEC, "missing name in %%changelog");
+           return RPMERR_BADSPEC;
+       }
+
+       /* name */
+       name = s;
+       while (*s) s++;
+       while (s > name && isspace(*s)) {
+           *s-- = '\0';
+       }
+       if (s == name) {
+           rpmError(RPMERR_BADSPEC, "missing name in %%changelog");
+           return RPMERR_BADSPEC;
+       }
+
+       /* text */
+       SKIPSPACE(text);
+       if (! *text) {
+           rpmError(RPMERR_BADSPEC, "no description in %%changelog");
+           return RPMERR_BADSPEC;
+       }
+           
+       /* 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) && isspace(*s)) {
+           *s-- = '\0';
+       }
+       
+       addChangelogEntry(h, time, name, text);
+       s = next;
+    }
+
+    return 0;
+}
+
+static void addChangelogEntry(Header h, int time, char *name, char *text)
+{
+    if (headerIsEntry(h, RPMTAG_CHANGELOGTIME)) {
+       headerAppendEntry(h, RPMTAG_CHANGELOGTIME, RPM_INT32_TYPE,
+                         &time, 1);
+       headerAppendEntry(h, RPMTAG_CHANGELOGNAME, RPM_STRING_ARRAY_TYPE,
+                         &name, 1);
+       headerAppendEntry(h, RPMTAG_CHANGELOGTEXT, RPM_STRING_ARRAY_TYPE,
+                        &text, 1);
+    } else {
+       headerAddEntry(h, RPMTAG_CHANGELOGTIME, RPM_INT32_TYPE,
+                      &time, 1);
+       headerAddEntry(h, RPMTAG_CHANGELOGNAME, RPM_STRING_ARRAY_TYPE,
+                      &name, 1);
+       headerAddEntry(h, RPMTAG_CHANGELOGTEXT, RPM_STRING_ARRAY_TYPE,
+                      &text, 1);
+    }
+}
+
+/* datestr is of the form 'Wed Jan 1 1997' */
+static int dateToTimet(const char * datestr, time_t * secs)
+{
+    struct tm time;
+    char * chptr, * end, ** idx;
+    char * date = strcpy(alloca(strlen(datestr) + 1), datestr);
+    static char * days[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", 
+                               NULL };
+    static char * months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+                            "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL };
+    static char lengths[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+    
+    memset(&time, 0, sizeof(time));
+
+    end = chptr = date;
+
+    /* day of week */
+    if ((chptr = strtok(date, " \t\n")) == NULL) return -1;
+    idx = days;
+    while (*idx && strcmp(*idx, chptr)) idx++;
+    if (!*idx) return -1;
+
+    /* month */
+    if ((chptr = strtok(NULL, " \t\n")) == NULL) return -1;
+    idx = months;
+    while (*idx && strcmp(*idx, chptr)) idx++;
+    if (!*idx) return -1;
+
+    time.tm_mon = idx - months;
+
+    /* day */
+    if ((chptr = strtok(NULL, " \t\n")) == NULL) return -1;
+
+    /* make this noon so the day is always right (as we make this UTC) */
+    time.tm_hour = 12;
+
+    time.tm_mday = strtol(chptr, &chptr, 10);
+    if (*chptr) return -1;
+    if (time.tm_mday < 0 || time.tm_mday > lengths[time.tm_mon]) return -1;
+
+    /* year */
+    if ((chptr = strtok(NULL, " \t\n")) == NULL) return -1;
+
+    time.tm_year = strtol(chptr, &chptr, 10);
+    if (*chptr) return -1;
+    if (time.tm_year < 1997 || time.tm_year >= 3000) return -1;
+    time.tm_year -= 1900;
+
+    *secs = mktime(&time);
+    if (*secs == -1) return -1;
+
+    /* adjust to GMT */
+    *secs += timezone;
+
+    return 0;
+}
diff --git a/build/parseDescription.c b/build/parseDescription.c
new file mode 100644 (file)
index 0000000..01af29e
--- /dev/null
@@ -0,0 +1,110 @@
+#include <malloc.h>
+
+#include "spec.h"
+#include "header.h"
+#include "read.h"
+#include "part.h"
+#include "misc.h"
+#include "rpmlib.h"
+#include "package.h"
+#include "popt/popt.h"
+
+int parseDescription(Spec spec)
+{
+    int nextPart;
+    StringBuf sb;
+    char *name = NULL;
+    char *lang = RPMBUILD_DEFAULT_LANG;
+    int flag = PART_SUBNAME;
+    Package pkg;
+    int rc, argc;
+    char arg, **argv = NULL;
+    poptContext optCon = NULL;
+    struct poptOption optionsTable[] = {
+       { NULL, 'n', POPT_ARG_STRING, &name, 'n' },
+       { NULL, 'l', POPT_ARG_STRING, &lang, 'l' },
+       { 0, 0, 0, 0, 0 }
+    };
+
+    if ((rc = poptParseArgvString(spec->line, &argc, &argv))) {
+       rpmError(RPMERR_BADSPEC, "line %d: Error parsing %%description: %s",
+                spec->lineNum, poptStrerror(rc));
+       return RPMERR_BADSPEC;
+    }
+
+    optCon = poptGetContext(NULL, argc, argv, optionsTable, 0);
+    while ((arg = poptGetNextOpt(optCon)) > 0) {
+       if (arg == 'n') {
+           flag = PART_NAME;
+       }
+    }
+
+    if (arg < -1) {
+       rpmError(RPMERR_BADSPEC, "line %d: Bad option %s: %s",
+                spec->lineNum,
+                poptBadOption(optCon, POPT_BADOPTION_NOALIAS), 
+                spec->line);
+       FREE(argv);
+       poptFreeContext(optCon);
+       return RPMERR_BADSPEC;
+    }
+
+    if (poptPeekArg(optCon)) {
+       if (! name) {
+           name = poptGetArg(optCon);
+       }
+       if (poptPeekArg(optCon)) {
+           rpmError(RPMERR_BADSPEC, "line %d: Too many names: %s",
+                    spec->lineNum,
+                    spec->line);
+           FREE(argv);
+           poptFreeContext(optCon);
+           return RPMERR_BADSPEC;
+       }
+    }
+
+    if (lookupPackage(spec, name, flag, &pkg)) {
+       rpmError(RPMERR_BADSPEC, "line %d: Package does not exist: %s",
+                spec->lineNum, spec->line);
+       FREE(argv);
+       poptFreeContext(optCon);
+       return RPMERR_BADSPEC;
+    }
+
+
+    /******************/
+
+#if 0    
+    if (headerIsEntry(pkg->header, RPMTAG_DESCRIPTION)) {
+       rpmError(RPMERR_BADSPEC, "line %d: Second description", spec->lineNum);
+       FREE(argv);
+       poptFreeContext(optCon);
+       return RPMERR_BADSPEC;
+    }
+#endif
+    
+    sb = newStringBuf();
+
+    if (readLine(spec, STRIP_TRAILINGSPACE) > 0) {
+       nextPart = PART_NONE;
+    } else {
+       while (! (nextPart = isPart(spec->line))) {
+           appendLineStringBuf(sb, spec->line);
+           if (readLine(spec, STRIP_TRAILINGSPACE) > 0) {
+               nextPart = PART_NONE;
+               break;
+           }
+       }
+    }
+    
+    stripTrailingBlanksStringBuf(sb);
+    headerAddI18NString(pkg->header, RPMTAG_DESCRIPTION,
+                       getStringBuf(sb), lang);
+    
+    freeStringBuf(sb);
+     
+    FREE(argv);
+    poptFreeContext(optCon);
+    
+    return nextPart;
+}
diff --git a/build/parseFiles.c b/build/parseFiles.c
new file mode 100644 (file)
index 0000000..68cd72e
--- /dev/null
@@ -0,0 +1,103 @@
+#include <string.h>
+#include <malloc.h>
+
+#include "header.h"
+#include "read.h"
+#include "part.h"
+#include "misc.h"
+#include "rpmlib.h"
+#include "package.h"
+#include "stringbuf.h"
+#include "popt/popt.h"
+
+int parseFiles(Spec spec)
+{
+    int nextPart;
+    Package pkg;
+    char *name = NULL;
+    char *file = NULL;
+    int rc, argc;
+    char arg, **argv = NULL;
+    int flag = PART_SUBNAME;
+    poptContext optCon = NULL;
+    struct poptOption optionsTable[] = {
+       { NULL, 'n', POPT_ARG_STRING, &name, 'n' },
+       { NULL, 'f', POPT_ARG_STRING, &file, 'f' },
+       { 0, 0, 0, 0, 0 }
+    };
+
+    if ((rc = poptParseArgvString(spec->line, &argc, &argv))) {
+       rpmError(RPMERR_BADSPEC, "line %d: Error parsing %%files: %s",
+                spec->lineNum, poptStrerror(rc));
+       return RPMERR_BADSPEC;
+    }
+
+    optCon = poptGetContext(NULL, argc, argv, optionsTable, 0);
+    while ((arg = poptGetNextOpt(optCon)) > 0) {
+       if (arg == 'n') {
+           flag = PART_NAME;
+       }
+    }
+
+    if (arg < -1) {
+       rpmError(RPMERR_BADSPEC, "line %d: Bad option %s: %s",
+                spec->lineNum,
+                poptBadOption(optCon, POPT_BADOPTION_NOALIAS), 
+                spec->line);
+       FREE(argv);
+       poptFreeContext(optCon);
+       return RPMERR_BADSPEC;
+    }
+
+    if (poptPeekArg(optCon)) {
+       if (! name) {
+           name = poptGetArg(optCon);
+       }
+       if (poptPeekArg(optCon)) {
+           rpmError(RPMERR_BADSPEC, "line %d: Too many names: %s",
+                    spec->lineNum,
+                    spec->line);
+           FREE(argv);
+           poptFreeContext(optCon);
+           return RPMERR_BADSPEC;
+       }
+    }
+
+    if (lookupPackage(spec, name, flag, &pkg)) {
+       rpmError(RPMERR_BADSPEC, "line %d: Package does not exist: %s",
+                spec->lineNum, spec->line);
+       FREE(argv);
+       poptFreeContext(optCon);
+       return RPMERR_BADSPEC;
+    }
+
+    if (pkg->fileList) {
+       rpmError(RPMERR_BADSPEC, "line %d: Second %%files list",
+                spec->lineNum);
+       FREE(argv);
+       poptFreeContext(optCon);
+       return RPMERR_BADSPEC;
+    }
+
+    if (file) {
+       pkg->fileFile = strdup(file);
+    }
+    pkg->fileList = newStringBuf();
+    
+    if (readLine(spec, STRIP_NOTHING) > 0) {
+       nextPart = PART_NONE;
+    } else {
+       while (! (nextPart = isPart(spec->line))) {
+           appendLineStringBuf(pkg->fileList, spec->line);
+           if (readLine(spec, STRIP_NOTHING) > 0) {
+               nextPart = PART_NONE;
+               break;
+           }
+       }
+    }
+
+    FREE(argv);
+    poptFreeContext(optCon);
+       
+    return nextPart;
+}
diff --git a/build/parsePreamble.c b/build/parsePreamble.c
new file mode 100644 (file)
index 0000000..cf87f9e
--- /dev/null
@@ -0,0 +1,631 @@
+#include <string.h>
+#include <malloc.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "read.h"
+#include "part.h"
+#include "rpmlib.h"
+#include "spec.h"
+#include "package.h"
+#include "misc.h"
+#include "reqprov.h"
+#include "parse.h"
+#include "popt/popt.h"
+
+static int copyTags[] = {
+    RPMTAG_VERSION,
+    RPMTAG_RELEASE,
+    RPMTAG_COPYRIGHT,
+    RPMTAG_PACKAGER,
+    RPMTAG_DISTRIBUTION,
+    RPMTAG_VENDOR,
+    RPMTAG_ICON,
+    0
+};
+
+static int requiredTags[] = {
+    RPMTAG_NAME,
+    RPMTAG_VERSION,
+    RPMTAG_RELEASE,
+    RPMTAG_SUMMARY,
+    RPMTAG_GROUP,
+    RPMTAG_COPYRIGHT,
+    RPMTAG_PACKAGER,
+    RPMTAG_DISTRIBUTION,
+    RPMTAG_VENDOR,
+    0
+};
+
+static int handlePreambleTag(Spec spec, Package pkg, int tag, char *macro,
+                            char *lang);
+static void initPreambleList(void);
+static int findPreambleTag(Spec spec, int *tag, char **macro, char *lang);
+static int checkForRequired(Header h, char *name);
+static void copyFromMain(Spec spec, Package pkg);
+static int checkForDuplicates(Header h, char *name);
+static void fillOutMainPackage(Header h);
+static char *tagName(int tag);
+static int checkForValidArchitectures(Spec spec);
+static int isMemberInEntry(Header header, char *name, int tag);
+static int readIcon(Header h, char *file);
+    
+int parsePreamble(Spec spec, int initialPackage)
+{
+    int nextPart;
+    int tag;
+    char *name, *mainName, *linep, *macro;
+    int flag;
+    Package pkg;
+    char fullName[BUFSIZ];
+    char lang[BUFSIZ];
+
+    strcpy(fullName, "(main package)");
+
+    pkg = newPackage(spec);
+       
+    if (! initialPackage) {
+       /* There is one option to %package: <pkg> or -n <pkg> */
+       if (parseSimplePart(spec->line, &name, &flag)) {
+           rpmError(RPMERR_BADSPEC, "Bad package specification: %s",
+                    spec->line);
+           return RPMERR_BADSPEC;
+       }
+       
+       if (!lookupPackage(spec, name, flag, NULL)) {
+           rpmError(RPMERR_BADSPEC, "Package already exists: %s", spec->line);
+           return RPMERR_BADSPEC;
+       }
+       
+       /* Construct the package */
+       if (flag == PART_SUBNAME) {
+           headerGetEntry(spec->packages->header, RPMTAG_NAME,
+                          NULL, (void *) &mainName, NULL);
+           sprintf(fullName, "%s-%s", mainName, name);
+       } else {
+           strcpy(fullName, name);
+       }
+       headerAddEntry(pkg->header, RPMTAG_NAME, RPM_STRING_TYPE, fullName, 1);
+    }
+
+    if (readLine(spec, STRIP_TRAILINGSPACE) > 0) {
+       nextPart = PART_NONE;
+    } else {
+       while (! (nextPart = isPart(spec->line))) {
+           /* Skip blank lines */
+           linep = spec->line;
+           SKIPSPACE(linep);
+           if (*linep) {
+               if (findPreambleTag(spec, &tag, &macro, lang)) {
+                   rpmError(RPMERR_BADSPEC, "line %d: Unknown tag: %s",
+                            spec->lineNum, spec->line);
+                   return RPMERR_BADSPEC;
+               }
+               if (handlePreambleTag(spec, pkg, tag, macro, lang)) {
+                   return RPMERR_BADSPEC;
+               }
+               if (spec->buildArchitectures && !spec->inBuildArchitectures) {
+                   return PART_BUILDARCHITECTURES;
+               }
+           }
+           if (readLine(spec, STRIP_TRAILINGSPACE) > 0) {
+               nextPart = PART_NONE;
+               break;
+           }
+       }
+    }
+
+    /* Do some final processing on the header */
+    
+    if (!spec->gotBuildRoot && spec->buildRoot) {
+       rpmError(RPMERR_BADSPEC, "Spec file can't use BuildRoot");
+       return RPMERR_BADSPEC;
+    }
+
+    if (checkForValidArchitectures(spec)) {
+       return RPMERR_BADSPEC;
+    }
+
+    if (pkg == spec->packages) {
+       fillOutMainPackage(pkg->header);
+    }
+
+    if (checkForDuplicates(pkg->header, fullName)) {
+       return RPMERR_BADSPEC;
+    }
+
+    if (pkg != spec->packages) {
+       copyFromMain(spec, pkg);
+    }
+
+    if (checkForRequired(pkg->header, fullName)) {
+       return RPMERR_BADSPEC;
+    }
+
+    return nextPart;
+}
+
+static int isMemberInEntry(Header header, char *name, int tag)
+{
+    char **names;
+    int count;
+
+    if (headerGetEntry(header, tag, NULL, (void **)&names, &count)) {
+       while (count--) {
+           if (!strcmp(names[count], name)) {
+               FREE(names);
+               return 1;
+           }
+       }
+       FREE(names);
+       return 0;
+    }
+
+    return -1;
+}
+
+static int checkForValidArchitectures(Spec spec)
+{
+    char *arch, *os;
+
+    rpmGetArchInfo(&arch, NULL);
+    rpmGetOsInfo(&os, NULL);
+    
+    if (isMemberInEntry(spec->buildRestrictions,
+                       arch, RPMTAG_EXCLUDEARCH) == 1) {
+       rpmError(RPMERR_BADSPEC, "Architecture is excluded: %s", arch);
+       return RPMERR_BADSPEC;
+    }
+    if (isMemberInEntry(spec->buildRestrictions,
+                       arch, RPMTAG_EXCLUSIVEARCH) == 0) {
+       rpmError(RPMERR_BADSPEC, "Architecture is not included: %s", arch);
+       return RPMERR_BADSPEC;
+    }
+    if (isMemberInEntry(spec->buildRestrictions,
+                       os, RPMTAG_EXCLUDEOS) == 1) {
+       rpmError(RPMERR_BADSPEC, "OS is excluded: %s", os);
+       return RPMERR_BADSPEC;
+    }
+    if (isMemberInEntry(spec->buildRestrictions,
+                       os, RPMTAG_EXCLUSIVEOS) == 0) {
+       rpmError(RPMERR_BADSPEC, "OS is not included: %s", os);
+       return RPMERR_BADSPEC;
+    }
+
+    return 0;
+}
+
+static int checkForRequired(Header h, char *name)
+{
+    int res = 0;
+    int *p = requiredTags;
+
+    while (*p) {
+       if (!headerIsEntry(h, *p)) {
+           rpmError(RPMERR_BADSPEC, "%s field must be present in package: %s",
+                    tagName(*p), name);
+           res = 1;
+       }
+       p++;
+    }
+
+    return res;
+}
+
+static void copyFromMain(Spec spec, Package pkg)
+{
+    Header headerFrom, headerTo;
+    int *p = copyTags;
+    char *s;
+    int type, count;
+
+    headerFrom = spec->packages->header;
+    headerTo = pkg->header;
+
+    while (*p) {
+       if (!headerIsEntry(headerTo, *p)) {
+           if (headerGetEntry(headerFrom, *p, &type, (void **) &s, &count)) {
+               headerAddEntry(headerTo, *p, type, s, count);
+               if (type == RPM_STRING_ARRAY_TYPE) {
+                   FREE(s);
+               }
+           }
+       }
+       p++;
+    }
+}
+
+static int checkForDuplicates(Header h, char *name)
+{
+    int res = 0;
+    int lastTag, tag;
+    HeaderIterator hi;
+    
+    headerSort(h);
+    hi = headerInitIterator(h);
+    lastTag = 0;
+    while (headerNextIterator(hi, &tag, NULL, NULL, NULL)) {
+       if (tag == lastTag) {
+           rpmError(RPMERR_BADSPEC, "Duplicate %s entries in package: %s",
+                    tagName(tag), name);
+           res = 1;
+       }
+       lastTag = tag;
+    }
+    headerFreeIterator(hi);
+
+    return res;
+}
+
+static void fillOutMainPackage(Header h)
+{
+    if (!headerIsEntry(h, RPMTAG_VENDOR)) {
+       if (rpmGetVar(RPMVAR_VENDOR)) {
+           headerAddEntry(h, RPMTAG_VENDOR, RPM_STRING_TYPE,
+                          rpmGetVar(RPMVAR_VENDOR), 1);
+       }
+    }
+    if (!headerIsEntry(h, RPMTAG_PACKAGER)) {
+       if (rpmGetVar(RPMVAR_PACKAGER)) {
+           headerAddEntry(h, RPMTAG_PACKAGER, RPM_STRING_TYPE,
+                          rpmGetVar(RPMVAR_PACKAGER), 1);
+       }
+    }
+    if (!headerIsEntry(h, RPMTAG_DISTRIBUTION)) {
+       if (rpmGetVar(RPMVAR_DISTRIBUTION)) {
+           headerAddEntry(h, RPMTAG_DISTRIBUTION, RPM_STRING_TYPE,
+                          rpmGetVar(RPMVAR_DISTRIBUTION), 1);
+       }
+    }
+}
+
+#define SINGLE_TOKEN_ONLY \
+if (multiToken) { \
+    rpmError(RPMERR_BADSPEC, "line %d: Tag takes single token only: %s", \
+            spec->lineNum, spec->line); \
+    return RPMERR_BADSPEC; \
+}
+
+static int handlePreambleTag(Spec spec, Package pkg, int tag, char *macro,
+                            char *lang)
+{
+    char *field = spec->line;
+    char *end;
+    int multiToken = 0;
+    int num, rc;
+    
+    /* Find the start of the "field" and strip trailing space */
+    while ((*field) && (*field != ':')) {
+       field++;
+    }
+    if (*field != ':') {
+       rpmError(RPMERR_BADSPEC, "line %d: Malformed tag: %s",
+                spec->lineNum, spec->line);
+       return RPMERR_BADSPEC;
+    }
+    field++;
+    SKIPSPACE(field);
+    if (! *field) {
+       /* Empty field */
+       rpmError(RPMERR_BADSPEC, "line %d: Empty tag: %s",
+                spec->lineNum, spec->line);
+       return RPMERR_BADSPEC;
+    }
+    end = findLastChar(field);
+    *(end+1) = '\0';
+
+    /* See if this is multi-token */
+    end = field;
+    SKIPNONSPACE(end);
+    if (*end) {
+       multiToken = 1;
+    }
+
+    if (macro) {
+       addMacro(&spec->macros, macro, field);
+    }
+    
+    switch (tag) {
+      case RPMTAG_NAME:
+      case RPMTAG_VERSION:
+      case RPMTAG_RELEASE:
+      case RPMTAG_GROUP:
+      case RPMTAG_URL:
+       SINGLE_TOKEN_ONLY;
+       /* These are for backward compatibility */
+       if (tag == RPMTAG_VERSION) {
+           addMacro(&spec->macros, "PACKAGE_VERSION", field);
+       } else if (tag == RPMTAG_RELEASE) {
+           addMacro(&spec->macros, "PACKAGE_RELEASE", field);
+       }
+       /* fall through */
+      case RPMTAG_SUMMARY:
+      case RPMTAG_DISTRIBUTION:
+      case RPMTAG_VENDOR:
+      case RPMTAG_COPYRIGHT:
+      case RPMTAG_PACKAGER:
+       if (! *lang) {
+           headerAddEntry(pkg->header, tag, RPM_STRING_TYPE, field, 1);
+       } else {
+           headerAddI18NString(pkg->header, tag, field, lang);
+       }
+       break;
+      case RPMTAG_BUILDROOT:
+       SINGLE_TOKEN_ONLY;
+       if (! spec->buildRoot) {
+           if (rpmGetVar(RPMVAR_BUILDROOT)) {
+               spec->buildRoot = strdup(rpmGetVar(RPMVAR_BUILDROOT));
+           } else {
+               spec->buildRoot = strdup(field);
+           }
+       }
+       if (!strcmp(spec->buildRoot, "/")) {
+           rpmError(RPMERR_BADSPEC,
+                    "line %d: BuildRoot can not be \"/\": %s",
+                    spec->lineNum, spec->line);
+           return RPMERR_BADSPEC;
+       }
+       spec->gotBuildRoot = 1;
+       break;
+      case RPMTAG_DEFAULTPREFIX:
+       SINGLE_TOKEN_ONLY;
+       if (field[0] != '/') {
+           rpmError(RPMERR_BADSPEC,
+                    "line %d: Prefix must begin with '/': %s",
+                    spec->lineNum, spec->line);
+           return RPMERR_BADSPEC;
+       }
+       headerAddEntry(pkg->header, tag, RPM_STRING_TYPE, field, 1);
+       break;
+      case RPMTAG_DOCDIR:
+       SINGLE_TOKEN_ONLY;
+       FREE(spec->docDir);
+       if (field[0] != '/') {
+           rpmError(RPMERR_BADSPEC,
+                    "line %d: Docdir must begin with '/': %s",
+                    spec->lineNum, spec->line);
+           return RPMERR_BADSPEC;
+       }
+       spec->docDir = strdup(field);
+       break;
+      case RPMTAG_SERIAL:
+       SINGLE_TOKEN_ONLY;
+       if (parseNum(field, &num)) {
+           rpmError(RPMERR_BADSPEC,
+                    "line %d: Serial field must be a number: %s",
+                    spec->lineNum, spec->line);
+           return RPMERR_BADSPEC;
+       }
+       headerAddEntry(pkg->header, tag, RPM_INT32_TYPE, &num, 1);
+       break;
+      case RPMTAG_AUTOREQPROV:
+       spec->autoReqProv = parseYesNo(field);
+       break;
+      case RPMTAG_SOURCE:
+      case RPMTAG_PATCH:
+       SINGLE_TOKEN_ONLY;
+       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 RPMERR_BADSPEC;
+       }
+       break;
+      case RPMTAG_NOSOURCE:
+      case RPMTAG_NOPATCH:
+       spec->noSource = 1;
+       if ((rc = parseNoSource(spec, field, tag))) {
+           return rc;
+       }
+       break;
+      case RPMTAG_OBSOLETES:
+      case RPMTAG_PROVIDES:
+       if ((rc = parseProvidesObsoletes(spec, pkg, field, tag))) {
+           return rc;
+       }
+       break;
+      case RPMTAG_REQUIREFLAGS:
+      case RPMTAG_CONFLICTFLAGS:
+      case RPMTAG_PREREQ:
+       if ((rc = parseRequiresConflicts(spec, pkg, field, tag))) {
+           return rc;
+       }
+       break;
+      case RPMTAG_EXCLUDEARCH:
+      case RPMTAG_EXCLUSIVEARCH:
+      case RPMTAG_EXCLUDEOS:
+      case RPMTAG_EXCLUSIVEOS:
+       addOrAppendListEntry(spec->buildRestrictions, tag, field);
+       break;
+      case RPMTAG_BUILDARCHS:
+       if ((rc = poptParseArgvString(field,
+                                     &(spec->buildArchitectureCount),
+                                     &(spec->buildArchitectures)))) {
+           rpmError(RPMERR_BADSPEC,
+                    "line %d: Bad BuildArchitecture format: %s",
+                    spec->lineNum, spec->line);
+           return RPMERR_BADSPEC;
+       }
+       if (! spec->buildArchitectureCount) {
+           FREE(spec->buildArchitectures);
+       }
+       break;
+
+      default:
+       rpmError(RPMERR_INTERNAL, "Internal error: Bogus tag %d", tag);
+       return RPMERR_INTERNAL;
+    }
+
+    return 0;
+}
+
+static struct PreambleRec {
+    int tag;
+    int len;
+    int multiLang;
+    char *token;
+} preambleList[] = {
+    {RPMTAG_NAME,          0, 0, "name"},
+    {RPMTAG_VERSION,       0, 0, "version"},
+    {RPMTAG_RELEASE,       0, 0, "release"},
+    {RPMTAG_SERIAL,        0, 0, "serial"},
+/*    {RPMTAG_DESCRIPTION,   0, "description"}, */
+    {RPMTAG_SUMMARY,       0, 1, "summary"},
+    {RPMTAG_COPYRIGHT,     0, 0, "copyright"},
+    {RPMTAG_COPYRIGHT,     0, 0, "license"},
+    {RPMTAG_DISTRIBUTION,  0, 0, "distribution"},
+    {RPMTAG_VENDOR,        0, 0, "vendor"},
+    {RPMTAG_GROUP,         0, 0, "group"},
+    {RPMTAG_PACKAGER,      0, 0, "packager"},
+    {RPMTAG_URL,           0, 0, "url"},
+/*    {RPMTAG_ROOT,          0, "root"}, */
+    {RPMTAG_SOURCE,        0, 0, "source"},
+    {RPMTAG_PATCH,         0, 0, "patch"},
+    {RPMTAG_NOSOURCE,      0, 0, "nosource"},
+    {RPMTAG_NOPATCH,       0, 0, "nopatch"},
+    {RPMTAG_EXCLUDEARCH,   0, 0, "excludearch"},
+    {RPMTAG_EXCLUSIVEARCH, 0, 0, "exclusivearch"},
+    {RPMTAG_EXCLUDEOS,     0, 0, "excludeos"},
+    {RPMTAG_EXCLUSIVEOS,   0, 0, "exclusiveos"},
+/*    {RPMTAG_EXCLUDE,       0, "exclude"}, */
+/*    {RPMTAG_EXCLUSIVE,     0, "exclusive"}, */
+    {RPMTAG_ICON,          0, 0, "icon"},
+    {RPMTAG_PROVIDES,      0, 0, "provides"},
+    {RPMTAG_REQUIREFLAGS,  0, 0, "requires"},
+    {RPMTAG_PREREQ,        0, 0, "prereq"},
+    {RPMTAG_CONFLICTFLAGS, 0, 0, "conflicts"},
+    {RPMTAG_OBSOLETES,     0, 0, "obsoletes"},
+    {RPMTAG_DEFAULTPREFIX, 0, 0, "prefix"},
+    {RPMTAG_BUILDROOT,     0, 0, "buildroot"},
+    {RPMTAG_BUILDARCHS,    0, 0, "buildarchitectures"},
+    {RPMTAG_AUTOREQPROV,   0, 0, "autoreqprov"},
+    {RPMTAG_DOCDIR,        0, 0, "docdir"},
+    {0, 0, 0, 0}
+};
+
+static void initPreambleList(void)
+{
+    struct PreambleRec *p = preambleList;
+
+    while (p->token) {
+       p->len = strlen(p->token);
+       p++;
+    }
+}
+
+static int findPreambleTag(Spec spec, int *tag, char **macro, char *lang)
+{
+    char *s;
+    struct PreambleRec *p = preambleList;
+
+    if (! p->len) {
+       initPreambleList();
+    }
+
+    while (p->token && strncasecmp(spec->line, p->token, p->len)) {
+       p++;
+    }
+
+    if (!p->token) {
+       return 1;
+    }
+
+    s = spec->line + p->len;
+    SKIPSPACE(s);
+
+    if (! p->multiLang) {
+       /* 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';
+    } else {
+       if (*s != ':') {
+           if (*s != '(') return 1;
+           s++;
+           SKIPSPACE(s);
+           while (! isspace(*s) && *s != ')') {
+               *lang++ = *s++;
+           }
+           *lang = '\0';
+           SKIPSPACE(s);
+           if (*s != ')') return 1;
+           s++;
+           SKIPSPACE(s);
+           if (*s != ':') return 1;
+       } else {
+           strcpy(lang, RPMBUILD_DEFAULT_LANG);
+       }
+    }
+
+    *tag = p->tag;
+    if (macro) {
+       *macro = p->token;
+    }
+    return 0;
+}
+
+static char *tagName(int tag)
+{
+    int i = 0;
+    static char nameBuf[1024];
+    char *s;
+
+    strcpy(nameBuf, "(unknown)");
+    while (i < rpmTagTableSize) {
+       if (tag == rpmTagTable[i].val) {
+           strcpy(nameBuf, rpmTagTable[i].name + 7);
+           s = nameBuf+1;
+           while (*s) {
+               *s = tolower(*s);
+               s++;
+           }
+       }
+       i++;
+    }
+
+    return nameBuf;
+}
+
+static int readIcon(Header h, char *file)
+{
+    char buf[BUFSIZ], *icon;
+    struct stat statbuf;
+    int fd;
+
+    sprintf(buf, "%s/%s", rpmGetVar(RPMVAR_SOURCEDIR), file);
+    if (stat(buf, &statbuf)) {
+       rpmError(RPMERR_BADSPEC, "Unable to read icon: %s", file);
+       return RPMERR_BADSPEC;
+    }
+    icon = malloc(statbuf.st_size);
+    fd = open(buf, O_RDONLY);
+    if (read(fd, icon, statbuf.st_size) != statbuf.st_size) {
+       close(fd);
+       rpmError(RPMERR_BADSPEC, "Unable to read icon: %s", file);
+       return RPMERR_BADSPEC;
+    }
+    close(fd);
+
+    if (! strncmp(icon, "GIF", 3)) {
+       headerAddEntry(h, RPMTAG_GIF, RPM_BIN_TYPE, icon, statbuf.st_size);
+    } else if (! strncmp(icon, "/* XPM", 6)) {
+       headerAddEntry(h, RPMTAG_XPM, RPM_BIN_TYPE, icon, statbuf.st_size);
+    } else {
+       rpmError(RPMERR_BADSPEC, "Unknown icon type: %s", file);
+       return RPMERR_BADSPEC;
+    }
+    free(icon);
+    
+    return 0;
+}
diff --git a/build/parsePrep.c b/build/parsePrep.c
new file mode 100644 (file)
index 0000000..bf611ea
--- /dev/null
@@ -0,0 +1,480 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <malloc.h>
+#include <errno.h>
+
+#include "spec.h"
+#include "read.h"
+#include "part.h"
+#include "rpmlib.h"
+#include "lib/misc.h"
+#include "popt/popt.h"
+#include "names.h"
+#include "misc.h"
+#include "config.h"
+
+static int doSetupMacro(Spec spec, char *line);
+static int doPatchMacro(Spec spec, char *line);
+static char *doPatch(Spec spec, int c, int strip, char *db,
+                    int reverse, int removeEmpties);
+static int isCompressed(char *file, int *compressed);
+static int checkOwners(char *file);
+static char *doUntar(Spec spec, int c, int quietly);
+
+int parsePrep(Spec spec)
+{
+    int nextPart, res;
+    StringBuf buf;
+    char **lines, **saveLines;
+
+    if (spec->prep) {
+       rpmError(RPMERR_BADSPEC, "line %d: second %%prep", spec->lineNum);
+       return RPMERR_BADSPEC;
+    }
+
+    spec->prep = newStringBuf();
+
+    buf = newStringBuf();
+    
+    /* There are no options to %prep */
+    if (readLine(spec, STRIP_NOTHING) > 0) {
+       return PART_NONE;
+    }
+    
+    while (! (nextPart = isPart(spec->line))) {
+       /* Need to expand the macros inline.  That way we  */
+       /* can give good line number information on error. */
+       appendLineStringBuf(buf, spec->line);
+       if (readLine(spec, STRIP_NOTHING) > 0) {
+           nextPart = PART_NONE;
+           break;
+       }
+    }
+
+    lines = splitString(getStringBuf(buf), strlen(getStringBuf(buf)), '\n');
+    saveLines = lines;
+    while (*lines) {
+       res = 0;
+       if (! strncmp(*lines, "%setup", 6)) {
+           res = doSetupMacro(spec, *lines);
+       } else if (! strncmp(*lines, "%patch", 6)) {
+           res = doPatchMacro(spec, *lines);
+       } else {
+           appendLineStringBuf(spec->prep, *lines);
+       }
+       if (res) {
+           freeSplitString(saveLines);
+           return res;
+       }
+       lines++;
+    }
+    freeSplitString(saveLines);
+
+    return nextPart;
+}
+
+static int doSetupMacro(Spec spec, char *line)
+{
+    char *version, *name;
+    int leaveDirs = 0, skipDefaultAction = 0;
+    int createDir = 0, quietly = 0;
+    char * dirName = NULL;
+    char buf[BUFSIZ];
+    StringBuf before;
+    StringBuf after;
+    poptContext optCon;
+    int argc;
+    char ** argv;
+    int arg;
+    char * optArg;
+    char * chptr;
+    int rc;
+    int num;
+    struct poptOption optionsTable[] = {
+           { NULL, 'a', POPT_ARG_STRING, NULL, 'a' },
+           { NULL, 'b', POPT_ARG_STRING, NULL, 'b' },
+           { NULL, 'c', 0, &createDir, 0 },
+           { NULL, 'D', 0, &leaveDirs, 0 },
+           { NULL, 'n', POPT_ARG_STRING, &dirName, 0 },
+           { NULL, 'T', 0, &skipDefaultAction, 0 },
+           { NULL, 'q', 0, &quietly, 0 },
+           { 0, 0, 0, 0, 0 }
+    };
+
+    if ((rc = poptParseArgvString(line, &argc, &argv))) {
+       rpmError(RPMERR_BADSPEC, "Error parsing %%setup: %s",
+                       poptStrerror(rc));
+       return RPMERR_BADSPEC;
+    }
+
+    before = newStringBuf();
+    after = newStringBuf();
+
+    optCon = poptGetContext(NULL, argc, argv, optionsTable, 0);
+    while ((arg = poptGetNextOpt(optCon)) > 0) {
+       optArg = poptGetOptArg(optCon);
+
+       /* We only parse -a and -b here */
+
+       if (parseNum(optArg, &num)) {
+           rpmError(RPMERR_BADSPEC, "line %d: Bad arg to %%setup %c: %s",
+                    spec->lineNum, num, optArg);
+           free(argv);
+           freeStringBuf(before);
+           freeStringBuf(after);
+           poptFreeContext(optCon);
+           return RPMERR_BADSPEC;
+       }
+
+       chptr = doUntar(spec, num, quietly);
+       if (!chptr) {
+           return RPMERR_BADSPEC;
+       }
+
+       if (arg == 'a')
+           appendLineStringBuf(after, chptr);
+       else
+           appendLineStringBuf(before, chptr);
+    }
+
+    if (arg < -1) {
+       rpmError(RPMERR_BADSPEC, "line %d: Bad %%setup option %s: %s",
+                spec->lineNum,
+                poptBadOption(optCon, POPT_BADOPTION_NOALIAS), 
+                poptStrerror(arg));
+       free(argv);
+       freeStringBuf(before);
+       freeStringBuf(after);
+       poptFreeContext(optCon);
+       return RPMERR_BADSPEC;
+    }
+
+    if (dirName) {
+       spec->buildSubdir = strdup(dirName);
+    } else {
+       headerGetEntry(spec->packages->header, RPMTAG_VERSION, NULL,
+                (void *) &version, NULL);
+       headerGetEntry(spec->packages->header, RPMTAG_NAME, NULL,
+                (void *) &name, NULL);
+       sprintf(buf, "%s-%s", name, version);
+       spec->buildSubdir = strdup(buf);
+    }
+    
+    free(argv);
+    poptFreeContext(optCon);
+
+    /* cd to the build dir */
+    sprintf(buf, "cd %s", rpmGetVar(RPMVAR_BUILDDIR));
+    appendLineStringBuf(spec->prep, buf);
+    
+    /* delete any old sources */
+    if (!leaveDirs) {
+       sprintf(buf, "rm -rf %s", spec->buildSubdir);
+       appendLineStringBuf(spec->prep, buf);
+    }
+
+    /* if necessary, create and cd into the proper dir */
+    if (createDir) {
+       sprintf(buf, "mkdir -p %s\ncd %s",
+               spec->buildSubdir, spec->buildSubdir);
+       appendLineStringBuf(spec->prep, buf);
+    }
+
+    /* do the default action */
+    if (!createDir && !skipDefaultAction) {
+       chptr = doUntar(spec, 0, quietly);
+       if (!chptr) {
+           return RPMERR_BADSPEC;
+       }
+       appendLineStringBuf(spec->prep, chptr);
+    }
+
+    appendStringBuf(spec->prep, getStringBuf(before));
+    freeStringBuf(before);
+
+    if (!createDir) {
+       sprintf(buf, "cd %s", spec->buildSubdir);
+       appendLineStringBuf(spec->prep, buf);
+    }
+
+    if (createDir && !skipDefaultAction) {
+       chptr = doUntar(spec, 0, quietly);
+       if (!chptr) {
+           return RPMERR_BADSPEC;
+       }
+       appendLineStringBuf(spec->prep, chptr);
+    }
+    
+    appendStringBuf(spec->prep, getStringBuf(after));
+    freeStringBuf(after);
+
+    /* clean up permissions etc */
+    if (!geteuid()) {
+       appendLineStringBuf(spec->prep, "chown -R root .");
+       appendLineStringBuf(spec->prep, "chgrp -R root .");
+    }
+
+    if (rpmGetVar(RPMVAR_FIXPERMS)) {
+       appendStringBuf(spec->prep, "chmod -R ");
+       appendStringBuf(spec->prep, rpmGetVar(RPMVAR_FIXPERMS));
+       appendLineStringBuf(spec->prep, " .");
+    }
+    
+    return 0;
+}
+
+static char *doUntar(Spec spec, int c, int quietly)
+{
+    static char buf[BUFSIZ];
+    char file[BUFSIZ];
+    char *s, *taropts;
+    struct Source *sp;
+    int compressed;
+
+    s = NULL;
+    sp = spec->sources;
+    while (sp) {
+       if ((sp->flags & RPMBUILD_ISSOURCE) && (sp->num == c)) {
+           s = sp->source;
+           break;
+       }
+       sp = sp->next;
+    }
+    if (! s) {
+       rpmError(RPMERR_BADSPEC, "No source number %d", c);
+       return NULL;
+    }
+
+    sprintf(file, "%s/%s", rpmGetVar(RPMVAR_SOURCEDIR), s);
+    taropts = (rpmIsVerbose() && !quietly ? "-xvvf" : "-xf");
+
+    if (isCompressed(file, &compressed)) {
+       return NULL;
+    }
+    if (checkOwners(file)) {
+       return NULL;
+    }
+    if (compressed) {
+       sprintf(buf,
+               "%s -dc %s | tar %s -\n"
+               "if [ $? -ne 0 ]; then\n"
+               "  exit $?\n"
+               "fi",
+               rpmGetVar(RPMVAR_GZIPBIN), file, taropts);
+    } else {
+       sprintf(buf, "tar %s %s", taropts, file);
+    }
+
+    return buf;
+}
+
+static int doPatchMacro(Spec spec, char *line)
+{
+    char *opt_b;
+    int opt_P, opt_p, opt_R, opt_E;
+    char *s;
+    char buf[BUFSIZ];
+    int patch_nums[1024];  /* XXX - we can only handle 1024 patches! */
+    int patch_index, x;
+
+    opt_P = opt_p = opt_R = opt_E = 0;
+    opt_b = NULL;
+    patch_index = 0;
+
+    if (! strchr(" \t\n", line[6])) {
+       /* %patchN */
+       sprintf(buf, "%%patch -P %s", line + 6);
+    } else {
+       strcpy(buf, line);
+    }
+    
+    strtok(buf, " \t\n");  /* remove %patch */
+    while ((s = strtok(NULL, " \t\n"))) {
+       if (!strcmp(s, "-P")) {
+           opt_P = 1;
+       } else if (!strcmp(s, "-R")) {
+           opt_R = 1;
+       } else if (!strcmp(s, "-E")) {
+           opt_E = 1;
+       } else if (!strcmp(s, "-b")) {
+           /* orig suffix */
+           opt_b = strtok(NULL, " \t\n");
+           if (! opt_b) {
+               rpmError(RPMERR_BADSPEC, "line %d: Need arg to %%patch -b: %s",
+                        spec->lineNum, spec->line);
+               return RPMERR_BADSPEC;
+           }
+       } else if (!strcmp(s, "-z")) {
+           /* orig suffix */
+           opt_b = strtok(NULL, " \t\n");
+           if (! opt_b) {
+               rpmError(RPMERR_BADSPEC, "line %d: Need arg to %%patch -z: %s",
+                        spec->lineNum, spec->line);
+               return RPMERR_BADSPEC;
+           }
+       } else if (!strncmp(s, "-p", 2)) {
+           /* unfortunately, we must support -pX */
+           if (! strchr(" \t\n", s[2])) {
+               s = s + 2;
+           } else {
+               s = strtok(NULL, " \t\n");
+               if (! s) {
+                   rpmError(RPMERR_BADSPEC,
+                            "line %d: Need arg to %%patch -p: %s",
+                            spec->lineNum, spec->line);
+                   return RPMERR_BADSPEC;
+               }
+           }
+           if (parseNum(s, &opt_p)) {
+               rpmError(RPMERR_BADSPEC, "line %d: Bad arg to %%patch -p: %s",
+                        spec->lineNum, spec->line);
+               return RPMERR_BADSPEC;
+           }
+       } else {
+           /* Must be a patch num */
+           if (patch_index == 1024) {
+               rpmError(RPMERR_BADSPEC, "Too many patches!");
+               return RPMERR_BADSPEC;
+           }
+           if (parseNum(s, &(patch_nums[patch_index]))) {
+               rpmError(RPMERR_BADSPEC, "line %d: Bad arg to %%patch: %s",
+                        spec->lineNum, spec->line);
+               return RPMERR_BADSPEC;
+           }
+           patch_index++;
+       }
+    }
+
+    /* All args processed */
+
+    if (! opt_P) {
+       s = doPatch(spec, 0, opt_p, opt_b, opt_R, opt_E);
+       if (! s) {
+           return RPMERR_BADSPEC;
+       }
+       appendLineStringBuf(spec->prep, s);
+    }
+
+    x = 0;
+    while (x < patch_index) {
+       s = doPatch(spec, patch_nums[x], opt_p, opt_b, opt_R, opt_E);
+       if (! s) {
+           return RPMERR_BADSPEC;
+       }
+       appendLineStringBuf(spec->prep, s);
+       x++;
+    }
+    
+    return 0;
+}
+
+static char *doPatch(Spec spec, int c, int strip, char *db,
+                    int reverse, int removeEmpties)
+{
+    static char buf[BUFSIZ];
+    char file[BUFSIZ];
+    char args[BUFSIZ];
+    char *s;
+    struct Source *sp;
+    int compressed;
+
+    s = NULL;
+    sp = spec->sources;
+    while (sp) {
+       if ((sp->flags & RPMBUILD_ISPATCH) && (sp->num == c)) {
+           s = sp->source;
+           break;
+       }
+       sp = sp->next;
+    }
+    if (! s) {
+       rpmError(RPMERR_BADSPEC, "No patch number %d", c);
+       return NULL;
+    }
+
+    sprintf(file, "%s/%s", rpmGetVar(RPMVAR_SOURCEDIR), s);
+
+    args[0] = '\0';
+    if (db) {
+#if HAVE_OLDPATCH_21 == 0
+       strcat(args, "-b ");
+#endif
+       strcat(args, "--suffix ");
+       strcat(args, db);
+    }
+    if (reverse) {
+       strcat(args, " -R");
+    }
+    if (removeEmpties) {
+       strcat(args, " -E");
+    }
+
+    if (isCompressed(file, &compressed)) {
+       return NULL;
+    }
+    if (checkOwners(file)) {
+       return NULL;
+    }
+    if (compressed) {
+       sprintf(buf,
+               "echo \"Patch #%d:\"\n"
+               "%s -dc %s | patch -p%d %s -s\n"
+               "if [ $? -ne 0 ]; then\n"
+               "  exit $?\n"
+               "fi",
+               c, rpmGetVar(RPMVAR_GZIPBIN), file, strip, args);
+    } else {
+       sprintf(buf,
+               "echo \"Patch #%d:\"\n"
+               "patch -p%d %s -s < %s", c, strip, args, file);
+    }
+
+    return buf;
+}
+
+static int checkOwners(char *file)
+{
+    struct stat sb;
+
+    if (lstat(file, &sb)) {
+       rpmError(RPMERR_BADSPEC, "Bad source: %s: %s", file, strerror(errno));
+       return RPMERR_BADSPEC;
+    }
+    if (!getUname(sb.st_uid) || !getGname(sb.st_gid)) {
+       rpmError(RPMERR_BADSPEC, "Bad owner/group: %s", file);
+       return RPMERR_BADSPEC;
+    }
+
+    return 0;
+}
+
+static int isCompressed(char *file, int *compressed)
+{
+    int fd;
+    unsigned char magic[4];
+
+    *compressed = 0;
+
+    if (!(fd = open(file, O_RDONLY))) {
+       return 1;
+    }
+    if (! read(fd, magic, 4)) {
+       return 1;
+    }
+    close(fd);
+
+    if (((magic[0] == 0037) && (magic[1] == 0213)) ||  /* gzip */
+       ((magic[0] == 0037) && (magic[1] == 0236)) ||  /* old gzip */
+       ((magic[0] == 0037) && (magic[1] == 0036)) ||  /* pack */
+       ((magic[0] == 0037) && (magic[1] == 0240)) ||  /* SCO lzh */
+       ((magic[0] == 0037) && (magic[1] == 0235)) ||  /* compress */
+       ((magic[0] == 0120) && (magic[1] == 0113) &&
+        (magic[2] == 0003) && (magic[3] == 0004))     /* pkzip */
+       ) {
+       *compressed = 1;
+    }
+
+    return 0;
+}
diff --git a/build/parseReqs.c b/build/parseReqs.c
new file mode 100644 (file)
index 0000000..87241ad
--- /dev/null
@@ -0,0 +1,125 @@
+#include <string.h>
+
+#include "spec.h"
+#include "rpmlib.h"
+#include "reqprov.h"
+
+static struct ReqComp {
+    char *token;
+    int sense;
+} ReqComparisons[] = {
+    { "<=", RPMSENSE_LESS | RPMSENSE_EQUAL},
+    { "<=S", RPMSENSE_LESS | RPMSENSE_EQUAL | RPMSENSE_SERIAL},
+    { "=<", RPMSENSE_LESS | RPMSENSE_EQUAL},
+    { "=<S", RPMSENSE_LESS | RPMSENSE_EQUAL | RPMSENSE_SERIAL},
+    { "<", RPMSENSE_LESS},
+    { "<S", RPMSENSE_LESS | RPMSENSE_SERIAL},
+
+    { "=", RPMSENSE_EQUAL},
+    { "=S", RPMSENSE_EQUAL | RPMSENSE_SERIAL},
+    
+    { ">=", RPMSENSE_GREATER | RPMSENSE_EQUAL},
+    { ">=S", RPMSENSE_GREATER | RPMSENSE_EQUAL | RPMSENSE_SERIAL},
+    { "=>", RPMSENSE_GREATER | RPMSENSE_EQUAL},
+    { "=>S", RPMSENSE_GREATER | RPMSENSE_EQUAL | RPMSENSE_SERIAL},
+    { ">", RPMSENSE_GREATER},
+    { ">S", RPMSENSE_GREATER | RPMSENSE_SERIAL},
+    { NULL, 0 },
+};
+
+int parseRequiresConflicts(Spec spec, Package pkg, char *field, int tag)
+{
+    char buf[BUFSIZ], *bufp, *version, *name;
+    int flags;
+    char *req = NULL;
+    struct ReqComp *rc;
+
+    strcpy(buf, field);
+    bufp = buf;
+
+    while (req || (req = strtok(bufp, " ,\t\n"))) {
+       bufp = NULL;
+       
+       if (tag == RPMTAG_CONFLICTFLAGS) {
+           if (req[0] == '/') {
+               rpmError(RPMERR_BADSPEC,
+                        "line %d: No file names in Conflicts: %s",
+                        spec->lineNum, spec->line);
+               return RPMERR_BADSPEC;
+           }
+           flags = RPMSENSE_CONFLICTS;
+           name = "Conflicts";
+       } else if (tag == RPMTAG_PREREQ) {
+           flags = RPMSENSE_PREREQ;
+           name = "PreReq";
+       } else {
+           flags = RPMSENSE_ANY;
+           name = "Requires";
+       }
+
+       if ((version = strtok(NULL, " ,\t\n"))) {
+           rc = ReqComparisons;
+           while (rc->token && strcmp(version, rc->token)) {
+               rc++;
+           }
+           if (rc->token) {
+               if (req[0] == '/') {
+                   rpmError(RPMERR_BADSPEC,
+                            "line %d: No versions on file names in %s: %s",
+                            spec->lineNum, name, spec->line);
+                   return RPMERR_BADSPEC;
+               }
+               if (tag == RPMTAG_PREREQ) {
+                   rpmError(RPMERR_BADSPEC,
+                            "line %d: No versions in PreReq: %s",
+                            spec->lineNum, spec->line);
+                   return RPMERR_BADSPEC;
+               }
+               /* read a version */
+               flags |= rc->sense;
+               version = strtok(NULL, " ,\t\n");
+           }
+       }
+
+       if ((flags & RPMSENSE_SENSEMASK) && !version) {
+           rpmError(RPMERR_BADSPEC,
+                    "line %d: Version required in %s: %s",
+                    spec->lineNum, name, spec->line);
+           return RPMERR_BADSPEC;
+       }
+
+       addReqProv(spec, pkg, flags, req,
+                  (flags & RPMSENSE_SENSEMASK) ? version : NULL);
+
+       /* If there is no sense, we just read the next token */
+       req = (flags & RPMSENSE_SENSEMASK) ? NULL : version;
+    }
+
+    return 0;
+}
+
+int parseProvidesObsoletes(Spec spec, Package pkg, char *field, int tag)
+{
+    char *prov, buf[BUFSIZ], *line;
+    int flags;
+
+    flags = (tag == RPMTAG_PROVIDES) ? RPMSENSE_PROVIDES : RPMSENSE_OBSOLETES;
+
+    strcpy(buf, field);
+    line = buf;
+    
+    while ((prov = strtok(line, " ,\t\n"))) {
+       if (prov[0] == '/') {
+           rpmError(RPMERR_BADSPEC,
+                    "line %d: No file names in %s: %s",
+                    spec->lineNum,
+                    (tag == RPMTAG_PROVIDES) ? "Provides" : "Obsoletes",
+                    spec->line);
+           return RPMERR_BADSPEC;
+       }
+       addReqProv(spec, pkg, flags, prov, NULL);
+       line = NULL;
+    }
+
+    return 0;
+}
diff --git a/build/parseScript.c b/build/parseScript.c
new file mode 100644 (file)
index 0000000..b2f604f
--- /dev/null
@@ -0,0 +1,199 @@
+#include <malloc.h>
+#include <string.h>
+
+#include "header.h"
+#include "read.h"
+#include "part.h"
+#include "misc.h"
+#include "rpmlib.h"
+#include "popt/popt.h"
+#include "reqprov.h"
+#include "package.h"
+
+/* Define this to be 1 to turn on -p "<prog> <args>..." ability */
+#define USE_PROG_STRING_ARRAY 0
+
+int parseScript(Spec spec, int parsePart)
+{
+    /* There are a few options to scripts: */
+    /*  <pkg>                              */
+    /*  -n <pkg>                           */
+    /*  -p <sh>                            */
+    /*  -p "<sh> <args>..."                */
+    /*  -f <file>                          */
+
+    char *p;
+    char *name = NULL;
+    char *prog = "/bin/sh";
+    char *file = NULL;
+    char **progArgv = NULL;
+    int progArgc;
+    char *partname = NULL;
+    int tag = 0;
+    int progtag = 0;
+    int flag = PART_SUBNAME;
+    Package pkg;
+    StringBuf sb;
+    int nextPart;
+    
+    int rc, argc;
+    char arg, **argv = NULL;
+    poptContext optCon = NULL;
+    struct poptOption optionsTable[] = {
+       { NULL, 'p', POPT_ARG_STRING, &prog, 'p' },
+       { NULL, 'n', POPT_ARG_STRING, &name, 'n' },
+       { NULL, 'f', POPT_ARG_STRING, &file, 'f' },
+       { 0, 0, 0, 0, 0 }
+    };
+    
+    switch (parsePart) {
+      case PART_PRE:
+       tag = RPMTAG_PREIN;
+       progtag = RPMTAG_PREINPROG;
+       partname = "%pre";
+       break;
+      case PART_POST:
+       tag = RPMTAG_POSTIN;
+       progtag = RPMTAG_POSTINPROG;
+       partname = "%post";
+       break;
+      case PART_PREUN:
+       tag = RPMTAG_PREUN;
+       progtag = RPMTAG_PREUNPROG;
+       partname = "%preun";
+       break;
+      case PART_POSTUN:
+       tag = RPMTAG_POSTUN;
+       progtag = RPMTAG_POSTUNPROG;
+       partname = "%postun";
+       break;
+      case PART_VERIFYSCRIPT:
+       tag = PART_VERIFYSCRIPT;
+       progtag = RPMTAG_VERIFYSCRIPTPROG;
+       partname = "%verifyscript";
+    }
+
+    if ((rc = poptParseArgvString(spec->line, &argc, &argv))) {
+       rpmError(RPMERR_BADSPEC, "line %d: Error parsing %s: %s",
+                spec->lineNum, partname, poptStrerror(rc));
+       return RPMERR_BADSPEC;
+    }
+    
+    optCon = poptGetContext(NULL, argc, argv, optionsTable, 0);
+    while ((arg = poptGetNextOpt(optCon)) > 0) {
+       if (arg == 'p') {
+           if (prog[0] != '/') {
+               rpmError(RPMERR_BADSPEC,
+                        "line %d: script program must begin "
+                        "with \'/\': %s", prog);
+               FREE(argv);
+               poptFreeContext(optCon);
+               return RPMERR_BADSPEC;
+           }
+       } else if (arg == 'n') {
+           flag = PART_NAME;
+       }
+    }
+    
+    if (arg < -1) {
+       rpmError(RPMERR_BADSPEC, "line %d: Bad option %s: %s",
+                spec->lineNum,
+                poptBadOption(optCon, POPT_BADOPTION_NOALIAS), 
+                spec->line);
+       FREE(argv);
+       poptFreeContext(optCon);
+       return RPMERR_BADSPEC;
+    }
+    
+    if (poptPeekArg(optCon)) {
+       if (! name) {
+           name = poptGetArg(optCon);
+       }
+       if (poptPeekArg(optCon)) {
+           rpmError(RPMERR_BADSPEC, "line %d: Too many names: %s",
+                    spec->lineNum,
+                    spec->line);
+           FREE(argv);
+           poptFreeContext(optCon);
+           return RPMERR_BADSPEC;
+       }
+    }
+    
+    if (lookupPackage(spec, name, flag, &pkg)) {
+       rpmError(RPMERR_BADSPEC, "line %d: Package does not exist: %s",
+                spec->lineNum, spec->line);
+       FREE(argv);
+       poptFreeContext(optCon);
+       return RPMERR_BADSPEC;
+    }
+
+    if (headerIsEntry(pkg->header, progtag)) {
+       rpmError(RPMERR_BADSPEC, "line %d: Second %s",
+                spec->lineNum, partname);
+       FREE(argv);
+       poptFreeContext(optCon);
+       return RPMERR_BADSPEC;
+    }
+
+    if ((rc = poptParseArgvString(prog, &progArgc, &progArgv))) {
+       rpmError(RPMERR_BADSPEC, "line %d: Error parsing %s: %s",
+                spec->lineNum, partname, poptStrerror(rc));
+       FREE(argv);
+       poptFreeContext(optCon);
+       return RPMERR_BADSPEC;
+    }
+    
+    sb = newStringBuf();
+    if (readLine(spec, STRIP_NOTHING) > 0) {
+       nextPart = PART_NONE;
+    } else {
+       while (! (nextPart = isPart(spec->line))) {
+           appendLineStringBuf(sb, spec->line);
+           if (readLine(spec, STRIP_NOTHING) > 0) {
+               nextPart = PART_NONE;
+               break;
+           }
+       }
+    }
+    stripTrailingBlanksStringBuf(sb);
+    p = getStringBuf(sb);
+
+#if USE_PROG_STRING_ARRAY
+    addReqProv(spec, pkg, RPMSENSE_PREREQ, progArgv[0], NULL);
+    headerAddEntry(pkg->header, progtag, RPM_STRING_ARRAY_TYPE,
+                  progArgv, progArgc);
+#else
+    addReqProv(spec, pkg, RPMSENSE_PREREQ, prog, NULL);
+    headerAddEntry(pkg->header, progtag, RPM_STRING_TYPE, prog, 1);
+#endif
+    if (*p) {
+       headerAddEntry(pkg->header, tag, RPM_STRING_TYPE, p, 1);
+    }
+
+    if (file) {
+       switch (parsePart) {
+         case PART_PRE:
+           pkg->preInFile = strdup(file);
+           break;
+         case PART_POST:
+           pkg->postInFile = strdup(file);
+           break;
+         case PART_PREUN:
+           pkg->preUnFile = strdup(file);
+           break;
+         case PART_POSTUN:
+           pkg->postUnFile = strdup(file);
+           break;
+         case PART_VERIFYSCRIPT:
+           pkg->verifyFile = strdup(file);
+           break;
+       }
+    }
+    
+    freeStringBuf(sb);
+    FREE(progArgv);
+    FREE(argv);
+    poptFreeContext(optCon);
+    
+    return nextPart;
+}
diff --git a/build/parseSpec.c b/build/parseSpec.c
new file mode 100644 (file)
index 0000000..a3badbb
--- /dev/null
@@ -0,0 +1,186 @@
+#include <ctype.h>
+#include <unistd.h>
+#include <string.h>
+#include <malloc.h>
+
+#include "header.h"
+#include "rpmlib.h"
+#include "part.h"
+#include "spec.h"
+#include "parse.h"
+#include "read.h"
+#include "misc.h"
+
+static void setStandardMacros(Spec spec, char *arch, char *os);
+
+int parseSpec(Spec *specp, char *specFile, char *buildRoot,
+             int inBuildArch, char *passPhrase, char *cookie)
+{
+    int parsePart = PART_PREAMBLE;
+    int initialPackage = 1;
+    char *name, *arch, *os;
+    Package pkg;
+    int x, index;
+    Spec spec;
+    
+    /* Set up a new Spec structure with no packages. */
+    spec = newSpec();
+
+    spec->specFile = strdup(specFile);
+    if (buildRoot) {
+       spec->gotBuildRoot = 1;
+       spec->buildRoot = strdup(buildRoot);
+    }
+    spec->docDir = strdup(rpmGetVar(RPMVAR_DEFAULTDOCDIR));
+    spec->inBuildArchitectures = inBuildArch;
+    if (passPhrase) {
+       spec->passPhrase = strdup(passPhrase);
+    }
+    if (cookie) {
+       spec->cookie = strdup(cookie);
+    }
+
+    if (rpmGetVar(RPMVAR_TIMECHECK)) {
+       if (parseNum(rpmGetVar(RPMVAR_TIMECHECK), &(spec->timeCheck))) {
+           rpmError(RPMERR_BADSPEC, "Timecheck value must be an integer: %s",
+                    rpmGetVar(RPMVAR_TIMECHECK));
+           freeSpec(spec);
+           return RPMERR_BADSPEC;
+       }
+    } else {
+       spec->timeCheck = 0;
+    }
+
+    /* Add some standard macros */
+    rpmGetArchInfo(&arch, NULL);
+    rpmGetOsInfo(&os, NULL);
+    setStandardMacros(spec, arch, os);
+
+    /* 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) {
+       switch (parsePart) {
+         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_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_VERIFYSCRIPT:
+           parsePart = parseScript(spec, parsePart);
+           break;
+
+         case PART_FILES:
+           parsePart = parseFiles(spec);
+           break;
+
+         case PART_TRIGGERIN:
+         case PART_TRIGGERUN:
+           printf("Triggers are not supported yet.\n");
+           exit(1);
+           /*parsePart = parseTrigger(spec, parsePart);*/
+           break;
+       }
+
+       if (parsePart < 0) {
+           freeSpec(spec);
+           return parsePart;
+       }
+
+       if (parsePart == PART_BUILDARCHITECTURES) {
+           spec->buildArchitectureSpecs =
+               malloc(sizeof(Spec) * spec->buildArchitectureCount);
+           x = 0;
+           index = 0;
+           while (x < spec->buildArchitectureCount) {
+               if (rpmMachineScore(RPM_MACHTABLE_BUILDARCH,
+                                   spec->buildArchitectures[x])) {
+                   rpmSetMachine(spec->buildArchitectures[x], NULL);
+                   if (parseSpec(&(spec->buildArchitectureSpecs[index]),
+                                 specFile, buildRoot, 1,
+                                 passPhrase, cookie)) {
+                       spec->buildArchitectureCount = index;
+                       freeSpec(spec);
+                       return RPMERR_BADSPEC;
+                   }
+                   index++;
+               }
+               x++;
+           }
+           spec->buildArchitectureCount = index;
+           if (! index) {
+               freeSpec(spec);
+               rpmError(RPMERR_BADSPEC, "No buildable architectures");
+               return RPMERR_BADSPEC;
+           }
+           closeSpec(spec);
+           *specp = spec;
+           return 0;
+       }
+    }
+
+    /* Check for description in each package and add arch and os */
+    pkg = spec->packages;
+    while (pkg) {
+       headerGetEntry(pkg->header, RPMTAG_NAME, NULL, (void **) &name, NULL);
+       if (!headerIsEntry(pkg->header, RPMTAG_DESCRIPTION)) {
+           rpmError(RPMERR_BADSPEC, "Package has no %%description: %s", name);
+           freeSpec(spec);
+           return RPMERR_BADSPEC;
+       }
+
+       headerAddEntry(pkg->header, RPMTAG_OS, RPM_STRING_TYPE, os, 1);
+       headerAddEntry(pkg->header, RPMTAG_ARCH, RPM_STRING_TYPE, arch, 1);
+
+       pkg = pkg->next;
+    }
+
+    closeSpec(spec);
+    *specp = spec;
+    return 0;
+}
+
+static void setStandardMacros(Spec spec, char *arch, char *os)
+{
+    char buf[BUFSIZ];
+    int x;
+
+    addMacro(&spec->macros, "sourcedir", rpmGetVar(RPMVAR_SOURCEDIR));
+    addMacro(&spec->macros, "builddir", rpmGetVar(RPMVAR_BUILDDIR));
+    addMacro(&spec->macros, "optflags", rpmGetVar(RPMVAR_OPTFLAGS));
+    addMacro(&spec->macros, "buildarch", arch);
+    addMacro(&spec->macros, "buildos", os);
+    
+    x = 0;
+    while (arch[x]) {
+       buf[x] = tolower(arch[x]);
+       x++;
+    }
+    buf[x] = '\0';
+    addMacro(&spec->macros, "buildarch_lc", buf);
+    x = 0;
+    while (os[x]) {
+       buf[x] = tolower(os[x]);
+       x++;
+    }
+    buf[x] = '\0';
+    addMacro(&spec->macros, "buildos_lc", buf);
+}
similarity index 72%
rename from build/trigger.c
rename to build/parseTrigger.c
index ea6e94a..1d18c1a 100644 (file)
@@ -1,22 +1,50 @@
 /* handle triggers */
 
-#include "config.h"
-#include "miscfn.h"
-
 #include <stdlib.h>
 #include <string.h>
 
-#include "trigger.h"
-#include "header.h"
-#include "spec.h"
-#include "specP.h"
-#include "messages.h"
-#include "rpmlib.h"
-#include "stringbuf.h"
-#include "misc.h"
-
-#define FREE(x) { if (x) free(x); }
-#define CHUNK 8
+/* %trigger is a strange combination of %pre and Requires: behavior */
+/* We can handle it by handing the args before "--" to parseScript, */
+/* which in the case of triggers should return an index.  We then   */
+/* can pass the remaining arguments to parseReqProv, along with the */
+/* index we just obtained.  In theory, then, all script arguments   */
+/* and behavior is in parseScript, and the require behavior is in   */
+/* parseReqProv, with just a little glue used here.                 */
+
+int parseTrigger(Spec spec, int parsePart)
+{
+    int nextPart;
+    char **lineArgv, **triggerArgv;
+    int lineArgc, triggerArgc;
+    int rc, dash;
+
+    if ((rc = poptParseArgvString(spec->line, &lineArgc, &lineArgv))) {
+       rpmError(RPMERR_BADSPEC, "line %d: Error parsing trigger options: %s",
+                spec->lineNum, partname, poptStrerror(rc));
+       return RPMERR_BADSPEC;
+    }
+    triggerArgv = lineArgv;
+    triggerArgc = lineArgc;
+
+    dash = 0;
+    while (dash <= lineArgc) {
+       if (!strcmp(lineArgv[dash], "--")) {
+           lineArgv[dash] = NULL;
+           if ((rc = parseScriptArgs(spec, dash, lineArgv,
+                                     &prog, &name, &flag))) {
+               free(lineArgv);
+               return rc;
+           }
+           triggerArgv = &(lineArgv[dash + 1]);
+           triggerArgc = lineArgc - dash - 1;
+           break;
+       }
+       dash++;
+    }
+
+    if ((rc = parseReqArgs(spec, triggerArgc, triggerArgv,
+
+}
 
 int addTrigger(struct PackageRec *package,
               int sense, char *script, char *args)
diff --git a/build/part.c b/build/part.c
new file mode 100644 (file)
index 0000000..ca3ce2e
--- /dev/null
@@ -0,0 +1,56 @@
+#include <string.h>
+
+#include "part.h"
+
+static struct PartRec {
+    int part;
+    int len;
+    char *token;
+} partList[] = {
+    {PART_PREAMBLE,     0, "%package"},
+    {PART_PREP,         0, "%prep"},
+    {PART_BUILD,        0, "%build"},
+    {PART_INSTALL,      0, "%install"},
+    {PART_CLEAN,        0, "%clean"},
+    {PART_PREUN,        0, "%preun"},
+    {PART_POSTUN,       0, "%postun"},
+    {PART_PRE,          0, "%pre"},
+    {PART_POST,         0, "%post"},
+    {PART_FILES,        0, "%files"},
+    {PART_CHANGELOG,    0, "%changelog"},
+    {PART_DESCRIPTION,  0, "%description"},
+    {PART_TRIGGERUN,    0, "%triggerun"},
+    {PART_TRIGGERIN,    0, "%trigger"},
+    {PART_VERIFYSCRIPT, 0, "%verifyscript"},
+    {0, 0, 0}
+};
+
+static void initParts(void)
+{
+    struct PartRec *p = partList;
+
+    while (p->token) {
+       p->len = strlen(p->token);
+       p++;
+    }
+}
+
+int isPart(char *line)
+{
+    struct PartRec *p = partList;
+
+    if (p->len == 0) {
+       initParts();
+    }
+    
+    while (p->token && strncmp(line, p->token, p->len)) {
+       p++;
+    }
+
+    if (p->token) {
+       return p->part;
+    } else {
+       return PART_NONE;
+    }
+}
+
diff --git a/build/part.h b/build/part.h
new file mode 100644 (file)
index 0000000..6ddae32
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef _PART_H_
+#define _PART_H_
+
+#define PART_NONE                0
+#define PART_PREAMBLE            1
+#define PART_PREP                2
+#define PART_BUILD               3
+#define PART_INSTALL             4
+#define PART_CLEAN               5
+#define PART_FILES               6
+#define PART_PRE                 7
+#define PART_POST                8
+#define PART_PREUN               9
+#define PART_POSTUN             10
+#define PART_DESCRIPTION        11
+#define PART_CHANGELOG          12
+#define PART_TRIGGERIN          13
+#define PART_TRIGGERUN          14
+#define PART_VERIFYSCRIPT       15
+#define PART_BUILDARCHITECTURES 16
+
+int isPart(char *line);
+
+#endif
diff --git a/build/read.c b/build/read.c
new file mode 100644 (file)
index 0000000..b5790fe
--- /dev/null
@@ -0,0 +1,138 @@
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "spec.h"
+#include "rpmlib.h"
+#include "messages.h"
+#include "macro.h"
+#include "misc.h"
+
+static int matchTok(char *token, char *line);
+
+/* returns 0 - success */
+/*         1 - EOF     */
+/*        <0 - error   */
+
+int readLine(Spec spec, int strip)
+{
+    char *from, *to, *last, *s, *arch, *os;
+    int match;
+    char ch;
+    struct ReadLevelEntry *rl;
+
+    /* Make sure the spec file is open */
+    if (!spec->file) {
+       if (!(spec->file = fopen(spec->specFile, "r"))) {
+           rpmError(RPMERR_BADSPEC, "Unable to open: %s\n", spec->specFile);
+           return RPMERR_BADSPEC;
+       }
+       spec->lineNum = 0;
+    }
+
+    /* Make sure we have something in the read buffer */
+    if (!spec->readPtr || ! *(spec->readPtr)) {
+       if (!fgets(spec->readBuf, BUFSIZ, spec->file)) {
+           /* EOF */
+           if (spec->readStack->next) {
+               rpmError(RPMERR_UNMATCHEDIF, "Unclosed %%if");
+               return RPMERR_UNMATCHEDIF;
+           }
+           return 1;
+       }
+       spec->readPtr = spec->readBuf;
+       spec->lineNum++;
+       /*rpmMessage(RPMMESS_DEBUG, "LINE: %s", spec->readBuf);*/
+    }
+    
+    /* Copy a single line to the line buffer */
+    from = spec->readPtr;
+    to = last = spec->line;
+    ch = ' ';
+    while (*from && ch != '\n') {
+       ch = *to++ = *from++;
+       if (!isspace(ch)) {
+           last = to;
+       }
+    }
+    *to = '\0';
+    spec->readPtr = from;
+    
+    if (strip) {
+       *last = '\0';
+    }
+
+    rpmGetArchInfo(&arch, NULL);
+    rpmGetOsInfo(&os, NULL);
+    s = spec->line;
+    SKIPSPACE(s);
+    match = -1;
+    if (! strncmp("%ifarch", s, 7)) {
+       match = matchTok(arch, s);
+    } else if (! strncmp("%ifnarch", s, 8)) {
+       match = !matchTok(arch, s);
+    } else if (! strncmp("%ifos", s, 5)) {
+       match = matchTok(os, s);
+    } else if (! strncmp("%ifnos", s, 6)) {
+       match = !matchTok(os, s);
+    } else if (! strncmp("%else", s, 5)) {
+       if (! spec->readStack->next) {
+           /* Got an else with no %if ! */
+           rpmError(RPMERR_UNMATCHEDIF, "line %d: Got a %%else with no if",
+                    spec->lineNum);
+           return RPMERR_UNMATCHEDIF;
+       }
+       spec->readStack->reading =
+           spec->readStack->next->reading && ! spec->readStack->reading;
+       spec->line[0] = '\0';
+    } else if (! strncmp("%endif", s, 6)) {
+       if (! spec->readStack->next) {
+           /* Got an end with no %if ! */
+           rpmError(RPMERR_UNMATCHEDIF, "line %d: Got a %%endif with no if",
+                    spec->lineNum);
+           return RPMERR_UNMATCHEDIF;
+       }
+       rl = spec->readStack;
+       spec->readStack = spec->readStack->next;
+       free(rl);
+       spec->line[0] = '\0';
+    }
+    if (match != -1) {
+       rl = malloc(sizeof(struct ReadLevelEntry));
+       rl->reading = spec->readStack->reading && match;
+       rl->next = spec->readStack;
+       spec->readStack = rl;
+       spec->line[0] = '\0';
+    }
+    
+    if (spec->readStack->reading) {
+       expandMacros(&spec->macros, spec->line);
+    } else {
+       spec->line[0] = '\0';
+    }
+
+    return 0;
+}
+
+static int matchTok(char *token, char *line)
+{
+    char buf[BUFSIZ], *tok;
+
+    strcpy(buf, line);
+    strtok(buf, " \n\t");
+    while ((tok = strtok(NULL, " \n\t"))) {
+       if (! strcmp(tok, token)) {
+           return 1;
+       }
+    }
+
+    return 0;
+}
+
+void closeSpec(Spec spec)
+{
+    if (spec->file) {
+       fclose(spec->file);
+    }
+    spec->file = NULL;
+}
diff --git a/build/read.h b/build/read.h
new file mode 100644 (file)
index 0000000..041125d
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef _READ_H_
+#define _READ_H_
+
+#include "spec.h"
+
+#define STRIP_NOTHING       0
+#define STRIP_TRAILINGSPACE 1
+
+/* returns 0 - success */
+/*         1 - EOF     */
+/*        <0 - error   */
+
+int readLine(Spec spec, int strip);
+void closeSpec(Spec spec);
+
+#endif
index b06e930..0a2abfb 100644 (file)
 /* reqprov.c -- require/provide handling */
 
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <signal.h>
-#include <fcntl.h>
-#include <ctype.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <sys/stat.h>
-#include <errno.h>
+#include <malloc.h>
 
-#include "specP.h"
+#include "spec.h"
 #include "reqprov.h"
 #include "messages.h"
 #include "rpmlib.h"
 #include "misc.h"
+#include "lib/misc.h"
 
-static StringBuf getOutputFrom(char *dir, char *argv[],
-                              char *writePtr, int writeBytesLeft,
-                              int failNonZero);
-
-/*************************************************************/
-/*                                                           */
-/* Adding entries to the package reqprov list                */
-/* Handle duplicate entries                                  */
-/*                                                           */
-/*************************************************************/
-
-int addReqProv(struct PackageRec *p, int flags,
-              char *name, char *version)
+int addReqProv(Spec spec, Package pkg, int flag, char *name, char *version)
 {
-    struct ReqProv *rd;
-    int same;
-
-    /* Frist see if the same entry is already there */
-    rd = p->reqprov;
-    while (rd) {
-       if (rd->flags == flags) {
-           if (rd->version == version) {
-               same = 1;
-           } else if (!rd->version || !version) {
-               same = 0;
-           } else if (!strcmp(rd->version, version)) {
-               same = 1;
-           } else {
-               same = 0;
-           }
-
-           if (same && !strcmp(rd->name, name)) {
-               /* They are exacty the same */
-               break;
-           }
-       }
-       rd = rd->next;
-    }
-    if (rd) {
-       /* already there */
-       rpmMessage(RPMMESS_DEBUG, "Already Got: %s\n", name);
-       return 0;
-    }
+    char **names;
+    char **versions = NULL;
+    int *flags = NULL;
+    int nametag = 0;
+    int versiontag = 0;
+    int flagtag = 0;
+    int len;
+    int extra = 0;
     
-    rd = (struct ReqProv *)malloc(sizeof(*rd));
-    rd->flags = flags;
-    rd->name = strdup(name);
-    rd->version = version ? strdup(version) : NULL;
-    rd->next = p->reqprov;
-    p->reqprov = rd;
-
-    if (flags & RPMSENSE_PROVIDES) {
-       rpmMessage(RPMMESS_DEBUG, "Adding provide: %s\n", name);
-       p->numProv++;
-    } else if (flags & RPMSENSE_CONFLICTS) {
-       rpmMessage(RPMMESS_DEBUG, "Adding conflict: %s\n", name);
-       p->numConflict++;
-    } else if (flags & RPMSENSE_PREREQ) {
-       rpmMessage(RPMMESS_DEBUG, "Adding prereq: %s\n", name);
-       p->numPreReq++;
-    } else if (flags & RPMSENSE_OBSOLETES) {
-       rpmMessage(RPMMESS_DEBUG, "Adding obsoletes: %s\n", name);
-       p->numObsoletes++;
+    if (flag & RPMSENSE_PROVIDES) {
+       nametag = RPMTAG_PROVIDES;
+    } else if (flag & RPMSENSE_OBSOLETES) {
+       nametag = RPMTAG_OBSOLETES;
+    } else if (flag & RPMSENSE_CONFLICTS) {
+       nametag = RPMTAG_CONFLICTNAME;
+       versiontag = RPMTAG_CONFLICTVERSION;
+       flagtag = RPMTAG_CONFLICTFLAGS;
+    } else if (flag & RPMSENSE_PREREQ) {
+       nametag = RPMTAG_REQUIRENAME;
+       versiontag = RPMTAG_REQUIREVERSION;
+       flagtag = RPMTAG_REQUIREFLAGS;
+       extra = RPMSENSE_PREREQ;
     } else {   
-       rpmMessage(RPMMESS_DEBUG, "Adding require: %s\n", name);
-       p->numReq++;
+       nametag = RPMTAG_REQUIRENAME;
+       versiontag = RPMTAG_REQUIREVERSION;
+       flagtag = RPMTAG_REQUIREFLAGS;
     }
 
-    return 0;
-}
-
-/*************************************************************/
-/*                                                           */
-/* Add require/provides for the files in the header          */
-/*  (adds to the package structure)                          */
-/*                                                           */
-/*************************************************************/
-
-static StringBuf getOutputFrom(char *dir, char *argv[],
-                              char *writePtr, int writeBytesLeft,
-                              int failNonZero)
-{
-    int progPID;
-    int progDead;
-    int toProg[2];
-    int fromProg[2];
-    int status;
-    void *oldhandler;
-    int bytesWritten;
-    StringBuf readBuff;
-    int bytes;
-    unsigned char buf[8193];
-
-    oldhandler = signal(SIGPIPE, SIG_IGN);
-
-    pipe(toProg);
-    pipe(fromProg);
-    
-    if (!(progPID = fork())) {
-       close(toProg[1]);
-       close(fromProg[0]);
-       
-       dup2(toProg[0], 0);   /* Make stdin the in pipe */
-       dup2(fromProg[1], 1); /* Make stdout the out pipe */
-
-       close(toProg[0]);
-       close(fromProg[1]);
-
-       chdir(dir);
-       
-       execvp(argv[0], argv);
-       rpmError(RPMERR_EXEC, "Couldn't exec %s", argv[0]);
-       _exit(RPMERR_EXEC);
-    }
-    if (progPID < 0) {
-       rpmError(RPMERR_FORK, "Couldn't fork %s", argv[0]);
-       return NULL;
+    flag = (flag & RPMSENSE_SENSEMASK) | extra;
+    if (!version) {
+       version = "";
     }
-
-    close(toProg[0]);
-    close(fromProg[1]);
-
-    /* Do not block reading or writing from/to prog. */
-    fcntl(fromProg[0], F_SETFL, O_NONBLOCK);
-    fcntl(toProg[1], F_SETFL, O_NONBLOCK);
     
-    readBuff = newStringBuf();
-
-    progDead = 0;
-    do {
-       if (waitpid(progPID, &status, WNOHANG)) {
-           progDead = 1;
+    if (headerGetEntry(pkg->header, nametag, NULL, (void *) &names, &len)) {
+       if (flagtag) {
+           headerGetEntry(pkg->header, versiontag, NULL,
+                          (void *) &versions, NULL);
+           headerGetEntry(pkg->header, flagtag, NULL, (void *) &flags, NULL);
        }
-
-       /* Write some stuff to the process if possible */
-        if (writeBytesLeft) {
-           if ((bytesWritten =
-                 write(toProg[1], writePtr,
-                   (1024<writeBytesLeft) ? 1024 : writeBytesLeft)) < 0) {
-               if (errno != EAGAIN) {
-                   perror("getOutputFrom()");
-                   exit(1);
+       while (len) {
+           len--;
+           if (!strcmp(names[len], name)) {
+               if (!flagtag ||
+                   (!strcmp(versions[len], version) && flags[len] == flag)) {
+                   /* The same */
+                   FREE(names);
+                   FREE(versions);
+                   return 0;
                }
-               bytesWritten = 0;
            }
-           writeBytesLeft -= bytesWritten;
-           writePtr += bytesWritten;
-       } else {
-           close(toProg[1]);
        }
-       
-       /* Read any data from prog */
-       bytes = read(fromProg[0], buf, sizeof(buf)-1);
-       while (bytes > 0) {
-           buf[bytes] = '\0';
-           appendStringBuf(readBuff, buf);
-           bytes = read(fromProg[0], buf, sizeof(buf)-1);
-       }
-
-       /* terminate when prog dies */
-    } while (!progDead);
-
-    close(toProg[1]);
-    close(fromProg[0]);
-    signal(SIGPIPE, oldhandler);
-
-    if (writeBytesLeft) {
-       rpmError(RPMERR_EXEC, "failed to write all data to %s", argv[0]);
-       return NULL;
+       FREE(names);
+       FREE(versions);
     }
-    waitpid(progPID, &status, 0);
-    if (failNonZero && (!WIFEXITED(status) || WEXITSTATUS(status))) {
-       rpmError(RPMERR_EXEC, "%s failed", argv[0]);
-       return NULL;
+
+    headerAddOrAppendEntry(pkg->header, nametag,
+                          RPM_STRING_ARRAY_TYPE, &name, 1);
+    if (flagtag) {
+       headerAddOrAppendEntry(pkg->header, versiontag,
+                              RPM_STRING_ARRAY_TYPE, &version, 1);
+       headerAddOrAppendEntry(pkg->header, flagtag,
+                              RPM_INT32_TYPE, &flag, 1);
     }
 
-    return readBuff;
+    return 0;
 }
 
-int generateAutoReqProv(Header header, struct PackageRec *p)
+int generateAutoReqProv(Spec spec, Package pkg,
+                       struct cpioFileMapping *cpioList, int cpioCount)
 {
-    char **f, **fsave, *s;
-    int count;
-    int_16 *modes;
-
-    StringBuf writeBuff;
-    StringBuf readBuff;
-    char *writePtr;
+    StringBuf writeBuf;
     int writeBytes;
-    char dir[1024];
-    char *argv[8];
-
-    rpmMessage(RPMMESS_VERBOSE, "Finding dependencies...\n");
-
-    /*** Get root directory ***/
-    
-    if (rpmGetVar(RPMVAR_ROOT)) {
-       strcpy(dir, rpmGetVar(RPMVAR_ROOT));
-    } else {
-       strcpy(dir, "/");
-    }
+    StringBuf readBuf;
+    char *argv[2];
+    char **f, **fsave;
 
-    /*** Generate File List ***/
-    
-    if (!headerGetEntry(header, RPMTAG_FILENAMES, NULL, (void **) &f, &count)) {
-       return 0;
-    }
-    if (!count) {
+    if (!cpioCount) {
        return 0;
     }
-    fsave = f;
-    headerGetEntry(header, RPMTAG_FILEMODES, NULL, (void **) &modes, NULL);
 
-    writeBuff = newStringBuf();
+    writeBuf = newStringBuf();
     writeBytes = 0;
-    while (count--) {
-        s = *f++;
-       /* We skip the leading "/" (already normalized) */
-       writeBytes += strlen(s);
-       appendLineStringBuf(writeBuff, s + 1);
-    }
-    if (fsave) {
-       free(fsave);
+    while (cpioCount--) {
+       writeBytes += strlen(cpioList->fsPath) + 1;
+       appendLineStringBuf(writeBuf, cpioList->fsPath);
+       cpioList++;
     }
-    writePtr = getStringBuf(writeBuff);
 
     /*** Do Provides ***/
+
+    rpmMessage(RPMMESS_NORMAL, "Finding provides...\n");
     
     argv[0] = "find-provides";
     argv[1] = NULL;
-    readBuff = getOutputFrom(dir, argv, writePtr, writeBytes, 1);
-    if (!readBuff) {
+    readBuf = getOutputFrom(NULL, argv,
+                            getStringBuf(writeBuf), writeBytes, 1);
+    if (!readBuf) {
        rpmError(RPMERR_EXEC, "Failed to find provides");
-       exit(1);
+       freeStringBuf(writeBuf);
+       return RPMERR_EXEC;
     }
     
-    s = getStringBuf(readBuff);
-    f = fsave = splitString(s, strlen(s), '\n');
-    freeStringBuf(readBuff);
+    f = fsave = splitString(getStringBuf(readBuf),
+                           strlen(getStringBuf(readBuf)), '\n');
+    freeStringBuf(readBuf);
     while (*f) {
        if (**f) {
-           addReqProv(p, RPMSENSE_PROVIDES, *f, NULL);
+           addReqProv(spec, pkg, RPMSENSE_PROVIDES, *f, NULL);
        }
        f++;
     }
-    free(fsave);
+    FREE(fsave);
 
     /*** Do Requires ***/
     
+    rpmMessage(RPMMESS_NORMAL, "Finding requires...\n");
+
     argv[0] = "find-requires";
     argv[1] = NULL;
-    readBuff = getOutputFrom(dir, argv, writePtr, writeBytes, 0);
-    if (!readBuff) {
+    readBuf = getOutputFrom(NULL, argv,
+                            getStringBuf(writeBuf), writeBytes, 0);
+    if (!readBuf) {
        rpmError(RPMERR_EXEC, "Failed to find requires");
-       exit(1);
+       freeStringBuf(writeBuf);
+       return RPMERR_EXEC;
     }
 
-    s = getStringBuf(readBuff);
-    f = fsave = splitString(s, strlen(s), '\n');
-    freeStringBuf(readBuff);
+    f = fsave = splitString(getStringBuf(readBuf),
+                           strlen(getStringBuf(readBuf)), '\n');
+    freeStringBuf(readBuf);
     while (*f) {
        if (**f) {
-           addReqProv(p, RPMSENSE_ANY, *f, NULL);
+           addReqProv(spec, pkg, RPMSENSE_ANY, *f, NULL);
        }
        f++;
     }
-    free(fsave);
+    FREE(fsave);
 
     /*** Clean Up ***/
 
-    freeStringBuf(writeBuff);
+    freeStringBuf(writeBuf);
 
     return 0;
 }
 
-/*************************************************************/
-/*                                                           */
-/* Generate and add header entry from package record         */
-/*                                                           */
-/*************************************************************/
-
-int processReqProv(Header h, struct PackageRec *p)
+void printReqs(Spec spec, Package pkg)
 {
-    struct ReqProv *rd;
-    char **nameArray, **namePtr;
-    char **versionArray, **versionPtr;
-    int_32 *flagArray, *flagPtr;
-    int x;
-
-    if (p->numProv) {
-       rd = p->reqprov;
-       nameArray = namePtr = malloc(p->numProv * sizeof(*nameArray));
-       rpmMessage(RPMMESS_VERBOSE, "Provides (%d):", p->numProv);
-       while (rd) {
-           if (rd->flags & RPMSENSE_PROVIDES) {
-               rpmMessage(RPMMESS_VERBOSE, " %s", rd->name);
-               *namePtr++ = rd->name;
-           }
-           rd = rd->next;
+    int startedPreReq = 0;
+    int startedReq = 0;
+
+    char **names;
+    int x, count;
+    int *flags;
+
+    if (headerGetEntry(pkg->header, RPMTAG_PROVIDES,
+                      NULL, (void **) &names, &count)) {
+       rpmMessage(RPMMESS_NORMAL, "Provides:");
+       x = 0;
+       while (x < count) {
+           rpmMessage(RPMMESS_NORMAL, " %s", names[x]);
+           x++;
        }
-       rpmMessage(RPMMESS_VERBOSE, "\n");
-
-       headerAddEntry(h, RPMTAG_PROVIDES, RPM_STRING_ARRAY_TYPE, nameArray, p->numProv);
-       free(nameArray);
-    }
-
-    if (p->numObsoletes) {
-       rd = p->reqprov;
-       nameArray = namePtr = malloc(p->numObsoletes * sizeof(*nameArray));
-       rpmMessage(RPMMESS_VERBOSE, "Obsoletes (%d):", p->numObsoletes);
-       while (rd) {
-           if (rd->flags & RPMSENSE_OBSOLETES) {
-               rpmMessage(RPMMESS_VERBOSE, " %s", rd->name);
-               *namePtr++ = rd->name;
-           }
-           rd = rd->next;
-       }
-       rpmMessage(RPMMESS_VERBOSE, "\n");
-
-       headerAddEntry(h, RPMTAG_OBSOLETES, RPM_STRING_ARRAY_TYPE, nameArray, p->numObsoletes);
-       free(nameArray);
-    }
-
-    if (p->numConflict) {
-       rd = p->reqprov;
-       nameArray = namePtr = malloc(p->numConflict * sizeof(*nameArray));
-       versionArray = versionPtr =
-           malloc(p->numConflict * sizeof(*versionArray));
-       flagArray = flagPtr = malloc(p->numConflict * sizeof(*flagArray));
-       rpmMessage(RPMMESS_VERBOSE, "Conflicts (%d):", p->numConflict);
-       while (rd) {
-           if (rd->flags & RPMSENSE_CONFLICTS) {
-               rpmMessage(RPMMESS_VERBOSE, " %s", rd->name);
-               *namePtr++ = rd->name;
-               *versionPtr++ = rd->version ? rd->version : "";
-               *flagPtr++ = rd->flags & RPMSENSE_SENSEMASK;
+       rpmMessage(RPMMESS_NORMAL, "\n");
+       FREE(names);
+    }
+
+    if (headerGetEntry(pkg->header, RPMTAG_REQUIRENAME,
+                      NULL, (void **) &names, &count)) {
+       headerGetEntry(pkg->header, RPMTAG_REQUIREFLAGS,
+                      NULL, (void **) &flags, NULL);
+       x = 0;
+       while (x < count) {
+           if (flags[x] & RPMSENSE_PREREQ) {
+               if (! startedPreReq) {
+                   rpmMessage(RPMMESS_NORMAL, "Prereqs:");
+                   startedPreReq = 1;
+               }
+               rpmMessage(RPMMESS_NORMAL, " %s", names[x]);
            }
-           rd = rd->next;
+           x++;
        }
-       rpmMessage(RPMMESS_VERBOSE, "\n");
-
-       headerAddEntry(h, RPMTAG_CONFLICTNAME, RPM_STRING_ARRAY_TYPE,
-                nameArray, p->numConflict);
-       headerAddEntry(h, RPMTAG_CONFLICTVERSION, RPM_STRING_ARRAY_TYPE,
-                versionArray, p->numConflict);
-       headerAddEntry(h, RPMTAG_CONFLICTFLAGS, RPM_INT32_TYPE,
-                flagArray, p->numConflict);
-
-       free(nameArray);
-       free(versionArray);
-       free(flagArray);
-    }
-
-    x = p->numReq + p->numPreReq;
-    if (x) {
-       rd = p->reqprov;
-       nameArray = namePtr = malloc(x * sizeof(*nameArray));
-       versionArray = versionPtr = malloc(x * sizeof(*versionArray));
-       flagArray = flagPtr = malloc(x * sizeof(*flagArray));
-       rpmMessage(RPMMESS_VERBOSE, "[Pre]Requires (%d):", x);
-       while (rd) {
-           if (! ((rd->flags & RPMSENSE_PROVIDES) ||
-                  (rd->flags & RPMSENSE_OBSOLETES) ||
-                  (rd->flags & RPMSENSE_CONFLICTS))) {
-               if (rd->flags & RPMSENSE_PREREQ) {
-                   rpmMessage(RPMMESS_VERBOSE, " [%s]", rd->name);
-               } else {
-                   rpmMessage(RPMMESS_VERBOSE, " %s", rd->name);
+       rpmMessage(RPMMESS_NORMAL, "\n");
+       x = 0;
+       while (x < count) {
+           if (! (flags[x] & RPMSENSE_PREREQ)) {
+               if (! startedReq) {
+                   rpmMessage(RPMMESS_NORMAL, "Requires:");
+                   startedReq = 1;
                }
-               *namePtr++ = rd->name;
-               *versionPtr++ = rd->version ? rd->version : "";
-               *flagPtr++ = (rd->flags & RPMSENSE_SENSEMASK) |
-                   (rd->flags & RPMSENSE_PREREQ);
+               rpmMessage(RPMMESS_NORMAL, " %s", names[x]);
            }
-           rd = rd->next;
+           x++;
        }
-       rpmMessage(RPMMESS_VERBOSE, "\n");
-
-       headerAddEntry(h, RPMTAG_REQUIRENAME, RPM_STRING_ARRAY_TYPE,
-                nameArray, x);
-       headerAddEntry(h, RPMTAG_REQUIREVERSION, RPM_STRING_ARRAY_TYPE,
-                versionArray, x);
-       headerAddEntry(h, RPMTAG_REQUIREFLAGS, RPM_INT32_TYPE,
-                flagArray, x);
-
-       free(nameArray);
-       free(versionArray);
-       free(flagArray);
+       rpmMessage(RPMMESS_NORMAL, "\n");
+       FREE(names);
     }
 
-    return 0;
 }
index f45e8a6..7069c47 100644 (file)
@@ -1,11 +1,13 @@
 #ifndef _REQPROV_H_
 #define _REQPROV_H_
 
-#include "specP.h"
+#include "spec.h"
+#include "package.h"
+#include "lib/cpio.h"
 
-int addReqProv(struct PackageRec *p, int flags,
-              char *name, char *version);
-int generateAutoReqProv(Header header, struct PackageRec *p);
-int processReqProv(Header h, struct PackageRec *p);
+int addReqProv(Spec spec, Package pkg, int flag, char *name, char *version);
+int generateAutoReqProv(Spec spec, Package pkg,
+                       struct cpioFileMapping *cpioList, int cpioCount);
+void printReqs(Spec spec, Package pkg);
 
-#endif _REQPROV_H_
+#endif
index 6bc0476..1be377c 100644 (file)
-/* RPM - Copyright (C) 1995 Red Hat Software
- * 
- * spec.c - routines for parsing a spec file
- */
-
-/*****************************
-TODO:
-
-. should be able to drop the -n in non-%package parts
-
-******************************/
-
-#include "config.h"
-#include "miscfn.h"
-
-#if HAVE_ALLOCA_H
-# include <alloca.h>
-#endif
-
 #include <stdlib.h>
+#include <malloc.h>
 #include <string.h>
-#include <sys/types.h>
-#include <time.h>
-#include <sys/time.h>
-#include <limits.h>
-#include <ctype.h>
 
-#include "header.h"
 #include "spec.h"
-#include "specP.h"
-#include "messages.h"
-#include "rpmlib.h"
-#include "stringbuf.h"
 #include "misc.h"
-#include "reqprov.h"
-#include "trigger.h"
+#include "rpmlib.h"
+#include "package.h"
+#include "read.h"
+#include "files.h"
 #include "macro.h"
 
-#define LINE_BUF_SIZE 1024
-#define FREE(x) { if (x) free(x); }
-
-static struct PackageRec *new_packagerec(void);
-static int read_line(FILE *f, char *line);
-static int match_arch(char *s);
-static int match_os(char *s);
-static void free_packagerec(struct PackageRec *p);
-static void generateNames(Spec s);
-static void reset_spec(void);
-static int find_preamble_line(char *line, char **s);
-static int check_part(char *line, char **s);
-static int lookup_package(Spec s, struct PackageRec **pr,
-                         char *name, int flags);
-static void dumpPackage(struct PackageRec *p, FILE *f);
-
-static int dateToTimet(const char * datestr, time_t * secs);
-static void addChangelogEntry(Header h, int time, char *name, char *text);
-static int addChangelog(Header h, StringBuf sb);
-
-static int parseProvides(struct PackageRec *p, char *line, int tag);
-static int parseRequiresConflicts(struct PackageRec *p, char *line,
-                                 int flag);
-static void free_reqprov(struct ReqProv *p);
-static int noSourcePatch(Spec s, char *line, int_32 tag);
+static char *getSourceAux(Spec spec, int num, int flag, int full);
+static struct Source *findSource(Spec spec, int num, int flag);
 
-static void addListEntry(Header h, int_32 tag, char *line);
-static int finishCurrentPart(Spec spec, StringBuf sb,
-                            struct PackageRec *cur_package,
-                            int cur_part, char *triggerArgs,
-                            char *scriptProg);
-
-Spec parseSpecAux(FILE *f, char *specfile, char *buildRootOverride,
-                 char ***buildArchs);
-
-/**********************************************************************/
-/*                                                                    */
-/* Source and patch structure creation/deletion/lookup                */
-/*                                                                    */
-/**********************************************************************/
-
-static int addSource(Spec spec, char *line)
+Spec newSpec(void)
 {
-    struct sources *p;
-    char *s, *s1, c;
-    char *file;
-    unsigned long int x;
-    char name[1024], expansion[1024];
-
-    p = malloc(sizeof(struct sources));
-    p->next = spec->sources;
-    spec->sources = p;
-
-    if (! strncasecmp(line, "source", 6)) {
-       spec->numSources++;
-       p->ispatch = 0;
-       s = line + 6;
-    } else if (! strncasecmp(line, "patch", 5)) {
-       spec->numPatches++;
-       p->ispatch = 1;
-       s = line + 5;
-    } else {
-       rpmError(RPMERR_BADSPEC, "Not a source/patch line: %s\n", line);
-       return(RPMERR_BADSPEC);
-    }
-
-    s += strspn(s, " \t\n");
-    p->num = 0;
-    if (*s != ':') {
-        x = strspn(s, "0123456789");
-       if (! x) {
-           rpmError(RPMERR_BADSPEC, "Bad source/patch line: %s\n", line);
-           return(RPMERR_BADSPEC);
-       }
-       c = s[x];
-       s[x] = '\0';
-       s1 = NULL;
-       p->num = strtoul(s, &s1, 10);
-       if ((*s1) || (s1 == s) || (p->num == ULONG_MAX)) {
-           s[x] = c;
-           rpmError(RPMERR_BADSPEC, "Bad source/patch number: %s\n", s);
-           return(RPMERR_BADSPEC);
-       }
-       s[x] = c;
-       s += x;
-       /* skip spaces */
-       s += strspn(s, " \t\n");
-    }
+    Spec spec;
 
-    if (*s != ':') {
-       rpmError(RPMERR_BADSPEC, "Bad source/patch line: %s\n", line);
-       return(RPMERR_BADSPEC);
-    }
+    spec = (Spec)malloc(sizeof *spec);
     
-    /* skip to actual source */
-    s++;
-    s += strspn(s, " \t\n");
+    spec->specFile = NULL;
+    spec->sourceRpmName = NULL;
+
+    spec->file = NULL;
+    spec->readBuf[0] = '\0';
+    spec->readPtr = NULL;
+    spec->line[0] = '\0';
+    spec->readStack = malloc(sizeof(struct ReadLevelEntry));
+    spec->readStack->next = NULL;
+    spec->readStack->reading = 1;
+
+    spec->prep = NULL;
+    spec->build = NULL;
+    spec->install = NULL;
+    spec->clean = NULL;
 
-    file = strtok(s, " \t\n");
-    if (! file) {
-       rpmError(RPMERR_BADSPEC, "Bad source/patch line: %s\n", line);
-       return(RPMERR_BADSPEC);
-    }
-    p->fullSource = strdup(file);
-    p->source = strrchr(p->fullSource, '/');
-    if (p->source) {
-       p->source++;
-    } else {
-       p->source = p->fullSource;
-    }
+    spec->sources = NULL;
+    spec->packages = NULL;
+    spec->noSource = 0;
+    spec->numSources = 0;
 
-    sprintf(expansion, "%s/%s", rpmGetVar(RPMVAR_SOURCEDIR), p->source);
-    sprintf(name, "%s%d", (p->ispatch) ? "PATCH" : "SOURCE", p->num);
-    addMacro(name, expansion);
-    sprintf(name, "%sURL%d", (p->ispatch) ? "PATCH" : "SOURCE", p->num);
-    addMacro(name, p->fullSource);
+    spec->sourceHeader = NULL;
+
+    spec->sourceCpioCount = 0;
+    spec->sourceCpioList = NULL;
     
-    if (p->ispatch) {
-       rpmMessage(RPMMESS_DEBUG, "Patch(%d) = %s\n", p->num, p->fullSource);
-    } else {
-       rpmMessage(RPMMESS_DEBUG, "Source(%d) = %s\n", p->num, p->fullSource);
-    }
+    spec->gotBuildRoot = 0;
+    spec->buildRoot = NULL;
     
-    return 0;
-}
-
-static void freeSources(Spec s)
-{
-    struct sources *p1, *p2;
-
-    p1 = s->sources;
-    while (p1) {
-       p2 = p1;
-       p1 = p1->next;
-       free(p2->fullSource);
-       free(p2);
-    }
-}
+    spec->buildSubdir = NULL;
 
-char *getSource(Spec s, int ispatch, int num)
-{
-    struct sources *p = s->sources;
+    spec->docDir = NULL;
 
-    while (p) {
-       if ((ispatch == p->ispatch) &&
-           (num == p->num)) {
-           break;
-       } else {
-           p = p->next;
-       }
-    }
-
-    if (p) {
-       return(p->source);
-    } else {
-       return(NULL);
-    }
-}
+    spec->passPhrase = NULL;
+    spec->timeCheck = 0;
+    spec->cookie = NULL;
 
-char *getFullSource(Spec s, int ispatch, int num)
-{
-    struct sources *p = s->sources;
+    spec->buildRestrictions = headerNew();
+    spec->buildArchitectures = NULL;
+    spec->buildArchitectureCount = 0;
+    spec->inBuildArchitectures = 0;
+    spec->buildArchitectureSpecs = NULL;
 
-    while (p) {
-       if ((ispatch == p->ispatch) &&
-           (num == p->num)) {
-           break;
-       } else {
-           p = p->next;
-       }
-    }
+    initMacros(&spec->macros);
+    
+    spec->autoReqProv = 1;
 
-    if (p) {
-       return(p->fullSource);
-    } else {
-       return(NULL);
-    }
+    return spec;
 }
 
-int noSourcePatch(Spec s, char *line, int_32 tag)
+void freeSpec(Spec spec)
 {
-    int_32 array[1024];  /* XXX - max 1024 sources or patches */
-    int_32 num;
-    int count;
-    char *t, *te;
-
-    if (((tag == RPMTAG_NOSOURCE) && s->numNoSource) ||
-       ((tag == RPMTAG_NOPATCH) && s->numNoPatch)) {
-       rpmError(RPMERR_BADSPEC, "Only one nosource/nopatch line allowed\n");
-       return(RPMERR_BADSPEC);
-    }
+    struct ReadLevelEntry *rl;
     
-    count = 0;
-    while ((t = strtok(line, ", \t"))) {
-       num = strtoul(t, &te, 10);
-       if ((*te) || (te == t) || (num == ULONG_MAX)) {
-           rpmError(RPMERR_BADSPEC, "Bad source/patch number: %s\n", t);
-           return(RPMERR_BADSPEC);
-       }
-       array[count++] = num;
-       rpmMessage(RPMMESS_DEBUG, "Skipping source/patch number: %d\n", num);
-       line = NULL;
-    }
-
-    if (count) {
-       if (tag == RPMTAG_NOSOURCE) {
-           s->numNoSource = count;
-           s->noSource = malloc(sizeof(int_32) * count);
-           memcpy(s->noSource, array, sizeof(int_32) * count);
-       } else {
-           s->numNoPatch = count;
-           s->noPatch = malloc(sizeof(int_32) * count);
-           memcpy(s->noPatch, array, sizeof(int_32) * count);
-       }
+    freeStringBuf(spec->prep);
+    freeStringBuf(spec->build);
+    freeStringBuf(spec->install);
+    freeStringBuf(spec->clean);
+
+    FREE(spec->buildRoot);
+    FREE(spec->buildSubdir);
+    FREE(spec->specFile);
+    FREE(spec->sourceRpmName);
+    FREE(spec->docDir);
+
+    while (spec->readStack) {
+       rl = spec->readStack;
+       spec->readStack = spec->readStack->next;
+       free(rl);
     }
-
-    return 0;
-}
-
-/**********************************************************************/
-/*                                                                    */
-/* Provide/Require handling                                           */
-/*                                                                    */
-/**********************************************************************/
-
-static void free_reqprov(struct ReqProv *p)
-{
-    struct ReqProv *s;
     
-    while (p) {
-       s = p;
-       p = p->next;
-       FREE(s->name);
-       FREE(s->version);
-       free(s);
+    if (spec->sourceHeader) {
+       headerFree(spec->sourceHeader);
     }
-}
 
-struct ReqComp ReqComparisons[] = {
-    { "<=", RPMSENSE_LESS | RPMSENSE_EQUAL},
-    { "<=S", RPMSENSE_LESS | RPMSENSE_EQUAL | RPMSENSE_SERIAL},
-    { "=<", RPMSENSE_LESS | RPMSENSE_EQUAL},
-    { "=<S", RPMSENSE_LESS | RPMSENSE_EQUAL | RPMSENSE_SERIAL},
-    { "<", RPMSENSE_LESS},
-    { "<S", RPMSENSE_LESS | RPMSENSE_SERIAL},
-
-    { "=", RPMSENSE_EQUAL},
-    { "=S", RPMSENSE_EQUAL | RPMSENSE_SERIAL},
+    freeCpioList(spec->sourceCpioList, spec->sourceCpioCount);
     
-    { ">=", RPMSENSE_GREATER | RPMSENSE_EQUAL},
-    { ">=S", RPMSENSE_GREATER | RPMSENSE_EQUAL | RPMSENSE_SERIAL},
-    { "=>", RPMSENSE_GREATER | RPMSENSE_EQUAL},
-    { "=>S", RPMSENSE_GREATER | RPMSENSE_EQUAL | RPMSENSE_SERIAL},
-    { ">", RPMSENSE_GREATER},
-    { ">S", RPMSENSE_GREATER | RPMSENSE_SERIAL},
-    { NULL, 0 },
-};
-
-static int parseRequiresConflicts(struct PackageRec *p, char *line,
-                                 int flag)
-{
-    char *req = NULL;
-    char *version = NULL;
-    int flags;
-    struct ReqComp *rc;
-
-    while (req || (req = strtok(line, " ,\t\n"))) {
-       switch (flag) {
-         case RPMTAG_CONFLICTFLAGS:
-           flags = RPMSENSE_CONFLICTS;
-           break;
-         case RPMTAG_PREREQ:
-           flags = RPMSENSE_PREREQ;
-           break;
-         default:
-           flags = RPMSENSE_ANY;
-           break;
-       }
-       if (flag == RPMTAG_CONFLICTFLAGS && req[0] == '/') {
-           rpmError(RPMERR_BADSPEC,
-                    "No file names in Conflicts: %s", req);
-           return RPMERR_BADSPEC;
-       }
-       if ((version = strtok(NULL, " ,\t\n"))) {
-           rc = ReqComparisons;
-           while (rc->token && strcmp(version, rc->token)) {
-               rc++;
-           }
-           if (rc->token) {
-               if (req[0] == '/') {
-                   rpmError(RPMERR_BADSPEC,
-                            "No versions on file names in Requires: %s", req);
-                   return RPMERR_BADSPEC;
-               }
-               if (flag == RPMTAG_PREREQ) {
-                   rpmError(RPMERR_BADSPEC,
-                            "No versions in PreReq: %s", req);
-                   return RPMERR_BADSPEC;
-               }
-               /* read a version */
-               flags |= rc->flags;
-               version = strtok(NULL, " ,\t\n");
-           }
-       }
-       if ((flags & RPMSENSE_SENSEMASK) && !version) {
-           rpmError(RPMERR_BADSPEC, "Version required in require/conflict");
-           return RPMERR_BADSPEC;
-       }
-
-       addReqProv(p, flags, req,
-                  (flags & RPMSENSE_SENSEMASK) ? version : NULL);
+    headerFree(spec->buildRestrictions);
+    FREE(spec->buildArchitectures);
 
-       req = NULL;
-       if (! (flags & RPMSENSE_SENSEMASK)) {
-           /* No version -- we just read a name */
-           req = version;
+    if (!spec->inBuildArchitectures) {
+       while (spec->buildArchitectureCount--) {
+           freeSpec(
+               spec->buildArchitectureSpecs[spec->buildArchitectureCount]);
        }
-       line = NULL;
     }
-    
-    return 0;
-}
+    FREE(spec->buildArchitectures);
 
-static int parseProvides(struct PackageRec *p, char *line, int tag)
-{
-    char *prov;
-    int flags;
+    FREE(spec->passPhrase);
+    FREE(spec->cookie);
 
-    flags = (tag == RPMTAG_PROVIDES) ? RPMSENSE_PROVIDES : RPMSENSE_OBSOLETES;
+    freeMacros(&spec->macros);
     
-    while ((prov = strtok(line, " ,\t\n"))) {
-       if (prov[0] == '/') {
-           rpmError(RPMERR_BADSPEC,
-                    "No file names in %s: %s",
-                    (tag == RPMTAG_PROVIDES) ? "provides" : "obsoletes",
-                    prov);
-           return RPMERR_BADSPEC;
-       }
-       addReqProv(p, flags, prov, NULL);
-       line = NULL;
-    }
-    return 0;
-}
-
-/**********************************************************************/
-/*                                                                    */
-/* Spec and package structure creation/deletion/lookup                */
-/*                                                                    */
-/**********************************************************************/
-
-static struct PackageRec *new_packagerec(void)
-{
-    struct PackageRec *p = malloc(sizeof(struct PackageRec));
-
-    p->subname = NULL;
-    p->newname = NULL;
-    p->icon = NULL;
-    p->header = headerNew();
-    p->filelist = newStringBuf();
-    p->files = -1;  /* -1 means no %files, thus no package */
-    p->fileFile = NULL;
-    p->doc = newStringBuf();
-    p->reqprov = NULL;
-    p->numReq = 0;
-    p->numProv = 0;
-    p->numConflict = 0;
-    p->numPreReq = 0;
-    p->numObsoletes = 0;
-    p->trigger.alloced = 0;
-    p->trigger.used = 0;
-    p->trigger.triggerScripts = NULL;
-    p->trigger.trigger = NULL;
-    p->trigger.triggerCount = 0;
-    p->next = NULL;
-
-    return p;
-}
-
-void free_packagerec(struct PackageRec *p)
-{
-    if (! p ) return;
+    freeSources(spec);
+    freePackages(spec);
+    closeSpec(spec);
     
-    headerFree(p->header);
-    freeStringBuf(p->filelist);
-    freeStringBuf(p->doc);
-    FREE(p->subname);
-    FREE(p->newname);
-    FREE(p->icon);
-    FREE(p->fileFile);
-    free_reqprov(p->reqprov);
-    freeTriggers(p->trigger);
-    if (p->next) {
-        free_packagerec(p->next);
-    }
-    free(p);
+    free(spec);
 }
 
-void freeSpec(Spec s)
+int addSource(Spec spec, Package pkg, char *field, int tag)
 {
-    FREE(s->name);
-    FREE(s->specfile);
-    FREE(s->noSource);
-    FREE(s->noPatch);
-    FREE(s->buildroot);
-    FREE(s->buildArch);
-    freeSources(s);
-    freeStringBuf(s->prep);
-    freeStringBuf(s->build);
-    freeStringBuf(s->install);
-    freeStringBuf(s->doc);
-    freeStringBuf(s->clean);
-    free_packagerec(s->packages);
-    free(s);
-}
-
-#define LP_CREATE           1
-#define LP_FAIL_EXISTS     (1 << 1)
-#define LP_SUBNAME         (1 << 2)
-#define LP_NEWNAME         (1 << 3)
-
-int lookup_package(Spec s, struct PackageRec **pr, char *name, int flags)
-{
-    struct PackageRec *package;
-    struct PackageRec **ppp;
-
-    package = s->packages;
-    while (package) {
-       if (flags & LP_SUBNAME) {
-           if (! package->subname) {
-               package = package->next;
-               continue;
-           }
-           if (! strcmp(package->subname, name)) {
-               break;
-           }
-       } else if (flags & LP_NEWNAME) {
-           if (! package->newname) {
-               package = package->next;
-               continue;
-           }
-           if (! strcmp(package->newname, name)) {
-               break;
-           }
-       } else {
-           /* Base package */
-           if ((! package->newname) && (! package->subname)) {
-               break;
-           }
-       }
-       package = package->next;
-    }
-    
-    if (package && (flags & LP_FAIL_EXISTS)) {
-       return 0;
-    }
-
-    if (package) {
-       *pr = package;
-       return 1;
+    struct Source *p;
+    int flag = 0;
+    char *name = NULL;
+    char *nump, *fieldp = NULL;
+    char buf[BUFSIZ];
+    char expansion[BUFSIZ];
+    int 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;
+       break;
     }
 
-    /* At this point the package does not exist */
-
-    if (! (flags & LP_CREATE)) {
-       return 0;
-    }
+    /* Get the number */
+    if (tag != RPMTAG_ICON) {
+       /* We already know that a ':' exists, and that there */
+       /* are no spaces before it.                          */
 
-    /* Create it */
-    package = new_packagerec();
-    if (name) {
-       if (flags & LP_SUBNAME) {
-           package->subname = strdup(name);
-       } else if (flags & LP_NEWNAME) {
-           package->newname = strdup(name);
+       nump = buf;
+       while (*fieldp != ':') {
+           *nump++ = *fieldp++;
        }
-    }
-
-    /* Link it in to the spec */
-    ppp = &(s->packages);
-    while (*ppp) {
-       ppp = &((*ppp)->next);
-    }
-    *ppp = package;
+       *nump = '\0';
 
-    *pr = package;
-    return 1;
-}
-
-static void generateNames(Spec s)
-{
-    struct PackageRec *package;
-    char buf[1024];
-    char *name;
-
-    package = s->packages;
-    while (package) {
-       if (package->subname) {
-           sprintf(buf, "%s-%s", s->name, package->subname);
-           name = buf;
-       } else if (package->newname) {
-           name = package->newname;
+       nump = buf;
+       SKIPSPACE(nump);
+       if (! *nump) {
+           num = 0;
        } else {
-           /* Must be the main package */
-           name = s->name;
+           if (parseNum(buf, &num)) {
+               rpmError(RPMERR_BADSPEC, "line %d: Bad %s number: %s\n",
+                        spec->lineNum, name, spec->line);
+               return RPMERR_BADSPEC;
+           }
        }
-       headerAddEntry(package->header, RPMTAG_NAME, RPM_STRING_TYPE, name, 1);
-       
-       package = package->next;
     }
-}
-
-/* datestr is of the form 'Wed Jan 1 1997' */
-static int dateToTimet(const char * datestr, time_t * secs)
-{
-    struct tm time;
-    char * chptr, * end, ** idx;
-    char * date = strcpy(alloca(strlen(datestr) + 1), datestr);
-    static char * days[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", 
-                               NULL };
-    static char * months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
-                            "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL };
-    static char lengths[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
-    
-    memset(&time, 0, sizeof(time));
-
-    end = chptr = date;
-
-    /* day of week */
-    if ((chptr = strtok(date, " \t\n")) == NULL) return -1;
-    idx = days;
-    while (*idx && strcmp(*idx, chptr)) idx++;
-    if (!*idx) return -1;
-
-    /* month */
-    if ((chptr = strtok(NULL, " \t\n")) == NULL) return -1;
-    idx = months;
-    while (*idx && strcmp(*idx, chptr)) idx++;
-    if (!*idx) return -1;
-
-    time.tm_mon = idx - months;
-
-    /* day */
-    if ((chptr = strtok(NULL, " \t\n")) == NULL) return -1;
-
-    /* make this noon so the day is always right (as we make this UTC) */
-    time.tm_hour = 12;
-
-    time.tm_mday = strtol(chptr, &chptr, 10);
-    if (*chptr) return -1;
-    if (time.tm_mday < 0 || time.tm_mday > lengths[time.tm_mon]) return -1;
 
-    /* year */
-    if ((chptr = strtok(NULL, " \t\n")) == NULL) return -1;
-
-    time.tm_year = strtol(chptr, &chptr, 10);
-    if (*chptr) return -1;
-    if (time.tm_year < 1997 || time.tm_year >= 3000) return -1;
-    time.tm_year -= 1900;
-
-    *secs = mktime(&time);
-    if (*secs == -1) return -1;
-
-    /* adjust to GMT */
-    *secs += timezone;
-
-    return 0;
-}
-
-static void addChangelogEntry(Header h, int time, char *name, char *text)
-{
-    if (headerIsEntry(h, RPMTAG_CHANGELOGTIME)) {
-       headerAppendEntry(h, RPMTAG_CHANGELOGTIME, RPM_INT32_TYPE,
-                         &time, 1);
-       headerAppendEntry(h, RPMTAG_CHANGELOGNAME, RPM_STRING_ARRAY_TYPE,
-                         &name, 1);
-       headerAppendEntry(h, RPMTAG_CHANGELOGTEXT, RPM_STRING_ARRAY_TYPE,
-                        &text, 1);
+    /* Create the entry and link it in */
+    p = malloc(sizeof(struct Source));
+    p->num = num;
+    p->fullSource = strdup(field);
+    p->source = strrchr(p->fullSource, '/');
+    p->flags = flag;
+    if (p->source) {
+       p->source++;
     } else {
-       headerAddEntry(h, RPMTAG_CHANGELOGTIME, RPM_INT32_TYPE,
-                      &time, 1);
-       headerAddEntry(h, RPMTAG_CHANGELOGNAME, RPM_STRING_ARRAY_TYPE,
-                      &name, 1);
-       headerAddEntry(h, RPMTAG_CHANGELOGTEXT, RPM_STRING_ARRAY_TYPE,
-                      &text, 1);
-    }
-}
-
-static int addChangelog(Header h, StringBuf sb)
-{
-    char *s;
-    int i;
-    int time, lastTime = 0;
-    char *date, *name, *text, *next;
-
-    s = getStringBuf(sb);
-
-    /* skip space */
-    while (*s && isspace(*s)) s++;
-
-    while (*s) {
-       if (*s != '*') {
-           rpmError(RPMERR_BADSPEC, "%%changelog entries must start with *");
-           return RPMERR_BADSPEC;
-       }
-
-       /* find end of line */
-       date = s;
-       while (*s && *s != '\n') s++;
-       if (! *s) {
-           rpmError(RPMERR_BADSPEC, "incomplete %%changelog entry");
-           return RPMERR_BADSPEC;
-       }
-       *s = '\0';
-       text = s + 1;
-       
-       /* 4 fields of date */
-       date++;
-       s = date;
-       for (i = 0; i < 4; i++) {
-           while (*s && isspace(*s)) s++;
-           while (*s && !isspace(*s)) s++;
-       }
-       while (isspace(*date)) date++;
-       if (dateToTimet(date, (time_t *)&time)) {
-           rpmError(RPMERR_BADSPEC, "bad date in %%changelog: %s", date);
-           return RPMERR_BADSPEC;
-       }
-       if (lastTime && lastTime < time) {
-           rpmError(RPMERR_BADSPEC,
-                    "%%changelog not in decending chronological order");
-           return RPMERR_BADSPEC;
-       }
-       lastTime = time;
-
-       /* skip space to the name */
-       while (*s && isspace(*s)) s++;
-       if (! *s) {
-           rpmError(RPMERR_BADSPEC, "missing name in %%changelog");
-           return RPMERR_BADSPEC;
-       }
-
-       /* name */
-       name = s;
-       while (*s) s++;
-       while (s > name && isspace(*s)) {
-           *s-- = '\0';
-       }
-       if (s == name) {
-           rpmError(RPMERR_BADSPEC, "missing name in %%changelog");
-           return RPMERR_BADSPEC;
-       }
-
-       /* text */
-       while (*text && isspace(*text)) text++;
-       if (! *text) {
-           rpmError(RPMERR_BADSPEC, "no description in %%changelog");
-           return RPMERR_BADSPEC;
-       }
-           
-       /* 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) && isspace(*s)) {
-           *s-- = '\0';
-       }
-       
-       addChangelogEntry(h, time, name, text);
-       s = next;
+       p->source = p->fullSource;
     }
 
-    return 0;
-}
-
-/**********************************************************************/
-/*                                                                    */
-/* Line reading                                                       */
-/*                                                                    */
-/**********************************************************************/
-
-static int match_arch(char *s)
-{
-    char *tok, *arch;
-    int sense, match;
-
-    rpmGetArchInfo(&arch, NULL);
-    match = 0;
-    
-    tok = strtok(s, " \n\t");
-    sense = (! strcmp(tok, "%ifarch")) ? 1 : 0;
-
-    while ((tok = strtok(NULL, " \n\t"))) {
-       if (! strcmp(tok, arch)) {
-           match |= 1;
-       }
+    if (tag != RPMTAG_ICON) {
+       p->next = spec->sources;
+       spec->sources = p;
+    } else {
+       p->next = pkg->icon;
+       pkg->icon = p;
     }
 
-    return (sense == match);
-}
-
-static int match_os(char *s)
-{
-    char *tok, *os;
-    int sense, match;
-
-    rpmGetOsInfo(&os, NULL);
-    match = 0;
-    
-    tok = strtok(s, " \n\t");
-    sense = (! strcmp(tok, "%ifos")) ? 1 : 0;
+    spec->numSources++;
 
-    while ((tok = strtok(NULL, " \n\t"))) {
-       if (! strcmp(tok, os)) {
-           match |= 1;
-       }
+    if (tag != RPMTAG_ICON) {
+       sprintf(expansion, "%s/%s", rpmGetVar(RPMVAR_SOURCEDIR), p->source);
+       sprintf(buf, "%s%d",
+               (flag & RPMBUILD_ISPATCH) ? "PATCH" : "SOURCE", num);
+       addMacro(&spec->macros, buf, expansion);
+       sprintf(buf, "%sURL%d",
+               (flag & RPMBUILD_ISPATCH) ? "PATCH" : "SOURCE", num);
+       addMacro(&spec->macros, buf, p->fullSource);
     }
-
-    return (sense == match);
-}
-
-static struct read_level_entry {
-    int reading;  /* 1 if we are reading at this level */
-    struct read_level_entry *next;
-} *read_level = NULL;
-
-static int read_line(FILE *f, char *line)
-{
-    static struct read_level_entry *rl;
-    int gotline;
-    char *r;
-    char *firstChar;
-
-    do {
-        gotline = 0;
-       if (! fgets(line, LINE_BUF_SIZE, f)) {
-           /* the end */
-           if (read_level->next) {
-               rpmError(RPMERR_UNMATCHEDIF, "Unclosed %%if");
-               return RPMERR_UNMATCHEDIF;
-           } else {
-               return 0;
-           }
-       }
-       firstChar = line;
-       while (*firstChar && isspace(*firstChar)) {
-           firstChar++;
-       }
-       if ((! strncmp("%ifarch", firstChar, 7)) ||
-           (! strncmp("%ifnarch", firstChar, 8))) {
-           expandMacros(line);
-           rl = malloc(sizeof(struct read_level_entry));
-           rl->next = read_level;
-           rl->reading = read_level->reading && match_arch(line);
-           read_level = rl;
-       } else if ((! strncmp("%ifos", firstChar, 5)) ||
-                  (! strncmp("%ifnos", firstChar, 6))) {
-           expandMacros(line);
-           rl = malloc(sizeof(struct read_level_entry));
-           rl->next = read_level;
-           rl->reading = read_level->reading && match_os(line);
-           read_level = rl;
-       } else if (! strncmp("%else", firstChar, 5)) {
-           expandMacros(line);
-           if (! read_level->next) {
-               /* Got an else with no %if ! */
-               rpmError(RPMERR_UNMATCHEDIF, "Got a %%else with no if");
-               return RPMERR_UNMATCHEDIF;
-           }
-           read_level->reading =
-               read_level->next->reading && ! read_level->reading;
-       } else if (! strncmp("%endif", firstChar, 6)) {
-           expandMacros(line);
-           if (! read_level->next) {
-               rpmError(RPMERR_UNMATCHEDIF, "Got a %%endif with no if");
-               return RPMERR_UNMATCHEDIF;
-           }
-           rl = read_level;
-           read_level = rl->next;
-           free(rl);
-       } else {
-           if (read_level->reading) {
-               expandMacros(line);
-           }
-            gotline = 1;
-        }
-    } while (! (gotline && read_level->reading));
     
-    r = line + (strlen(line)) - 1;
-    while (isspace(*r)) {
-        *(r--) = '\0';
-    }
-    return 1;
+    return 0;
 }
 
-/**********************************************************************/
-/*                                                                    */
-/* Line parsing                                                       */
-/*                                                                    */
-/**********************************************************************/
-
-struct preamble_line {
-    int tag;
-    int len;
-    char *token;
-} preamble_spec[] = {
-    {RPMTAG_NAME,          0, "name"},
-    {RPMTAG_VERSION,       0, "version"},
-    {RPMTAG_RELEASE,       0, "release"},
-    {RPMTAG_SERIAL,        0, "serial"},
-    {RPMTAG_DESCRIPTION,   0, "description"},
-    {RPMTAG_SUMMARY,       0, "summary"},
-    {RPMTAG_COPYRIGHT,     0, "copyright"},
-    {RPMTAG_COPYRIGHT,     0, "license"},
-    {RPMTAG_DISTRIBUTION,  0, "distribution"},
-    {RPMTAG_VENDOR,        0, "vendor"},
-    {RPMTAG_GROUP,         0, "group"},
-    {RPMTAG_PACKAGER,      0, "packager"},
-    {RPMTAG_URL,           0, "url"},
-    {RPMTAG_ROOT,          0, "root"},
-    {RPMTAG_SOURCE,        0, "source"},
-    {RPMTAG_PATCH,         0, "patch"},
-    {RPMTAG_NOSOURCE,      0, "nosource"},
-    {RPMTAG_NOPATCH,       0, "nopatch"},
-    {RPMTAG_EXCLUDEARCH,   0, "excludearch"},
-    {RPMTAG_EXCLUSIVEARCH, 0, "exclusivearch"},
-    {RPMTAG_EXCLUDEOS,     0, "excludeos"},
-    {RPMTAG_EXCLUSIVEOS,   0, "exclusiveos"},
-    {RPMTAG_EXCLUDE,       0, "exclude"},
-    {RPMTAG_EXCLUSIVE,     0, "exclusive"},
-    {RPMTAG_ICON,          0, "icon"},
-    {RPMTAG_PROVIDES,      0, "provides"},
-    {RPMTAG_REQUIREFLAGS,  0, "requires"},
-    {RPMTAG_PREREQ,        0, "prereq"},
-    {RPMTAG_CONFLICTFLAGS, 0, "conflicts"},
-    {RPMTAG_OBSOLETES,     0, "obsoletes"},
-    {RPMTAG_DEFAULTPREFIX, 0, "prefix"},
-    {RPMTAG_BUILDROOT,     0, "buildroot"},
-    {RPMTAG_BUILDARCHS,    0, "buildarchitectures"},
-    {RPMTAG_AUTOREQPROV,   0, "autoreqprov"},
-    {0, 0, 0}
-};
-
-static int find_preamble_line(char *line, char **s)
+char *getSource(Spec spec, int num, int flag)
 {
-    struct preamble_line *p = preamble_spec;
-
-    while (p->token && strncasecmp(line, p->token, p->len)) {
-        p++;
-    }
-    if (!p->token) return 0;
-    *s = line + p->len;
-
-    /* Unless this is a source or a patch, a ':' better be next */
-    if (p->tag != RPMTAG_SOURCE && p->tag != RPMTAG_PATCH) {
-       *s += strspn(*s, " \t");
-       if (**s != ':') {
-           return 0;
-       }
-    }
-    
-    *s += strspn(*s, ": \t");
-    return p->tag;
+    return getSourceAux(spec, num, flag, 0);
 }
 
-/* None of these can be 0 !! */
-#define PREAMBLE_PART       1
-#define PREP_PART           2
-#define BUILD_PART          3
-#define INSTALL_PART        4
-#define CLEAN_PART          5
-#define PREIN_PART          6
-#define POSTIN_PART         7
-#define PREUN_PART          8
-#define POSTUN_PART         9
-#define FILES_PART         10
-#define CHANGELOG_PART     11
-#define DESCRIPTION_PART   12
-#define TRIGGERIN_PART     13
-#define TRIGGERUN_PART     14
-#define VERIFYSCRIPT_PART  15
-
-static struct part_rec {
-    int part;
-    int len;
-    char *s;
-} part_list[] = {
-    {PREAMBLE_PART,    0, "%package"},
-    {PREP_PART,        0, "%prep"},
-    {BUILD_PART,       0, "%build"},
-    {INSTALL_PART,     0, "%install"},
-    {CLEAN_PART,       0, "%clean"},
-    {PREUN_PART,       0, "%preun"},
-    {POSTUN_PART,      0, "%postun"},
-    {PREIN_PART,       0, "%pre"},
-    {POSTIN_PART,      0, "%post"},
-    {FILES_PART,       0, "%files"},
-    {CHANGELOG_PART,   0, "%changelog"},
-    {DESCRIPTION_PART, 0, "%description"},
-    {TRIGGERUN_PART,   0, "%triggerun"},
-    {TRIGGERIN_PART,   0, "%trigger"},
-    {VERIFYSCRIPT_PART, 0, "%verifyscript"},
-    {0, 0, 0}
-};
-
-static int check_part(char *line, char **s)
+char *getFullSource(Spec spec, int num, int flag)
 {
-    struct part_rec *p = part_list;
-
-    while (p->s && strncmp(line, p->s, p->len)) {
-        p++;
-    }
-    if (!p) return 0;
-    *s = line + p->len;
-    *s += strspn(*s, " \t");
-    if (**s == '\0') {
-        *s = NULL;
-    }
-    return p->part;
+    return getSourceAux(spec, num, flag, 1);
 }
 
-#if 0
-static char *chop_line(char *s)
+static char *getSourceAux(Spec spec, int num, int flag, int full)
 {
-    char *p, *e;
-
-    p = s;
-    p += strspn(s, " \t");
-    if (*p == '\0') {
-       return NULL;
-    }
-    e = s + strlen(s) - 1;
-    while (index(" \t", *e)) {
-       e--;
-    }
-    return p;
-}
-#endif
+    struct Source *p = spec->sources;
 
-static void addListEntry(Header h, int_32 tag, char *line)
-{
-    int argc;
-    char **argv;
-    char **argvs;
-    char *s;
+    p = findSource(spec, num, flag);
 
-    argvs = argv = malloc(strlen(line) * sizeof(char *));
-    argc = 0;
-    while ((s = strtok(line, " \t"))) {
-       *argv = s;
-       argc++;
-       argv++;
-       line = NULL;
-    }
-    if (argc) {
-       headerAddEntry(h, tag, RPM_STRING_ARRAY_TYPE, argvs, argc);
-    }
-    free(argvs);
+    return (p) ? (full ? p->fullSource : p->source) : NULL;
 }
 
-static int finishCurrentPart(Spec spec, StringBuf sb,
-                            struct PackageRec *cur_package,
-                            int cur_part, char *triggerArgs,
-                            char *scriptProg)
+static struct Source *findSource(Spec spec, int num, int flag)
 {
-    int t1 = 0;
-    int t2 = 0;
-
-    stripTrailingBlanksStringBuf(sb);
-
-    switch (cur_part) {
-      case PREIN_PART:
-       t1 = RPMTAG_PREIN;
-       t2 = RPMTAG_PREINPROG;
-       break;
-      case POSTIN_PART:
-       t1 = RPMTAG_POSTIN;
-       t2 = RPMTAG_POSTINPROG;
-       break;
-      case PREUN_PART:
-       t1 = RPMTAG_PREUN;
-       t2 = RPMTAG_PREUNPROG;
-       break; 
-      case POSTUN_PART:
-       t1 = RPMTAG_POSTUN;
-       t2 = RPMTAG_POSTUNPROG;
-       break;
-      case VERIFYSCRIPT_PART:
-       t1 = RPMTAG_VERIFYSCRIPT;
-       break;
-      case DESCRIPTION_PART:
-       t1 = RPMTAG_DESCRIPTION;
-       break;
-      case CHANGELOG_PART:
-       /* %changelog is a little special.  It goes in the   */
-       /* "main" package no matter where it appears, and it */
-       /* ends up in all the packages.                      */
-       if (addChangelog(spec->packages->header, sb)) {
-           return 1;
-       }
-       break;
-      case TRIGGERIN_PART:
-       if (addTrigger(cur_package, RPMSENSE_TRIGGER_IN,
-                      getStringBuf(sb), triggerArgs)) {
-           return 1;
-       }
-       break;
-      case TRIGGERUN_PART:
-       if (addTrigger(cur_package, RPMSENSE_TRIGGER_UN,
-                      getStringBuf(sb), triggerArgs)) {
-           return 1;
-       }
-       break;
-    }
-    if (t1 && (*(getStringBuf(sb)) != '\0')) {
-       headerAddEntry(cur_package->header, t1,
-                      RPM_STRING_TYPE, getStringBuf(sb), 1);
-    }
-    if (t2) {
-       addReqProv(cur_package, RPMSENSE_PREREQ, scriptProg, NULL);
-       headerAddEntry(cur_package->header, t2,
-                      RPM_STRING_TYPE, scriptProg, 1);
-    }
-    return 0;
-}
+    struct Source *p = spec->sources;
 
-/**********************************************************************/
-/*                                                                    */
-/* Main specfile parsing routine                                      */
-/*                                                                    */
-/**********************************************************************/
-
-Spec *parseSpec(FILE *f, char *specfile, char *buildRootOverride)
-{
-    Spec *res;
-    Spec s;
-    char **archs = NULL;
-    char **arch;
-    int i, count;
-    
-    s = parseSpecAux(f, specfile, buildRootOverride, &archs);
-
-    if (s) {
-       /* No BuildArchitectures field */
-       res = (Spec *) malloc(2 * sizeof(Spec));
-       res[0] = s;
-       res[1] = NULL;
-       return res;
-    }
-
-    if (! archs) {
-       /* Error */
-       return NULL;
-    }
-
-    /* We have a BuildArchitectures field */
-    count = 0;
-    while (archs[count]) {
-       count++;
-    }
-    res = (Spec *) malloc(count * sizeof(Spec));
-
-    i = 0;
-    arch = archs;
-    while (*arch) {
-       if (rpmMachineScore(RPM_MACHTABLE_BUILDARCH, *arch)) {
-           rewind(f);
-           rpmSetMachine(*arch, NULL);
-           res[i] = parseSpecAux(f, specfile, buildRootOverride, NULL);
-           if (! res[i]) {
-               /* Error */
-               freeSplitString(archs);
-               while (i) {
-                   i--;
-                   freeSpec(res[i]);
-               }
-               free(res);
-               return NULL;
-           }
-           headerAddEntry(res[i]->packages->header, RPMTAG_BUILDARCHS,
-                          RPM_STRING_ARRAY_TYPE, archs, count);
-           res[i]->buildArch = strdup(*arch);
-           i++;
+    while (p) {
+       if ((num == p->num) && (p->flags & flag)) {
+           return p;
        }
-       arch++;
+       p = p->next;
     }
-    res[i] = NULL;
 
-    freeSplitString(archs);
-    
-    return res;
+    return NULL;
 }
 
-Spec parseSpecAux(FILE *f, char *specfile, char *buildRootOverride,
-                 char ***buildArchs)
+void freeSources(Spec spec)
 {
-    char buf[LINE_BUF_SIZE]; /* read buffer          */
-    char buf2[LINE_BUF_SIZE];
-    char fileFile[LINE_BUF_SIZE];
-    char scriptProg[LINE_BUF_SIZE];
-    char triggerArgs[LINE_BUF_SIZE];
-    char *line;              /* "parsed" read buffer */
-    
-    int x, serial, tag, cur_part;
-    int lookupopts;
-    StringBuf sb;
-    char *s = NULL;
-    char *s1, *s2;
-    int gotBuildroot = 0;
-    int gotRoot = 0;
-    int versionMacroSet = 0;
-    int releaseMacroSet = 0;
-    char *arch, *os;
-
-    struct PackageRec *cur_package = NULL;
-    Spec spec = (struct SpecRec *) malloc(sizeof(struct SpecRec));
-
-    spec->name = NULL;
-    spec->specfile = strdup(specfile);
-    spec->numSources = 0;
-    spec->numPatches = 0;
-    spec->sources = NULL;
-    spec->prep = newStringBuf();
-    spec->build = newStringBuf();
-    spec->install = newStringBuf();
-    spec->doc = newStringBuf();
-    spec->clean = newStringBuf();
-    spec->packages = NULL;
-    spec->noSource = NULL;
-    spec->noPatch = NULL;
-    spec->numNoSource = 0;
-    spec->numNoPatch = 0;
-    spec->buildroot = NULL;
-    spec->autoReqProv = 1;
-    spec->buildArch = NULL;
-
-    sb = newStringBuf();
-    reset_spec();         /* Reset the parser */
+    struct Source *p1, *p2;
 
-    rpmGetArchInfo(&arch, NULL);
-    rpmGetOsInfo(&os, NULL);
-    addMacro("buildarch", arch);
-    addMacro("buildos", os);
-
-    scriptProg[0] = '\0';
-    cur_part = PREAMBLE_PART;
-    while ((x = read_line(f, buf)) > 0) {
-       line = buf;
-       s = NULL;
-        if ((tag = check_part(line, &s))) {
-           rpmMessage(RPMMESS_DEBUG, "Switching to part: %d\n", tag);
-           if (finishCurrentPart(spec, sb, cur_package,
-                                cur_part, triggerArgs, scriptProg)) {
-               return NULL;
-           }
-           cur_part = tag;
-           truncStringBuf(sb);
-
-           /* Now switch the current package to s */
-           if (s) {
-               switch (tag) {
-                 case PREP_PART:
-                 case BUILD_PART:
-                 case INSTALL_PART:
-                 case CLEAN_PART:
-                 case CHANGELOG_PART:
-                   rpmError(RPMERR_BADARG, "Tag takes no arguments: %s", s);
-                   return NULL;
-               }
-           }
-
-           /* Rip through s for -f in %files */
-           /* not only is this code disgusting, but it allows -f on any tag */
-           fileFile[0] = '\0';
-           s1 = NULL;
-           if (s &&
-               ((s1 = strstr(s, " -f ")) ||
-                (!strncmp(s, "-f ", 3)))) {
-               if (s1) {
-                   s1[0] = ' ';
-                   s1++;
-               } else {
-                   s1 = s;
-               }
-               s1[0] = ' '; s1[1] = ' '; s1[2] = ' ';
-               s1 += 3;
-               while (isspace(*s1)) {
-                   s1++;
-               }
-
-               s2 = fileFile;
-               while (*s1 && !isspace(*s1)) {
-                   *s2 = *s1;
-                   *s1 = ' ';
-                   s1++;
-                   s2++;
-               }
-               *s2 = '\0';
-               while (isspace(*s)) {
-                   s++;
-               }
-               if (! *s) {
-                   s = NULL;
-               }
-           }
-
-           rpmMessage(RPMMESS_DEBUG, "fileFile = %s\n", 
-                       fileFile[0] ? fileFile : "(null)");
-
-           /* If trigger, pull off the args */
-           if (tag == TRIGGERIN_PART || tag == TRIGGERUN_PART) {
-               s1 = strstr(s, "--");
-               if (s1) {
-                   strcpy(triggerArgs, s1+2);
-                   *s1 = '\0';
-                   s = strtok(s, " \n\t");
-               } else {
-                   strcpy(triggerArgs, s);
-                   s = NULL;
-               }
-           }
-
-           /* find possible -p <prog> */
-           if ((tag == PREIN_PART) ||
-               (tag == POSTIN_PART) ||
-               (tag == PREUN_PART) ||
-               (tag == POSTUN_PART)) {
-
-               scriptProg[0] = '\0';
-               s1 = NULL;
-
-               if (s &&
-                   ((s1 = strstr(s, " -p ")) ||
-                    (!strncmp(s, "-p ", 3)))) {
-                   
-                   if (s1) {
-                       s1[0] = ' ';
-                       s1++;
-                   } else {
-                       s1 = s;
-                   }
-                   s1[0] = ' '; s1[1] = ' '; s1[2] = ' ';
-                   s1 += 3;
-                   while (isspace(*s1)) {
-                       s1++;
-                   }
-                   
-                   s2 = scriptProg;
-                   while (*s1 && !isspace(*s1)) {
-                       *s2 = *s1;
-                       *s1 = ' ';
-                       s1++;
-                       s2++;
-                   }
-
-                   *s2 = '\0';
-                   while (isspace(*s)) {
-                       s++;
-                   }
-                   if (! *s) {
-                       s = NULL;
-                   }
-               }
-               
-               /* defaults to /bin/sh */
-               if (! scriptProg[0]) {
-                   strcpy(scriptProg, "/bin/sh");
-               } else {
-                   if (scriptProg[0] != '/') {
-                       rpmError(RPMERR_BADSPEC, "pre/post -p arg must begin with \'/\': %s", scriptProg);
-                       return NULL;
-                   }
-               }
-               rpmMessage(RPMMESS_DEBUG, "scriptProg = %s\n", scriptProg);
-           }
-           
-           /* At this point s is the remaining args, which can only */
-           /* be -n <pkg>, or simply <pkg>.                         */
-           
-           /* Handle -n in part tags */
-           lookupopts = 0;
-           if (s) {
-               if (!strncmp(s, "-n", 2)) {
-                   s += 2;
-                   s += strspn(s, ": \t");
-                   if (*s == '\0') {
-                       rpmError(RPMERR_BADARG, "-n takes argument");
-                       return NULL;
-                   }
-                   lookupopts = LP_NEWNAME;
-               } else {
-                   lookupopts = LP_SUBNAME;
-               }
-               /* Handle trailing whitespace */
-               s1 = s + strlen(s) - 1;
-               while (*s1 == ' ' || *s1 == '\n' || *s1 == '\t') {
-                  s1--;
-               }
-               s1++;
-               *s1 = '\0';
-           }
-           
-           switch (tag) {
-             case PREP_PART:
-             case BUILD_PART:
-             case INSTALL_PART:
-             case CLEAN_PART:
-             case CHANGELOG_PART:
-               /* Do not switch parts for these */
-               break;
-             case PREAMBLE_PART:
-               lookupopts |= LP_CREATE | LP_FAIL_EXISTS;
-               /* Fall through */
-             default:
-               /* XXX - should be able to drop the -n in non-%package parts */
-               if (! lookup_package(spec, &cur_package, s, lookupopts)) {
-                   rpmError(RPMERR_INTERNAL, "Package lookup failed: %s",
-                            (s) ? s : "(main)");
-                   return NULL;
-               }
-               rpmMessage(RPMMESS_DEBUG, "Switched to package: %s\n", 
-                       s ? s : "(main)");
-           }
-
-           if (cur_part == FILES_PART) {
-               /* set files to 0 (current -1 means no %files, no package */
-               cur_package->files = 0;
-               if (fileFile[0]) {
-                   cur_package->fileFile = strdup(fileFile);
-               }
-           }
-
-           /* This line has no content -- it was just a control line */
-           continue;
-        }
-
-       /* Check for implicit "base" package.                        */
-        /* That means that the specfile does not start with %package */
-       if (! cur_package) {
-           lookupopts = 0;
-           if (cur_part == PREAMBLE_PART) {
-               lookupopts = LP_CREATE | LP_FAIL_EXISTS;
-           }
-           if (! lookup_package(spec, &cur_package, NULL, lookupopts)) {
-               rpmError(RPMERR_INTERNAL, "Base package lookup failed!");
-               return NULL;
-           }
-           rpmMessage(RPMMESS_DEBUG, "Switched to BASE package\n");
-       }
-
-       switch (cur_part) {
-         case PREAMBLE_PART:
-           if ((tag = find_preamble_line(line, &s))) {
-               switch (tag) {
-                 case RPMTAG_EXCLUDE:
-                 case RPMTAG_EXCLUSIVE:
-                     rpmMessage(RPMMESS_WARNING,
-                             "Exclude/Exclusive are depricated.\n"
-                             "Use ExcludeArch/ExclusiveArch instead.\n");
-                     sprintf(buf2, "%s %s",
-                             (tag == RPMTAG_EXCLUDE) ? "%ifarch" : "%ifnarch",
-                             s);
-                     if (match_arch(buf2)) {
-                         rpmError(RPMERR_BADARCH, "Arch mismatch!");
-                         return NULL;
-                     }
-                     addListEntry(cur_package->header,
-                                  (tag == RPMTAG_EXCLUDE) ?
-                                  RPMTAG_EXCLUDEARCH : RPMTAG_EXCLUSIVEARCH,
-                                  s);
-                     break;
-                 case RPMTAG_EXCLUDEARCH:      
-                 case RPMTAG_EXCLUSIVEARCH:
-                   sprintf(buf2, "%s %s", (tag == RPMTAG_EXCLUDEARCH) ?
-                           "%ifarch" : "%ifnarch",  s);
-                   if (match_arch(buf2)) {
-                       rpmError(RPMERR_BADARCH, "Arch mismatch!");
-                       return NULL;
-                   }
-                   addListEntry(cur_package->header, tag, s);
-                   break;
-                 case RPMTAG_EXCLUDEOS:
-                 case RPMTAG_EXCLUSIVEOS:
-                   sprintf(buf2, "%s %s", (tag == RPMTAG_EXCLUDEOS) ?
-                           "%ifos" : "%ifnos",  s);
-                   if (match_os(buf2)) {
-                       rpmError(RPMERR_BADOS, "OS mismatch!");
-                       return NULL;
-                   }
-                   addListEntry(cur_package->header, tag, s);
-                   break;
-                 case RPMTAG_NAME:
-                   s1 = s;
-                   while (*s1 && *s1 != ' ' && *s1 != '\t') s1++;
-                   *s1 = '\0';
-                   if (!spec->name) {
-                       spec->name = strdup(s);
-                   }
-                   /* The NAME entries must be generated after */
-                   /* the whole spec file is parsed.           */
-                   break;
-                 case RPMTAG_VERSION:
-                 case RPMTAG_RELEASE:
-                   s1 = s;
-                   while (*s1 && *s1 != ' ' && *s1 != '\t') s1++;
-                   *s1 = '\0';
-                   if (s1 == s) {
-                       rpmError(RPMERR_BADSPEC, (tag == RPMTAG_VERSION) ?
-                                "Empty version field." :
-                                "Empty release field.");
-                       return NULL;
-                   }
-                   if (tag == RPMTAG_VERSION) {
-                       if (! versionMacroSet) {
-                           versionMacroSet = 1;
-                           addMacro("PACKAGE_VERSION", s);
-                       }
-                   } else {
-                       if (! releaseMacroSet) {
-                           releaseMacroSet = 1;
-                           addMacro("PACKAGE_RELEASE", s);
-                       }
-                   }
-                 case RPMTAG_SUMMARY:
-                 case RPMTAG_DISTRIBUTION:
-                 case RPMTAG_VENDOR:
-                 case RPMTAG_COPYRIGHT:
-                 case RPMTAG_PACKAGER:
-                 case RPMTAG_GROUP:
-                 case RPMTAG_URL:
-                   headerAddEntry(cur_package->header, tag, RPM_STRING_TYPE, s, 1);
-                   break;
-                 case RPMTAG_BUILDARCHS:
-                   if (buildArchs) {
-                       *buildArchs = splitString(s, strlen(s), ' ');
-                       freeSpec(spec);
-                       freeStringBuf(sb);
-                       return NULL;
-                   }
-                   break;
-                 case RPMTAG_BUILDROOT:
-                   gotBuildroot = 1;
-                   spec->buildroot = strdup(s);
-                   break;
-                 case RPMTAG_DEFAULTPREFIX:
-                   headerAddEntry(cur_package->header, tag, RPM_STRING_TYPE, s, 1);
-                   break;
-                 case RPMTAG_SERIAL:
-                   serial = atoi(s);
-                   headerAddEntry(cur_package->header, tag, RPM_INT32_TYPE, &serial, 1);
-                   break;
-                 case RPMTAG_DESCRIPTION:
-                   /* Special case -- need to handle backslash */
-                   truncStringBuf(sb);
-                   while (1) {
-                       s1 = s + strlen(s) - 1;
-                       if (*s1 != '\\') {
-                           break;
-                       }
-                       *s1 = '\0';
-                       appendLineStringBuf(sb, s);
-                       read_line(f, buf);
-                       s = buf;
-                   }
-                   appendStringBuf(sb, s);
-                   headerAddEntry(cur_package->header, RPMTAG_DESCRIPTION,
-                            RPM_STRING_TYPE, getStringBuf(sb), 1);
-                   break;
-                 case RPMTAG_ROOT:
-                   /* special case */
-                   gotRoot = 1;
-                   rpmMessage(RPMMESS_DEBUG, "Got root: %s\n", s);
-                   rpmMessage(RPMMESS_WARNING, "The Root: tag is depricated.  Use Buildroot: instead\n");
-                   rpmSetVar(RPMVAR_ROOT, s);
-                   break;
-                 case RPMTAG_ICON:
-                     cur_package->icon = strdup(s);
-                     break;
-                 case RPMTAG_NOPATCH:
-                 case RPMTAG_NOSOURCE:
-                     if (noSourcePatch(spec, s, tag)) {
-                         return NULL;
-                     }
-                     break;
-                 case RPMTAG_SOURCE:
-                 case RPMTAG_PATCH:
-                     if (addSource(spec, line)) {
-                         return NULL;
-                     }
-                     break;
-                 case RPMTAG_OBSOLETES:
-                 case RPMTAG_PROVIDES:
-                     if (parseProvides(cur_package, s, tag)) {
-                         return NULL;
-                     }
-                     break;
-                 case RPMTAG_REQUIREFLAGS:
-                 case RPMTAG_CONFLICTFLAGS:
-                 case RPMTAG_PREREQ:
-                     if (parseRequiresConflicts(cur_package, s, tag)) {
-                         return NULL;
-                     }
-                     break;
-                 case RPMTAG_AUTOREQPROV:
-                   s1 = strtok(s, " \t\n");
-                   if (!s1) {
-                       spec->autoReqProv = 0;
-                   } else if (s1[0] == 'n' || s1[0] == 'N') {
-                       spec->autoReqProv = 0;
-                   } else if (!strcasecmp(s1, "false")) {
-                       spec->autoReqProv = 0;
-                   } else if (!strcasecmp(s1, "off")) {
-                       spec->autoReqProv = 0;
-                   } else if (!strcmp(s1, "0")) {
-                       spec->autoReqProv = 0;
-                   }
-                   break;
-                 default:
-                     /* rpmMessage(RPMMESS_DEBUG, "Skipping: %s\n", line); */
-                     /* This shouldn't happen? */
-                     rpmError(RPMERR_INTERNAL, "Bogus token");
-                     return NULL;
-               }               
-           } else {
-               /* Not a recognized preamble part */
-               s1 = line;
-               while (*s1 && (*s1 == ' ' || *s1 == '\t')) s1++;
-               /* Handle blanks lines and comments */
-               if (*s1 && (*s1 != '#')) {
-                   /*rpmMessage(RPMMESS_WARNING, "Unknown Field: %s\n", line);*/
-                   rpmError(RPMERR_BADSPEC, "Unknown Field: %s\n", line);
-                   return NULL;
-               }
-           }
-           break;
-         case PREP_PART:
-           appendLineStringBuf(spec->prep, line);
-           break;
-         case BUILD_PART:
-           appendLineStringBuf(spec->build, line);
-           break;
-         case INSTALL_PART:
-           appendLineStringBuf(spec->install, line);
-           break;
-         case CLEAN_PART:
-           appendLineStringBuf(spec->clean, line);
-           break;
-         case DESCRIPTION_PART:
-         case CHANGELOG_PART:
-         case PREIN_PART:
-         case POSTIN_PART:
-         case PREUN_PART:
-         case POSTUN_PART:
-         case VERIFYSCRIPT_PART:
-           appendLineStringBuf(sb, line);
-           break;
-         case TRIGGERIN_PART:
-         case TRIGGERUN_PART:
-           appendLineStringBuf(sb, line);
-           break;
-         case FILES_PART:
-             s1 = line;
-             while (*s1 && (*s1 == ' ' || *s1 == '\t')) s1++;
-             /* Handle blanks lines and comments */
-             if (*s1 && (*s1 != '#')) {
-                 cur_package->files++;
-                 appendLineStringBuf(cur_package->filelist, line);
-             }
-           break;
-         default:
-           rpmError(RPMERR_INTERNAL, "Bad part");
-           return NULL;
-       } /* switch */
-    }
-    if (x < 0) {
-       return NULL;
-    }
-
-    /* finish current part */
-    if (finishCurrentPart(spec, sb, cur_package,
-                         cur_part, triggerArgs,
-                         scriptProg)) {
-       return NULL;
-    }
-
-    freeStringBuf(sb);
-    
-    if (gotRoot && gotBuildroot) {
-       freeSpec(spec);
-       rpmError(RPMERR_BADSPEC,
-             "Spec file can not have both Root: and Buildroot:");
-       return NULL;
-    }
-    if (spec->buildroot) {
-       /* This package can do build roots */
-       if (buildRootOverride) {
-           rpmSetVar(RPMVAR_ROOT, buildRootOverride);
-           rpmSetVar(RPMVAR_BUILDROOT, buildRootOverride);
-       } else {
-           if ((s = rpmGetVar(RPMVAR_BUILDROOT))) {
-               /* Take build prefix from rpmrc */
-               rpmSetVar(RPMVAR_ROOT, s);
-           } else {
-               /* Use default */
-               rpmSetVar(RPMVAR_ROOT, spec->buildroot);
-               rpmSetVar(RPMVAR_BUILDROOT, spec->buildroot);
-           }
-       }
-    } else {
-       /* Package can not do build prefixes */
-       if (buildRootOverride) {
-           freeSpec(spec);
-           rpmError(RPMERR_BADARG, "Package can not do build prefixes");
-           return NULL;
-       }
+    p1 = spec->sources;
+    while (p1) {
+       p2 = p1;
+       p1 = p1->next;
+       FREE(p2->fullSource);
+       free(p2);
     }
-
-    generateNames(spec);
-    return spec;
 }
 
-/**********************************************************************/
-/*                                                                    */
-/* Resets the parser                                                  */
-/*                                                                    */
-/**********************************************************************/
-
-static void reset_spec()
+int parseNoSource(Spec spec, char *field, int tag)
 {
-    static done = 0;
-    struct read_level_entry *rl;
-    struct preamble_line *p = preamble_spec;
-    struct part_rec *p1 = part_list;
-
-    rpmSetVar(RPMVAR_ROOT, NULL);
-
-    while (read_level) {
-       rl = read_level;
-        read_level = read_level->next;
-       free(rl);
+    char buf[BUFSIZ];
+    char *s, *name;
+    int num, flag;
+    struct Source *p;
+
+    if (tag == RPMTAG_NOSOURCE) {
+       flag = RPMBUILD_ISSOURCE;
+       name = "source";
+    } else {
+       flag = RPMBUILD_ISPATCH;
+       name = "patch";
     }
-    read_level = malloc(sizeof(struct read_level_entry));
-    read_level->next = NULL;
-    read_level->reading = 1;
-
-    resetMacros();
     
-    if (! done) {
-        /* Put one time only things in here */
-        while (p->tag) {
-           p->len = strlen(p->token);
-           p++;
-       }
-        while (p1->part) {
-           p1->len = strlen(p1->s);
-           p1++;
+    strcpy(buf, field);
+    field = buf;
+    while ((s = strtok(field, ", \t"))) {
+       if (parseNum(s, &num)) {
+           rpmError(RPMERR_BADSPEC, "line %d: Bad number: %s",
+                    spec->lineNum, spec->line);
+           return RPMERR_BADSPEC;
        }
 
-       done = 1;
-    }
-}
-
-/**********************************************************************/
-/*                                                                    */
-/* Spec struct dumping (for debugging)                                */
-/*                                                                    */
-/**********************************************************************/
+       if (! (p = findSource(spec, num, flag))) {
+           rpmError(RPMERR_BADSPEC, "line %d: Bad no%s number: %d",
+                    spec->lineNum, name, num);
+           return RPMERR_BADSPEC;
+       }
 
-void dumpSpec(Spec s, FILE *f)
-{
-    struct PackageRec *p;
-    
-    fprintf(f, "########################################################\n");
-    fprintf(f, "SPEC NAME = (%s)\n", s->name);
-    fprintf(f, "PREP =v\n");
-    fprintf(f, "%s", getStringBuf(s->prep));
-    fprintf(f, "PREP =^\n");
-    fprintf(f, "BUILD =v\n");
-    fprintf(f, "%s", getStringBuf(s->build));
-    fprintf(f, "BUILD =^\n");
-    fprintf(f, "INSTALL =v\n");
-    fprintf(f, "%s", getStringBuf(s->install));
-    fprintf(f, "INSTALL =^\n");
-    fprintf(f, "CLEAN =v\n");
-    fprintf(f, "%s", getStringBuf(s->clean));
-    fprintf(f, "CLEAN =^\n");
+       p->flags |= RPMBUILD_ISNO;
 
-    p = s->packages;
-    while (p) {
-       dumpPackage(p, f);
-       p = p->next;
+       field = NULL;
     }
-}
-
-static void dumpPackage(struct PackageRec *p, FILE *f)
-{
-    fprintf(f, "_________________________________________________________\n");
-    fprintf(f, "SUBNAME = (%s)\n", p->subname);
-    fprintf(f, "NEWNAME = (%s)\n", p->newname);
-    fprintf(f, "FILES = %d\n", p->files);
-    fprintf(f, "FILES =v\n");
-    fprintf(f, "%s", getStringBuf(p->filelist));
-    fprintf(f, "FILES =^\n");
-    fprintf(f, "HEADER =v\n");
-    headerDump(p->header, f, 1, rpmTagTable);
-    fprintf(f, "HEADER =^\n");
 
+    return 0;
 }
index d6d967c..28d7369 100644 (file)
-/* RPM - Copyright (C) 1995 Red Hat Software
- * 
- * spec.h - routines for parsing are looking up info in a spec file
- */
-
 #ifndef _SPEC_H_
 #define _SPEC_H_
 
-#include <stdio.h>
+#include "header.h"
+#include "stringbuf.h"
+#include "macro.h"
+
+#if 0
+struct ReqProvTrigger {
+    int flags;
+    char *name;
+    char *version;
+    int index;      /* Only used for triggers */
+    struct ReqProvTrigger *next;
+};
+#endif
+
+#define RPMBUILD_ISSOURCE     1
+#define RPMBUILD_ISPATCH     (1 << 1)
+#define RPMBUILD_ISICON      (1 << 2)
+#define RPMBUILD_ISNO        (1 << 3)
+
+#define RPMBUILD_DEFAULT_LANG "C"
+
+struct Source {
+    char *fullSource;
+    char *source;     /* Pointer into fullSource */
+    int flags;
+    int num;
+    struct Source *next;
+};
+
+struct ReadLevelEntry {
+    int reading;
+    struct ReadLevelEntry *next;
+};
+
+struct SpecStruct {
+    char *specFile;
+    char *sourceRpmName;
+
+    FILE *file;
+    char readBuf[BUFSIZ];
+    char *readPtr;
+    char line[BUFSIZ];
+    int lineNum;
+
+    struct ReadLevelEntry *readStack;
+
+    Header buildRestrictions;
+    struct SpecStruct **buildArchitectureSpecs;
+    char ** buildArchitectures;
+    int buildArchitectureCount;
+    int inBuildArchitectures;
+    
+    int gotBuildRoot;
+    char *buildRoot;
+    char *buildSubdir;
+
+    char *docDir;
+
+    char *passPhrase;
+    int timeCheck;
+    char *cookie;
+
+    struct Source *sources;
+    int numSources;
+    int noSource;
+
+    Header sourceHeader;
+    int sourceCpioCount;
+    struct cpioFileMapping *sourceCpioList;
+    
+    struct MacroContext macros;
+
+    int autoReqProv;
+    
+    StringBuf prep;
+    StringBuf build;
+    StringBuf install;
+    StringBuf clean;
+
+    struct PackageStruct *packages;
+};
+
+struct PackageStruct {
+    Header header;
+
+    int cpioCount;
+    struct cpioFileMapping *cpioList;
+
+    struct Source *icon;
+
+    int autoReqProv;
+
+    char *preInFile;
+    char *postInFile;
+    char *preUnFile;
+    char *postUnFile;
+    char *verifyFile;
 
-typedef struct SpecRec *Spec;
+    StringBuf specialDoc;
+    
+#if 0    
+    struct ReqProvTrigger *triggers;
+    char *triggerScripts;
+#endif
+    
+    char *fileFile;
+    StringBuf fileList; /* If NULL, package will not be written */
 
-Spec *parseSpec(FILE *f, char *specfile, char *buildRootOverride);
-void freeSpec(Spec s);
+    struct PackageStruct *next;
+};
 
-void dumpSpec(Spec s, FILE *f);
+typedef struct SpecStruct *Spec;
+typedef struct PackageStruct *Package;
 
-char *getSource(Spec s, int ispatch, int num);
-char *getFullSource(Spec s, int ispatch, int num);
+Spec newSpec(void);
+void freeSpec(Spec spec);
 
-int verifySpec(Spec s);
+int addSource(Spec spec, Package pkg, char *field, int tag);
+char *getSource(Spec spec, int num, int flag);
+char *getFullSource(Spec spec, int num, int flag);
+void freeSources(Spec spec);
+int parseNoSource(Spec spec, char *field, int tag);
 
 #endif _SPEC_H_
diff --git a/build/specP.h b/build/specP.h
deleted file mode 100644 (file)
index a90ed7f..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-#ifndef _SPECP_H_
-#define _SPECP_H_
-
-#include "spec.h"
-#include "header.h"
-#include "stringbuf.h"
-
-typedef struct sources {
-    char *fullSource;
-    char *source;
-    int ispatch;
-    int num;
-    struct sources *next;
-} *Sources;
-
-struct ReqProv {
-    int flags;
-    char *name;
-    char *version;
-    struct ReqProv *next;
-};
-
-struct TriggerEntry {
-    int flags;
-    char *name;
-    char *version;
-    int index;
-    struct TriggerEntry *next;
-};
-
-struct TriggerStruct {
-    char **triggerScripts;
-    int alloced;
-    int used;
-    int triggerCount;
-    struct TriggerEntry *trigger;
-};
-
-struct SpecRec {
-    char *name;      /* package base name */
-    char *specfile;
-
-    int numSources;
-    int numPatches;
-    Sources sources;
-
-    int numNoSource;
-    int numNoPatch;
-    int_32 *noSource;
-    int_32 *noPatch;
-
-    int autoReqProv;
-
-    StringBuf prep;
-    StringBuf build;
-    StringBuf install;
-    StringBuf doc;
-    StringBuf clean;
-
-    char *buildroot;
-    char *buildArch;
-    
-    struct PackageRec *packages;
-    /* The first package record is the "main" package and contains
-     * the bulk of the preamble information.  Subsequent package
-     * records "inherit" from the main record.  Note that the
-     * "main" package may be, in pre-rpm-2.0 terms, a "subpackage".
-     */
-};
-
-struct PackageRec {
-    char *subname;   /* If both of these are NULL, then this is          */
-    char *newname;   /* the main package.  subname concats with name     */
-    Header header;
-    char *icon;
-    int files;       /* If -1, package has no files, and won't be written */
-    char *fileFile;
-    StringBuf filelist;
-    StringBuf doc;   /* Used to buffer up %doc lines until fully parsed */
-    int numReq;
-    int numPreReq;
-    int numProv;
-    int numConflict;
-    int numObsoletes;
-    struct ReqProv *reqprov;
-    struct PackageRec *next;
-    struct TriggerStruct trigger;
-};
-
-struct ReqComp {
-    char *token;
-    int flags;
-};
-
-extern struct ReqComp ReqComparisons[];
-
-#endif _SPECP_H_
diff --git a/build/trigger.h b/build/trigger.h
deleted file mode 100644 (file)
index 00fc483..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-#ifndef _TRIGGER_H_
-#define _TRIGGER_H_
-
-#include "specP.h"
-
-int addTrigger(struct PackageRec *package,
-              int sense, char *script, char *args);
-
-void generateTriggerEntries(Header h, struct PackageRec *p);
-
-void freeTriggers(struct TriggerStruct t);
-
-#endif _TRIGGER_H_
diff --git a/build/vspec.c b/build/vspec.c
deleted file mode 100644 (file)
index cc3f355..0000000
+++ /dev/null
@@ -1,206 +0,0 @@
-/* Routine to "verify" a parsed spec file */
-
-/***************
-
-Here's what we do
-
-. check for duplicate fields
-. make sure a certain set of fields are present
-. in subpackages, make sure a certain set of fields are present
-. in subpackages, make sure certain fields are *not* present
-
-. check for duplicate sources/patch numbers
-. do some checking on the field values for legit characters
-
-****************/
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <string.h>
-
-#include "header.h"
-#include "spec.h"
-#include "specP.h"
-#include "messages.h"
-#include "rpmlib.h"
-#include "stringbuf.h"
-#include "misc.h"
-
-#define EMPTY(s) ((!(s)) || (!(*(s))))
-#define OOPS(s) {fprintf(stderr, "verifySpec: %s\n", s); res = 1;}
-#define MUST 1
-
-struct packageFieldsRec {
-    int tag;
-    int present;
-    int shouldBePresent;
-};
-
-struct fieldNamesRec {
-    int tag;
-    char *name;
-};
-
-static int checkHeaderTags(Header inh, struct packageFieldsRec *pfr);
-static char *tagName(int tag);
-
-/* This is a list of tags related to the "main" package. */
-/* Only list those that must or must not be present.     */
-
-static struct packageFieldsRec packageFields[] = {
-    { RPMTAG_NAME,            0, MUST },
-    { RPMTAG_VERSION,         0, MUST },
-    { RPMTAG_RELEASE,         0, MUST },
-/*    { RPMTAG_SUMMARY,         0, MUST }, */
-    { RPMTAG_DESCRIPTION,     0, MUST },
-    { RPMTAG_COPYRIGHT,       0, MUST },
-/*    { RPMTAG_PACKAGER,        0, MUST }, */
-    { RPMTAG_GROUP,           0, MUST },
-    { 0, 0, 0 },
-};
-
-/* This is a list of tags related to "sub" packages. */
-/* Only list those that must or must not be present. */
-
-static struct packageFieldsRec subpackageFields[] = {
-    { RPMTAG_NAME,            0, MUST },  /* This is inserted automaticaly */
-    { RPMTAG_SUMMARY,         0, MUST },
-    { RPMTAG_DESCRIPTION,     0, MUST },
-    { RPMTAG_GROUP,           0, MUST },
-    { RPMTAG_NAME,            0, 0 },
-/*    { RPMTAG_COPYRIGHT,       0, 0 }, */
-    { RPMTAG_PACKAGER,        0, 0 },
-    { RPMTAG_BUILDROOT,       0, 0 },
-    { 0, 0, 0 },
-};
-
-static struct packageFieldsRec *findTag(int tag, struct packageFieldsRec *pfr)
-{
-    struct packageFieldsRec *p = pfr;
-
-    while (p->tag) {
-       if (p->tag == tag)
-           return p;
-       p++;
-    }
-
-    return NULL;
-}
-
-static char *tagName(int tag)
-{
-    int i = 0;
-    static char nameBuf[1024];
-    char *s;
-
-    strcpy(nameBuf, "(unknown)");
-    while (i < rpmTagTableSize) {
-       if (tag == rpmTagTable[i].val) {
-           strcpy(nameBuf, rpmTagTable[i].name + 7);
-           s = nameBuf+1;
-           while (*s) {
-               *s = tolower(*s);
-               s++;
-           }
-       }
-       i++;
-    }
-
-    return nameBuf;
-}
-
-static int checkHeaderTags(Header inh, struct packageFieldsRec *pfr)
-{
-    Header h;
-    HeaderIterator headerIter;
-    int res = 0;
-    int_32 tag, lastTag, type, c;
-    void *ptr;
-    struct packageFieldsRec *p;
-
-    /* Sort the index, so it'll be easy to catch dups */
-    h = headerCopy(inh);
-    headerSort(h);
-
-    headerIter = headerInitIterator(h);
-    lastTag = 0;
-    while (headerNextIterator(headerIter, &tag, &type, &ptr, &c)) {
-       if (tag == lastTag) {
-           rpmError(RPMERR_BADSPEC, "Duplicate fields for     : %s",
-                 tagName(tag));
-           res = 1;
-       }
-       p = findTag(tag, pfr);
-       if (p) 
-           p->present = 1;
-       lastTag = tag;
-    }
-    headerFreeIterator(headerIter);
-    headerFree(h);
-
-    p = pfr;
-    while (p->tag) {
-       if (p->shouldBePresent != p->present) {
-           rpmError(RPMERR_BADSPEC, "Field must%s be present%s: %s",
-                 p->present ? " NOT" : "", p->present ? "" : "    ",
-                 tagName(p->tag));
-           res = 1;
-       }
-       p->present = 0;  /* hack to clear it for next time :-) */
-       p++;
-    }
-    
-    return res;
-}
-
-int verifySpec(Spec s)
-{
-    int res = 0;
-    struct PackageRec *pr;
-    struct packageFieldsRec *fields;
-    char name[1024];
-    char *val;
-    
-    if (EMPTY(s->name)) {
-       OOPS("No Name field");
-    }
-
-    /* Check each package/subpackage */
-    pr = s->packages;
-    fields = packageFields;
-    while (pr) {
-       if (! pr->subname) {
-           if (pr->newname) {
-               strcpy(name, pr->newname);
-           } else {
-               strcpy(name, s->name);
-           }
-       } else {
-           sprintf(name, "%s-%s", s->name, pr->subname);
-       }
-       printf("* Package: %s\n", name);
-
-       if (checkHeaderTags(pr->header, fields)) {
-           res = 1;
-       }
-
-       val = NULL;
-       headerGetEntry(pr->header, RPMTAG_VERSION, NULL, (void *) &val, NULL);
-       if (val && strchr(val, '-')) {
-           rpmError(RPMERR_BADSPEC, "Illegal '-' char in version: %s\n", val);
-           res = 1;
-       }
-       val = NULL;
-       headerGetEntry(pr->header, RPMTAG_RELEASE, NULL, (void *) &val, NULL);
-       if (val && strchr(val, '-')) {
-           rpmError(RPMERR_BADSPEC, "Illegal '-' char in release: %s\n", val);
-           res = 1;
-       }
-       
-       pr = pr->next;
-       fields = subpackageFields;
-    }
-    
-    return res;
-}
index 4f2999e..c24fe49 100644 (file)
@@ -34,6 +34,12 @@ all: $(TARGET)
 
 allprogs: $(LIBRPM) $(PROGS)
 
+misc.o:
+       $(CC) $(CFLAGS) -DVERSION=\"$(VERSION)\" -o $@ -c $<
+
+rpmrc.o:
+       $(CC) $(CFLAGS) -DLIBRPMRC_FILENAME="\"$(LIBRPMRC_FILENAME)"\" -o $@ -c $<
+
 $(PROGS): $(LIBRPM)
 
 $(LIBRPM): $(LIBRPM)($(LIBOBJECTS) $(TAGTABLE))
index 0f65333..7127b5e 100644 (file)
@@ -23,7 +23,7 @@
 #include "rpmlib.h"
 #include "messages.h"
 
-char * RPMVERSION = VERSION;
+char * RPMVERSION = VERSION;   /* just to put a marker in librpm.a */
 
 char ** splitString(char * str, int length, char sep) {
     char * s, * source, * dest;
diff --git a/rpm.c b/rpm.c
index febd704..098c786 100755 (executable)
--- a/rpm.c
+++ b/rpm.c
@@ -17,6 +17,7 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 
+#include "build.h"
 #include "build/build.h"
 #include "checksig.h"
 #include "install.h"
@@ -42,6 +43,7 @@
 #define GETOPT_TIMECHECK        1012
 #define GETOPT_REBUILDDB        1013
 #define GETOPT_INSTALL         1014
+#define GETOPT_RMSOURCE                1015
 
 char * version = VERSION;
 
@@ -62,8 +64,6 @@ static void printVersion(void);
 static void printBanner(void);
 static void printUsage(void);
 static void printHelpLine(char * prefix, char * help);
-static int build(char *arg, int buildAmount, char *passPhrase,
-                char *buildRootOverride, int fromTarball);
 
 static void printVersion(void) {
     printf(_("RPM version %s\n"), version);
@@ -113,7 +113,8 @@ static void printUsage(void) {
     puts(_("                        [--justdb] package1 ... packageN"));
     puts(_("       rpm {-b|t}[plciba] [-v] [--short-circuit] [--clean] [--rcfile  <file>]"));
     puts(_("                        [--sign] [--test] [--timecheck <s>] [--buildos <os>]"));
-    puts(_("                        [--buildarch <arch>] specfile"));
+    puts(_("                        [--buildarch <arch>] [--rmsource] specfile"));
+    puts(_("       rpm {--rmsource} [--rcfile <file>] [-v] specfile"));
     puts(_("       rpm {--rebuild} [--rcfile <file>] [-v] source1.rpm ... sourceN.rpm"));
     puts(_("       rpm {--recompile} [--rcfile <file>] [-v] source1.rpm ... sourceN.rpm"));
     puts(_("       rpm {--resign} [--rcfile <file>] package1 package2 ... packageN"));
@@ -325,6 +326,8 @@ static void printHelp(void) {
                  _("skip straight to specified stage (only for c,i)"));
     printHelpLine("      --clean             ",
                  _("remove build tree when done"));
+    printHelpLine("      --rmsource          ",
+                 _("remove sources and spec file when done"));
     printHelpLine("      --sign              ",
                  _("generate PGP signature"));
     printHelpLine("      --buildroot <dir>     ",
@@ -340,6 +343,8 @@ static void printHelp(void) {
     puts("");
     printHelpLine("    --rebuild <src_pkg>   ",
                  _("install source package, build binary package and remove spec file, sources, patches, and icons."));
+    printHelpLine("    --rmsource <spec>   ",
+                 _("remove sources and spec file"));
     printHelpLine("    --recompile <src_pkg> ",
                  _("like --rebuild, but don't build any package"));
     printHelpLine("    --resign <pkg>+       ",
@@ -365,144 +370,6 @@ static void printHelp(void) {
                  _("use <dir> as the top level directory"));
 }
 
-static int build(char *arg, int buildAmount, char *passPhrase,
-                char *buildRootOverride, int fromTarball) {
-    FILE *f;
-    Spec *s, *specArray;
-    char * specfile;
-    int res = 0;
-    struct stat statbuf;
-    char * specDir;
-    char * tmpSpecFile;
-    char * cmd;
-    char buf[1024];
-    int flags;
-
-    if (fromTarball) {
-       specDir = rpmGetVar(RPMVAR_SPECDIR);
-       tmpSpecFile = alloca(1024);
-       sprintf(tmpSpecFile, "%s/rpm-spec-file-%d", specDir, (int) getpid());
-
-       cmd = alloca(strlen(arg) + 50 + strlen(tmpSpecFile));
-       sprintf(cmd, "gunzip < %s | tar xOvf - \\*.spec 2>&1 > %s", arg,
-                       tmpSpecFile);
-       if (!(f = popen(cmd, "r"))) {
-           fprintf(stderr, _("Failed to open tar pipe: %s\n"), 
-                       strerror(errno));
-           return 1;
-       }
-       if (!fgets(buf, sizeof(buf) - 1, f)) {
-           fprintf(stderr, _("Failed to read spec file from %s\n"), arg);
-           unlink(tmpSpecFile);
-           return 1;
-       }
-       pclose(f);
-
-       cmd = specfile = buf;
-       while (*cmd) {
-           if (*cmd == '/') specfile = cmd + 1;
-           cmd++;
-       }
-
-       cmd = specfile;
-
-       /* remove trailing \n */
-       specfile = cmd + strlen(cmd) - 1;
-       *specfile = '\0';
-
-       specfile = alloca(strlen(specDir) + strlen(cmd) + 5);
-       sprintf(specfile, "%s/%s", specDir, cmd);
-       
-       if (rename(tmpSpecFile, specfile)) {
-           fprintf(stderr, _("Failed to rename %s to %s: %s\n"),
-                   tmpSpecFile, specfile, strerror(errno));
-           unlink(tmpSpecFile);
-           return 1;
-       }
-
-       /* Make the directory which contains the tarball the source 
-          directory for this run */
-
-       if (*arg != '/') {
-           /* XXX this is broken if PWD is near 1024 */
-           getcwd(buf, 1024);
-           strcat(buf, "/");
-           strcat(buf, arg);
-       } else 
-           strcpy(buf, arg);
-
-       cmd = buf + strlen(buf) - 1;
-       while (*cmd != '/') cmd--;
-       *cmd = '\0';
-
-       rpmSetVar(RPMVAR_SOURCEDIR, buf);
-    } else if (arg[0] == '/') {
-       specfile = arg;
-    } else {
-       /* XXX this is broken if PWD is near 1024 */
-       specfile = alloca(1024);
-       getcwd(specfile, 1024);
-       strcat(specfile, "/");
-       strcat(specfile, arg);
-    }
-
-    stat(specfile, &statbuf);
-    if (! S_ISREG(statbuf.st_mode)) {
-       rpmError(RPMERR_BADSPEC, _("File is not a regular file: %s\n"),
-                specfile);
-       return 1;
-    }
-    
-    if (!(f = fopen(specfile, "r"))) {
-       fprintf(stderr, _("unable to open: %s\n"), specfile);
-       return 1;
-    }
-    
-    s = specArray = parseSpec(f, specfile, buildRootOverride);
-    fclose(f);
-    if (! specArray) {
-       /* Spec parse failed -- could be Exclude: Exclusive: */
-       res = 1;
-       if (rpmErrorCode() == RPMERR_BADARCH) {
-           fprintf(stderr, _("%s doesn't build on this architecture\n"), arg);
-       } else {
-           fprintf(stderr, _("Build failed.\n"));
-       }
-    } else {
-       while (*s && !res) {
-           if (verifySpec(*s)) {
-               fprintf(stderr, _("\n%cSpec file check failed!!\n"), 7);
-               fprintf(stderr,
-                       _("Tell rpm-list@redhat.com if this is incorrect.\n\n"));
-               res = 1;
-           } else {
-               flags = buildAmount;
-               /* Don't build source package or remove sources */
-               /* unless this is the last package being built. */
-               if (*(s+1)) {
-                   flags = flags & ~RPMBUILD_SOURCE;
-                   flags = flags & ~RPMBUILD_RMSOURCE;
-               }
-               if (doBuild(*s, flags, passPhrase)) {
-                   fprintf(stderr, _("Build failed.\n"));
-                   res = 1;
-               }
-           }
-           s++;
-       }
-
-       s = specArray;
-       while (*s) {
-           freeSpec(*s++);
-       }
-       free(specArray);
-    }
-
-    if (fromTarball) unlink(specfile);
-
-    return res;
-}
-
 int main(int argc, char ** argv) {
     enum modes bigMode = MODE_UNKNOWN;
     enum querysources querySource = QUERY_PACKAGE;
@@ -516,9 +383,8 @@ int main(int argc, char ** argv) {
     int incldocs = 0, noScripts = 0, noDeps = 0, allMatches = 0, noOrder = 0;
     int noPgp = 0, dump = 0, initdb = 0, ignoreArch = 0, showrc = 0;
     int gotDbpath = 0, building = 0, ignoreOs = 0, noFiles = 0, verifyFlags;
-    int noMd5 = 0, allFiles = 0, justdb = 0;
+    int noMd5 = 0, allFiles = 0, justdb = 0, rmsource = 0;
     int checksigFlags = 0;
-    char *tce;
     int timeCheck = 0;
     int addSign = NEW_SIGNATURE;
     char * rcfile = NULL, * queryFormat = NULL, * prefix = NULL;
@@ -526,11 +392,12 @@ int main(int argc, char ** argv) {
     char * rootdir = "/";
     char * pipeOutput = NULL;
     char * specFile;
-    char *passPhrase = "";
-    char *buildRootOverride = NULL;
-    char *arch = NULL;
+    char * tce;
+    char * passPhrase = "";
+    char * buildRootOverride = NULL, * cookie = NULL;
+    char * arch = NULL;
     char * ftpProxy = NULL, * ftpPort = NULL;
-    char *os = NULL;
+    char * os = NULL;
     char * optArg;
     pid_t pipeChild = 0;
     char * pkg;
@@ -599,6 +466,7 @@ int main(int argc, char ** argv) {
            { "replacefiles", '\0', 0, &replaceFiles, 0 },
            { "replacepkgs", '\0', 0, &replacePackages, 0 },
            { "resign", '\0', 0, 0, GETOPT_RESIGN },
+           { "rmsource", '\0', 0, 0, GETOPT_RMSOURCE },
            { "root", 'r', POPT_ARG_STRING, &rootdir, 0 },
            { "short-circuit", '\0', 0, &shortCircuit, 0 },
            { "showrc", '\0', 0, 0, 0 },
@@ -643,6 +511,7 @@ int main(int argc, char ** argv) {
                   !strcmp(*currarg, "--tarbuild") ||
                   !strcmp(*currarg, "--build") ||
                   !strcmp(*currarg, "--rebuild") ||
+                  !strcmp(*currarg, "--rmsource") ||
                   !strcmp(*currarg, "--recompile")) {
            building = 1;
        }
@@ -839,10 +708,17 @@ int main(int argc, char ** argv) {
            bigMode = MODE_REBUILD;
            break;
 
+         case GETOPT_RMSOURCE:
+           if (bigMode != MODE_UNKNOWN && bigMode != MODE_BUILD)
+               argerror(_("only one major mode may be specified"));
+           bigMode = MODE_BUILD;
+           break;
+
          case GETOPT_RECOMPILE:
            if (bigMode != MODE_UNKNOWN && bigMode != MODE_RECOMPILE)
                argerror(_("only one major mode may be specified"));
            bigMode = MODE_RECOMPILE;
+           rmsource = 1;
            break;
 
          case GETOPT_BUILDROOT:
@@ -890,6 +766,7 @@ int main(int argc, char ** argv) {
                argerror("Argument to --timecheck must be integer");
            }
            rpmSetVar(RPMVAR_TIMECHECK, optArg);
+           timeCheck = 1;
            break;
 
          case GETOPT_REBUILDDB:
@@ -1042,6 +919,9 @@ int main(int argc, char ** argv) {
     if (bigMode != MODE_BUILD && bigMode != MODE_TARBUILD && clean) 
        argerror(_("--clean may only be used during package building"));
 
+    if (bigMode != MODE_BUILD && bigMode != MODE_TARBUILD && rmsource) 
+       argerror(_("--rmsource may only be used during package building"));
+
     if (bigMode != MODE_BUILD && bigMode != MODE_TARBUILD && shortCircuit) 
        argerror(_("--short-circuit may only be used during package building"));
 
@@ -1164,19 +1044,21 @@ int main(int argc, char ** argv) {
            argerror(_("no packages files given for rebuild"));
 
        buildAmount = RPMBUILD_PREP | RPMBUILD_BUILD | RPMBUILD_INSTALL |
-           RPMBUILD_SWEEP | RPMBUILD_RMSOURCE;
+           RPMBUILD_CLEAN | RPMBUILD_RMSOURCE;
        if (bigMode == MODE_REBUILD) {
-           buildAmount |= RPMBUILD_BINARY;
+           buildAmount |= RPMBUILD_PACKAGEBINARY;
        }
 
        while ((pkg = poptGetArg(optCon))) {
-           if (doSourceInstall("/", pkg, &specFile, NULL))
+           if (doSourceInstall("/", pkg, &specFile, &cookie))
                exit(1);
 
            if (build(specFile, buildAmount, passPhrase, buildRootOverride,
-                       0)) {
+                       0, test, cookie)) {
                exit(1);
            }
+           free(cookie);
+           free(specFile);
        }
        break;
 
@@ -1188,9 +1070,10 @@ int main(int argc, char ** argv) {
        switch (buildChar) {
          /* these fallthroughs are intentional */
          case 'a':
-           buildAmount |= RPMBUILD_SOURCE;
+           buildAmount |= RPMBUILD_PACKAGESOURCE;
          case 'b':
-           buildAmount |= RPMBUILD_BINARY;
+           buildAmount |= RPMBUILD_PACKAGEBINARY;
+           buildAmount |= RPMBUILD_CLEAN;
          case 'i':
            buildAmount |= RPMBUILD_INSTALL;
            if ((buildChar == 'i') && shortCircuit)
@@ -1204,15 +1087,15 @@ int main(int argc, char ** argv) {
            break;
            
          case 'l':
-           buildAmount |= RPMBUILD_LIST;
+           buildAmount |= RPMBUILD_FILECHECK;
            break;
        }
 
-       if (clean)
-           buildAmount |= RPMBUILD_SWEEP;
+       if (rmsource)
+           buildAmount |= RPMBUILD_RMSOURCE;
 
-       if (test)
-           buildAmount |= RPMBUILD_TEST;
+       if (clean)
+           buildAmount |= RPMBUILD_RMBUILD;
 
        if (!poptPeekArg(optCon))
            if (bigMode == MODE_BUILD)
@@ -1222,7 +1105,7 @@ int main(int argc, char ** argv) {
 
        while ((pkg = poptGetArg(optCon)))
            if (build(pkg, buildAmount, passPhrase, buildRootOverride,
-                       bigMode == MODE_TARBUILD)) {
+                       bigMode == MODE_TARBUILD, test, NULL)) {
                exit(1);
            }
        break;
index 854234b..014eecf 100644 (file)
--- a/rpm.spec
+++ b/rpm.spec
@@ -1,6 +1,6 @@
 Summary: Red Hat Package Manager
 Name: rpm
-%define version 2.4.12
+%define version 2.4.99
 Version: %{version}
 Release: 1
 Group: Utilities/System