2 * \file build/parseSpec.c
3 * Top level dispatcher for spec file parsing.
10 #include <rpm/rpmtypes.h>
11 #include <rpm/rpmlib.h> /* RPM_MACHTABLE & related */
12 #include <rpm/rpmds.h>
13 #include <rpm/rpmts.h>
14 #include <rpm/rpmlog.h>
15 #include <rpm/rpmfileutil.h>
16 #include "build/rpmbuild_internal.h"
17 #include "build/rpmbuild_misc.h"
20 #define SKIPSPACE(s) { while (*(s) && risspace(*(s))) (s)++; }
21 #define SKIPNONSPACE(s) { while (*(s) && !risspace(*(s))) (s)++; }
23 #define LEN_AND_STR(_tag) (sizeof(_tag)-1), (_tag)
25 typedef struct OpenFileInfo {
31 struct OpenFileInfo * next;
34 static const struct PartRec {
38 } const partList[] = {
39 { PART_PREAMBLE, LEN_AND_STR("%package")},
40 { PART_PREP, LEN_AND_STR("%prep")},
41 { PART_BUILD, LEN_AND_STR("%build")},
42 { PART_INSTALL, LEN_AND_STR("%install")},
43 { PART_CHECK, LEN_AND_STR("%check")},
44 { PART_CLEAN, LEN_AND_STR("%clean")},
45 { PART_PREUN, LEN_AND_STR("%preun")},
46 { PART_POSTUN, LEN_AND_STR("%postun")},
47 { PART_PRETRANS, LEN_AND_STR("%pretrans")},
48 { PART_POSTTRANS, LEN_AND_STR("%posttrans")},
49 { PART_PRE, LEN_AND_STR("%pre")},
50 { PART_POST, LEN_AND_STR("%post")},
51 { PART_FILES, LEN_AND_STR("%files")},
52 { PART_CHANGELOG, LEN_AND_STR("%changelog")},
53 { PART_DESCRIPTION, LEN_AND_STR("%description")},
54 { PART_TRIGGERPOSTUN, LEN_AND_STR("%triggerpostun")},
55 { PART_TRIGGERPREIN, LEN_AND_STR("%triggerprein")},
56 { PART_TRIGGERUN, LEN_AND_STR("%triggerun")},
57 { PART_TRIGGERIN, LEN_AND_STR("%triggerin")},
58 { PART_TRIGGERIN, LEN_AND_STR("%trigger")},
59 { PART_VERIFYSCRIPT, LEN_AND_STR("%verifyscript")},
60 { PART_POLICIES, LEN_AND_STR("%sepolicy")},
64 rpmParseState isPart(const char *line)
66 const struct PartRec *p;
68 for (p = partList; p->token != NULL; p++) {
70 if (rstrncasecmp(line, p->token, p->len))
73 if (c == '\0' || risspace(c))
77 return (p->token ? p->part : PART_NONE);
82 static int matchTok(const char *token, const char *line)
84 const char *b, *be = line;
85 size_t toklen = strlen(token);
88 while ( *(b = be) != '\0' ) {
94 if (toklen != (be-b) || rstrncasecmp(token, b, (be-b)))
103 void handleComments(char *s)
110 static struct OpenFileInfo * newOpenFileInfo(void)
112 struct OpenFileInfo *ofi;
114 ofi = xmalloc(sizeof(*ofi));
116 ofi->fileName = NULL;
118 ofi->readBuf[0] = '\0';
127 static void forceIncludeFile(rpmSpec spec, const char * fileName)
131 ofi = newOpenFileInfo();
132 ofi->fileName = xstrdup(fileName);
133 ofi->next = spec->fileStack;
134 spec->fileStack = ofi;
137 static int restoreFirstChar(rpmSpec spec)
139 /* Restore 1st char in (possible) next line */
140 if (spec->nextline != NULL && spec->nextpeekc != '\0') {
141 *spec->nextline = spec->nextpeekc;
142 spec->nextpeekc = '\0';
148 /* Return zero on success, 1 if we need to read more and -1 on errors. */
149 static int copyNextLineFromOFI(rpmSpec spec, OFI_t *ofi)
153 /* Expand next line from file into line buffer */
154 if (!(spec->nextline && *spec->nextline)) {
155 int pc = 0, bc = 0, nc = 0;
157 to = spec->lbufPtr ? spec->lbufPtr : spec->lbuf;
160 while (from && *from && ch != '\n')
161 ch = *to++ = *from++;
166 /* Check if we need another line before expanding the buffer. */
167 for (p = spec->lbuf; *p; p++) {
171 case '\n': p++, nc = 1; break;
176 case '\n': nc = 0; break;
179 case '{': p++, bc++; break;
180 case '(': p++, pc++; break;
181 case '%': p++; break;
184 case '{': if (bc > 0) bc++; break;
185 case '}': if (bc > 0) bc--; break;
186 case '(': if (pc > 0) pc++; break;
187 case ')': if (pc > 0) pc--; break;
191 /* If it doesn't, ask for one more line. */
192 if (pc || bc || nc ) {
196 spec->lbufPtr = spec->lbuf;
198 /* Don't expand macros (eg. %define) in false branch of %if clause */
199 if (spec->readStack->reading &&
200 expandMacros(spec, spec->macros, spec->lbuf, sizeof(spec->lbuf))) {
201 rpmlog(RPMLOG_ERR, _("line %d: %s\n"),
202 spec->lineNum, spec->lbuf);
205 spec->nextline = spec->lbuf;
210 static void copyNextLineFinish(rpmSpec spec, int strip)
215 /* Find next line in expanded line buffer */
216 spec->line = last = spec->nextline;
218 while (*spec->nextline && ch != '\n') {
219 ch = *spec->nextline++;
221 last = spec->nextline;
224 /* Save 1st char of next line in order to terminate current line. */
225 if (*spec->nextline != '\0') {
226 spec->nextpeekc = *spec->nextline;
227 *spec->nextline = '\0';
230 if (strip & STRIP_COMMENTS)
231 handleComments(spec->line);
233 if (strip & STRIP_TRAILINGSPACE)
237 static int readLineFromOFI(rpmSpec spec, OFI_t *ofi)
240 /* Make sure the current file is open */
241 if (ofi->fp == NULL) {
242 ofi->fp = fopen(ofi->fileName, "r");
243 if (ofi->fp == NULL || ferror(ofi->fp)) {
245 rpmlog(RPMLOG_ERR, _("Unable to open %s: %s\n"),
246 ofi->fileName, strerror(errno));
249 spec->lineNum = ofi->lineNum = 0;
252 /* Make sure we have something in the read buffer */
253 if (!(ofi->readPtr && *(ofi->readPtr))) {
254 if (!fgets(ofi->readBuf, BUFSIZ, ofi->fp)) {
256 if (spec->readStack->next) {
257 rpmlog(RPMLOG_ERR, _("Unclosed %%if\n"));
261 /* remove this file from the stack */
262 spec->fileStack = ofi->next;
264 ofi->fileName = _free(ofi->fileName);
267 /* only on last file do we signal EOF to caller */
268 ofi = spec->fileStack;
272 /* otherwise, go back and try the read again. */
275 ofi->readPtr = ofi->readBuf;
277 spec->lineNum = ofi->lineNum;
279 speclines sl = spec->sl;
280 if (sl->sl_nlines == sl->sl_nalloc) {
281 sl->sl_nalloc += 100;
282 sl->sl_lines = (char **) xrealloc(sl->sl_lines,
283 sl->sl_nalloc * sizeof(*(sl->sl_lines)));
285 sl->sl_lines[sl->sl_nlines++] = xstrdup(ofi->readBuf);
291 int readLine(rpmSpec spec, int strip)
295 struct ReadLevelEntry *rl;
296 OFI_t *ofi = spec->fileStack;
299 if (!restoreFirstChar(spec)) {
301 if ((rc = readLineFromOFI(spec, ofi)) != 0)
303 ofi = spec->fileStack;
305 /* Copy next file line into the spec line buffer */
306 rc = copyNextLineFromOFI(spec, ofi);
314 copyNextLineFinish(spec, strip);
320 if (!spec->readStack->reading && rstreqn("%if", s, sizeof("%if")-1)) {
322 } else if (rstreqn("%ifarch", s, sizeof("%ifarch")-1)) {
323 char *arch = rpmExpand("%{_target_cpu}", NULL);
325 match = matchTok(arch, s);
327 } else if (rstreqn("%ifnarch", s, sizeof("%ifnarch")-1)) {
328 char *arch = rpmExpand("%{_target_cpu}", NULL);
330 match = !matchTok(arch, s);
332 } else if (rstreqn("%ifos", s, sizeof("%ifos")-1)) {
333 char *os = rpmExpand("%{_target_os}", NULL);
335 match = matchTok(os, s);
337 } else if (rstreqn("%ifnos", s, sizeof("%ifnos")-1)) {
338 char *os = rpmExpand("%{_target_os}", NULL);
340 match = !matchTok(os, s);
342 } else if (rstreqn("%if", s, sizeof("%if")-1)) {
344 match = parseExpressionBoolean(spec, s);
347 _("%s:%d: parseExpressionBoolean returns %d\n"),
348 ofi->fileName, ofi->lineNum, match);
351 } else if (rstreqn("%else", s, sizeof("%else")-1)) {
353 if (! spec->readStack->next) {
354 /* Got an else with no %if ! */
356 _("%s:%d: Got a %%else with no %%if\n"),
357 ofi->fileName, ofi->lineNum);
360 spec->readStack->reading =
361 spec->readStack->next->reading && ! spec->readStack->reading;
362 spec->line[0] = '\0';
363 } else if (rstreqn("%endif", s, sizeof("%endif")-1)) {
365 if (! spec->readStack->next) {
366 /* Got an end with no %if ! */
368 _("%s:%d: Got a %%endif with no %%if\n"),
369 ofi->fileName, ofi->lineNum);
372 rl = spec->readStack;
373 spec->readStack = spec->readStack->next;
375 spec->line[0] = '\0';
376 } else if (rstreqn("%include", s, sizeof("%include")-1)) {
377 char *fileName, *endFileName, *p;
381 if (! risspace(*fileName)) {
382 rpmlog(RPMLOG_ERR, _("malformed %%include statement\n"));
386 endFileName = fileName;
387 SKIPNONSPACE(endFileName);
391 rpmlog(RPMLOG_ERR, _("malformed %%include statement\n"));
396 forceIncludeFile(spec, fileName);
398 ofi = spec->fileStack;
403 rl = xmalloc(sizeof(*rl));
404 rl->reading = spec->readStack->reading && match;
405 rl->next = spec->readStack;
406 spec->readStack = rl;
407 spec->line[0] = '\0';
410 if (! spec->readStack->reading) {
411 spec->line[0] = '\0';
414 /* FIX: spec->readStack->next should be dependent */
418 void closeSpec(rpmSpec spec)
422 while (spec->fileStack) {
423 ofi = spec->fileStack;
424 spec->fileStack = spec->fileStack->next;
425 if (ofi->fp) (void) fclose(ofi->fp);
426 ofi->fileName = _free(ofi->fileName);
431 static const rpmTag sourceTags[] = {
446 RPMTAG_CHANGELOGTIME,
447 RPMTAG_CHANGELOGNAME,
448 RPMTAG_CHANGELOGTEXT,
451 RPMTAG_HEADERI18NTABLE,
455 static void initSourceHeader(rpmSpec spec)
459 struct Source *srcPtr;
461 spec->sourceHeader = headerNew();
462 /* Only specific tags are added to the source package header */
463 headerCopyTags(spec->packages->header, spec->sourceHeader, sourceTags);
465 /* Add the build restrictions */
466 hi = headerInitIterator(spec->buildRestrictions);
467 while (headerNext(hi, &td)) {
468 if (rpmtdCount(&td) > 0) {
469 (void) headerPut(spec->sourceHeader, &td, HEADERPUT_DEFAULT);
473 hi = headerFreeIterator(hi);
475 if (spec->BANames && spec->BACount > 0) {
476 headerPutStringArray(spec->sourceHeader, RPMTAG_BUILDARCHS,
477 spec->BANames, spec->BACount);
480 /* Add tags for sources and patches */
481 for (srcPtr = spec->sources; srcPtr != NULL; srcPtr = srcPtr->next) {
482 if (srcPtr->flags & RPMBUILD_ISSOURCE) {
483 headerPutString(spec->sourceHeader, RPMTAG_SOURCE, srcPtr->source);
484 if (srcPtr->flags & RPMBUILD_ISNO) {
485 headerPutUint32(spec->sourceHeader, RPMTAG_NOSOURCE,
489 if (srcPtr->flags & RPMBUILD_ISPATCH) {
490 headerPutString(spec->sourceHeader, RPMTAG_PATCH, srcPtr->source);
491 if (srcPtr->flags & RPMBUILD_ISNO) {
492 headerPutUint32(spec->sourceHeader, RPMTAG_NOPATCH,
499 static void addTargets(Package Pkgs)
501 char *platform = rpmExpand("%{_target_platform}", NULL);
502 char *arch = rpmExpand("%{_target_cpu}", NULL);
503 char *os = rpmExpand("%{_target_os}", NULL);
505 for (Package pkg = Pkgs; pkg != NULL; pkg = pkg->next) {
506 headerPutString(pkg->header, RPMTAG_OS, os);
507 /* noarch subpackages already have arch set here, leave it alone */
508 if (!headerIsEntry(pkg->header, RPMTAG_ARCH)) {
509 headerPutString(pkg->header, RPMTAG_ARCH, arch);
511 headerPutString(pkg->header, RPMTAG_PLATFORM, platform);
513 pkg->ds = rpmdsThis(pkg->header, RPMTAG_REQUIRENAME, RPMSENSE_EQUAL);
520 static rpmSpec parseSpec(const char *specFile, rpmSpecFlags flags,
521 const char *buildRoot, int recursing)
523 rpmParseState parsePart = PART_PREAMBLE;
524 int initialPackage = 1;
527 /* Set up a new Spec structure with no packages. */
530 spec->specFile = rpmGetPath(specFile, NULL);
531 spec->fileStack = newOpenFileInfo();
532 spec->fileStack->fileName = xstrdup(spec->specFile);
533 /* If buildRoot not specified, use default %{buildroot} */
535 spec->buildRoot = xstrdup(buildRoot);
537 spec->buildRoot = rpmGetPath("%{?buildroot:%{buildroot}}", NULL);
539 addMacro(NULL, "_docdir", NULL, "%{_defaultdocdir}", RMIL_SPEC);
540 spec->recursing = recursing;
543 /* All the parse*() functions expect to have a line pre-read */
544 /* in the spec's line buffer. Except for parsePreamble(), */
545 /* which handles the initial entry into a spec file. */
547 while (parsePart != PART_NONE) {
550 /* XXX Trap unexpected RPMRC_FAIL returns for now */
552 rpmlog(RPMLOG_ERR, "FIXME: got RPMRC_FAIL from spec parse\n");
554 case PART_ERROR: /* fallthrough */
559 parsePart = parsePreamble(spec, initialPackage);
563 parsePart = parsePrep(spec);
569 parsePart = parseBuildInstallClean(spec, parsePart);
572 parsePart = parseChangelog(spec);
574 case PART_DESCRIPTION:
575 parsePart = parseDescription(spec);
584 case PART_VERIFYSCRIPT:
585 case PART_TRIGGERPREIN:
588 case PART_TRIGGERPOSTUN:
589 parsePart = parseScript(spec, parsePart);
593 parsePart = parseFiles(spec);
597 parsePart = parsePolicies(spec);
600 case PART_NONE: /* XXX avoid gcc whining */
602 case PART_BUILDARCHITECTURES:
606 if (goterror || parsePart >= PART_LAST) {
610 if (parsePart == PART_BUILDARCHITECTURES) {
616 spec->BASpecs = xcalloc(spec->BACount, sizeof(*spec->BASpecs));
618 if (spec->BANames != NULL)
619 for (x = 0; x < spec->BACount; x++) {
621 /* Skip if not arch is not compatible. */
622 if (!rpmMachineScore(RPM_MACHTABLE_BUILDARCH, spec->BANames[x]))
624 addMacro(NULL, "_target_cpu", NULL, spec->BANames[x], RMIL_RPMRC);
625 spec->BASpecs[index] = parseSpec(specFile, flags, buildRoot, 1);
626 if (spec->BASpecs[index] == NULL) {
627 spec->BACount = index;
630 delMacro(NULL, "_target_cpu");
634 spec->BACount = index;
637 _("No compatible architectures found for build\n"));
642 * Return the 1st child's fully parsed Spec structure.
643 * The restart of the parse when encountering BuildArch
644 * causes problems for "rpm -q --specfile". This is
645 * still a hack because there may be more than 1 arch
646 * specified (unlikely but possible.) There's also the
647 * further problem that the macro context, particularly
648 * %{_target_cpu}, disagrees with the info in the header.
650 if (spec->BACount >= 1) {
651 rpmSpec nspec = spec->BASpecs[0];
652 spec->BASpecs = _free(spec->BASpecs);
653 spec = rpmSpecFree(spec);
661 if (spec->clean == NULL) {
662 char *body = rpmExpand("%{?buildroot: %{__rm} -rf %{buildroot}}", NULL);
663 spec->clean = newStringBuf();
664 appendLineStringBuf(spec->clean, body);
668 /* Check for description in each package */
669 for (Package pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
670 if (!headerIsEntry(pkg->header, RPMTAG_DESCRIPTION)) {
671 rpmlog(RPMLOG_ERR, _("Package has no %%description: %s\n"),
672 headerGetString(pkg->header, RPMTAG_NAME));
677 /* Add arch, os and platform for each package */
678 addTargets(spec->packages);
682 /* Assemble source header from parsed components */
683 initSourceHeader(spec);
688 spec = rpmSpecFree(spec);
692 rpmSpec rpmSpecParse(const char *specFile, rpmSpecFlags flags,
693 const char *buildRoot)
695 return parseSpec(specFile, flags, buildRoot, 0);