2 * \file build/parsePreamble.c
3 * Parse tags in global section from spec file.
8 #include <rpm/header.h>
9 #include <rpm/rpmbuild.h>
10 #include <rpm/rpmlog.h>
11 #include <rpm/rpmfileutil.h>
14 #define SKIPSPACE(s) { while (*(s) && risspace(*(s))) (s)++; }
15 #define SKIPNONSPACE(s) { while (*(s) && !risspace(*(s))) (s)++; }
19 static const rpmTag copyTagsDuringParse[] = {
40 static const rpmTag requiredTags[] = {
51 static void addOrAppendListEntry(Header h, rpmTag tag, const char * line)
57 xx = poptParseArgvString(line, &argc, &argv);
59 headerPutStringArray(h, tag, argv, argc);
63 /* Parse a simple part line that only take -n <pkg> or <pkg> */
64 /* <pkg> is returned in name as a pointer into a dynamic buffer */
68 static int parseSimplePart(const char *line, char **name, int *flag)
71 char *linebuf = xstrdup(line);
74 /* Throw away the first token (the %xxxx) */
75 (void)strtok(linebuf, " \t\n");
78 if (!(tok = strtok(NULL, " \t\n"))) {
83 if (!strcmp(tok, "-n")) {
84 if (!(tok = strtok(NULL, " \t\n"))) {
93 rc = strtok(NULL, " \t\n") ? 1 : 0;
102 static inline int parseYesNo(const char * s)
104 return ((!s || (s[0] == 'n' || s[0] == 'N' || s[0] == '0') ||
105 !rstrcasecmp(s, "false") || !rstrcasecmp(s, "off"))
109 typedef const struct tokenBits_s {
116 static struct tokenBits_s const installScriptBits[] = {
117 { "interp", RPMSENSE_INTERP },
118 { "prereq", RPMSENSE_PREREQ },
119 { "preun", RPMSENSE_SCRIPT_PREUN },
120 { "pre", RPMSENSE_SCRIPT_PRE },
121 { "postun", RPMSENSE_SCRIPT_POSTUN },
122 { "post", RPMSENSE_SCRIPT_POST },
123 { "rpmlib", RPMSENSE_RPMLIB },
124 { "verify", RPMSENSE_SCRIPT_VERIFY },
130 static const struct tokenBits_s const buildScriptBits[] = {
131 { "prep", RPMSENSE_SCRIPT_PREP },
132 { "build", RPMSENSE_SCRIPT_BUILD },
133 { "install", RPMSENSE_SCRIPT_INSTALL },
134 { "clean", RPMSENSE_SCRIPT_CLEAN },
140 static int parseBits(const char * s, const tokenBits tokbits,
145 rpmsenseFlags bits = RPMSENSE_ANY;
150 while ((c = *s) && risspace(c)) s++;
152 while ((c = *se) && risalpha(c)) se++;
155 for (tb = tokbits; tb->name; tb++) {
156 if (tb->name != NULL &&
157 strlen(tb->name) == (se-s) && !strncmp(tb->name, s, (se-s)))
160 if (tb->name == NULL)
163 while ((c = *se) && risspace(c)) se++;
169 if (c == 0 && bp) *bp = bits;
170 return (c ? RPMRC_FAIL : RPMRC_OK);
175 static inline char * findLastChar(char * s)
190 static int isMemberInEntry(Header h, const char *name, rpmTag tag)
196 if (!headerGet(h, tag, &td, HEADERGET_MINMEM))
199 while ((str = rpmtdNextString(&td))) {
200 if (!rstrcasecmp(str, name)) {
212 static rpmRC checkForValidArchitectures(rpmSpec spec)
214 char *arch = rpmExpand("%{_target_cpu}", NULL);
215 char *os = rpmExpand("%{_target_os}", NULL);
216 rpmRC rc = RPMRC_FAIL; /* assume failure */
218 if (isMemberInEntry(spec->buildRestrictions,
219 arch, RPMTAG_EXCLUDEARCH) == 1) {
220 rpmlog(RPMLOG_ERR, _("Architecture is excluded: %s\n"), arch);
223 if (isMemberInEntry(spec->buildRestrictions,
224 arch, RPMTAG_EXCLUSIVEARCH) == 0) {
225 rpmlog(RPMLOG_ERR, _("Architecture is not included: %s\n"), arch);
228 if (isMemberInEntry(spec->buildRestrictions,
229 os, RPMTAG_EXCLUDEOS) == 1) {
230 rpmlog(RPMLOG_ERR, _("OS is excluded: %s\n"), os);
233 if (isMemberInEntry(spec->buildRestrictions,
234 os, RPMTAG_EXCLUSIVEOS) == 0) {
235 rpmlog(RPMLOG_ERR, _("OS is not included: %s\n"), os);
248 * Check that required tags are present in header.
250 * @param NVR package name-version-release
251 * @return RPMRC_OK if OK
253 static int checkForRequired(Header h, const char * NVR)
254 /* LCL: parse error here with modifies */
259 for (p = requiredTags; *p != 0; p++) {
260 if (!headerIsEntry(h, *p)) {
262 _("%s field must be present in package: %s\n"),
263 rpmTagGetName(*p), NVR);
272 * Check that no duplicate tags are present in header.
274 * @param NVR package name-version-release
275 * @return RPMRC_OK if OK
277 static int checkForDuplicates(Header h, const char * NVR)
284 for (hi = headerInitIterator(h), lastTag = 0;
285 headerNext(hi, &td), tag = rpmtdTag(&td);
288 if (tag == lastTag) {
289 rpmlog(RPMLOG_ERR, _("Duplicate %s entries in package: %s\n"),
290 rpmTagGetName(tag), NVR);
295 hi = headerFreeIterator(hi);
302 static struct optionalTag {
305 } const optionalTags[] = {
306 { RPMTAG_VENDOR, "%{vendor}" },
307 { RPMTAG_PACKAGER, "%{packager}" },
308 { RPMTAG_DISTRIBUTION, "%{distribution}" },
309 { RPMTAG_DISTURL, "%{disturl}" },
315 static void fillOutMainPackage(Header h)
317 const struct optionalTag *ot;
319 for (ot = optionalTags; ot->ot_mac != NULL; ot++) {
320 if (!headerIsEntry(h, ot->ot_tag)) {
321 char *val = rpmExpand(ot->ot_mac, NULL);
322 if (val && *val != '%') {
323 headerPutString(h, ot->ot_tag, val);
330 static int getSpecialDocDir(Package pkg)
332 const char *errstr, *docdir_fmt = "%{NAME}-%{VERSION}";
333 char *fmt_macro, *fmt;
336 fmt_macro = rpmExpand("%{?_docdir_fmt}", NULL);
337 if (fmt_macro && strlen(fmt_macro) > 0) {
338 docdir_fmt = fmt_macro;
340 fmt = headerFormat(pkg->header, docdir_fmt, &errstr);
342 rpmlog(RPMLOG_ERR, _("illegal _docdir_fmt: %s\n"), errstr);
345 pkg->specialDocDir = rpmGetPath("%{_docdir}/", fmt, NULL);
356 static rpmRC readIcon(Header h, const char * file)
365 /* XXX use rpmGenPath(rootdir, "%{_sourcedir}/", file) for icon path. */
366 fn = rpmGetPath("%{_sourcedir}/", file, NULL);
368 fd = Fopen(fn, "r.ufdio");
369 if (fd == NULL || Ferror(fd)) {
370 rpmlog(RPMLOG_ERR, _("Unable to open icon %s: %s\n"),
376 iconsize = (size >= 0 ? size : (8 * BUFSIZ));
383 icon = xmalloc(iconsize + 1);
386 nb = Fread(icon, sizeof(icon[0]), iconsize, fd);
387 if (Ferror(fd) || (size >= 0 && nb != size)) {
388 rpmlog(RPMLOG_ERR, _("Unable to read icon %s: %s\n"),
396 if (! strncmp((char*)icon, "GIF", sizeof("GIF")-1)) {
397 headerPutBin(h, RPMTAG_GIF, icon, iconsize);
398 } else if (! strncmp((char*)icon, "/* XPM", sizeof("/* XPM")-1)) {
399 headerPutBin(h, RPMTAG_XPM, icon, iconsize);
401 rpmlog(RPMLOG_ERR, _("Unknown icon type: %s\n"), file);
412 spectag stashSt(rpmSpec spec, Header h, rpmTag tag, const char * lang)
417 spectags st = spec->st;
418 if (st->st_ntags == st->st_nalloc) {
420 st->st_t = xrealloc(st->st_t, st->st_nalloc * sizeof(*(st->st_t)));
422 t = st->st_t + st->st_ntags++;
424 t->t_startx = spec->lineNum - 1;
426 t->t_lang = xstrdup(lang);
428 if (!(t->t_lang && strcmp(t->t_lang, RPMBUILD_DEFAULT_LANG))) {
430 if (headerGet(h, RPMTAG_NAME, &td, HEADERGET_MINMEM)) {
431 rasprintf(&t->t_msgid, "%s(%s)",
432 rpmtdGetString(&td), rpmTagGetName(tag));
440 #define SINGLE_TOKEN_ONLY \
442 rpmlog(RPMLOG_ERR, _("line %d: Tag takes single token only: %s\n"), \
443 spec->lineNum, spec->line); \
451 static int handlePreambleTag(rpmSpec spec, Package pkg, rpmTag tag,
452 const char *macro, const char *lang)
454 char * field = spec->line;
457 rpmsenseFlags tagflags;
461 if (field == NULL) return RPMRC_FAIL; /* XXX can't happen */
462 /* Find the start of the "field" and strip trailing space */
463 while ((*field) && (*field != ':'))
466 rpmlog(RPMLOG_ERR, _("line %d: Malformed tag: %s\n"),
467 spec->lineNum, spec->line);
474 rpmlog(RPMLOG_ERR, _("line %d: Empty tag: %s\n"),
475 spec->lineNum, spec->line);
478 end = findLastChar(field);
481 /* See if this is multi-token */
494 /* These macros are for backward compatibility */
495 if (tag == RPMTAG_VERSION) {
496 if (strchr(field, '-') != NULL) {
497 rpmlog(RPMLOG_ERR, _("line %d: Illegal char '-' in %s: %s\n"),
498 spec->lineNum, "version", spec->line);
501 addMacro(spec->macros, "PACKAGE_VERSION", NULL, field, RMIL_OLDSPEC);
502 } else if (tag == RPMTAG_RELEASE) {
503 if (strchr(field, '-') != NULL) {
504 rpmlog(RPMLOG_ERR, _("line %d: Illegal char '-' in %s: %s\n"),
505 spec->lineNum, "release", spec->line);
508 addMacro(spec->macros, "PACKAGE_RELEASE", NULL, field, RMIL_OLDSPEC-1);
510 headerPutString(pkg->header, tag, field);
514 (void) stashSt(spec, pkg->header, tag, lang);
515 case RPMTAG_DISTRIBUTION:
518 case RPMTAG_PACKAGER:
520 headerPutString(pkg->header, tag, field);
521 } else if (!(noLang && strcmp(lang, RPMBUILD_DEFAULT_LANG)))
522 (void) headerAddI18NString(pkg->header, tag, field, lang);
524 case RPMTAG_BUILDROOT:
525 /* just silently ignore BuildRoot */
528 case RPMTAG_PREFIXES: {
531 addOrAppendListEntry(pkg->header, tag, field);
532 xx = headerGet(pkg->header, tag, &td, HEADERGET_MINMEM);
533 while ((str = rpmtdNextString(&td))) {
534 size_t len = strlen(str);
535 if (len > 1 && str[len-1] == '/') {
537 _("line %d: Prefixes must not end with \"/\": %s\n"),
538 spec->lineNum, spec->line);
548 if (field[0] != '/') {
550 _("line %d: Docdir must begin with '/': %s\n"),
551 spec->lineNum, spec->line);
555 delMacro(NULL, "_docdir");
556 addMacro(NULL, "_docdir", NULL, field, RMIL_SPEC);
561 if (parseUnsignedNum(field, &epoch)) {
563 _("line %d: Epoch field must be an unsigned number: %s\n"),
564 spec->lineNum, spec->line);
567 headerPutUint32(pkg->header, tag, &epoch, 1);
570 case RPMTAG_AUTOREQPROV:
571 pkg->autoReq = parseYesNo(field);
572 pkg->autoProv = pkg->autoReq;
575 pkg->autoReq = parseYesNo(field);
577 case RPMTAG_AUTOPROV:
578 pkg->autoProv = parseYesNo(field);
583 if ((rc = addSource(spec, pkg, field, tag)))
588 if ((rc = addSource(spec, pkg, field, tag)))
590 if ((rc = readIcon(pkg->header, field)))
593 case RPMTAG_NOSOURCE:
596 if ((rc = parseNoSource(spec, field, tag)))
599 case RPMTAG_BUILDPREREQ:
600 case RPMTAG_BUILDREQUIRES:
601 if ((rc = parseBits(lang, buildScriptBits, &tagflags))) {
603 _("line %d: Bad %s: qualifiers: %s\n"),
604 spec->lineNum, rpmTagGetName(tag), spec->line);
607 if ((rc = parseRCPOT(spec, pkg, field, tag, 0, tagflags)))
610 case RPMTAG_REQUIREFLAGS:
612 if ((rc = parseBits(lang, installScriptBits, &tagflags))) {
614 _("line %d: Bad %s: qualifiers: %s\n"),
615 spec->lineNum, rpmTagGetName(tag), spec->line);
618 if ((rc = parseRCPOT(spec, pkg, field, tag, 0, tagflags)))
621 case RPMTAG_BUILDCONFLICTS:
622 case RPMTAG_CONFLICTFLAGS:
623 case RPMTAG_OBSOLETEFLAGS:
624 case RPMTAG_PROVIDEFLAGS:
625 tagflags = RPMSENSE_ANY;
626 if ((rc = parseRCPOT(spec, pkg, field, tag, 0, tagflags)))
629 case RPMTAG_EXCLUDEARCH:
630 case RPMTAG_EXCLUSIVEARCH:
631 case RPMTAG_EXCLUDEOS:
632 case RPMTAG_EXCLUSIVEOS:
633 addOrAppendListEntry(spec->buildRestrictions, tag, field);
635 case RPMTAG_BUILDARCHS: {
637 const char **BANames = NULL;
638 if ((rc = poptParseArgvString(field, &BACount, &BANames))) {
640 _("line %d: Bad BuildArchitecture format: %s\n"),
641 spec->lineNum, spec->line);
644 if (spec->packages == pkg) {
645 spec->BACount = BACount;
646 spec->BANames = BANames;
648 if (BACount != 1 || strcmp(BANames[0], "noarch")) {
650 _("line %d: Only noarch subpackages are supported: %s\n"),
651 spec->lineNum, spec->line);
652 BANames = _free(BANames);
655 headerAddEntry(pkg->header, RPMTAG_ARCH, RPM_STRING_TYPE, "noarch", 1);
658 spec->BANames = _free(spec->BANames);
662 rpmlog(RPMLOG_ERR, _("Internal error: Bogus tag %d\n"), tag);
667 addMacro(spec->macros, macro, NULL, field, RMIL_SPEC);
672 /* This table has to be in a peculiar order. If one tag is the */
673 /* same as another, plus a few letters, it must come first. */
677 typedef struct PreambleRec_s {
685 /* XXX FIXME: strlen for these is calculated at runtime, preventing const */
686 static struct PreambleRec_s preambleList[] = {
687 {RPMTAG_NAME, 0, 0, 0, "name"},
688 {RPMTAG_VERSION, 0, 0, 0, "version"},
689 {RPMTAG_RELEASE, 0, 0, 0, "release"},
690 {RPMTAG_EPOCH, 0, 0, 0, "epoch"},
691 {RPMTAG_SUMMARY, 0, 1, 0, "summary"},
692 {RPMTAG_LICENSE, 0, 0, 0, "license"},
693 {RPMTAG_DISTRIBUTION, 0, 0, 0, "distribution"},
694 {RPMTAG_DISTURL, 0, 0, 0, "disturl"},
695 {RPMTAG_VENDOR, 0, 0, 0, "vendor"},
696 {RPMTAG_GROUP, 0, 1, 0, "group"},
697 {RPMTAG_PACKAGER, 0, 0, 0, "packager"},
698 {RPMTAG_URL, 0, 0, 0, "url"},
699 {RPMTAG_SOURCE, 0, 0, 0, "source"},
700 {RPMTAG_PATCH, 0, 0, 0, "patch"},
701 {RPMTAG_NOSOURCE, 0, 0, 0, "nosource"},
702 {RPMTAG_NOPATCH, 0, 0, 0, "nopatch"},
703 {RPMTAG_EXCLUDEARCH, 0, 0, 0, "excludearch"},
704 {RPMTAG_EXCLUSIVEARCH, 0, 0, 0, "exclusivearch"},
705 {RPMTAG_EXCLUDEOS, 0, 0, 0, "excludeos"},
706 {RPMTAG_EXCLUSIVEOS, 0, 0, 0, "exclusiveos"},
707 {RPMTAG_ICON, 0, 0, 0, "icon"},
708 {RPMTAG_PROVIDEFLAGS, 0, 0, 0, "provides"},
709 {RPMTAG_REQUIREFLAGS, 0, 1, 0, "requires"},
710 {RPMTAG_PREREQ, 0, 1, 0, "prereq"},
711 {RPMTAG_CONFLICTFLAGS, 0, 0, 0, "conflicts"},
712 {RPMTAG_OBSOLETEFLAGS, 0, 0, 0, "obsoletes"},
713 {RPMTAG_PREFIXES, 0, 0, 0, "prefixes"},
714 {RPMTAG_PREFIXES, 0, 0, 0, "prefix"},
715 {RPMTAG_BUILDROOT, 0, 0, 0, "buildroot"},
716 {RPMTAG_BUILDARCHS, 0, 0, 0, "buildarchitectures"},
717 {RPMTAG_BUILDARCHS, 0, 0, 0, "buildarch"},
718 {RPMTAG_BUILDCONFLICTS, 0, 0, 0, "buildconflicts"},
719 {RPMTAG_BUILDPREREQ, 0, 1, 0, "buildprereq"},
720 {RPMTAG_BUILDREQUIRES, 0, 1, 0, "buildrequires"},
721 {RPMTAG_AUTOREQPROV, 0, 0, 0, "autoreqprov"},
722 {RPMTAG_AUTOREQ, 0, 0, 0, "autoreq"},
723 {RPMTAG_AUTOPROV, 0, 0, 0, "autoprov"},
724 {RPMTAG_DOCDIR, 0, 0, 0, "docdir"},
725 {RPMTAG_DISTTAG, 0, 0, 0, "disttag"},
726 /* LCL: can't add null annotation */
732 static inline void initPreambleList(void)
735 for (p = preambleList; p->token != NULL; p++)
736 if (p->token) p->len = strlen(p->token);
741 static int findPreambleTag(rpmSpec spec,rpmTag * tag,
742 const char ** macro, char * lang)
747 if (preambleList[0].len == 0)
750 for (p = preambleList; p->token != NULL; p++) {
751 if (!(p->token && !rstrncasecmp(spec->line, p->token, p->len)))
754 rpmlog(RPMLOG_ERR, _("Legacy syntax is unsupported: %s\n"),
760 if (p == NULL || p->token == NULL)
763 s = spec->line + p->len;
766 switch (p->multiLang) {
769 /* Unless this is a source or a patch, a ':' better be next */
770 if (p->tag != RPMTAG_SOURCE && p->tag != RPMTAG_PATCH) {
771 if (*s != ':') return 1;
775 case 1: /* Parse optional ( <token> ). */
777 strcpy(lang, RPMBUILD_DEFAULT_LANG);
780 if (*s != '(') return 1;
783 while (!risspace(*s) && *s != ')')
787 if (*s != ')') return 1;
790 if (*s != ':') return 1;
800 int parsePreamble(rpmSpec spec, int initialPackage)
802 int nextPart = PART_ERROR;
803 int res = PART_ERROR; /* assume failure */
811 pkg = newPackage(spec);
813 if (! initialPackage) {
814 /* There is one option to %package: <pkg> or -n <pkg> */
815 if (parseSimplePart(spec->line, &name, &flag)) {
816 rpmlog(RPMLOG_ERR, _("Bad package specification: %s\n"),
821 if (!lookupPackage(spec, name, flag, NULL)) {
822 rpmlog(RPMLOG_ERR, _("Package already exists: %s\n"), spec->line);
827 /* Construct the package */
828 if (flag == PART_SUBNAME) {
829 const char * mainName;
830 xx = headerNVR(spec->packages->header, &mainName, NULL, NULL);
831 rasprintf(&NVR, "%s-%s", mainName, name);
835 headerPutString(pkg->header, RPMTAG_NAME, NVR);
837 NVR = xstrdup("(main package)");
840 if ((rc = readLine(spec, STRIP_TRAILINGSPACE | STRIP_COMMENTS)) > 0) {
841 nextPart = PART_NONE;
845 while (! (nextPart = isPart(spec->line))) {
849 /* Skip blank lines */
852 if (*linep != '\0') {
853 if (findPreambleTag(spec, &tag, ¯o, lang)) {
854 rpmlog(RPMLOG_ERR, _("line %d: Unknown tag: %s\n"),
855 spec->lineNum, spec->line);
858 if (handlePreambleTag(spec, pkg, tag, macro, lang)) {
861 if (spec->BANames && !spec->recursing) {
862 res = PART_BUILDARCHITECTURES;
867 readLine(spec, STRIP_TRAILINGSPACE | STRIP_COMMENTS)) > 0) {
868 nextPart = PART_NONE;
877 /* XXX Skip valid arch check if not building binary package */
878 if (!spec->anyarch && checkForValidArchitectures(spec)) {
882 if (pkg == spec->packages)
883 fillOutMainPackage(pkg->header);
885 if (checkForDuplicates(pkg->header, NVR)) {
889 if (pkg != spec->packages)
890 headerCopyTags(spec->packages->header, pkg->header,
891 (rpmTag *)copyTagsDuringParse);
893 if (checkForRequired(pkg->header, NVR)) {
897 if (getSpecialDocDir(pkg)) {
901 /* if we get down here nextPart has been set to non-error */