2 * \file build/parsePreamble.c
3 * Parse tags in global section from spec file.
8 #include <rpm/rpmbuild.h>
9 #include <rpm/rpmlog.h>
10 #include <rpm/rpmfileutil.h>
13 #define SKIPSPACE(s) { while (*(s) && xisspace(*(s))) (s)++; }
14 #define SKIPNONSPACE(s) { while (*(s) && !xisspace(*(s))) (s)++; }
18 static rpm_tag_t copyTagsDuringParse[] = {
41 static rpm_tag_t requiredTags[] = {
53 static void addOrAppendListEntry(Header h, rpm_tag_t tag, const char * line)
59 xx = poptParseArgvString(line, &argc, &argv);
61 xx = headerAddOrAppendEntry(h, tag, RPM_STRING_ARRAY_TYPE,
62 argv, (rpm_count_t) argc);
66 /* Parse a simple part line that only take -n <pkg> or <pkg> */
67 /* <pkg> is return in name as a pointer into a static buffer */
71 static int parseSimplePart(const char *line, char **name, int *flag)
75 static char buf[BUFSIZ];
77 strcpy(linebuf, line);
79 /* Throw away the first token (the %xxxx) */
80 (void)strtok(linebuf, " \t\n");
82 if (!(tok = strtok(NULL, " \t\n"))) {
87 if (!strcmp(tok, "-n")) {
88 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 !xstrcasecmp(s, "false") || !xstrcasecmp(s, "off"))
109 typedef struct tokenBits_s {
116 static struct tokenBits_s 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 struct tokenBits_s 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) && xisspace(c)) s++;
152 while ((c = *se) && xisalpha(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) && xisspace(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, rpm_tag_t tag)
192 HGE_t hge = (HGE_t)headerGetEntryMinMemory;
193 HFD_t hfd = headerFreeData;
198 if (!hge(h, tag, &type, (void **)&names, &count))
201 if (!xstrcasecmp(names[count], name))
204 names = hfd(names, type);
205 return (count >= 0 ? 1 : 0);
210 static int checkForValidArchitectures(rpmSpec spec)
212 char *arch = rpmExpand("%{_target_cpu}", NULL);
213 char *os = rpmExpand("%{_target_os}", NULL);
215 if (isMemberInEntry(spec->buildRestrictions,
216 arch, RPMTAG_EXCLUDEARCH) == 1) {
217 rpmlog(RPMLOG_ERR, _("Architecture is excluded: %s\n"), arch);
220 if (isMemberInEntry(spec->buildRestrictions,
221 arch, RPMTAG_EXCLUSIVEARCH) == 0) {
222 rpmlog(RPMLOG_ERR, _("Architecture is not included: %s\n"), arch);
225 if (isMemberInEntry(spec->buildRestrictions,
226 os, RPMTAG_EXCLUDEOS) == 1) {
227 rpmlog(RPMLOG_ERR, _("OS is excluded: %s\n"), os);
230 if (isMemberInEntry(spec->buildRestrictions,
231 os, RPMTAG_EXCLUSIVEOS) == 0) {
232 rpmlog(RPMLOG_ERR, _("OS is not included: %s\n"), os);
243 * Check that required tags are present in header.
245 * @param NVR package name-version-release
246 * @return RPMRC_OK if OK
248 static int checkForRequired(Header h, const char * NVR)
249 /* LCL: parse error here with modifies */
254 for (p = requiredTags; *p != 0; p++) {
255 if (!headerIsEntry(h, *p)) {
257 _("%s field must be present in package: %s\n"),
258 rpmTagGetName(*p), NVR);
267 * Check that no duplicate tags are present in header.
269 * @param NVR package name-version-release
270 * @return RPMRC_OK if OK
272 static int checkForDuplicates(Header h, const char * NVR)
275 rpm_tag_t lastTag, tag;
278 for (hi = headerInitIterator(h), lastTag = 0;
279 headerNextIterator(hi, &tag, NULL, NULL, NULL);
284 rpmlog(RPMLOG_ERR, _("Duplicate %s entries in package: %s\n"),
285 rpmTagGetName(tag), NVR);
288 hi = headerFreeIterator(hi);
295 static struct optionalTag {
299 { RPMTAG_VENDOR, "%{vendor}" },
300 { RPMTAG_PACKAGER, "%{packager}" },
301 { RPMTAG_DISTRIBUTION, "%{distribution}" },
302 { RPMTAG_DISTURL, "%{disturl}" },
308 static void fillOutMainPackage(Header h)
310 struct optionalTag *ot;
312 for (ot = optionalTags; ot->ot_mac != NULL; ot++) {
313 if (!headerIsEntry(h, ot->ot_tag)) {
314 char *val = rpmExpand(ot->ot_mac, NULL);
315 if (val && *val != '%')
316 (void) headerAddEntry(h, ot->ot_tag, RPM_STRING_TYPE, (void *)val, 1);
324 static rpmRC readIcon(Header h, const char * file)
333 /* XXX use rpmGenPath(rootdir, "%{_sourcedir}/", file) for icon path. */
334 fn = rpmGetPath("%{_sourcedir}/", file, NULL);
336 fd = Fopen(fn, "r.ufdio");
337 if (fd == NULL || Ferror(fd)) {
338 rpmlog(RPMLOG_ERR, _("Unable to open icon %s: %s\n"),
344 iconsize = (size >= 0 ? size : (8 * BUFSIZ));
351 icon = xmalloc(iconsize + 1);
354 nb = Fread(icon, sizeof(icon[0]), iconsize, fd);
355 if (Ferror(fd) || (size >= 0 && nb != size)) {
356 rpmlog(RPMLOG_ERR, _("Unable to read icon %s: %s\n"),
364 if (! strncmp(icon, "GIF", sizeof("GIF")-1)) {
365 (void) headerAddEntry(h, RPMTAG_GIF, RPM_BIN_TYPE, icon, iconsize);
366 } else if (! strncmp(icon, "/* XPM", sizeof("/* XPM")-1)) {
367 (void) headerAddEntry(h, RPMTAG_XPM, RPM_BIN_TYPE, icon, iconsize);
369 rpmlog(RPMLOG_ERR, _("Unknown icon type: %s\n"), file);
380 spectag stashSt(rpmSpec spec, Header h, rpm_tag_t tag, const char * lang)
382 HGE_t hge = (HGE_t)headerGetEntryMinMemory;
386 spectags st = spec->st;
387 if (st->st_ntags == st->st_nalloc) {
389 st->st_t = xrealloc(st->st_t, st->st_nalloc * sizeof(*(st->st_t)));
391 t = st->st_t + st->st_ntags++;
393 t->t_startx = spec->lineNum - 1;
395 t->t_lang = xstrdup(lang);
397 if (!(t->t_lang && strcmp(t->t_lang, RPMBUILD_DEFAULT_LANG))) {
399 if (hge(h, RPMTAG_NAME, NULL, (void **) &n, NULL)) {
401 sprintf(buf, "%s(%s)", n, rpmTagGetName(tag));
402 t->t_msgid = xstrdup(buf);
409 #define SINGLE_TOKEN_ONLY \
411 rpmlog(RPMLOG_ERR, _("line %d: Tag takes single token only: %s\n"), \
412 spec->lineNum, spec->line); \
420 static int handlePreambleTag(rpmSpec spec, Package pkg, rpm_tag_t tag,
421 const char *macro, const char *lang)
423 HGE_t hge = (HGE_t)headerGetEntryMinMemory;
424 HFD_t hfd = headerFreeData;
425 char * field = spec->line;
429 rpmsenseFlags tagflags;
436 if (field == NULL) return RPMRC_FAIL; /* XXX can't happen */
437 /* Find the start of the "field" and strip trailing space */
438 while ((*field) && (*field != ':'))
441 rpmlog(RPMLOG_ERR, _("line %d: Malformed tag: %s\n"),
442 spec->lineNum, spec->line);
449 rpmlog(RPMLOG_ERR, _("line %d: Empty tag: %s\n"),
450 spec->lineNum, spec->line);
453 end = findLastChar(field);
456 /* See if this is multi-token */
467 case RPMTAG_RHNPLATFORM:
471 /* These macros are for backward compatibility */
472 if (tag == RPMTAG_VERSION) {
473 if (strchr(field, '-') != NULL) {
474 rpmlog(RPMLOG_ERR, _("line %d: Illegal char '-' in %s: %s\n"),
475 spec->lineNum, "version", spec->line);
478 addMacro(spec->macros, "PACKAGE_VERSION", NULL, field, RMIL_OLDSPEC);
479 } else if (tag == RPMTAG_RELEASE) {
480 if (strchr(field, '-') != NULL) {
481 rpmlog(RPMLOG_ERR, _("line %d: Illegal char '-' in %s: %s\n"),
482 spec->lineNum, "release", spec->line);
485 addMacro(spec->macros, "PACKAGE_RELEASE", NULL, field, RMIL_OLDSPEC-1);
487 (void) headerAddEntry(pkg->header, tag, RPM_STRING_TYPE, field, 1);
491 (void) stashSt(spec, pkg->header, tag, lang);
492 case RPMTAG_DISTRIBUTION:
495 case RPMTAG_PACKAGER:
497 (void) headerAddEntry(pkg->header, tag, RPM_STRING_TYPE, field, 1);
498 else if (!(noLang && strcmp(lang, RPMBUILD_DEFAULT_LANG)))
499 (void) headerAddI18NString(pkg->header, tag, field, lang);
501 case RPMTAG_BUILDROOT:
503 { const 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->buildRootURL == NULL) {
514 buildRootURL = rpmGenPath(NULL, "%{?buildroot:%{buildroot}}", NULL);
515 if (strcmp(buildRootURL, "/")) {
516 spec->buildRootURL = buildRootURL;
519 const char * specURL = field;
521 buildRootURL = _free(buildRootURL);
522 (void) urlPath(specURL, (const char **)&field);
523 if (*field == '\0') field = "/";
524 buildRootURL = rpmGenPath(spec->rootURL, field, NULL);
525 spec->buildRootURL = buildRootURL;
526 field = (char *) buildRootURL;
528 spec->gotBuildRootURL = 1;
532 buildRootURL = rpmGenPath(NULL, spec->buildRootURL, NULL);
533 (void) urlPath(buildRootURL, &buildRoot);
534 if (*buildRoot == '\0') buildRoot = "/";
535 if (!strcmp(buildRoot, "/")) {
537 _("BuildRoot can not be \"/\": %s\n"), spec->buildRootURL);
538 buildRootURL = _free(buildRootURL);
541 buildRootURL = _free(buildRootURL);
543 case RPMTAG_PREFIXES:
544 addOrAppendListEntry(pkg->header, tag, field);
545 xx = hge(pkg->header, tag, &type, (void **)&array, &num);
547 len = strlen(array[num]);
548 if (array[num][len - 1] == '/' && len > 1) {
550 _("line %d: Prefixes must not end with \"/\": %s\n"),
551 spec->lineNum, spec->line);
552 array = hfd(array, type);
556 array = hfd(array, type);
560 if (field[0] != '/') {
562 _("line %d: Docdir must begin with '/': %s\n"),
563 spec->lineNum, spec->line);
567 delMacro(NULL, "_docdir");
568 addMacro(NULL, "_docdir", NULL, field, RMIL_SPEC);
573 if (parseNum(field, &epoch)) {
575 _("line %d: Epoch/Serial field must be a number: %s\n"),
576 spec->lineNum, spec->line);
579 xx = headerAddEntry(pkg->header, tag, RPM_INT32_TYPE, &epoch, 1);
582 case RPMTAG_AUTOREQPROV:
583 pkg->autoReq = parseYesNo(field);
584 pkg->autoProv = pkg->autoReq;
587 pkg->autoReq = parseYesNo(field);
589 case RPMTAG_AUTOPROV:
590 pkg->autoProv = parseYesNo(field);
596 if ((rc = addSource(spec, pkg, field, tag)))
601 if ((rc = addSource(spec, pkg, field, tag)))
603 if ((rc = readIcon(pkg->header, field)))
606 case RPMTAG_NOSOURCE:
609 if ((rc = parseNoSource(spec, field, tag)))
612 case RPMTAG_BUILDPREREQ:
613 case RPMTAG_BUILDREQUIRES:
614 if ((rc = parseBits(lang, buildScriptBits, &tagflags))) {
616 _("line %d: Bad %s: qualifiers: %s\n"),
617 spec->lineNum, rpmTagGetName(tag), spec->line);
620 if ((rc = parseRCPOT(spec, pkg, field, tag, 0, tagflags)))
623 case RPMTAG_REQUIREFLAGS:
625 if ((rc = parseBits(lang, installScriptBits, &tagflags))) {
627 _("line %d: Bad %s: qualifiers: %s\n"),
628 spec->lineNum, rpmTagGetName(tag), spec->line);
631 if ((rc = parseRCPOT(spec, pkg, field, tag, 0, tagflags)))
634 case RPMTAG_BUILDCONFLICTS:
635 case RPMTAG_CONFLICTFLAGS:
636 case RPMTAG_OBSOLETEFLAGS:
637 case RPMTAG_PROVIDEFLAGS:
638 tagflags = RPMSENSE_ANY;
639 if ((rc = parseRCPOT(spec, pkg, field, tag, 0, tagflags)))
642 case RPMTAG_EXCLUDEARCH:
643 case RPMTAG_EXCLUSIVEARCH:
644 case RPMTAG_EXCLUDEOS:
645 case RPMTAG_EXCLUSIVEOS:
646 addOrAppendListEntry(spec->buildRestrictions, tag, field);
648 case RPMTAG_BUILDARCHS:
649 if ((rc = poptParseArgvString(field,
651 &(spec->BANames)))) {
653 _("line %d: Bad BuildArchitecture format: %s\n"),
654 spec->lineNum, spec->line);
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 static struct PreambleRec_s preambleList[] = {
686 {RPMTAG_NAME, 0, 0, 0, "name"},
687 {RPMTAG_VERSION, 0, 0, 0, "version"},
688 {RPMTAG_RELEASE, 0, 0, 0, "release"},
689 {RPMTAG_EPOCH, 0, 0, 0, "epoch"},
690 {RPMTAG_EPOCH, 0, 0, 1, "serial"},
691 {RPMTAG_SUMMARY, 0, 1, 0, "summary"},
692 {RPMTAG_LICENSE, 0, 0, 1, "copyright"},
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_RHNPLATFORM, 0, 0, 1, "rhnplatform"},
727 {RPMTAG_DISTTAG, 0, 0, 0, "disttag"},
728 {RPMTAG_CVSID, 0, 0, 0, "cvsid"},
729 {RPMTAG_SVNID, 0, 0, 0, "svnid"},
730 /* LCL: can't add null annotation */
736 static inline void initPreambleList(void)
739 for (p = preambleList; p->token != NULL; p++)
740 if (p->token) p->len = strlen(p->token);
745 static int findPreambleTag(rpmSpec spec,rpm_tag_t * tag,
746 const char ** macro, char * lang)
751 if (preambleList[0].len == 0)
754 for (p = preambleList; p->token != NULL; p++) {
755 if (!(p->token && !xstrncasecmp(spec->line, p->token, p->len)))
758 rpmlog(RPMLOG_ERR, _("Legacy syntax is unsupported: %s\n"),
764 if (p == NULL || p->token == NULL)
767 s = spec->line + p->len;
770 switch (p->multiLang) {
773 /* Unless this is a source or a patch, a ':' better be next */
774 if (p->tag != RPMTAG_SOURCE && p->tag != RPMTAG_PATCH) {
775 if (*s != ':') return 1;
779 case 1: /* Parse optional ( <token> ). */
781 strcpy(lang, RPMBUILD_DEFAULT_LANG);
784 if (*s != '(') return 1;
787 while (!xisspace(*s) && *s != ')')
791 if (*s != ')') return 1;
794 if (*s != ':') return 1;
804 int parsePreamble(rpmSpec spec, int initialPackage)
814 strcpy(NVR, "(main package)");
816 pkg = newPackage(spec);
818 if (! initialPackage) {
819 /* There is one option to %package: <pkg> or -n <pkg> */
820 if (parseSimplePart(spec->line, &name, &flag)) {
821 rpmlog(RPMLOG_ERR, _("Bad package specification: %s\n"),
826 if (!lookupPackage(spec, name, flag, NULL)) {
827 rpmlog(RPMLOG_ERR, _("Package already exists: %s\n"),
832 /* Construct the package */
833 if (flag == PART_SUBNAME) {
834 const char * mainName;
835 xx = headerNVR(spec->packages->header, &mainName, NULL, NULL);
836 sprintf(NVR, "%s-%s", mainName, name);
839 xx = headerAddEntry(pkg->header, RPMTAG_NAME, RPM_STRING_TYPE, NVR, 1);
842 if ((rc = readLine(spec, STRIP_TRAILINGSPACE | STRIP_COMMENTS)) > 0) {
843 nextPart = PART_NONE;
847 while (! (nextPart = isPart(spec->line))) {
851 /* Skip blank lines */
854 if (*linep != '\0') {
855 if (findPreambleTag(spec, &tag, ¯o, lang)) {
856 rpmlog(RPMLOG_ERR, _("line %d: Unknown tag: %s\n"),
857 spec->lineNum, spec->line);
860 if (handlePreambleTag(spec, pkg, tag, macro, lang))
862 if (spec->BANames && !spec->recursing)
863 return PART_BUILDARCHITECTURES;
866 readLine(spec, STRIP_TRAILINGSPACE | STRIP_COMMENTS)) > 0) {
867 nextPart = PART_NONE;
875 /* Do some final processing on the header */
877 if (!spec->gotBuildRootURL && spec->buildRootURL) {
878 rpmlog(RPMLOG_ERR, _("Spec file can't use BuildRoot\n"));
882 /* XXX Skip valid arch check if not building binary package */
883 if (!spec->anyarch && checkForValidArchitectures(spec))
886 if (pkg == spec->packages)
887 fillOutMainPackage(pkg->header);
889 if (checkForDuplicates(pkg->header, NVR))
892 if (pkg != spec->packages)
893 headerCopyTags(spec->packages->header, pkg->header,
894 (rpm_tag_t *)copyTagsDuringParse);
896 if (checkForRequired(pkg->header, NVR))