2 * \file build/parseSpec.c
3 * Top level dispatcher for spec file parsing.
8 #include <rpmio_internal.h>
14 /*@access FD_t @*/ /* compared with NULL */
19 static struct PartRec {
22 /*@observer@*/ /*@null@*/
25 { PART_PREAMBLE, 0, "%package"},
26 { PART_PREP, 0, "%prep"},
27 { PART_BUILD, 0, "%build"},
28 { PART_INSTALL, 0, "%install"},
29 { PART_CHECK, 0, "%check"},
30 { PART_CLEAN, 0, "%clean"},
31 { PART_PREUN, 0, "%preun"},
32 { PART_POSTUN, 0, "%postun"},
33 { PART_PRETRANS, 0, "%pretrans"},
34 { PART_POSTTRANS, 0, "%posttrans"},
35 { PART_PRE, 0, "%pre"},
36 { PART_POST, 0, "%post"},
37 { PART_FILES, 0, "%files"},
38 { PART_CHANGELOG, 0, "%changelog"},
39 { PART_DESCRIPTION, 0, "%description"},
40 { PART_TRIGGERPOSTUN, 0, "%triggerpostun"},
41 { PART_TRIGGERUN, 0, "%triggerun"},
42 { PART_TRIGGERIN, 0, "%triggerin"},
43 { PART_TRIGGERIN, 0, "%trigger"},
44 { PART_VERIFYSCRIPT, 0, "%verifyscript"},
50 static inline void initParts(struct PartRec *p)
51 /*@modifies p->len @*/
53 for (; p->token != NULL; p++)
54 p->len = strlen(p->token);
57 rpmParseState isPart(const char *line)
62 if (partList[0].len == 0)
66 for (p = partList; p->token != NULL; p++) {
68 if (xstrncasecmp(line, p->token, p->len))
73 if (c == '\0' || xisspace(c))
77 return (p->token ? p->part : PART_NONE);
82 static int matchTok(const char *token, const char *line)
85 const char *b, *be = line;
86 size_t toklen = strlen(token);
90 while ( *(b = be) != '\0' ) {
96 if (toklen != (be-b) || xstrncasecmp(token, b, (be-b)))
107 void handleComments(char *s)
117 static void forceIncludeFile(Spec spec, const char * fileName)
118 /*@modifies spec->fileStack @*/
122 ofi = newOpenFileInfo();
123 ofi->fileName = xstrdup(fileName);
124 ofi->next = spec->fileStack;
125 spec->fileStack = ofi;
131 static int copyNextLine(Spec spec, OFI_t *ofi, int strip)
132 /*@globals rpmGlobalMacroContext, h_errno,
134 /*@modifies spec->nextline, spec->nextpeekc, spec->lbuf, spec->line,
136 rpmGlobalMacroContext, fileSystem @*/
141 /* Restore 1st char in (possible) next line */
142 if (spec->nextline != NULL && spec->nextpeekc != '\0') {
143 *spec->nextline = spec->nextpeekc;
144 spec->nextpeekc = '\0';
146 /* Expand next line from file into line buffer */
147 if (!(spec->nextline && *spec->nextline)) {
148 int pc = 0, bc = 0, nc = 0;
150 to = spec->lbufPtr ? spec->lbufPtr : spec->lbuf;
153 while (*from && ch != '\n')
154 ch = *to++ = *from++;
161 /* Check if we need another line before expanding the buffer. */
162 for (p = spec->lbuf; *p; p++) {
166 case '\n': p++, nc = 1; /*@innerbreak@*/ break;
167 case '\0': /*@innerbreak@*/ break;
168 default: p++; /*@innerbreak@*/ break;
170 /*@switchbreak@*/ break;
171 case '\n': nc = 0; /*@switchbreak@*/ break;
174 case '{': p++, bc++; /*@innerbreak@*/ break;
175 case '(': p++, pc++; /*@innerbreak@*/ break;
176 case '%': p++; /*@innerbreak@*/ break;
178 /*@switchbreak@*/ break;
179 case '{': if (bc > 0) bc++; /*@switchbreak@*/ break;
180 case '}': if (bc > 0) bc--; /*@switchbreak@*/ break;
181 case '(': if (pc > 0) pc++; /*@switchbreak@*/ break;
182 case ')': if (pc > 0) pc--; /*@switchbreak@*/ break;
186 /* If it doesn't, ask for one more line. We need a better
187 * error code for this. */
188 if (pc || bc || nc ) {
189 /*@-observertrans -readonlytrans@*/
191 /*@=observertrans =readonlytrans@*/
192 return RPMERR_UNMATCHEDIF;
195 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 rpmError(RPMERR_BADSPEC, _("line %d: %s\n"),
202 spec->lineNum, spec->lbuf);
203 return RPMERR_BADSPEC;
205 spec->nextline = spec->lbuf;
208 /* Find next line in expanded line buffer */
209 spec->line = last = spec->nextline;
211 while (*spec->nextline && ch != '\n') {
212 ch = *spec->nextline++;
214 last = spec->nextline;
217 /* Save 1st char of next line in order to terminate current line. */
218 if (*spec->nextline != '\0') {
219 spec->nextpeekc = *spec->nextline;
220 *spec->nextline = '\0';
223 if (strip & STRIP_COMMENTS)
224 handleComments(spec->line);
226 if (strip & STRIP_TRAILINGSPACE)
234 int readLine(Spec spec, int strip)
242 struct ReadLevelEntry *rl;
243 OFI_t *ofi = spec->fileStack;
247 /* Make sure the current file is open */
249 if (ofi->fd == NULL) {
250 ofi->fd = Fopen(ofi->fileName, "r.fpio");
251 if (ofi->fd == NULL || Ferror(ofi->fd)) {
253 rpmError(RPMERR_BADSPEC, _("Unable to open %s: %s\n"),
254 ofi->fileName, Fstrerror(ofi->fd));
255 return RPMERR_BADSPEC;
257 spec->lineNum = ofi->lineNum = 0;
261 /* Make sure we have something in the read buffer */
262 if (!(ofi->readPtr && *(ofi->readPtr))) {
263 /*@-type@*/ /* FIX: cast? */
264 FILE * f = fdGetFp(ofi->fd);
266 if (f == NULL || !fgets(ofi->readBuf, BUFSIZ, f)) {
268 if (spec->readStack->next) {
269 rpmError(RPMERR_UNMATCHEDIF, _("Unclosed %%if\n"));
270 return RPMERR_UNMATCHEDIF;
273 /* remove this file from the stack */
274 spec->fileStack = ofi->next;
275 (void) Fclose(ofi->fd);
276 ofi->fileName = _free(ofi->fileName);
279 /* only on last file do we signal EOF to caller */
280 ofi = spec->fileStack;
284 /* otherwise, go back and try the read again. */
287 ofi->readPtr = ofi->readBuf;
289 spec->lineNum = ofi->lineNum;
291 speclines sl = spec->sl;
292 if (sl->sl_nlines == sl->sl_nalloc) {
293 sl->sl_nalloc += 100;
294 sl->sl_lines = (char **) xrealloc(sl->sl_lines,
295 sl->sl_nalloc * sizeof(*(sl->sl_lines)));
297 sl->sl_lines[sl->sl_nlines++] = xstrdup(ofi->readBuf);
303 rpmGetArchInfo(&arch, NULL);
305 rpmGetOsInfo(&os, NULL);
308 /* Copy next file line into the spec line buffer */
309 if ((rc = copyNextLine(spec, ofi, strip)) != 0) {
310 if (rc == RPMERR_UNMATCHEDIF)
319 if (!spec->readStack->reading && !strncmp("%if", s, sizeof("%if")-1)) {
321 } else if (! strncmp("%ifarch", s, sizeof("%ifarch")-1)) {
322 const char *arch = rpmExpand("%{_target_cpu}", NULL);
324 match = matchTok(arch, s);
326 } else if (! strncmp("%ifnarch", s, sizeof("%ifnarch")-1)) {
327 const char *arch = rpmExpand("%{_target_cpu}", NULL);
329 match = !matchTok(arch, s);
331 } else if (! strncmp("%ifos", s, sizeof("%ifos")-1)) {
332 const char *os = rpmExpand("%{_target_os}", NULL);
334 match = matchTok(os, s);
336 } else if (! strncmp("%ifnos", s, sizeof("%ifnos")-1)) {
337 const char *os = rpmExpand("%{_target_os}", NULL);
339 match = !matchTok(os, s);
341 } else if (! strncmp("%if", s, sizeof("%if")-1)) {
343 match = parseExpressionBoolean(spec, s);
345 rpmError(RPMERR_UNMATCHEDIF,
346 _("%s:%d: parseExpressionBoolean returns %d\n"),
347 ofi->fileName, ofi->lineNum, match);
348 return RPMERR_BADSPEC;
350 } else if (! strncmp("%else", s, sizeof("%else")-1)) {
352 if (! spec->readStack->next) {
353 /* Got an else with no %if ! */
354 rpmError(RPMERR_UNMATCHEDIF,
355 _("%s:%d: Got a %%else with no %%if\n"),
356 ofi->fileName, ofi->lineNum);
357 return RPMERR_UNMATCHEDIF;
359 spec->readStack->reading =
360 spec->readStack->next->reading && ! spec->readStack->reading;
361 spec->line[0] = '\0';
362 } else if (! strncmp("%endif", s, sizeof("%endif")-1)) {
364 if (! spec->readStack->next) {
365 /* Got an end with no %if ! */
366 rpmError(RPMERR_UNMATCHEDIF,
367 _("%s:%d: Got a %%endif with no %%if\n"),
368 ofi->fileName, ofi->lineNum);
369 return RPMERR_UNMATCHEDIF;
371 rl = spec->readStack;
372 spec->readStack = spec->readStack->next;
374 spec->line[0] = '\0';
375 } else if (! strncmp("%include", s, sizeof("%include")-1)) {
376 char *fileName, *endFileName, *p;
380 if (! xisspace(*fileName)) {
381 rpmError(RPMERR_BADSPEC, _("malformed %%include statement\n"));
382 return RPMERR_BADSPEC;
385 endFileName = fileName;
386 SKIPNONSPACE(endFileName);
390 rpmError(RPMERR_BADSPEC, _("malformed %%include statement\n"));
391 return RPMERR_BADSPEC;
395 forceIncludeFile(spec, fileName);
397 ofi = spec->fileStack;
402 rl = xmalloc(sizeof(*rl));
403 rl->reading = spec->readStack->reading && match;
404 rl->next = spec->readStack;
405 spec->readStack = rl;
406 spec->line[0] = '\0';
409 if (! spec->readStack->reading) {
410 spec->line[0] = '\0';
413 /*@-compmempass@*/ /* FIX: spec->readStack->next should be dependent */
419 void closeSpec(Spec spec)
423 while (spec->fileStack) {
424 ofi = spec->fileStack;
425 spec->fileStack = spec->fileStack->next;
426 if (ofi->fd) (void) Fclose(ofi->fd);
427 ofi->fileName = _free(ofi->fileName);
434 extern int noLang; /* XXX FIXME: pass as arg */
437 /*@todo Skip parse recursion if os is not compatible. @*/
439 int parseSpec(rpmts ts, const char *specFile, const char *rootURL,
440 const char *buildRootURL, int recursing, const char *passPhrase,
441 const char *cookie, int anyarch, int force)
443 rpmParseState parsePart = PART_PREAMBLE;
444 int initialPackage = 1;
446 const char *saveArch;
451 /* Set up a new Spec structure with no packages. */
455 * Note: rpmGetPath should guarantee a "canonical" path. That means
456 * that the following pathologies should be weeded out:
459 * /.././../usr/../bin//./sh (XXX FIXME: dots not handled yet)
461 spec->specFile = rpmGetPath(specFile, NULL);
462 spec->fileStack = newOpenFileInfo();
463 spec->fileStack->fileName = xstrdup(spec->specFile);
465 const char * buildRoot;
466 (void) urlPath(buildRootURL, &buildRoot);
468 if (*buildRoot == '\0') buildRoot = "/";
470 if (!strcmp(buildRoot, "/")) {
471 rpmError(RPMERR_BADSPEC,
472 _("BuildRoot can not be \"/\": %s\n"), buildRootURL);
473 return RPMERR_BADSPEC;
475 spec->gotBuildRootURL = 1;
476 spec->buildRootURL = xstrdup(buildRootURL);
477 addMacro(spec->macros, "buildroot", NULL, buildRoot, RMIL_SPEC);
479 addMacro(NULL, "_docdir", NULL, "%{_defaultdocdir}", RMIL_SPEC);
480 spec->recursing = recursing;
481 spec->anyarch = anyarch;
485 spec->rootURL = xstrdup(rootURL);
487 spec->passPhrase = xstrdup(passPhrase);
489 spec->cookie = xstrdup(cookie);
491 spec->timeCheck = rpmExpandNumeric("%{_timecheck}");
493 /* All the parse*() functions expect to have a line pre-read */
494 /* in the spec's line buffer. Except for parsePreamble(), */
495 /* which handles the initial entry into a spec file. */
497 /*@-infloops@*/ /* LCL: parsePart is modified @*/
498 while (parsePart < PART_LAST && parsePart != PART_NONE) {
501 parsePart = parsePreamble(spec, initialPackage);
503 /*@switchbreak@*/ break;
505 parsePart = parsePrep(spec);
506 /*@switchbreak@*/ break;
511 parsePart = parseBuildInstallClean(spec, parsePart);
512 /*@switchbreak@*/ break;
514 parsePart = parseChangelog(spec);
515 /*@switchbreak@*/ break;
516 case PART_DESCRIPTION:
517 parsePart = parseDescription(spec);
518 /*@switchbreak@*/ break;
526 case PART_VERIFYSCRIPT:
529 case PART_TRIGGERPOSTUN:
530 parsePart = parseScript(spec, parsePart);
531 /*@switchbreak@*/ break;
534 parsePart = parseFiles(spec);
535 /*@switchbreak@*/ break;
537 case PART_NONE: /* XXX avoid gcc whining */
539 case PART_BUILDARCHITECTURES:
540 /*@switchbreak@*/ break;
543 if (parsePart >= PART_LAST) {
544 spec = freeSpec(spec);
548 if (parsePart == PART_BUILDARCHITECTURES) {
554 /* LCL: sizeof(spec->BASpecs[0]) -nullderef whine here */
555 spec->BASpecs = xcalloc(spec->BACount, sizeof(*spec->BASpecs));
557 if (spec->BANames != NULL)
558 for (x = 0; x < spec->BACount; x++) {
560 /* Skip if not arch is not compatible. */
561 if (!rpmMachineScore(RPM_MACHTABLE_BUILDARCH, spec->BANames[x]))
562 /*@innercontinue@*/ continue;
564 rpmGetMachine(&saveArch, NULL);
565 saveArch = xstrdup(saveArch);
566 rpmSetMachine(spec->BANames[x], NULL);
568 addMacro(NULL, "_target_cpu", NULL, spec->BANames[x], RMIL_RPMRC);
570 spec->BASpecs[index] = NULL;
571 if (parseSpec(ts, specFile, spec->rootURL, buildRootURL, 1,
572 passPhrase, cookie, anyarch, force)
573 || (spec->BASpecs[index] = rpmtsSetSpec(ts, NULL)) == NULL)
575 spec->BACount = index;
577 spec = freeSpec(spec);
578 return RPMERR_BADSPEC;
582 rpmSetMachine(saveArch, NULL);
583 saveArch = _free(saveArch);
585 delMacro(NULL, "_target_cpu");
590 spec->BACount = index;
592 rpmError(RPMERR_BADSPEC,
593 _("No compatible architectures found for build\n"));
595 spec = freeSpec(spec);
596 return RPMERR_BADSPEC;
601 * Return the 1st child's fully parsed Spec structure.
602 * The restart of the parse when encountering BuildArch
603 * causes problems for "rpm -q --specfile". This is
604 * still a hack because there may be more than 1 arch
605 * specified (unlikely but possible.) There's also the
606 * further problem that the macro context, particularly
607 * %{_target_cpu}, disagrees with the info in the header.
610 if (spec->BACount >= 1) {
611 Spec nspec = spec->BASpecs[0];
612 spec->BASpecs = _free(spec->BASpecs);
613 spec = freeSpec(spec);
618 (void) rpmtsSetSpec(ts, spec);
622 /*@=infloops@*/ /* LCL: parsePart is modified @*/
624 /* Check for description in each package and add arch and os */
627 const char *arch = NULL;
628 const char *os = NULL;
631 rpmGetArchInfo(&arch, NULL);
632 rpmGetOsInfo(&os, NULL);
634 * XXX Capitalizing the 'L' is needed to insure that old
635 * XXX os-from-uname (e.g. "Linux") is compatible with the new
636 * XXX os-from-platform (e.g "linux" from "sparc-*-linux").
637 * XXX A copy of this string is embedded in headers.
639 if (!strcmp(os, "linux")) {
645 const char *platform = rpmExpand("%{_target_platform}", NULL);
646 const char *arch = rpmExpand("%{_target_cpu}", NULL);
647 const char *os = rpmExpand("%{_target_os}", NULL);
650 for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
651 if (!headerIsEntry(pkg->header, RPMTAG_DESCRIPTION)) {
653 (void) headerNVR(pkg->header, &name, NULL, NULL);
654 rpmError(RPMERR_BADSPEC, _("Package has no %%description: %s\n"),
656 spec = freeSpec(spec);
657 return RPMERR_BADSPEC;
660 (void) headerAddEntry(pkg->header, RPMTAG_OS, RPM_STRING_TYPE, os, 1);
661 (void) headerAddEntry(pkg->header, RPMTAG_ARCH,
662 RPM_STRING_TYPE, arch, 1);
663 (void) headerAddEntry(pkg->header, RPMTAG_PLATFORM,
664 RPM_STRING_TYPE, platform, 1);
666 pkg->ds = rpmdsThis(pkg->header, RPMTAG_REQUIRENAME, RPMSENSE_EQUAL);
673 platform = _free(platform);
680 (void) rpmtsSetSpec(ts, spec);