2 * \file build/parsePreamble.c
3 * Parse tags in global section from spec file.
14 static rpmTag copyTagsDuringParse[] = {
37 static rpmTag requiredTags[] = {
49 static void addOrAppendListEntry(Header h, int32_t tag, char * line)
55 xx = poptParseArgvString(line, &argc, &argv);
57 xx = headerAddOrAppendEntry(h, tag, RPM_STRING_ARRAY_TYPE, argv, argc);
61 /* Parse a simple part line that only take -n <pkg> or <pkg> */
62 /* <pkg> is return in name as a pointer into a static buffer */
66 static int parseSimplePart(char *line,char **name,int *flag)
70 static char buf[BUFSIZ];
72 strcpy(linebuf, line);
74 /* Throw away the first token (the %xxxx) */
75 (void)strtok(linebuf, " \t\n");
77 if (!(tok = strtok(NULL, " \t\n"))) {
82 if (!strcmp(tok, "-n")) {
83 if (!(tok = strtok(NULL, " \t\n")))
92 return (strtok(NULL, " \t\n")) ? 1 : 0;
97 static inline int parseYesNo(const char * s)
99 return ((!s || (s[0] == 'n' || s[0] == 'N' || s[0] == '0') ||
100 !xstrcasecmp(s, "false") || !xstrcasecmp(s, "off"))
104 typedef struct tokenBits_s {
111 static struct tokenBits_s installScriptBits[] = {
112 { "interp", RPMSENSE_INTERP },
113 { "prereq", RPMSENSE_PREREQ },
114 { "preun", RPMSENSE_SCRIPT_PREUN },
115 { "pre", RPMSENSE_SCRIPT_PRE },
116 { "postun", RPMSENSE_SCRIPT_POSTUN },
117 { "post", RPMSENSE_SCRIPT_POST },
118 { "rpmlib", RPMSENSE_RPMLIB },
119 { "verify", RPMSENSE_SCRIPT_VERIFY },
125 static struct tokenBits_s buildScriptBits[] = {
126 { "prep", RPMSENSE_SCRIPT_PREP },
127 { "build", RPMSENSE_SCRIPT_BUILD },
128 { "install", RPMSENSE_SCRIPT_INSTALL },
129 { "clean", RPMSENSE_SCRIPT_CLEAN },
135 static int parseBits(const char * s, const tokenBits tokbits,
140 rpmsenseFlags bits = RPMSENSE_ANY;
145 while ((c = *s) && xisspace(c)) s++;
147 while ((c = *se) && xisalpha(c)) se++;
150 for (tb = tokbits; tb->name; tb++) {
151 if (tb->name != NULL &&
152 strlen(tb->name) == (se-s) && !strncmp(tb->name, s, (se-s)))
155 if (tb->name == NULL)
158 while ((c = *se) && xisspace(c)) se++;
164 if (c == 0 && bp) *bp = bits;
165 return (c ? RPMERR_BADSPEC : 0);
170 static inline char * findLastChar(char * s)
185 static int isMemberInEntry(Header h, const char *name, rpmTag tag)
187 HGE_t hge = (HGE_t)headerGetEntryMinMemory;
188 HFD_t hfd = headerFreeData;
193 if (!hge(h, tag, &type, (void **)&names, &count))
196 if (!xstrcasecmp(names[count], name))
199 names = hfd(names, type);
200 return (count >= 0 ? 1 : 0);
205 static int checkForValidArchitectures(rpmSpec spec)
208 const char *arch = NULL;
209 const char *os = NULL;
211 rpmGetArchInfo(&arch, NULL);
212 rpmGetOsInfo(&os, NULL);
214 const char *arch = rpmExpand("%{_target_cpu}", NULL);
215 const char *os = rpmExpand("%{_target_os}", NULL);
218 if (isMemberInEntry(spec->buildRestrictions,
219 arch, RPMTAG_EXCLUDEARCH) == 1) {
220 rpmlog(RPMERR_BADSPEC, _("Architecture is excluded: %s\n"), arch);
221 return RPMERR_BADSPEC;
223 if (isMemberInEntry(spec->buildRestrictions,
224 arch, RPMTAG_EXCLUSIVEARCH) == 0) {
225 rpmlog(RPMERR_BADSPEC, _("Architecture is not included: %s\n"), arch);
226 return RPMERR_BADSPEC;
228 if (isMemberInEntry(spec->buildRestrictions,
229 os, RPMTAG_EXCLUDEOS) == 1) {
230 rpmlog(RPMERR_BADSPEC, _("OS is excluded: %s\n"), os);
231 return RPMERR_BADSPEC;
233 if (isMemberInEntry(spec->buildRestrictions,
234 os, RPMTAG_EXCLUSIVEOS) == 0) {
235 rpmlog(RPMERR_BADSPEC, _("OS is not included: %s\n"), os);
236 return RPMERR_BADSPEC;
243 * Check that required tags are present in header.
245 * @param NVR package name-version-release
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)) {
256 rpmlog(RPMERR_BADSPEC,
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
272 static int checkForDuplicates(Header h, const char * NVR)
278 for (hi = headerInitIterator(h), lastTag = 0;
279 headerNextIterator(hi, &tag, NULL, NULL, NULL);
284 rpmlog(RPMERR_BADSPEC, _("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 const 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 int readIcon(Header h, const char * file)
326 const char *fn = NULL;
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(RPMERR_BADSPEC, _("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(RPMERR_BADSPEC, _("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(RPMERR_BADSPEC, _("Unknown icon type: %s\n"), file);
380 spectag stashSt(rpmSpec spec, Header h, int 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(RPMERR_BADSPEC, _("line %d: Tag takes single token only: %s\n"), \
412 spec->lineNum, spec->line); \
413 return RPMERR_BADSPEC; \
420 static int handlePreambleTag(rpmSpec spec, Package pkg, rpmTag 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 RPMERR_BADSPEC; /* XXX can't happen */
437 /* Find the start of the "field" and strip trailing space */
438 while ((*field) && (*field != ':'))
441 rpmlog(RPMERR_BADSPEC, _("line %d: Malformed tag: %s\n"),
442 spec->lineNum, spec->line);
443 return RPMERR_BADSPEC;
449 rpmlog(RPMERR_BADSPEC, _("line %d: Empty tag: %s\n"),
450 spec->lineNum, spec->line);
451 return RPMERR_BADSPEC;
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(RPMERR_BADSPEC, _("line %d: Illegal char '-' in %s: %s\n"),
475 spec->lineNum, "version", spec->line);
476 return RPMERR_BADSPEC;
478 addMacro(spec->macros, "PACKAGE_VERSION", NULL, field, RMIL_OLDSPEC);
479 } else if (tag == RPMTAG_RELEASE) {
480 if (strchr(field, '-') != NULL) {
481 rpmlog(RPMERR_BADSPEC, _("line %d: Illegal char '-' in %s: %s\n"),
482 spec->lineNum, "release", spec->line);
483 return RPMERR_BADSPEC;
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;
504 const char * buildRootURL = spec->buildRootURL;
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 (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, "/")) {
536 rpmlog(RPMERR_BADSPEC,
537 _("BuildRoot can not be \"/\": %s\n"), spec->buildRootURL);
538 buildRootURL = _free(buildRootURL);
539 return RPMERR_BADSPEC;
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) {
549 rpmlog(RPMERR_BADSPEC,
550 _("line %d: Prefixes must not end with \"/\": %s\n"),
551 spec->lineNum, spec->line);
552 array = hfd(array, type);
553 return RPMERR_BADSPEC;
556 array = hfd(array, type);
560 if (field[0] != '/') {
561 rpmlog(RPMERR_BADSPEC,
562 _("line %d: Docdir must begin with '/': %s\n"),
563 spec->lineNum, spec->line);
564 return RPMERR_BADSPEC;
567 delMacro(NULL, "_docdir");
568 addMacro(NULL, "_docdir", NULL, field, RMIL_SPEC);
572 if (parseNum(field, &num)) {
573 rpmlog(RPMERR_BADSPEC,
574 _("line %d: Epoch/Serial field must be a number: %s\n"),
575 spec->lineNum, spec->line);
576 return RPMERR_BADSPEC;
578 xx = headerAddEntry(pkg->header, tag, RPM_INT32_TYPE, &num, 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)))
602 return RPMERR_BADSPEC;
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))) {
613 rpmlog(RPMERR_BADSPEC,
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))) {
624 rpmlog(RPMERR_BADSPEC,
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)))) {
650 rpmlog(RPMERR_BADSPEC,
651 _("line %d: Bad BuildArchitecture format: %s\n"),
652 spec->lineNum, spec->line);
653 return RPMERR_BADSPEC;
656 spec->BANames = _free(spec->BANames);
660 rpmlog(RPMERR_INTERNAL, _("Internal error: Bogus tag %d\n"), tag);
661 return RPMERR_INTERNAL;
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 static struct PreambleRec_s preambleList[] = {
684 {RPMTAG_NAME, 0, 0, 0, "name"},
685 {RPMTAG_VERSION, 0, 0, 0, "version"},
686 {RPMTAG_RELEASE, 0, 0, 0, "release"},
687 {RPMTAG_EPOCH, 0, 0, 0, "epoch"},
688 {RPMTAG_EPOCH, 0, 0, 1, "serial"},
689 {RPMTAG_SUMMARY, 0, 1, 0, "summary"},
690 {RPMTAG_LICENSE, 0, 0, 1, "copyright"},
691 {RPMTAG_LICENSE, 0, 0, 0, "license"},
692 {RPMTAG_DISTRIBUTION, 0, 0, 0, "distribution"},
693 {RPMTAG_DISTURL, 0, 0, 0, "disturl"},
694 {RPMTAG_VENDOR, 0, 0, 0, "vendor"},
695 {RPMTAG_GROUP, 0, 1, 0, "group"},
696 {RPMTAG_PACKAGER, 0, 0, 0, "packager"},
697 {RPMTAG_URL, 0, 0, 0, "url"},
698 {RPMTAG_SOURCE, 0, 0, 0, "source"},
699 {RPMTAG_PATCH, 0, 0, 0, "patch"},
700 {RPMTAG_NOSOURCE, 0, 0, 0, "nosource"},
701 {RPMTAG_NOPATCH, 0, 0, 0, "nopatch"},
702 {RPMTAG_EXCLUDEARCH, 0, 0, 0, "excludearch"},
703 {RPMTAG_EXCLUSIVEARCH, 0, 0, 0, "exclusivearch"},
704 {RPMTAG_EXCLUDEOS, 0, 0, 0, "excludeos"},
705 {RPMTAG_EXCLUSIVEOS, 0, 0, 0, "exclusiveos"},
706 {RPMTAG_ICON, 0, 0, 0, "icon"},
707 {RPMTAG_PROVIDEFLAGS, 0, 0, 0, "provides"},
708 {RPMTAG_REQUIREFLAGS, 0, 1, 0, "requires"},
709 {RPMTAG_PREREQ, 0, 1, 0, "prereq"},
710 {RPMTAG_CONFLICTFLAGS, 0, 0, 0, "conflicts"},
711 {RPMTAG_OBSOLETEFLAGS, 0, 0, 0, "obsoletes"},
712 {RPMTAG_PREFIXES, 0, 0, 0, "prefixes"},
713 {RPMTAG_PREFIXES, 0, 0, 0, "prefix"},
714 {RPMTAG_BUILDROOT, 0, 0, 0, "buildroot"},
715 {RPMTAG_BUILDARCHS, 0, 0, 0, "buildarchitectures"},
716 {RPMTAG_BUILDARCHS, 0, 0, 0, "buildarch"},
717 {RPMTAG_BUILDCONFLICTS, 0, 0, 0, "buildconflicts"},
718 {RPMTAG_BUILDPREREQ, 0, 1, 0, "buildprereq"},
719 {RPMTAG_BUILDREQUIRES, 0, 1, 0, "buildrequires"},
720 {RPMTAG_AUTOREQPROV, 0, 0, 0, "autoreqprov"},
721 {RPMTAG_AUTOREQ, 0, 0, 0, "autoreq"},
722 {RPMTAG_AUTOPROV, 0, 0, 0, "autoprov"},
723 {RPMTAG_DOCDIR, 0, 0, 0, "docdir"},
724 {RPMTAG_RHNPLATFORM, 0, 0, 1, "rhnplatform"},
725 {RPMTAG_DISTTAG, 0, 0, 0, "disttag"},
726 {RPMTAG_CVSID, 0, 0, 0, "cvsid"},
727 {RPMTAG_SVNID, 0, 0, 0, "svnid"},
728 /* LCL: can't add null annotation */
734 static inline void initPreambleList(void)
737 for (p = preambleList; p->token != NULL; p++)
738 if (p->token) p->len = strlen(p->token);
743 static int findPreambleTag(rpmSpec spec,rpmTag * tag,
744 const char ** macro, char * lang)
749 if (preambleList[0].len == 0)
752 for (p = preambleList; p->token != NULL; p++) {
753 if (!(p->token && !xstrncasecmp(spec->line, p->token, p->len)))
756 rpmlog(RPMERR_BADSPEC, _("Legacy syntax is unsupported: %s\n"),
762 if (p == NULL || p->token == NULL)
765 s = spec->line + p->len;
768 switch (p->multiLang) {
771 /* Unless this is a source or a patch, a ':' better be next */
772 if (p->tag != RPMTAG_SOURCE && p->tag != RPMTAG_PATCH) {
773 if (*s != ':') return 1;
777 case 1: /* Parse optional ( <token> ). */
779 strcpy(lang, RPMBUILD_DEFAULT_LANG);
782 if (*s != '(') return 1;
785 while (!xisspace(*s) && *s != ')')
789 if (*s != ')') return 1;
792 if (*s != ':') return 1;
802 int parsePreamble(rpmSpec spec, int initialPackage)
812 strcpy(NVR, "(main package)");
814 pkg = newPackage(spec);
816 if (! initialPackage) {
817 /* There is one option to %package: <pkg> or -n <pkg> */
818 if (parseSimplePart(spec->line, &name, &flag)) {
819 rpmlog(RPMERR_BADSPEC, _("Bad package specification: %s\n"),
821 return RPMERR_BADSPEC;
824 if (!lookupPackage(spec, name, flag, NULL)) {
825 rpmlog(RPMERR_BADSPEC, _("Package already exists: %s\n"),
827 return RPMERR_BADSPEC;
830 /* Construct the package */
831 if (flag == PART_SUBNAME) {
832 const char * mainName;
833 xx = headerNVR(spec->packages->header, &mainName, NULL, NULL);
834 sprintf(NVR, "%s-%s", mainName, name);
837 xx = headerAddEntry(pkg->header, RPMTAG_NAME, RPM_STRING_TYPE, NVR, 1);
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(RPMERR_BADSPEC, _("line %d: Unknown tag: %s\n"),
855 spec->lineNum, spec->line);
856 return RPMERR_BADSPEC;
858 if (handlePreambleTag(spec, pkg, tag, macro, lang))
859 return RPMERR_BADSPEC;
860 if (spec->BANames && !spec->recursing)
861 return PART_BUILDARCHITECTURES;
864 readLine(spec, STRIP_TRAILINGSPACE | STRIP_COMMENTS)) > 0) {
865 nextPart = PART_NONE;
873 /* Do some final processing on the header */
875 if (!spec->gotBuildRootURL && spec->buildRootURL) {
876 rpmlog(RPMERR_BADSPEC, _("Spec file can't use BuildRoot\n"));
877 return RPMERR_BADSPEC;
880 /* XXX Skip valid arch check if not building binary package */
881 if (!spec->anyarch && checkForValidArchitectures(spec))
882 return RPMERR_BADSPEC;
884 if (pkg == spec->packages)
885 fillOutMainPackage(pkg->header);
887 if (checkForDuplicates(pkg->header, NVR))
888 return RPMERR_BADSPEC;
890 if (pkg != spec->packages)
891 headerCopyTags(spec->packages->header, pkg->header,
892 (int32_t *)copyTagsDuringParse);
894 if (checkForRequired(pkg->header, NVR))
895 return RPMERR_BADSPEC;