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[] = {
53 static void addOrAppendListEntry(Header h, rpmTag tag, const char * line)
59 xx = poptParseArgvString(line, &argc, &argv);
61 headerPutStringArray(h, tag, argv, 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 (rstreq(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) && rstreqn(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)
198 if (!headerGet(h, tag, &td, HEADERGET_MINMEM))
201 while ((str = rpmtdNextString(&td))) {
202 if (!rstrcasecmp(str, name)) {
214 static rpmRC checkForValidArchitectures(rpmSpec spec)
216 char *arch = rpmExpand("%{_target_cpu}", NULL);
217 char *os = rpmExpand("%{_target_os}", NULL);
218 rpmRC rc = RPMRC_FAIL; /* assume failure */
220 if (isMemberInEntry(spec->buildRestrictions,
221 arch, RPMTAG_EXCLUDEARCH) == 1) {
222 rpmlog(RPMLOG_ERR, _("Architecture is excluded: %s\n"), arch);
225 if (isMemberInEntry(spec->buildRestrictions,
226 arch, RPMTAG_EXCLUSIVEARCH) == 0) {
227 rpmlog(RPMLOG_ERR, _("Architecture is not included: %s\n"), arch);
230 if (isMemberInEntry(spec->buildRestrictions,
231 os, RPMTAG_EXCLUDEOS) == 1) {
232 rpmlog(RPMLOG_ERR, _("OS is excluded: %s\n"), os);
235 if (isMemberInEntry(spec->buildRestrictions,
236 os, RPMTAG_EXCLUSIVEOS) == 0) {
237 rpmlog(RPMLOG_ERR, _("OS is not included: %s\n"), os);
250 * Check that required tags are present in header.
252 * @param NVR package name-version-release
253 * @return RPMRC_OK if OK
255 static int checkForRequired(Header h, const char * NVR)
260 for (p = requiredTags; *p != 0; p++) {
261 if (!headerIsEntry(h, *p)) {
263 _("%s field must be present in package: %s\n"),
264 rpmTagGetName(*p), NVR);
273 * Check that no duplicate tags are present in header.
275 * @param NVR package name-version-release
276 * @return RPMRC_OK if OK
278 static int checkForDuplicates(Header h, const char * NVR)
285 for (hi = headerInitIterator(h), lastTag = 0;
286 headerNext(hi, &td), tag = rpmtdTag(&td);
289 if (tag == lastTag) {
290 rpmlog(RPMLOG_ERR, _("Duplicate %s entries in package: %s\n"),
291 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}" },
311 { RPMTAG_BUGURL, "%{bugurl}" },
317 static void fillOutMainPackage(Header h)
319 const struct optionalTag *ot;
321 for (ot = optionalTags; ot->ot_mac != NULL; ot++) {
322 if (!headerIsEntry(h, ot->ot_tag)) {
323 char *val = rpmExpand(ot->ot_mac, NULL);
324 if (val && *val != '%') {
325 headerPutString(h, ot->ot_tag, val);
332 static int getSpecialDocDir(Package pkg)
334 const char *errstr, *docdir_fmt = "%{NAME}-%{VERSION}";
335 char *fmt_macro, *fmt;
338 fmt_macro = rpmExpand("%{?_docdir_fmt}", NULL);
339 if (fmt_macro && strlen(fmt_macro) > 0) {
340 docdir_fmt = fmt_macro;
342 fmt = headerFormat(pkg->header, docdir_fmt, &errstr);
344 rpmlog(RPMLOG_ERR, _("illegal _docdir_fmt: %s\n"), errstr);
347 pkg->specialDocDir = rpmGetPath("%{_docdir}/", fmt, NULL);
358 static rpmRC readIcon(Header h, const char * file)
367 /* XXX use rpmGenPath(rootdir, "%{_sourcedir}/", file) for icon path. */
368 fn = rpmGetPath("%{_sourcedir}/", file, NULL);
370 fd = Fopen(fn, "r.ufdio");
371 if (fd == NULL || Ferror(fd)) {
372 rpmlog(RPMLOG_ERR, _("Unable to open icon %s: %s\n"),
378 iconsize = (size >= 0 ? size : (8 * BUFSIZ));
385 icon = xmalloc(iconsize + 1);
388 nb = Fread(icon, sizeof(icon[0]), iconsize, fd);
389 if (Ferror(fd) || (size >= 0 && nb != size)) {
390 rpmlog(RPMLOG_ERR, _("Unable to read icon %s: %s\n"),
398 if (rstreqn((char*)icon, "GIF", sizeof("GIF")-1)) {
399 headerPutBin(h, RPMTAG_GIF, icon, iconsize);
400 } else if (rstreqn((char*)icon, "/* XPM", sizeof("/* XPM")-1)) {
401 headerPutBin(h, RPMTAG_XPM, icon, iconsize);
403 rpmlog(RPMLOG_ERR, _("Unknown icon type: %s\n"), file);
414 spectag stashSt(rpmSpec spec, Header h, rpmTag tag, const char * lang)
419 spectags st = spec->st;
420 if (st->st_ntags == st->st_nalloc) {
422 st->st_t = xrealloc(st->st_t, st->st_nalloc * sizeof(*(st->st_t)));
424 t = st->st_t + st->st_ntags++;
426 t->t_startx = spec->lineNum - 1;
428 t->t_lang = xstrdup(lang);
430 if (!(t->t_lang && !rstreq(t->t_lang, RPMBUILD_DEFAULT_LANG))) {
431 rasprintf(&t->t_msgid, "%s(%s)",
432 headerGetString(h, RPMTAG_NAME), rpmTagGetName(tag));
438 #define SINGLE_TOKEN_ONLY \
440 rpmlog(RPMLOG_ERR, _("line %d: Tag takes single token only: %s\n"), \
441 spec->lineNum, spec->line); \
448 * Check for inappropriate characters. All alphanums are considered sane.
450 * @param field string to check
451 * @param fsize size of string to check
452 * @param whitelist string of permitted characters
453 * @return RPMRC_OK if OK
455 rpmRC rpmCharCheck(rpmSpec spec, char *field, size_t fsize, const char *whitelist)
457 char *ch, *stop = &field[fsize];
459 for (ch=field; *ch && ch < stop; ch++) {
460 if (risalnum(*ch) || strchr(whitelist, *ch)) continue;
462 rpmlog(RPMLOG_ERR, _("line %d: Illegal char '%c' in: %s\n"),
463 spec->lineNum, *ch, spec->line);
465 rpmlog(RPMLOG_ERR, _("line %d: Illegal char in: %s\n"),
466 spec->lineNum, spec->line);
470 if (strstr(field, "..") != NULL) {
471 rpmlog(RPMLOG_ERR, _("line %d: Illegal sequence \"..\" in: %s\n"),
472 spec->lineNum, spec->line);
481 static int handlePreambleTag(rpmSpec spec, Package pkg, rpmTag tag,
482 const char *macro, const char *lang)
484 char * field = spec->line;
487 rpmsenseFlags tagflags;
491 if (field == NULL) return RPMRC_FAIL; /* XXX can't happen */
492 /* Find the start of the "field" and strip trailing space */
493 while ((*field) && (*field != ':'))
496 rpmlog(RPMLOG_ERR, _("line %d: Malformed tag: %s\n"),
497 spec->lineNum, spec->line);
504 rpmlog(RPMLOG_ERR, _("line %d: Empty tag: %s\n"),
505 spec->lineNum, spec->line);
508 end = findLastChar(field);
511 /* See if this is multi-token */
520 if (rpmCharCheck(spec, field, strlen(field), ".-_+") != RPMRC_OK) return RPMRC_FAIL;
521 headerPutString(pkg->header, tag, field);
526 if (rpmCharCheck(spec, field, strlen(field), "._+") != RPMRC_OK) return RPMRC_FAIL;
527 headerPutString(pkg->header, tag, field);
533 headerPutString(pkg->header, tag, field);
537 (void) stashSt(spec, pkg->header, tag, lang);
538 case RPMTAG_DISTRIBUTION:
541 case RPMTAG_PACKAGER:
543 headerPutString(pkg->header, tag, field);
544 } else if (!(noLang && !rstreq(lang, RPMBUILD_DEFAULT_LANG)))
545 (void) headerAddI18NString(pkg->header, tag, field, lang);
547 case RPMTAG_BUILDROOT:
548 /* just silently ignore BuildRoot */
551 case RPMTAG_PREFIXES: {
554 addOrAppendListEntry(pkg->header, tag, field);
555 xx = headerGet(pkg->header, tag, &td, HEADERGET_MINMEM);
556 while ((str = rpmtdNextString(&td))) {
557 size_t len = strlen(str);
558 if (len > 1 && str[len-1] == '/') {
560 _("line %d: Prefixes must not end with \"/\": %s\n"),
561 spec->lineNum, spec->line);
571 if (field[0] != '/') {
573 _("line %d: Docdir must begin with '/': %s\n"),
574 spec->lineNum, spec->line);
578 delMacro(NULL, "_docdir");
579 addMacro(NULL, "_docdir", NULL, field, RMIL_SPEC);
584 if (parseUnsignedNum(field, &epoch)) {
586 _("line %d: Epoch field must be an unsigned number: %s\n"),
587 spec->lineNum, spec->line);
590 headerPutUint32(pkg->header, tag, &epoch, 1);
593 case RPMTAG_AUTOREQPROV:
594 pkg->autoReq = parseYesNo(field);
595 pkg->autoProv = pkg->autoReq;
598 pkg->autoReq = parseYesNo(field);
600 case RPMTAG_AUTOPROV:
601 pkg->autoProv = parseYesNo(field);
606 if ((rc = addSource(spec, pkg, field, tag)))
611 if ((rc = addSource(spec, pkg, field, tag)))
613 if ((rc = readIcon(pkg->header, field)))
616 case RPMTAG_NOSOURCE:
619 if ((rc = parseNoSource(spec, field, tag)))
622 case RPMTAG_BUILDPREREQ:
623 case RPMTAG_BUILDREQUIRES:
624 if ((rc = parseBits(lang, buildScriptBits, &tagflags))) {
626 _("line %d: Bad %s: qualifiers: %s\n"),
627 spec->lineNum, rpmTagGetName(tag), spec->line);
630 if ((rc = parseRCPOT(spec, pkg, field, tag, 0, tagflags)))
633 case RPMTAG_REQUIREFLAGS:
635 if ((rc = parseBits(lang, installScriptBits, &tagflags))) {
637 _("line %d: Bad %s: qualifiers: %s\n"),
638 spec->lineNum, rpmTagGetName(tag), spec->line);
641 if ((rc = parseRCPOT(spec, pkg, field, tag, 0, tagflags)))
644 case RPMTAG_BUILDCONFLICTS:
645 case RPMTAG_CONFLICTFLAGS:
646 case RPMTAG_OBSOLETEFLAGS:
647 case RPMTAG_PROVIDEFLAGS:
648 tagflags = RPMSENSE_ANY;
649 if ((rc = parseRCPOT(spec, pkg, field, tag, 0, tagflags)))
652 case RPMTAG_EXCLUDEARCH:
653 case RPMTAG_EXCLUSIVEARCH:
654 case RPMTAG_EXCLUDEOS:
655 case RPMTAG_EXCLUSIVEOS:
656 addOrAppendListEntry(spec->buildRestrictions, tag, field);
658 case RPMTAG_BUILDARCHS: {
660 const char **BANames = NULL;
661 if ((rc = poptParseArgvString(field, &BACount, &BANames))) {
663 _("line %d: Bad BuildArchitecture format: %s\n"),
664 spec->lineNum, spec->line);
667 if (spec->packages == pkg) {
668 spec->BACount = BACount;
669 spec->BANames = BANames;
671 if (BACount != 1 || !rstreq(BANames[0], "noarch")) {
673 _("line %d: Only noarch subpackages are supported: %s\n"),
674 spec->lineNum, spec->line);
675 BANames = _free(BANames);
678 headerPutString(pkg->header, RPMTAG_ARCH, "noarch");
681 spec->BANames = _free(spec->BANames);
685 rpmlog(RPMLOG_ERR, _("Internal error: Bogus tag %d\n"), tag);
690 addMacro(spec->macros, macro, NULL, field, RMIL_SPEC);
695 /* This table has to be in a peculiar order. If one tag is the */
696 /* same as another, plus a few letters, it must come first. */
700 typedef const struct PreambleRec_s {
708 #define LEN_AND_STR(_tag) (sizeof(_tag)-1), _tag
710 static struct PreambleRec_s const preambleList[] = {
711 {RPMTAG_NAME, 0, 0, LEN_AND_STR("name")},
712 {RPMTAG_VERSION, 0, 0, LEN_AND_STR("version")},
713 {RPMTAG_RELEASE, 0, 0, LEN_AND_STR("release")},
714 {RPMTAG_EPOCH, 0, 0, LEN_AND_STR("epoch")},
715 {RPMTAG_SUMMARY, 1, 0, LEN_AND_STR("summary")},
716 {RPMTAG_LICENSE, 0, 0, LEN_AND_STR("license")},
717 {RPMTAG_DISTRIBUTION, 0, 0, LEN_AND_STR("distribution")},
718 {RPMTAG_DISTURL, 0, 0, LEN_AND_STR("disturl")},
719 {RPMTAG_VENDOR, 0, 0, LEN_AND_STR("vendor")},
720 {RPMTAG_GROUP, 1, 0, LEN_AND_STR("group")},
721 {RPMTAG_PACKAGER, 0, 0, LEN_AND_STR("packager")},
722 {RPMTAG_URL, 0, 0, LEN_AND_STR("url")},
723 {RPMTAG_SOURCE, 0, 0, LEN_AND_STR("source")},
724 {RPMTAG_PATCH, 0, 0, LEN_AND_STR("patch")},
725 {RPMTAG_NOSOURCE, 0, 0, LEN_AND_STR("nosource")},
726 {RPMTAG_NOPATCH, 0, 0, LEN_AND_STR("nopatch")},
727 {RPMTAG_EXCLUDEARCH, 0, 0, LEN_AND_STR("excludearch")},
728 {RPMTAG_EXCLUSIVEARCH, 0, 0, LEN_AND_STR("exclusivearch")},
729 {RPMTAG_EXCLUDEOS, 0, 0, LEN_AND_STR("excludeos")},
730 {RPMTAG_EXCLUSIVEOS, 0, 0, LEN_AND_STR("exclusiveos")},
731 {RPMTAG_ICON, 0, 0, LEN_AND_STR("icon")},
732 {RPMTAG_PROVIDEFLAGS, 0, 0, LEN_AND_STR("provides")},
733 {RPMTAG_REQUIREFLAGS, 1, 0, LEN_AND_STR("requires")},
734 {RPMTAG_PREREQ, 1, 1, LEN_AND_STR("prereq")},
735 {RPMTAG_CONFLICTFLAGS, 0, 0, LEN_AND_STR("conflicts")},
736 {RPMTAG_OBSOLETEFLAGS, 0, 0, LEN_AND_STR("obsoletes")},
737 {RPMTAG_PREFIXES, 0, 0, LEN_AND_STR("prefixes")},
738 {RPMTAG_PREFIXES, 0, 0, LEN_AND_STR("prefix")},
739 {RPMTAG_BUILDROOT, 0, 0, LEN_AND_STR("buildroot")},
740 {RPMTAG_BUILDARCHS, 0, 0, LEN_AND_STR("buildarchitectures")},
741 {RPMTAG_BUILDARCHS, 0, 0, LEN_AND_STR("buildarch")},
742 {RPMTAG_BUILDCONFLICTS, 0, 0, LEN_AND_STR("buildconflicts")},
743 {RPMTAG_BUILDPREREQ, 1, 1, LEN_AND_STR("buildprereq")},
744 {RPMTAG_BUILDREQUIRES, 1, 0, LEN_AND_STR("buildrequires")},
745 {RPMTAG_AUTOREQPROV, 0, 0, LEN_AND_STR("autoreqprov")},
746 {RPMTAG_AUTOREQ, 0, 0, LEN_AND_STR("autoreq")},
747 {RPMTAG_AUTOPROV, 0, 0, LEN_AND_STR("autoprov")},
748 {RPMTAG_DOCDIR, 0, 0, LEN_AND_STR("docdir")},
749 {RPMTAG_DISTTAG, 0, 0, LEN_AND_STR("disttag")},
750 {RPMTAG_BUGURL, 0, 0, LEN_AND_STR("bugurl")},
756 static int findPreambleTag(rpmSpec spec,rpmTag * tag,
757 const char ** macro, char * lang)
762 for (p = preambleList; p->token != NULL; p++) {
763 if (!(p->token && !rstrncasecmp(spec->line, p->token, p->len)))
766 rpmlog(RPMLOG_WARNING, _("line %d: %s is deprecated: %s\n"),
767 spec->lineNum, p->token, spec->line);
771 if (p == NULL || p->token == NULL)
774 s = spec->line + p->len;
777 switch (p->multiLang) {
780 /* Unless this is a source or a patch, a ':' better be next */
781 if (p->tag != RPMTAG_SOURCE && p->tag != RPMTAG_PATCH) {
782 if (*s != ':') return 1;
786 case 1: /* Parse optional ( <token> ). */
788 strcpy(lang, RPMBUILD_DEFAULT_LANG);
791 if (*s != '(') return 1;
794 while (!risspace(*s) && *s != ')')
798 if (*s != ')') return 1;
801 if (*s != ':') return 1;
811 int parsePreamble(rpmSpec spec, int initialPackage)
813 int nextPart = PART_ERROR;
814 int res = PART_ERROR; /* assume failure */
822 pkg = newPackage(spec);
824 if (! initialPackage) {
825 /* There is one option to %package: <pkg> or -n <pkg> */
826 if (parseSimplePart(spec->line, &name, &flag)) {
827 rpmlog(RPMLOG_ERR, _("Bad package specification: %s\n"),
832 if (!lookupPackage(spec, name, flag, NULL)) {
833 rpmlog(RPMLOG_ERR, _("Package already exists: %s\n"), spec->line);
838 /* Construct the package */
839 if (flag == PART_SUBNAME) {
840 rasprintf(&NVR, "%s-%s",
841 headerGetString(spec->packages->header, RPMTAG_NAME), name);
845 headerPutString(pkg->header, RPMTAG_NAME, NVR);
847 NVR = xstrdup("(main package)");
850 if ((rc = readLine(spec, STRIP_TRAILINGSPACE | STRIP_COMMENTS)) > 0) {
851 nextPart = PART_NONE;
855 while (! (nextPart = isPart(spec->line))) {
859 /* Skip blank lines */
862 if (*linep != '\0') {
863 if (findPreambleTag(spec, &tag, ¯o, lang)) {
864 rpmlog(RPMLOG_ERR, _("line %d: Unknown tag: %s\n"),
865 spec->lineNum, spec->line);
868 if (handlePreambleTag(spec, pkg, tag, macro, lang)) {
871 if (spec->BANames && !spec->recursing) {
872 res = PART_BUILDARCHITECTURES;
877 readLine(spec, STRIP_TRAILINGSPACE | STRIP_COMMENTS)) > 0) {
878 nextPart = PART_NONE;
888 * Expand buildroot one more time to get %{version} and the like
889 * from the main package, validate sanity. The spec->buildRoot could
890 * still contain unexpanded macros but it cannot be empty or '/', and it
891 * can't be messed with by anything spec does beyond this point.
893 if (initialPackage) {
894 char *buildRoot = rpmGetPath(spec->buildRoot, NULL);
895 if (*buildRoot == '\0') {
896 rpmlog(RPMLOG_ERR, _("%%{buildroot} couldn't be empty\n"));
899 if (rstreq(buildRoot, "/")) {
900 rpmlog(RPMLOG_ERR, _("%%{buildroot} can not be \"/\"\n"));
903 free(spec->buildRoot);
904 spec->buildRoot = buildRoot;
905 addMacro(spec->macros, "buildroot", NULL, spec->buildRoot, RMIL_SPEC);
908 /* XXX Skip valid arch check if not building binary package */
909 if (!spec->anyarch && checkForValidArchitectures(spec)) {
913 /* It is the main package */
914 if (pkg == spec->packages) {
915 fillOutMainPackage(pkg->header);
916 /* Define group tag to something when group is undefined in main package*/
917 if (!headerIsEntry(pkg->header, RPMTAG_GROUP)) {
918 headerPutString(pkg->header, RPMTAG_GROUP, "Unspecified");
922 if (checkForDuplicates(pkg->header, NVR)) {
926 if (pkg != spec->packages) {
927 headerCopyTags(spec->packages->header, pkg->header,
928 (rpmTag *)copyTagsDuringParse);
931 if (checkForRequired(pkg->header, NVR)) {
935 if (getSpecialDocDir(pkg)) {
939 /* if we get down here nextPart has been set to non-error */