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[] = {
42 static const rpmTag requiredTags[] = {
54 static void addOrAppendListEntry(Header h, rpmTag tag, const char * line)
60 xx = poptParseArgvString(line, &argc, &argv);
62 xx = headerAddOrAppendEntry(h, tag, RPM_STRING_ARRAY_TYPE,
63 argv, (rpm_count_t) argc);
67 /* Parse a simple part line that only take -n <pkg> or <pkg> */
68 /* <pkg> is returned in name as a pointer into a dynamic buffer */
72 static int parseSimplePart(const char *line, char **name, int *flag)
75 char *linebuf = xstrdup(line);
77 /* Throw away the first token (the %xxxx) */
78 (void)strtok(linebuf, " \t\n");
81 if (!(tok = strtok(NULL, " \t\n"))) {
86 if (!strcmp(tok, "-n")) {
87 if (!(tok = strtok(NULL, " \t\n"))) {
97 return (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)
192 HGE_t hge = (HGE_t)headerGetEntryMinMemory;
193 HFD_t hfd = headerFreeData;
199 if (!hge(h, tag, &type, (rpm_data_t *)&names, &count))
202 if (!rstrcasecmp(names[count], name)) {
207 names = hfd(names, type);
213 static int checkForValidArchitectures(rpmSpec spec)
215 char *arch = rpmExpand("%{_target_cpu}", NULL);
216 char *os = rpmExpand("%{_target_os}", NULL);
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);
246 * Check that required tags are present in header.
248 * @param NVR package name-version-release
249 * @return RPMRC_OK if OK
251 static int checkForRequired(Header h, const char * NVR)
252 /* LCL: parse error here with modifies */
257 for (p = requiredTags; *p != 0; p++) {
258 if (!headerIsEntry(h, *p)) {
260 _("%s field must be present in package: %s\n"),
261 rpmTagGetName(*p), NVR);
270 * Check that no duplicate tags are present in header.
272 * @param NVR package name-version-release
273 * @return RPMRC_OK if OK
275 static int checkForDuplicates(Header h, const char * NVR)
281 for (hi = headerInitIterator(h), lastTag = 0;
282 headerNextIterator(hi, &tag, NULL, NULL, NULL);
287 rpmlog(RPMLOG_ERR, _("Duplicate %s entries in package: %s\n"),
288 rpmTagGetName(tag), NVR);
291 hi = headerFreeIterator(hi);
298 static struct optionalTag {
301 } const optionalTags[] = {
302 { RPMTAG_VENDOR, "%{vendor}" },
303 { RPMTAG_PACKAGER, "%{packager}" },
304 { RPMTAG_DISTRIBUTION, "%{distribution}" },
305 { RPMTAG_DISTURL, "%{disturl}" },
311 static void fillOutMainPackage(Header h)
313 const struct optionalTag *ot;
315 for (ot = optionalTags; ot->ot_mac != NULL; ot++) {
316 if (!headerIsEntry(h, ot->ot_tag)) {
317 char *val = rpmExpand(ot->ot_mac, NULL);
318 if (val && *val != '%')
319 (void) headerAddEntry(h, ot->ot_tag, RPM_STRING_TYPE, val, 1);
327 static rpmRC readIcon(Header h, const char * file)
336 /* XXX use rpmGenPath(rootdir, "%{_sourcedir}/", file) for icon path. */
337 fn = rpmGetPath("%{_sourcedir}/", file, NULL);
339 fd = Fopen(fn, "r.ufdio");
340 if (fd == NULL || Ferror(fd)) {
341 rpmlog(RPMLOG_ERR, _("Unable to open icon %s: %s\n"),
347 iconsize = (size >= 0 ? size : (8 * BUFSIZ));
354 icon = xmalloc(iconsize + 1);
357 nb = Fread(icon, sizeof(icon[0]), iconsize, fd);
358 if (Ferror(fd) || (size >= 0 && nb != size)) {
359 rpmlog(RPMLOG_ERR, _("Unable to read icon %s: %s\n"),
367 if (! strncmp(icon, "GIF", sizeof("GIF")-1)) {
368 (void) headerAddEntry(h, RPMTAG_GIF, RPM_BIN_TYPE, icon, iconsize);
369 } else if (! strncmp(icon, "/* XPM", sizeof("/* XPM")-1)) {
370 (void) headerAddEntry(h, RPMTAG_XPM, RPM_BIN_TYPE, icon, iconsize);
372 rpmlog(RPMLOG_ERR, _("Unknown icon type: %s\n"), file);
383 spectag stashSt(rpmSpec spec, Header h, rpmTag tag, const char * lang)
385 HGE_t hge = (HGE_t)headerGetEntryMinMemory;
389 spectags st = spec->st;
390 if (st->st_ntags == st->st_nalloc) {
392 st->st_t = xrealloc(st->st_t, st->st_nalloc * sizeof(*(st->st_t)));
394 t = st->st_t + st->st_ntags++;
396 t->t_startx = spec->lineNum - 1;
398 t->t_lang = xstrdup(lang);
400 if (!(t->t_lang && strcmp(t->t_lang, RPMBUILD_DEFAULT_LANG))) {
402 if (hge(h, RPMTAG_NAME, NULL, (rpm_data_t *) &n, NULL)) {
403 rasprintf(&t->t_msgid, "%s(%s)", n, rpmTagGetName(tag));
410 #define SINGLE_TOKEN_ONLY \
412 rpmlog(RPMLOG_ERR, _("line %d: Tag takes single token only: %s\n"), \
413 spec->lineNum, spec->line); \
421 static int handlePreambleTag(rpmSpec spec, Package pkg, rpmTag tag,
422 const char *macro, const char *lang)
424 HGE_t hge = (HGE_t)headerGetEntryMinMemory;
425 HFD_t hfd = headerFreeData;
426 char * field = spec->line;
430 rpmsenseFlags tagflags;
437 if (field == NULL) return RPMRC_FAIL; /* XXX can't happen */
438 /* Find the start of the "field" and strip trailing space */
439 while ((*field) && (*field != ':'))
442 rpmlog(RPMLOG_ERR, _("line %d: Malformed tag: %s\n"),
443 spec->lineNum, spec->line);
450 rpmlog(RPMLOG_ERR, _("line %d: Empty tag: %s\n"),
451 spec->lineNum, spec->line);
454 end = findLastChar(field);
457 /* See if this is multi-token */
468 case RPMTAG_RHNPLATFORM:
472 /* These macros are for backward compatibility */
473 if (tag == RPMTAG_VERSION) {
474 if (strchr(field, '-') != NULL) {
475 rpmlog(RPMLOG_ERR, _("line %d: Illegal char '-' in %s: %s\n"),
476 spec->lineNum, "version", spec->line);
479 addMacro(spec->macros, "PACKAGE_VERSION", NULL, field, RMIL_OLDSPEC);
480 } else if (tag == RPMTAG_RELEASE) {
481 if (strchr(field, '-') != NULL) {
482 rpmlog(RPMLOG_ERR, _("line %d: Illegal char '-' in %s: %s\n"),
483 spec->lineNum, "release", spec->line);
486 addMacro(spec->macros, "PACKAGE_RELEASE", NULL, field, RMIL_OLDSPEC-1);
488 (void) headerAddEntry(pkg->header, tag, RPM_STRING_TYPE, field, 1);
492 (void) stashSt(spec, pkg->header, tag, lang);
493 case RPMTAG_DISTRIBUTION:
496 case RPMTAG_PACKAGER:
498 (void) headerAddEntry(pkg->header, tag, RPM_STRING_TYPE, field, 1);
499 else if (!(noLang && strcmp(lang, RPMBUILD_DEFAULT_LANG)))
500 (void) headerAddI18NString(pkg->header, tag, field, lang);
502 case RPMTAG_BUILDROOT:
504 { char * buildRoot = NULL;
507 * Note: rpmGenPath should guarantee a "canonical" path. That means
508 * that the following pathologies should be weeded out:
511 * /.././../usr/../bin//./sh
513 if (spec->buildRoot == NULL) {
514 buildRoot = rpmGenPath(NULL, "%{?buildroot:%{buildroot}}", NULL);
515 if (strcmp(buildRoot, "/")) {
516 spec->buildRoot = buildRoot;
519 const char * specPath = field;
521 buildRoot = _free(buildRoot);
522 if (*field == '\0') field = "/";
523 buildRoot = rpmGenPath(spec->rootDir, specPath, NULL);
524 spec->buildRoot = buildRoot;
525 field = (char *) buildRoot;
527 spec->gotBuildRoot = 1;
531 buildRoot = rpmGenPath(NULL, spec->buildRoot, NULL);
532 if (*buildRoot == '\0') buildRoot = "/";
533 if (!strcmp(buildRoot, "/")) {
535 _("BuildRoot can not be \"/\": %s\n"), spec->buildRoot);
541 case RPMTAG_PREFIXES:
542 addOrAppendListEntry(pkg->header, tag, field);
543 xx = hge(pkg->header, tag, &type, (rpm_data_t *)&array, &num);
545 len = strlen(array[num]);
546 if (array[num][len - 1] == '/' && len > 1) {
548 _("line %d: Prefixes must not end with \"/\": %s\n"),
549 spec->lineNum, spec->line);
550 array = hfd(array, type);
554 array = hfd(array, type);
558 if (field[0] != '/') {
560 _("line %d: Docdir must begin with '/': %s\n"),
561 spec->lineNum, spec->line);
565 delMacro(NULL, "_docdir");
566 addMacro(NULL, "_docdir", NULL, field, RMIL_SPEC);
571 if (parseNum(field, &epoch)) {
573 _("line %d: Epoch/Serial field must be a number: %s\n"),
574 spec->lineNum, spec->line);
577 xx = headerAddEntry(pkg->header, tag, RPM_INT32_TYPE, &epoch, 1);
580 case RPMTAG_AUTOREQPROV:
581 pkg->autoReq = parseYesNo(field);
582 pkg->autoProv = pkg->autoReq;
585 pkg->autoReq = parseYesNo(field);
587 case RPMTAG_AUTOPROV:
588 pkg->autoProv = parseYesNo(field);
594 if ((rc = addSource(spec, pkg, field, tag)))
599 if ((rc = addSource(spec, pkg, field, tag)))
601 if ((rc = readIcon(pkg->header, field)))
604 case RPMTAG_NOSOURCE:
607 if ((rc = parseNoSource(spec, field, tag)))
610 case RPMTAG_BUILDPREREQ:
611 case RPMTAG_BUILDREQUIRES:
612 if ((rc = parseBits(lang, buildScriptBits, &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_REQUIREFLAGS:
623 if ((rc = parseBits(lang, installScriptBits, &tagflags))) {
625 _("line %d: Bad %s: qualifiers: %s\n"),
626 spec->lineNum, rpmTagGetName(tag), spec->line);
629 if ((rc = parseRCPOT(spec, pkg, field, tag, 0, tagflags)))
632 case RPMTAG_BUILDCONFLICTS:
633 case RPMTAG_CONFLICTFLAGS:
634 case RPMTAG_OBSOLETEFLAGS:
635 case RPMTAG_PROVIDEFLAGS:
636 tagflags = RPMSENSE_ANY;
637 if ((rc = parseRCPOT(spec, pkg, field, tag, 0, tagflags)))
640 case RPMTAG_EXCLUDEARCH:
641 case RPMTAG_EXCLUSIVEARCH:
642 case RPMTAG_EXCLUDEOS:
643 case RPMTAG_EXCLUSIVEOS:
644 addOrAppendListEntry(spec->buildRestrictions, tag, field);
646 case RPMTAG_BUILDARCHS:
647 if ((rc = poptParseArgvString(field,
649 &(spec->BANames)))) {
651 _("line %d: Bad BuildArchitecture format: %s\n"),
652 spec->lineNum, spec->line);
656 spec->BANames = _free(spec->BANames);
660 rpmlog(RPMLOG_ERR, _("Internal error: Bogus tag %d\n"), tag);
665 addMacro(spec->macros, macro, NULL, field, RMIL_SPEC);
670 /* This table has to be in a peculiar order. If one tag is the */
671 /* same as another, plus a few letters, it must come first. */
675 typedef struct PreambleRec_s {
683 /* XXX FIXME: strlen for these is calculated at runtime, preventing const */
684 static struct PreambleRec_s preambleList[] = {
685 {RPMTAG_NAME, 0, 0, 0, "name"},
686 {RPMTAG_VERSION, 0, 0, 0, "version"},
687 {RPMTAG_RELEASE, 0, 0, 0, "release"},
688 {RPMTAG_EPOCH, 0, 0, 0, "epoch"},
689 {RPMTAG_EPOCH, 0, 0, 1, "serial"},
690 {RPMTAG_SUMMARY, 0, 1, 0, "summary"},
691 {RPMTAG_LICENSE, 0, 0, 1, "copyright"},
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_RHNPLATFORM, 0, 0, 1, "rhnplatform"},
726 {RPMTAG_DISTTAG, 0, 0, 0, "disttag"},
727 {RPMTAG_CVSID, 0, 0, 0, "cvsid"},
728 {RPMTAG_SVNID, 0, 0, 0, "svnid"},
729 /* LCL: can't add null annotation */
735 static inline void initPreambleList(void)
738 for (p = preambleList; p->token != NULL; p++)
739 if (p->token) p->len = strlen(p->token);
744 static int findPreambleTag(rpmSpec spec,rpmTag * tag,
745 const char ** macro, char * lang)
750 if (preambleList[0].len == 0)
753 for (p = preambleList; p->token != NULL; p++) {
754 if (!(p->token && !rstrncasecmp(spec->line, p->token, p->len)))
757 rpmlog(RPMLOG_ERR, _("Legacy syntax is unsupported: %s\n"),
763 if (p == NULL || p->token == NULL)
766 s = spec->line + p->len;
769 switch (p->multiLang) {
772 /* Unless this is a source or a patch, a ':' better be next */
773 if (p->tag != RPMTAG_SOURCE && p->tag != RPMTAG_PATCH) {
774 if (*s != ':') return 1;
778 case 1: /* Parse optional ( <token> ). */
780 strcpy(lang, RPMBUILD_DEFAULT_LANG);
783 if (*s != '(') return 1;
786 while (!risspace(*s) && *s != ')')
790 if (*s != ')') return 1;
793 if (*s != ':') return 1;
803 int parsePreamble(rpmSpec spec, int initialPackage)
813 pkg = newPackage(spec);
815 if (! initialPackage) {
816 /* There is one option to %package: <pkg> or -n <pkg> */
817 if (parseSimplePart(spec->line, &name, &flag)) {
818 rpmlog(RPMLOG_ERR, _("Bad package specification: %s\n"),
823 if (!lookupPackage(spec, name, flag, NULL)) {
824 rpmlog(RPMLOG_ERR, _("Package already exists: %s\n"),
830 /* Construct the package */
831 if (flag == PART_SUBNAME) {
832 const char * mainName;
833 xx = headerNVR(spec->packages->header, &mainName, NULL, NULL);
834 rasprintf(&NVR, "%s-%s", mainName, name);
838 xx = headerAddEntry(pkg->header, RPMTAG_NAME, RPM_STRING_TYPE, NVR, 1);
840 NVR = xstrdup("(main package)");
843 if ((rc = readLine(spec, STRIP_TRAILINGSPACE | STRIP_COMMENTS)) > 0) {
844 nextPart = PART_NONE;
850 while (! (nextPart = isPart(spec->line))) {
854 /* Skip blank lines */
857 if (*linep != '\0') {
858 if (findPreambleTag(spec, &tag, ¯o, lang)) {
859 rpmlog(RPMLOG_ERR, _("line %d: Unknown tag: %s\n"),
860 spec->lineNum, spec->line);
864 if (handlePreambleTag(spec, pkg, tag, macro, lang)) {
868 if (spec->BANames && !spec->recursing) {
870 return PART_BUILDARCHITECTURES;
874 readLine(spec, STRIP_TRAILINGSPACE | STRIP_COMMENTS)) > 0) {
875 nextPart = PART_NONE;
885 /* Do some final processing on the header */
887 if (!spec->gotBuildRoot && spec->buildRoot) {
888 rpmlog(RPMLOG_ERR, _("Spec file can't use BuildRoot\n"));
893 /* XXX Skip valid arch check if not building binary package */
894 if (!spec->anyarch && checkForValidArchitectures(spec)) {
899 if (pkg == spec->packages)
900 fillOutMainPackage(pkg->header);
902 if (checkForDuplicates(pkg->header, NVR)) {
907 if (pkg != spec->packages)
908 headerCopyTags(spec->packages->header, pkg->header,
909 (rpmTag *)copyTagsDuringParse);
911 if (checkForRequired(pkg->header, NVR)) {