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[] = {
52 static void addOrAppendListEntry(Header h, rpmTag tag, const char * line)
58 xx = poptParseArgvString(line, &argc, &argv);
60 xx = headerAddOrAppendEntry(h, tag, RPM_STRING_ARRAY_TYPE,
61 argv, (rpm_count_t) argc);
65 /* Parse a simple part line that only take -n <pkg> or <pkg> */
66 /* <pkg> is returned in name as a pointer into a dynamic buffer */
70 static int parseSimplePart(const char *line, char **name, int *flag)
73 char *linebuf = xstrdup(line);
76 /* Throw away the first token (the %xxxx) */
77 (void)strtok(linebuf, " \t\n");
80 if (!(tok = strtok(NULL, " \t\n"))) {
85 if (!strcmp(tok, "-n")) {
86 if (!(tok = strtok(NULL, " \t\n"))) {
95 rc = strtok(NULL, " \t\n") ? 1 : 0;
104 static inline int parseYesNo(const char * s)
106 return ((!s || (s[0] == 'n' || s[0] == 'N' || s[0] == '0') ||
107 !rstrcasecmp(s, "false") || !rstrcasecmp(s, "off"))
111 typedef const struct tokenBits_s {
118 static struct tokenBits_s const installScriptBits[] = {
119 { "interp", RPMSENSE_INTERP },
120 { "prereq", RPMSENSE_PREREQ },
121 { "preun", RPMSENSE_SCRIPT_PREUN },
122 { "pre", RPMSENSE_SCRIPT_PRE },
123 { "postun", RPMSENSE_SCRIPT_POSTUN },
124 { "post", RPMSENSE_SCRIPT_POST },
125 { "rpmlib", RPMSENSE_RPMLIB },
126 { "verify", RPMSENSE_SCRIPT_VERIFY },
132 static const struct tokenBits_s const buildScriptBits[] = {
133 { "prep", RPMSENSE_SCRIPT_PREP },
134 { "build", RPMSENSE_SCRIPT_BUILD },
135 { "install", RPMSENSE_SCRIPT_INSTALL },
136 { "clean", RPMSENSE_SCRIPT_CLEAN },
142 static int parseBits(const char * s, const tokenBits tokbits,
147 rpmsenseFlags bits = RPMSENSE_ANY;
152 while ((c = *s) && risspace(c)) s++;
154 while ((c = *se) && risalpha(c)) se++;
157 for (tb = tokbits; tb->name; tb++) {
158 if (tb->name != NULL &&
159 strlen(tb->name) == (se-s) && !strncmp(tb->name, s, (se-s)))
162 if (tb->name == NULL)
165 while ((c = *se) && risspace(c)) se++;
171 if (c == 0 && bp) *bp = bits;
172 return (c ? RPMRC_FAIL : RPMRC_OK);
177 static inline char * findLastChar(char * s)
192 static int isMemberInEntry(Header h, const char *name, rpmTag tag)
194 HGE_t hge = (HGE_t)headerGetEntryMinMemory;
195 HFD_t hfd = headerFreeData;
201 if (!hge(h, tag, &type, (rpm_data_t *)&names, &count))
204 if (!rstrcasecmp(names[count], name)) {
209 names = hfd(names, type);
215 static rpmRC checkForValidArchitectures(rpmSpec spec)
217 char *arch = rpmExpand("%{_target_cpu}", NULL);
218 char *os = rpmExpand("%{_target_os}", NULL);
219 rpmRC rc = RPMRC_FAIL; /* assume failure */
221 if (isMemberInEntry(spec->buildRestrictions,
222 arch, RPMTAG_EXCLUDEARCH) == 1) {
223 rpmlog(RPMLOG_ERR, _("Architecture is excluded: %s\n"), arch);
226 if (isMemberInEntry(spec->buildRestrictions,
227 arch, RPMTAG_EXCLUSIVEARCH) == 0) {
228 rpmlog(RPMLOG_ERR, _("Architecture is not included: %s\n"), arch);
231 if (isMemberInEntry(spec->buildRestrictions,
232 os, RPMTAG_EXCLUDEOS) == 1) {
233 rpmlog(RPMLOG_ERR, _("OS is excluded: %s\n"), os);
236 if (isMemberInEntry(spec->buildRestrictions,
237 os, RPMTAG_EXCLUSIVEOS) == 0) {
238 rpmlog(RPMLOG_ERR, _("OS is not included: %s\n"), os);
251 * Check that required tags are present in header.
253 * @param NVR package name-version-release
254 * @return RPMRC_OK if OK
256 static int checkForRequired(Header h, const char * NVR)
257 /* LCL: parse error here with modifies */
262 for (p = requiredTags; *p != 0; p++) {
263 if (!headerIsEntry(h, *p)) {
265 _("%s field must be present in package: %s\n"),
266 rpmTagGetName(*p), NVR);
275 * Check that no duplicate tags are present in header.
277 * @param NVR package name-version-release
278 * @return RPMRC_OK if OK
280 static int checkForDuplicates(Header h, const char * NVR)
286 for (hi = headerInitIterator(h), lastTag = 0;
287 headerNextIterator(hi, &tag, NULL, NULL, NULL);
292 rpmlog(RPMLOG_ERR, _("Duplicate %s entries in package: %s\n"),
293 rpmTagGetName(tag), NVR);
296 hi = headerFreeIterator(hi);
303 static struct optionalTag {
306 } const optionalTags[] = {
307 { RPMTAG_VENDOR, "%{vendor}" },
308 { RPMTAG_PACKAGER, "%{packager}" },
309 { RPMTAG_DISTRIBUTION, "%{distribution}" },
310 { RPMTAG_DISTURL, "%{disturl}" },
316 static void fillOutMainPackage(Header h)
318 const struct optionalTag *ot;
320 for (ot = optionalTags; ot->ot_mac != NULL; ot++) {
321 if (!headerIsEntry(h, ot->ot_tag)) {
322 char *val = rpmExpand(ot->ot_mac, NULL);
323 if (val && *val != '%')
324 (void) headerAddEntry(h, ot->ot_tag, RPM_STRING_TYPE, val, 1);
332 static rpmRC readIcon(Header h, const char * file)
341 /* XXX use rpmGenPath(rootdir, "%{_sourcedir}/", file) for icon path. */
342 fn = rpmGetPath("%{_sourcedir}/", file, NULL);
344 fd = Fopen(fn, "r.ufdio");
345 if (fd == NULL || Ferror(fd)) {
346 rpmlog(RPMLOG_ERR, _("Unable to open icon %s: %s\n"),
352 iconsize = (size >= 0 ? size : (8 * BUFSIZ));
359 icon = xmalloc(iconsize + 1);
362 nb = Fread(icon, sizeof(icon[0]), iconsize, fd);
363 if (Ferror(fd) || (size >= 0 && nb != size)) {
364 rpmlog(RPMLOG_ERR, _("Unable to read icon %s: %s\n"),
372 if (! strncmp(icon, "GIF", sizeof("GIF")-1)) {
373 (void) headerAddEntry(h, RPMTAG_GIF, RPM_BIN_TYPE, icon, iconsize);
374 } else if (! strncmp(icon, "/* XPM", sizeof("/* XPM")-1)) {
375 (void) headerAddEntry(h, RPMTAG_XPM, RPM_BIN_TYPE, icon, iconsize);
377 rpmlog(RPMLOG_ERR, _("Unknown icon type: %s\n"), file);
388 spectag stashSt(rpmSpec spec, Header h, rpmTag tag, const char * lang)
390 HGE_t hge = (HGE_t)headerGetEntryMinMemory;
394 spectags st = spec->st;
395 if (st->st_ntags == st->st_nalloc) {
397 st->st_t = xrealloc(st->st_t, st->st_nalloc * sizeof(*(st->st_t)));
399 t = st->st_t + st->st_ntags++;
401 t->t_startx = spec->lineNum - 1;
403 t->t_lang = xstrdup(lang);
405 if (!(t->t_lang && strcmp(t->t_lang, RPMBUILD_DEFAULT_LANG))) {
407 if (hge(h, RPMTAG_NAME, NULL, (rpm_data_t *) &n, NULL)) {
408 rasprintf(&t->t_msgid, "%s(%s)", n, rpmTagGetName(tag));
415 #define SINGLE_TOKEN_ONLY \
417 rpmlog(RPMLOG_ERR, _("line %d: Tag takes single token only: %s\n"), \
418 spec->lineNum, spec->line); \
426 static int handlePreambleTag(rpmSpec spec, Package pkg, rpmTag tag,
427 const char *macro, const char *lang)
429 HGE_t hge = (HGE_t)headerGetEntryMinMemory;
430 HFD_t hfd = headerFreeData;
431 char * field = spec->line;
435 rpmsenseFlags tagflags;
442 if (field == NULL) return RPMRC_FAIL; /* XXX can't happen */
443 /* Find the start of the "field" and strip trailing space */
444 while ((*field) && (*field != ':'))
447 rpmlog(RPMLOG_ERR, _("line %d: Malformed tag: %s\n"),
448 spec->lineNum, spec->line);
455 rpmlog(RPMLOG_ERR, _("line %d: Empty tag: %s\n"),
456 spec->lineNum, spec->line);
459 end = findLastChar(field);
462 /* See if this is multi-token */
475 /* These macros are for backward compatibility */
476 if (tag == RPMTAG_VERSION) {
477 if (strchr(field, '-') != NULL) {
478 rpmlog(RPMLOG_ERR, _("line %d: Illegal char '-' in %s: %s\n"),
479 spec->lineNum, "version", spec->line);
482 addMacro(spec->macros, "PACKAGE_VERSION", NULL, field, RMIL_OLDSPEC);
483 } else if (tag == RPMTAG_RELEASE) {
484 if (strchr(field, '-') != NULL) {
485 rpmlog(RPMLOG_ERR, _("line %d: Illegal char '-' in %s: %s\n"),
486 spec->lineNum, "release", spec->line);
489 addMacro(spec->macros, "PACKAGE_RELEASE", NULL, field, RMIL_OLDSPEC-1);
491 (void) headerAddEntry(pkg->header, tag, RPM_STRING_TYPE, field, 1);
495 (void) stashSt(spec, pkg->header, tag, lang);
496 case RPMTAG_DISTRIBUTION:
499 case RPMTAG_PACKAGER:
501 (void) headerAddEntry(pkg->header, tag, RPM_STRING_TYPE, field, 1);
502 else if (!(noLang && strcmp(lang, RPMBUILD_DEFAULT_LANG)))
503 (void) headerAddI18NString(pkg->header, tag, field, lang);
505 case RPMTAG_BUILDROOT:
507 { char * buildRoot = NULL;
510 * Note: rpmGenPath should guarantee a "canonical" path. That means
511 * that the following pathologies should be weeded out:
514 * /.././../usr/../bin//./sh
516 if (spec->buildRoot == NULL) {
517 buildRoot = rpmGenPath(NULL, "%{?buildroot:%{buildroot}}", NULL);
518 if (strcmp(buildRoot, "/")) {
519 spec->buildRoot = buildRoot;
522 const char * specPath = field;
524 buildRoot = _free(buildRoot);
525 if (*field == '\0') field = "/";
526 buildRoot = rpmGenPath(spec->rootDir, specPath, NULL);
527 spec->buildRoot = buildRoot;
528 field = (char *) buildRoot;
530 spec->gotBuildRoot = 1;
534 buildRoot = rpmGenPath(NULL, spec->buildRoot, NULL);
535 if (*buildRoot == '\0') buildRoot = "/";
536 if (!strcmp(buildRoot, "/")) {
538 _("BuildRoot can not be \"/\": %s\n"), spec->buildRoot);
544 case RPMTAG_PREFIXES:
545 addOrAppendListEntry(pkg->header, tag, field);
546 xx = hge(pkg->header, tag, &type, (rpm_data_t *)&array, &num);
548 len = strlen(array[num]);
549 if (array[num][len - 1] == '/' && len > 1) {
551 _("line %d: Prefixes must not end with \"/\": %s\n"),
552 spec->lineNum, spec->line);
553 array = hfd(array, type);
557 array = hfd(array, type);
561 if (field[0] != '/') {
563 _("line %d: Docdir must begin with '/': %s\n"),
564 spec->lineNum, spec->line);
568 delMacro(NULL, "_docdir");
569 addMacro(NULL, "_docdir", NULL, field, RMIL_SPEC);
574 if (parseNum(field, &epoch)) {
576 _("line %d: Epoch field must be a number: %s\n"),
577 spec->lineNum, spec->line);
580 xx = headerAddEntry(pkg->header, tag, RPM_INT32_TYPE, &epoch, 1);
583 case RPMTAG_AUTOREQPROV:
584 pkg->autoReq = parseYesNo(field);
585 pkg->autoProv = pkg->autoReq;
588 pkg->autoReq = parseYesNo(field);
590 case RPMTAG_AUTOPROV:
591 pkg->autoProv = parseYesNo(field);
597 if ((rc = addSource(spec, pkg, field, tag)))
602 if ((rc = addSource(spec, pkg, field, tag)))
604 if ((rc = readIcon(pkg->header, field)))
607 case RPMTAG_NOSOURCE:
610 if ((rc = parseNoSource(spec, field, tag)))
613 case RPMTAG_BUILDPREREQ:
614 case RPMTAG_BUILDREQUIRES:
615 if ((rc = parseBits(lang, buildScriptBits, &tagflags))) {
617 _("line %d: Bad %s: qualifiers: %s\n"),
618 spec->lineNum, rpmTagGetName(tag), spec->line);
621 if ((rc = parseRCPOT(spec, pkg, field, tag, 0, tagflags)))
624 case RPMTAG_REQUIREFLAGS:
626 if ((rc = parseBits(lang, installScriptBits, &tagflags))) {
628 _("line %d: Bad %s: qualifiers: %s\n"),
629 spec->lineNum, rpmTagGetName(tag), spec->line);
632 if ((rc = parseRCPOT(spec, pkg, field, tag, 0, tagflags)))
635 case RPMTAG_BUILDCONFLICTS:
636 case RPMTAG_CONFLICTFLAGS:
637 case RPMTAG_OBSOLETEFLAGS:
638 case RPMTAG_PROVIDEFLAGS:
639 tagflags = RPMSENSE_ANY;
640 if ((rc = parseRCPOT(spec, pkg, field, tag, 0, tagflags)))
643 case RPMTAG_EXCLUDEARCH:
644 case RPMTAG_EXCLUSIVEARCH:
645 case RPMTAG_EXCLUDEOS:
646 case RPMTAG_EXCLUSIVEOS:
647 addOrAppendListEntry(spec->buildRestrictions, tag, field);
649 case RPMTAG_BUILDARCHS:
650 if ((rc = poptParseArgvString(field,
652 &(spec->BANames)))) {
654 _("line %d: Bad BuildArchitecture format: %s\n"),
655 spec->lineNum, spec->line);
659 spec->BANames = _free(spec->BANames);
663 rpmlog(RPMLOG_ERR, _("Internal error: Bogus tag %d\n"), tag);
668 addMacro(spec->macros, macro, NULL, field, RMIL_SPEC);
673 /* This table has to be in a peculiar order. If one tag is the */
674 /* same as another, plus a few letters, it must come first. */
678 typedef struct PreambleRec_s {
686 /* XXX FIXME: strlen for these is calculated at runtime, preventing const */
687 static struct PreambleRec_s preambleList[] = {
688 {RPMTAG_NAME, 0, 0, 0, "name"},
689 {RPMTAG_VERSION, 0, 0, 0, "version"},
690 {RPMTAG_RELEASE, 0, 0, 0, "release"},
691 {RPMTAG_EPOCH, 0, 0, 0, "epoch"},
692 {RPMTAG_SUMMARY, 0, 1, 0, "summary"},
693 {RPMTAG_LICENSE, 0, 0, 0, "license"},
694 {RPMTAG_DISTRIBUTION, 0, 0, 0, "distribution"},
695 {RPMTAG_DISTURL, 0, 0, 0, "disturl"},
696 {RPMTAG_VENDOR, 0, 0, 0, "vendor"},
697 {RPMTAG_GROUP, 0, 1, 0, "group"},
698 {RPMTAG_PACKAGER, 0, 0, 0, "packager"},
699 {RPMTAG_URL, 0, 0, 0, "url"},
700 {RPMTAG_SOURCE, 0, 0, 0, "source"},
701 {RPMTAG_PATCH, 0, 0, 0, "patch"},
702 {RPMTAG_NOSOURCE, 0, 0, 0, "nosource"},
703 {RPMTAG_NOPATCH, 0, 0, 0, "nopatch"},
704 {RPMTAG_EXCLUDEARCH, 0, 0, 0, "excludearch"},
705 {RPMTAG_EXCLUSIVEARCH, 0, 0, 0, "exclusivearch"},
706 {RPMTAG_EXCLUDEOS, 0, 0, 0, "excludeos"},
707 {RPMTAG_EXCLUSIVEOS, 0, 0, 0, "exclusiveos"},
708 {RPMTAG_ICON, 0, 0, 0, "icon"},
709 {RPMTAG_PROVIDEFLAGS, 0, 0, 0, "provides"},
710 {RPMTAG_REQUIREFLAGS, 0, 1, 0, "requires"},
711 {RPMTAG_PREREQ, 0, 1, 0, "prereq"},
712 {RPMTAG_CONFLICTFLAGS, 0, 0, 0, "conflicts"},
713 {RPMTAG_OBSOLETEFLAGS, 0, 0, 0, "obsoletes"},
714 {RPMTAG_PREFIXES, 0, 0, 0, "prefixes"},
715 {RPMTAG_PREFIXES, 0, 0, 0, "prefix"},
716 {RPMTAG_BUILDROOT, 0, 0, 0, "buildroot"},
717 {RPMTAG_BUILDARCHS, 0, 0, 0, "buildarchitectures"},
718 {RPMTAG_BUILDARCHS, 0, 0, 0, "buildarch"},
719 {RPMTAG_BUILDCONFLICTS, 0, 0, 0, "buildconflicts"},
720 {RPMTAG_BUILDPREREQ, 0, 1, 0, "buildprereq"},
721 {RPMTAG_BUILDREQUIRES, 0, 1, 0, "buildrequires"},
722 {RPMTAG_AUTOREQPROV, 0, 0, 0, "autoreqprov"},
723 {RPMTAG_AUTOREQ, 0, 0, 0, "autoreq"},
724 {RPMTAG_AUTOPROV, 0, 0, 0, "autoprov"},
725 {RPMTAG_DOCDIR, 0, 0, 0, "docdir"},
726 {RPMTAG_DISTTAG, 0, 0, 0, "disttag"},
727 /* LCL: can't add null annotation */
733 static inline void initPreambleList(void)
736 for (p = preambleList; p->token != NULL; p++)
737 if (p->token) p->len = strlen(p->token);
742 static int findPreambleTag(rpmSpec spec,rpmTag * tag,
743 const char ** macro, char * lang)
748 if (preambleList[0].len == 0)
751 for (p = preambleList; p->token != NULL; p++) {
752 if (!(p->token && !rstrncasecmp(spec->line, p->token, p->len)))
755 rpmlog(RPMLOG_ERR, _("Legacy syntax is unsupported: %s\n"),
761 if (p == NULL || p->token == NULL)
764 s = spec->line + p->len;
767 switch (p->multiLang) {
770 /* Unless this is a source or a patch, a ':' better be next */
771 if (p->tag != RPMTAG_SOURCE && p->tag != RPMTAG_PATCH) {
772 if (*s != ':') return 1;
776 case 1: /* Parse optional ( <token> ). */
778 strcpy(lang, RPMBUILD_DEFAULT_LANG);
781 if (*s != '(') return 1;
784 while (!risspace(*s) && *s != ')')
788 if (*s != ')') return 1;
791 if (*s != ':') return 1;
801 int parsePreamble(rpmSpec spec, int initialPackage)
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"),
828 /* Construct the package */
829 if (flag == PART_SUBNAME) {
830 const char * mainName;
831 xx = headerNVR(spec->packages->header, &mainName, NULL, NULL);
832 rasprintf(&NVR, "%s-%s", mainName, name);
836 xx = headerAddEntry(pkg->header, RPMTAG_NAME, RPM_STRING_TYPE, NVR, 1);
838 NVR = xstrdup("(main package)");
841 if ((rc = readLine(spec, STRIP_TRAILINGSPACE | STRIP_COMMENTS)) > 0) {
842 nextPart = PART_NONE;
848 while (! (nextPart = isPart(spec->line))) {
852 /* Skip blank lines */
855 if (*linep != '\0') {
856 if (findPreambleTag(spec, &tag, ¯o, lang)) {
857 rpmlog(RPMLOG_ERR, _("line %d: Unknown tag: %s\n"),
858 spec->lineNum, spec->line);
862 if (handlePreambleTag(spec, pkg, tag, macro, lang)) {
866 if (spec->BANames && !spec->recursing) {
868 return PART_BUILDARCHITECTURES;
872 readLine(spec, STRIP_TRAILINGSPACE | STRIP_COMMENTS)) > 0) {
873 nextPart = PART_NONE;
883 /* Do some final processing on the header */
885 if (!spec->gotBuildRoot && spec->buildRoot) {
886 rpmlog(RPMLOG_ERR, _("Spec file can't use BuildRoot\n"));
891 /* XXX Skip valid arch check if not building binary package */
892 if (!spec->anyarch && checkForValidArchitectures(spec)) {
897 if (pkg == spec->packages)
898 fillOutMainPackage(pkg->header);
900 if (checkForDuplicates(pkg->header, NVR)) {
905 if (pkg != spec->packages)
906 headerCopyTags(spec->packages->header, pkg->header,
907 (rpmTag *)copyTagsDuringParse);
909 if (checkForRequired(pkg->header, NVR)) {