summaryrefslogtreecommitdiff
path: root/build/parsePreamble.c
diff options
context:
space:
mode:
Diffstat (limited to 'build/parsePreamble.c')
-rw-r--r--build/parsePreamble.c1090
1 files changed, 1090 insertions, 0 deletions
diff --git a/build/parsePreamble.c b/build/parsePreamble.c
new file mode 100644
index 0000000..e8e3133
--- /dev/null
+++ b/build/parsePreamble.c
@@ -0,0 +1,1090 @@
+/** \ingroup rpmbuild
+ * \file build/parsePreamble.c
+ * Parse tags in global section from spec file.
+ */
+
+#include "system.h"
+
+#include <ctype.h>
+
+#include <rpm/header.h>
+#include <rpm/rpmlog.h>
+#include <rpm/rpmfileutil.h>
+#include "rpmio/rpmlua.h"
+#include "build/rpmbuild_internal.h"
+#include "build/rpmbuild_misc.h"
+#include "debug.h"
+
+#define SKIPSPACE(s) { while (*(s) && risspace(*(s))) (s)++; }
+#define SKIPNONSPACE(s) { while (*(s) && !risspace(*(s))) (s)++; }
+#define SKIPWHITE(_x) {while(*(_x) && (risspace(*_x) || *(_x) == ',')) (_x)++;}
+#define SKIPNONWHITE(_x){while(*(_x) &&!(risspace(*_x) || *(_x) == ',')) (_x)++;}
+
+/**
+ */
+static const rpmTagVal copyTagsDuringParse[] = {
+ RPMTAG_EPOCH,
+ RPMTAG_VERSION,
+ RPMTAG_RELEASE,
+ RPMTAG_LICENSE,
+ RPMTAG_PACKAGER,
+ RPMTAG_DISTRIBUTION,
+ RPMTAG_DISTURL,
+ RPMTAG_VENDOR,
+ RPMTAG_ICON,
+ RPMTAG_URL,
+ RPMTAG_VCS,
+ RPMTAG_CHANGELOGTIME,
+ RPMTAG_CHANGELOGNAME,
+ RPMTAG_CHANGELOGTEXT,
+ RPMTAG_PREFIXES,
+ RPMTAG_DISTTAG,
+ RPMTAG_BUGURL,
+ RPMTAG_GROUP,
+ 0
+};
+
+/**
+ */
+static const rpmTagVal requiredTags[] = {
+ RPMTAG_NAME,
+ RPMTAG_VERSION,
+ RPMTAG_RELEASE,
+ RPMTAG_SUMMARY,
+ RPMTAG_LICENSE,
+ 0
+};
+
+/**
+ */
+static void addOrAppendListEntry(Header h, rpmTagVal tag, const char * line)
+{
+ int xx;
+ int argc;
+ const char **argv;
+
+ xx = poptParseArgvString(line, &argc, &argv);
+ if (argc)
+ headerPutStringArray(h, tag, argv, argc);
+ argv = _free(argv);
+}
+
+/* Parse a simple part line that only take -n <pkg> or <pkg> */
+/* <pkg> is returned in name as a pointer into a dynamic buffer */
+
+/**
+ */
+static int parseSimplePart(const char *line, char **name, int *flag)
+{
+ char *tok;
+ char *linebuf = xstrdup(line);
+ int rc;
+
+ /* Throw away the first token (the %xxxx) */
+ (void)strtok(linebuf, " \t\n");
+ *name = NULL;
+
+ if (!(tok = strtok(NULL, " \t\n"))) {
+ rc = 0;
+ goto exit;
+ }
+
+ if (rstreq(tok, "-n")) {
+ if (!(tok = strtok(NULL, " \t\n"))) {
+ rc = 1;
+ goto exit;
+ }
+ *flag = PART_NAME;
+ } else {
+ *flag = PART_SUBNAME;
+ }
+ *name = xstrdup(tok);
+ rc = strtok(NULL, " \t\n") ? 1 : 0;
+
+exit:
+ free(linebuf);
+ return rc;
+}
+
+/**
+ */
+static inline int parseYesNo(const char * s)
+{
+ return ((!s || (s[0] == 'n' || s[0] == 'N' || s[0] == '0') ||
+ !rstrcasecmp(s, "false") || !rstrcasecmp(s, "off"))
+ ? 0 : 1);
+}
+
+static struct Source *findSource(rpmSpec spec, uint32_t num, int flag)
+{
+ struct Source *p;
+
+ for (p = spec->sources; p != NULL; p = p->next)
+ if ((num == p->num) && (p->flags & flag)) return p;
+
+ return NULL;
+}
+
+static int parseNoSource(rpmSpec spec, const char * field, rpmTagVal tag)
+{
+ const char *f, *fe;
+ const char *name;
+ int flag;
+ uint32_t num;
+
+ if (tag == RPMTAG_NOSOURCE) {
+ flag = RPMBUILD_ISSOURCE;
+ name = "source";
+ } else {
+ flag = RPMBUILD_ISPATCH;
+ name = "patch";
+ }
+
+ fe = field;
+ for (f = fe; *f != '\0'; f = fe) {
+ struct Source *p;
+
+ SKIPWHITE(f);
+ if (*f == '\0')
+ break;
+ fe = f;
+ SKIPNONWHITE(fe);
+ if (*fe != '\0') fe++;
+
+ if (parseUnsignedNum(f, &num)) {
+ rpmlog(RPMLOG_ERR, _("line %d: Bad number: %s\n"),
+ spec->lineNum, f);
+ return RPMRC_FAIL;
+ }
+
+ if (! (p = findSource(spec, num, flag))) {
+ rpmlog(RPMLOG_ERR, _("line %d: Bad no%s number: %u\n"),
+ spec->lineNum, name, num);
+ return RPMRC_FAIL;
+ }
+
+ p->flags |= RPMBUILD_ISNO;
+
+ }
+
+ return 0;
+}
+
+static int addSource(rpmSpec spec, Package pkg, const char *field, rpmTagVal tag)
+{
+ struct Source *p;
+ int flag = 0;
+ const char *name = NULL;
+ char *nump;
+ char *fieldp = NULL;
+ char *buf = NULL;
+ uint32_t num = 0;
+
+ switch (tag) {
+ case RPMTAG_SOURCE:
+ flag = RPMBUILD_ISSOURCE;
+ name = "source";
+ fieldp = spec->line + 6;
+ break;
+ case RPMTAG_PATCH:
+ flag = RPMBUILD_ISPATCH;
+ name = "patch";
+ fieldp = spec->line + 5;
+ break;
+ case RPMTAG_ICON:
+ flag = RPMBUILD_ISICON;
+ fieldp = NULL;
+ break;
+ default:
+ return -1;
+ break;
+ }
+
+ /* Get the number */
+ if (tag != RPMTAG_ICON) {
+ /* We already know that a ':' exists, and that there */
+ /* are no spaces before it. */
+ /* This also now allows for spaces and tabs between */
+ /* the number and the ':' */
+ char ch;
+ char *fieldp_backup = fieldp;
+
+ while ((*fieldp != ':') && (*fieldp != ' ') && (*fieldp != '\t')) {
+ fieldp++;
+ }
+ ch = *fieldp;
+ *fieldp = '\0';
+
+ nump = fieldp_backup;
+ SKIPSPACE(nump);
+ if (nump == NULL || *nump == '\0') {
+ num = flag == RPMBUILD_ISSOURCE ? 0 : INT_MAX;
+ } else {
+ if (parseUnsignedNum(fieldp_backup, &num)) {
+ rpmlog(RPMLOG_ERR, _("line %d: Bad %s number: %s\n"),
+ spec->lineNum, name, spec->line);
+ *fieldp = ch;
+ return RPMRC_FAIL;
+ }
+ }
+ *fieldp = ch;
+ }
+
+ /* Check whether tags of the same number haven't already been defined */
+ for (p = spec->sources; p != NULL; p = p->next) {
+ if ( p->num != num ) continue;
+ if ((tag == RPMTAG_SOURCE && p->flags == RPMBUILD_ISSOURCE) ||
+ (tag == RPMTAG_PATCH && p->flags == RPMBUILD_ISPATCH)) {
+ rpmlog(RPMLOG_ERR, _("%s %d defined multiple times\n"), name, num);
+ return RPMRC_FAIL;
+ }
+ }
+
+ /* Create the entry and link it in */
+ p = xmalloc(sizeof(*p));
+ p->num = num;
+ p->fullSource = xstrdup(field);
+ p->flags = flag;
+ p->source = strrchr(p->fullSource, '/');
+ if (p->source) {
+ p->source++;
+ } else {
+ p->source = p->fullSource;
+ }
+
+ if (tag != RPMTAG_ICON) {
+ p->next = spec->sources;
+ spec->sources = p;
+ } else {
+ p->next = pkg->icon;
+ pkg->icon = p;
+ }
+
+ spec->numSources++;
+
+ if (tag != RPMTAG_ICON) {
+ char *body = rpmGetPath("%{_sourcedir}/", p->source, NULL);
+
+ rasprintf(&buf, "%s%d",
+ (flag & RPMBUILD_ISPATCH) ? "PATCH" : "SOURCE", num);
+ addMacro(spec->macros, buf, NULL, body, RMIL_SPEC);
+ free(buf);
+ rasprintf(&buf, "%sURL%d",
+ (flag & RPMBUILD_ISPATCH) ? "PATCH" : "SOURCE", num);
+ addMacro(spec->macros, buf, NULL, p->fullSource, RMIL_SPEC);
+ free(buf);
+#ifdef WITH_LUA
+ if (!spec->recursing) {
+ rpmlua lua = NULL; /* global state */
+ const char * what = (flag & RPMBUILD_ISPATCH) ? "patches" : "sources";
+ rpmluaPushTable(lua, what);
+ rpmluav var = rpmluavNew();
+ rpmluavSetListMode(var, 1);
+ rpmluavSetValue(var, RPMLUAV_STRING, body);
+ rpmluaSetVar(lua, var);
+ var = rpmluavFree(var);
+ rpmluaPop(lua);
+ }
+#endif
+ body = _free(body);
+ }
+
+ return 0;
+}
+
+typedef const struct tokenBits_s {
+ const char * name;
+ rpmsenseFlags bits;
+} * tokenBits;
+
+/**
+ */
+static struct tokenBits_s const installScriptBits[] = {
+ { "interp", RPMSENSE_INTERP },
+ { "prereq", RPMSENSE_PREREQ },
+ { "preun", RPMSENSE_SCRIPT_PREUN },
+ { "pre", RPMSENSE_SCRIPT_PRE },
+ { "postun", RPMSENSE_SCRIPT_POSTUN },
+ { "post", RPMSENSE_SCRIPT_POST },
+ { "rpmlib", RPMSENSE_RPMLIB },
+ { "verify", RPMSENSE_SCRIPT_VERIFY },
+ { "pretrans", RPMSENSE_PRETRANS },
+ { "posttrans", RPMSENSE_POSTTRANS },
+ { NULL, 0 }
+};
+
+/**
+ */
+static int parseBits(const char * s, const tokenBits tokbits,
+ rpmsenseFlags * bp)
+{
+ tokenBits tb;
+ const char * se;
+ rpmsenseFlags bits = RPMSENSE_ANY;
+ int c = 0;
+ int rc = RPMRC_OK;
+
+ if (s) {
+ while (*s != '\0') {
+ while ((c = *s) && risspace(c)) s++;
+ se = s;
+ while ((c = *se) && risalpha(c)) se++;
+ if (s == se)
+ break;
+ for (tb = tokbits; tb->name; tb++) {
+ if (tb->name != NULL &&
+ strlen(tb->name) == (se-s) && rstreqn(tb->name, s, (se-s)))
+ break;
+ }
+ if (tb->name == NULL) {
+ rc = RPMRC_FAIL;
+ break;
+ }
+ bits |= tb->bits;
+ while ((c = *se) && risspace(c)) se++;
+ if (c != ',')
+ break;
+ s = ++se;
+ }
+ }
+ *bp |= bits;
+ return rc;
+}
+
+/**
+ */
+static inline char * findLastChar(char * s)
+{
+ char *res = s;
+
+ while (*s != '\0') {
+ if (! risspace(*s))
+ res = s;
+ s++;
+ }
+
+ return res;
+}
+
+/**
+ */
+static int isMemberInEntry(Header h, const char *name, rpmTagVal tag)
+{
+ struct rpmtd_s td;
+ int found = 0;
+ const char *str;
+
+ if (!headerGet(h, tag, &td, HEADERGET_MINMEM))
+ return -1;
+
+ while ((str = rpmtdNextString(&td))) {
+ if (!rstrcasecmp(str, name)) {
+ found = 1;
+ break;
+ }
+ }
+ rpmtdFreeData(&td);
+
+ return found;
+}
+
+/**
+ */
+static rpmRC checkForValidArchitectures(rpmSpec spec)
+{
+ char *arch = rpmExpand("%{_target_cpu}", NULL);
+ char *os = rpmExpand("%{_target_os}", NULL);
+ rpmRC rc = RPMRC_FAIL; /* assume failure */
+
+ if (isMemberInEntry(spec->buildRestrictions,
+ arch, RPMTAG_EXCLUDEARCH) == 1) {
+ rpmlog(RPMLOG_ERR, _("Architecture is excluded: %s\n"), arch);
+ goto exit;
+ }
+ if (isMemberInEntry(spec->buildRestrictions,
+ arch, RPMTAG_EXCLUSIVEARCH) == 0) {
+ rpmlog(RPMLOG_ERR, _("Architecture is not included: %s\n"), arch);
+ goto exit;
+ }
+ if (isMemberInEntry(spec->buildRestrictions,
+ os, RPMTAG_EXCLUDEOS) == 1) {
+ rpmlog(RPMLOG_ERR, _("OS is excluded: %s\n"), os);
+ goto exit;
+ }
+ if (isMemberInEntry(spec->buildRestrictions,
+ os, RPMTAG_EXCLUSIVEOS) == 0) {
+ rpmlog(RPMLOG_ERR, _("OS is not included: %s\n"), os);
+ goto exit;
+ }
+ rc = RPMRC_OK;
+
+exit:
+ arch = _free(arch);
+ os = _free(os);
+
+ return rc;
+}
+
+/**
+ * Check that required tags are present in header.
+ * @param h header
+ * @param NVR package name-version-release
+ * @return RPMRC_OK if OK
+ */
+static int checkForRequired(Header h, const char * NVR)
+{
+ int res = RPMRC_OK;
+ const rpmTagVal * p;
+
+ for (p = requiredTags; *p != 0; p++) {
+ if (!headerIsEntry(h, *p)) {
+ rpmlog(RPMLOG_ERR,
+ _("%s field must be present in package: %s\n"),
+ rpmTagGetName(*p), NVR);
+ res = RPMRC_FAIL;
+ }
+ }
+
+ return res;
+}
+
+/**
+ * Check that no duplicate tags are present in header.
+ * @param h header
+ * @param NVR package name-version-release
+ * @return RPMRC_OK if OK
+ */
+static int checkForDuplicates(Header h, const char * NVR)
+{
+ int res = RPMRC_OK;
+ rpmTagVal tag, lastTag = RPMTAG_NOT_FOUND;
+ HeaderIterator hi = headerInitIterator(h);
+
+ while ((tag = headerNextTag(hi)) != RPMTAG_NOT_FOUND) {
+ if (tag == lastTag) {
+ rpmlog(RPMLOG_ERR, _("Duplicate %s entries in package: %s\n"),
+ rpmTagGetName(tag), NVR);
+ res = RPMRC_FAIL;
+ }
+ lastTag = tag;
+ }
+ hi = headerFreeIterator(hi);
+
+ return res;
+}
+
+/**
+ */
+static struct optionalTag {
+ rpmTagVal ot_tag;
+ const char * ot_mac;
+} const optionalTags[] = {
+ { RPMTAG_VENDOR, "%{vendor}" },
+ { RPMTAG_PACKAGER, "%{packager}" },
+ { RPMTAG_DISTRIBUTION, "%{distribution}" },
+ { RPMTAG_DISTURL, "%{disturl}" },
+ { RPMTAG_BUGURL, "%{bugurl}" },
+ { -1, NULL }
+};
+
+/**
+ */
+static void fillOutMainPackage(Header h)
+{
+ const struct optionalTag *ot;
+
+ for (ot = optionalTags; ot->ot_mac != NULL; ot++) {
+ if (!headerIsEntry(h, ot->ot_tag)) {
+ char *val = rpmExpand(ot->ot_mac, NULL);
+ if (val && *val != '%') {
+ headerPutString(h, ot->ot_tag, val);
+ }
+ val = _free(val);
+ }
+ }
+}
+
+static int getSpecialDocDir(Package pkg)
+{
+ const char *errstr, *docdir_fmt = "%{NAME}-%{VERSION}";
+ char *fmt_macro, *fmt;
+ int rc = -1;
+
+ fmt_macro = rpmExpand("%{?_docdir_fmt}", NULL);
+ if (fmt_macro && strlen(fmt_macro) > 0) {
+ docdir_fmt = fmt_macro;
+ }
+ fmt = headerFormat(pkg->header, docdir_fmt, &errstr);
+ if (!fmt) {
+ rpmlog(RPMLOG_ERR, _("illegal _docdir_fmt: %s\n"), errstr);
+ goto exit;
+ }
+ pkg->specialDocDir = rpmGetPath("%{_docdir}/", fmt, NULL);
+ rc = 0;
+
+exit:
+ free(fmt);
+ free(fmt_macro);
+ return rc;
+}
+
+/**
+ */
+static rpmRC readIcon(Header h, const char * file)
+{
+ char *fn = NULL;
+ uint8_t *icon = NULL;
+ FD_t fd = NULL;
+ rpmRC rc = RPMRC_FAIL; /* assume failure */
+ off_t size;
+ size_t nb, iconsize;
+
+ /* XXX use rpmGenPath(rootdir, "%{_sourcedir}/", file) for icon path. */
+ fn = rpmGetPath("%{_sourcedir}/", file, NULL);
+
+ fd = Fopen(fn, "r.ufdio");
+ if (fd == NULL) {
+ rpmlog(RPMLOG_ERR, _("Unable to open icon %s: %s\n"),
+ fn, Fstrerror(fd));
+ goto exit;
+ }
+ size = fdSize(fd);
+ iconsize = (size >= 0 ? size : (8 * BUFSIZ));
+ if (iconsize == 0) {
+ rc = RPMRC_OK; /* XXX Eh? */
+ goto exit;
+ }
+
+ icon = xmalloc(iconsize + 1);
+ *icon = '\0';
+
+ nb = Fread(icon, sizeof(icon[0]), iconsize, fd);
+ if (Ferror(fd) || (size >= 0 && nb != size)) {
+ rpmlog(RPMLOG_ERR, _("Unable to read icon %s: %s\n"),
+ fn, Fstrerror(fd));
+ goto exit;
+ }
+
+ if (rstreqn((char*)icon, "GIF", sizeof("GIF")-1)) {
+ headerPutBin(h, RPMTAG_GIF, icon, iconsize);
+ } else if (rstreqn((char*)icon, "/* XPM", sizeof("/* XPM")-1)) {
+ headerPutBin(h, RPMTAG_XPM, icon, iconsize);
+ } else {
+ rpmlog(RPMLOG_ERR, _("Unknown icon type: %s\n"), file);
+ goto exit;
+ }
+ rc = RPMRC_OK;
+
+exit:
+ Fclose(fd);
+ free(fn);
+ free(icon);
+ return rc;
+}
+
+#define SINGLE_TOKEN_ONLY \
+if (multiToken) { \
+ rpmlog(RPMLOG_ERR, _("line %d: Tag takes single token only: %s\n"), \
+ spec->lineNum, spec->line); \
+ return RPMRC_FAIL; \
+}
+
+/**
+ * Check for inappropriate characters. All alphanums are considered sane.
+ * @param spec spec
+ * @param field string to check
+ * @param fsize size of string to check
+ * @param whitelist string of permitted characters
+ * @return RPMRC_OK if OK
+ */
+rpmRC rpmCharCheck(rpmSpec spec, const char *field, size_t fsize, const char *whitelist)
+{
+ const char *ch, *stop = &field[fsize];
+
+ for (ch=field; *ch && ch < stop; ch++) {
+ if (risalnum(*ch) || strchr(whitelist, *ch)) continue;
+ if (isprint(*ch)) {
+ rpmlog(RPMLOG_ERR, _("line %d: Illegal char '%c' in: %s\n"),
+ spec->lineNum, *ch, spec->line);
+ } else {
+ rpmlog(RPMLOG_ERR, _("line %d: Illegal char in: %s\n"),
+ spec->lineNum, spec->line);
+ }
+ return RPMRC_FAIL;
+ }
+ if (strstr(field, "..") != NULL) {
+ rpmlog(RPMLOG_ERR, _("line %d: Illegal sequence \"..\" in: %s\n"),
+ spec->lineNum, spec->line);
+ return RPMRC_FAIL;
+ }
+
+ return RPMRC_OK;
+}
+
+/**
+ */
+static int handlePreambleTag(rpmSpec spec, Package pkg, rpmTagVal tag,
+ const char *macro, const char *lang)
+{
+ char * field = spec->line;
+ char * end;
+ int multiToken = 0;
+ rpmsenseFlags tagflags = RPMSENSE_ANY;
+ int rc;
+ int xx;
+
+ if (field == NULL) return RPMRC_FAIL; /* XXX can't happen */
+ /* Find the start of the "field" and strip trailing space */
+ while ((*field) && (*field != ':'))
+ field++;
+ if (*field != ':') {
+ rpmlog(RPMLOG_ERR, _("line %d: Malformed tag: %s\n"),
+ spec->lineNum, spec->line);
+ return RPMRC_FAIL;
+ }
+ field++;
+ SKIPSPACE(field);
+ if (!*field) {
+ /* Empty field */
+ rpmlog(RPMLOG_ERR, _("line %d: Empty tag: %s\n"),
+ spec->lineNum, spec->line);
+ return RPMRC_FAIL;
+ }
+ end = findLastChar(field);
+ *(end+1) = '\0';
+
+ /* See if this is multi-token */
+ end = field;
+ SKIPNONSPACE(end);
+ if (*end != '\0')
+ multiToken = 1;
+
+ switch (tag) {
+ case RPMTAG_NAME:
+ SINGLE_TOKEN_ONLY;
+ if (rpmCharCheck(spec, field, strlen(field), ".-_+%{}") != RPMRC_OK) return RPMRC_FAIL;
+ headerPutString(pkg->header, tag, field);
+ break;
+ case RPMTAG_VERSION:
+ case RPMTAG_RELEASE:
+ SINGLE_TOKEN_ONLY;
+ if (rpmCharCheck(spec, field, strlen(field), "._+%{}") != RPMRC_OK) return RPMRC_FAIL;
+ headerPutString(pkg->header, tag, field);
+ break;
+ case RPMTAG_URL:
+ case RPMTAG_DISTTAG:
+ case RPMTAG_BUGURL:
+ /* XXX TODO: validate format somehow */
+ case RPMTAG_VCS:
+ SINGLE_TOKEN_ONLY;
+ headerPutString(pkg->header, tag, field);
+ break;
+ case RPMTAG_GROUP:
+ case RPMTAG_SUMMARY:
+ case RPMTAG_DISTRIBUTION:
+ case RPMTAG_VENDOR:
+ case RPMTAG_LICENSE:
+ case RPMTAG_PACKAGER:
+ if (!*lang) {
+ headerPutString(pkg->header, tag, field);
+ } else if (!((spec->flags & RPMSPEC_NOLANG) && !rstreq(lang, RPMBUILD_DEFAULT_LANG)))
+ (void) headerAddI18NString(pkg->header, tag, field, lang);
+ break;
+ case RPMTAG_BUILDROOT:
+ /* just silently ignore BuildRoot */
+ macro = NULL;
+ break;
+ case RPMTAG_PREFIXES: {
+ struct rpmtd_s td;
+ const char *str;
+ addOrAppendListEntry(pkg->header, tag, field);
+ xx = headerGet(pkg->header, tag, &td, HEADERGET_MINMEM);
+ while ((str = rpmtdNextString(&td))) {
+ size_t len = strlen(str);
+ if (len > 1 && str[len-1] == '/') {
+ rpmlog(RPMLOG_ERR,
+ _("line %d: Prefixes must not end with \"/\": %s\n"),
+ spec->lineNum, spec->line);
+ rpmtdFreeData(&td);
+ return RPMRC_FAIL;
+ }
+ }
+ rpmtdFreeData(&td);
+ break;
+ }
+ case RPMTAG_DOCDIR:
+ SINGLE_TOKEN_ONLY;
+ if (field[0] != '/') {
+ rpmlog(RPMLOG_ERR,
+ _("line %d: Docdir must begin with '/': %s\n"),
+ spec->lineNum, spec->line);
+ return RPMRC_FAIL;
+ }
+ macro = NULL;
+ delMacro(NULL, "_docdir");
+ addMacro(NULL, "_docdir", NULL, field, RMIL_SPEC);
+ break;
+ case RPMTAG_EPOCH: {
+ SINGLE_TOKEN_ONLY;
+ uint32_t epoch;
+ if (parseUnsignedNum(field, &epoch)) {
+ rpmlog(RPMLOG_ERR,
+ _("line %d: Epoch field must be an unsigned number: %s\n"),
+ spec->lineNum, spec->line);
+ return RPMRC_FAIL;
+ }
+ headerPutUint32(pkg->header, tag, &epoch, 1);
+ break;
+ }
+ case RPMTAG_AUTOREQPROV:
+ pkg->autoReq = parseYesNo(field);
+ pkg->autoProv = pkg->autoReq;
+ break;
+ case RPMTAG_AUTOREQ:
+ pkg->autoReq = parseYesNo(field);
+ break;
+ case RPMTAG_AUTOPROV:
+ pkg->autoProv = parseYesNo(field);
+ break;
+ case RPMTAG_SOURCE:
+ case RPMTAG_PATCH:
+ macro = NULL;
+ if ((rc = addSource(spec, pkg, field, tag)))
+ return rc;
+ break;
+ case RPMTAG_ICON:
+ SINGLE_TOKEN_ONLY;
+ if ((rc = addSource(spec, pkg, field, tag)))
+ return rc;
+ if ((rc = readIcon(pkg->header, field)))
+ return RPMRC_FAIL;
+ break;
+ case RPMTAG_NOSOURCE:
+ case RPMTAG_NOPATCH:
+ spec->noSource = 1;
+ if ((rc = parseNoSource(spec, field, tag)))
+ return rc;
+ break;
+ case RPMTAG_ORDERFLAGS:
+ case RPMTAG_REQUIREFLAGS:
+ case RPMTAG_PREREQ:
+ if ((rc = parseBits(lang, installScriptBits, &tagflags))) {
+ rpmlog(RPMLOG_ERR,
+ _("line %d: Bad %s: qualifiers: %s\n"),
+ spec->lineNum, rpmTagGetName(tag), spec->line);
+ return rc;
+ }
+ if ((rc = parseRCPOT(spec, pkg, field, tag, 0, tagflags)))
+ return rc;
+ break;
+ case RPMTAG_BUILDPREREQ:
+ case RPMTAG_BUILDREQUIRES:
+ case RPMTAG_BUILDCONFLICTS:
+ case RPMTAG_CONFLICTFLAGS:
+ case RPMTAG_OBSOLETEFLAGS:
+ case RPMTAG_PROVIDEFLAGS:
+ if ((rc = parseRCPOT(spec, pkg, field, tag, 0, tagflags)))
+ return rc;
+ break;
+ case RPMTAG_EXCLUDEARCH:
+ case RPMTAG_EXCLUSIVEARCH:
+ case RPMTAG_EXCLUDEOS:
+ case RPMTAG_EXCLUSIVEOS:
+ addOrAppendListEntry(spec->buildRestrictions, tag, field);
+ break;
+ case RPMTAG_BUILDARCHS: {
+ int BACount;
+ const char **BANames = NULL;
+ if ((rc = poptParseArgvString(field, &BACount, &BANames))) {
+ rpmlog(RPMLOG_ERR,
+ _("line %d: Bad BuildArchitecture format: %s\n"),
+ spec->lineNum, spec->line);
+ return RPMRC_FAIL;
+ }
+ if (spec->packages == pkg) {
+ spec->BACount = BACount;
+ spec->BANames = BANames;
+ } else {
+ if (BACount != 1 || !rstreq(BANames[0], "noarch")) {
+ rpmlog(RPMLOG_ERR,
+ _("line %d: Only noarch subpackages are supported: %s\n"),
+ spec->lineNum, spec->line);
+ BANames = _free(BANames);
+ return RPMRC_FAIL;
+ }
+ headerPutString(pkg->header, RPMTAG_ARCH, "noarch");
+ }
+ if (!BACount)
+ spec->BANames = _free(spec->BANames);
+ break;
+ }
+ case RPMTAG_COLLECTIONS:
+ addOrAppendListEntry(pkg->header, tag, field);
+ break;
+ default:
+ rpmlog(RPMLOG_ERR, _("Internal error: Bogus tag %d\n"), tag);
+ return RPMRC_FAIL;
+ }
+
+ if (macro)
+ addMacro(spec->macros, macro, NULL, field, RMIL_SPEC);
+
+ return RPMRC_OK;
+}
+
+/* This table has to be in a peculiar order. If one tag is the */
+/* same as another, plus a few letters, it must come first. */
+
+/**
+ */
+typedef const struct PreambleRec_s {
+ rpmTagVal tag;
+ int type;
+ int deprecated;
+ size_t len;
+ const char * token;
+} * PreambleRec;
+
+#define LEN_AND_STR(_tag) (sizeof(_tag)-1), _tag
+
+static struct PreambleRec_s const preambleList[] = {
+ {RPMTAG_NAME, 0, 0, LEN_AND_STR("name")},
+ {RPMTAG_VERSION, 0, 0, LEN_AND_STR("version")},
+ {RPMTAG_RELEASE, 0, 0, LEN_AND_STR("release")},
+ {RPMTAG_EPOCH, 0, 0, LEN_AND_STR("epoch")},
+ {RPMTAG_SUMMARY, 1, 0, LEN_AND_STR("summary")},
+ {RPMTAG_LICENSE, 0, 0, LEN_AND_STR("license")},
+ {RPMTAG_DISTRIBUTION, 0, 0, LEN_AND_STR("distribution")},
+ {RPMTAG_DISTURL, 0, 0, LEN_AND_STR("disturl")},
+ {RPMTAG_VENDOR, 0, 0, LEN_AND_STR("vendor")},
+ {RPMTAG_GROUP, 1, 0, LEN_AND_STR("group")},
+ {RPMTAG_PACKAGER, 0, 0, LEN_AND_STR("packager")},
+ {RPMTAG_URL, 0, 0, LEN_AND_STR("url")},
+ {RPMTAG_VCS, 0, 0, LEN_AND_STR("vcs")},
+ {RPMTAG_SOURCE, 0, 0, LEN_AND_STR("source")},
+ {RPMTAG_PATCH, 0, 0, LEN_AND_STR("patch")},
+ {RPMTAG_NOSOURCE, 0, 0, LEN_AND_STR("nosource")},
+ {RPMTAG_NOPATCH, 0, 0, LEN_AND_STR("nopatch")},
+ {RPMTAG_EXCLUDEARCH, 0, 0, LEN_AND_STR("excludearch")},
+ {RPMTAG_EXCLUSIVEARCH, 0, 0, LEN_AND_STR("exclusivearch")},
+ {RPMTAG_EXCLUDEOS, 0, 0, LEN_AND_STR("excludeos")},
+ {RPMTAG_EXCLUSIVEOS, 0, 0, LEN_AND_STR("exclusiveos")},
+ {RPMTAG_ICON, 0, 0, LEN_AND_STR("icon")},
+ {RPMTAG_PROVIDEFLAGS, 0, 0, LEN_AND_STR("provides")},
+ {RPMTAG_REQUIREFLAGS, 2, 0, LEN_AND_STR("requires")},
+ {RPMTAG_PREREQ, 2, 1, LEN_AND_STR("prereq")},
+ {RPMTAG_CONFLICTFLAGS, 0, 0, LEN_AND_STR("conflicts")},
+ {RPMTAG_OBSOLETEFLAGS, 0, 0, LEN_AND_STR("obsoletes")},
+ {RPMTAG_PREFIXES, 0, 0, LEN_AND_STR("prefixes")},
+ {RPMTAG_PREFIXES, 0, 0, LEN_AND_STR("prefix")},
+ {RPMTAG_BUILDROOT, 0, 0, LEN_AND_STR("buildroot")},
+ {RPMTAG_BUILDARCHS, 0, 0, LEN_AND_STR("buildarchitectures")},
+ {RPMTAG_BUILDARCHS, 0, 0, LEN_AND_STR("buildarch")},
+ {RPMTAG_BUILDCONFLICTS, 0, 0, LEN_AND_STR("buildconflicts")},
+ {RPMTAG_BUILDPREREQ, 0, 1, LEN_AND_STR("buildprereq")},
+ {RPMTAG_BUILDREQUIRES, 0, 0, LEN_AND_STR("buildrequires")},
+ {RPMTAG_AUTOREQPROV, 0, 0, LEN_AND_STR("autoreqprov")},
+ {RPMTAG_AUTOREQ, 0, 0, LEN_AND_STR("autoreq")},
+ {RPMTAG_AUTOPROV, 0, 0, LEN_AND_STR("autoprov")},
+ {RPMTAG_DOCDIR, 0, 0, LEN_AND_STR("docdir")},
+ {RPMTAG_DISTTAG, 0, 0, LEN_AND_STR("disttag")},
+ {RPMTAG_BUGURL, 0, 0, LEN_AND_STR("bugurl")},
+ {RPMTAG_COLLECTIONS, 0, 0, LEN_AND_STR("collections")},
+ {RPMTAG_ORDERFLAGS, 2, 0, LEN_AND_STR("orderwithrequires")},
+ {0, 0, 0, 0}
+};
+
+/**
+ */
+static int findPreambleTag(rpmSpec spec,rpmTagVal * tag,
+ const char ** macro, char * lang)
+{
+ PreambleRec p;
+ char *s;
+
+ for (p = preambleList; p->token != NULL; p++) {
+ if (!(p->token && !rstrncasecmp(spec->line, p->token, p->len)))
+ continue;
+ if (p->deprecated) {
+ rpmlog(RPMLOG_WARNING, _("line %d: %s is deprecated: %s\n"),
+ spec->lineNum, p->token, spec->line);
+ }
+ break;
+ }
+ if (p == NULL || p->token == NULL)
+ return 1;
+
+ s = spec->line + p->len;
+ SKIPSPACE(s);
+
+ switch (p->type) {
+ default:
+ case 0:
+ /* Unless this is a source or a patch, a ':' better be next */
+ if (p->tag != RPMTAG_SOURCE && p->tag != RPMTAG_PATCH) {
+ if (*s != ':') return 1;
+ }
+ *lang = '\0';
+ break;
+ case 1: /* Parse optional ( <token> ). */
+ case 2:
+ if (*s == ':') {
+ /* Type 1 is multilang, 2 is qualifiers with no defaults */
+ strcpy(lang, (p->type == 1) ? RPMBUILD_DEFAULT_LANG : "");
+ break;
+ }
+ if (*s != '(') return 1;
+ s++;
+ SKIPSPACE(s);
+ while (!risspace(*s) && *s != ')')
+ *lang++ = *s++;
+ *lang = '\0';
+ SKIPSPACE(s);
+ if (*s != ')') return 1;
+ s++;
+ SKIPSPACE(s);
+ if (*s != ':') return 1;
+ break;
+ }
+
+ *tag = p->tag;
+ if (macro)
+ *macro = p->token;
+ return 0;
+}
+
+int parsePreamble(rpmSpec spec, int initialPackage)
+{
+ int nextPart = PART_ERROR;
+ int res = PART_ERROR; /* assume failure */
+ int rc;
+ char *name, *linep;
+ int flag = 0;
+ Package pkg;
+ char *NVR = NULL;
+ char lang[BUFSIZ];
+
+ pkg = newPackage(spec);
+
+ if (! initialPackage) {
+ /* There is one option to %package: <pkg> or -n <pkg> */
+ if (parseSimplePart(spec->line, &name, &flag)) {
+ rpmlog(RPMLOG_ERR, _("Bad package specification: %s\n"),
+ spec->line);
+ goto exit;
+ }
+
+ if (!lookupPackage(spec, name, flag, NULL)) {
+ rpmlog(RPMLOG_ERR, _("Package already exists: %s\n"), spec->line);
+ free(name);
+ goto exit;
+ }
+
+ /* Construct the package */
+ if (flag == PART_SUBNAME) {
+ rasprintf(&NVR, "%s-%s",
+ headerGetString(spec->packages->header, RPMTAG_NAME), name);
+ } else
+ NVR = xstrdup(name);
+ free(name);
+ headerPutString(pkg->header, RPMTAG_NAME, NVR);
+ } else {
+ NVR = xstrdup("(main package)");
+ }
+
+ if ((rc = readLine(spec, STRIP_TRAILINGSPACE | STRIP_COMMENTS)) > 0) {
+ nextPart = PART_NONE;
+ } else if (rc < 0) {
+ goto exit;
+ } else {
+ while (! (nextPart = isPart(spec->line))) {
+ const char * macro;
+ rpmTagVal tag;
+
+ /* Skip blank lines */
+ linep = spec->line;
+ SKIPSPACE(linep);
+ if (*linep != '\0') {
+ if (findPreambleTag(spec, &tag, &macro, lang)) {
+ rpmlog(RPMLOG_ERR, _("line %d: Unknown tag: %s\n"),
+ spec->lineNum, spec->line);
+ goto exit;
+ }
+ if (handlePreambleTag(spec, pkg, tag, macro, lang)) {
+ goto exit;
+ }
+ if (spec->BANames && !spec->recursing) {
+ res = PART_BUILDARCHITECTURES;
+ goto exit;
+ }
+ }
+ if ((rc =
+ readLine(spec, STRIP_TRAILINGSPACE | STRIP_COMMENTS)) > 0) {
+ nextPart = PART_NONE;
+ break;
+ }
+ if (rc) {
+ goto exit;
+ }
+ }
+ }
+
+ /*
+ * Expand buildroot one more time to get %{version} and the like
+ * from the main package, validate sanity. The spec->buildRoot could
+ * still contain unexpanded macros but it cannot be empty or '/', and it
+ * can't be messed with by anything spec does beyond this point.
+ */
+ if (initialPackage) {
+ char *buildRoot = rpmGetPath(spec->buildRoot, NULL);
+ if (*buildRoot == '\0') {
+ rpmlog(RPMLOG_ERR, _("%%{buildroot} couldn't be empty\n"));
+ goto exit;
+ }
+ if (rstreq(buildRoot, "/")) {
+ rpmlog(RPMLOG_ERR, _("%%{buildroot} can not be \"/\"\n"));
+ goto exit;
+ }
+ free(spec->buildRoot);
+ spec->buildRoot = buildRoot;
+ addMacro(spec->macros, "buildroot", NULL, spec->buildRoot, RMIL_SPEC);
+ }
+
+ /* XXX Skip valid arch check if not building binary package */
+ if (!(spec->flags & RPMSPEC_ANYARCH) && checkForValidArchitectures(spec)) {
+ goto exit;
+ }
+
+ /* It is the main package */
+ if (pkg == spec->packages) {
+ fillOutMainPackage(pkg->header);
+ /* Define group tag to something when group is undefined in main package*/
+ if (!headerIsEntry(pkg->header, RPMTAG_GROUP)) {
+ headerPutString(pkg->header, RPMTAG_GROUP, "Unspecified");
+ }
+ }
+
+ if (checkForDuplicates(pkg->header, NVR)) {
+ goto exit;
+ }
+
+ if (pkg != spec->packages) {
+ headerCopyTags(spec->packages->header, pkg->header,
+ (rpmTagVal *)copyTagsDuringParse);
+ }
+
+ if (checkForRequired(pkg->header, NVR)) {
+ goto exit;
+ }
+
+ if (getSpecialDocDir(pkg)) {
+ goto exit;
+ }
+
+ /* if we get down here nextPart has been set to non-error */
+ res = nextPart;
+
+exit:
+ free(NVR);
+ return res;
+}