2 * \file build/parseSpec.c
3 * Top level dispatcher for spec file parsing.
8 #include "rpmio_internal.h"
17 static struct PartRec {
22 { PART_PREAMBLE, 0, "%package"},
23 { PART_PREP, 0, "%prep"},
24 { PART_BUILD, 0, "%build"},
25 { PART_INSTALL, 0, "%install"},
26 { PART_CHECK, 0, "%check"},
27 { PART_CLEAN, 0, "%clean"},
28 { PART_PREUN, 0, "%preun"},
29 { PART_POSTUN, 0, "%postun"},
30 { PART_PRETRANS, 0, "%pretrans"},
31 { PART_POSTTRANS, 0, "%posttrans"},
32 { PART_PRE, 0, "%pre"},
33 { PART_POST, 0, "%post"},
34 { PART_FILES, 0, "%files"},
35 { PART_CHANGELOG, 0, "%changelog"},
36 { PART_DESCRIPTION, 0, "%description"},
37 { PART_TRIGGERPOSTUN, 0, "%triggerpostun"},
38 { PART_TRIGGERUN, 0, "%triggerun"},
39 { PART_TRIGGERIN, 0, "%triggerin"},
40 { PART_TRIGGERIN, 0, "%trigger"},
41 { PART_VERIFYSCRIPT, 0, "%verifyscript"},
47 static inline void initParts(struct PartRec *p)
49 for (; p->token != NULL; p++)
50 p->len = strlen(p->token);
53 rpmParseState isPart(const char *line)
57 if (partList[0].len == 0)
60 for (p = partList; p->token != NULL; p++) {
62 if (xstrncasecmp(line, p->token, p->len))
65 if (c == '\0' || xisspace(c))
69 return (p->token ? p->part : PART_NONE);
74 static int matchTok(const char *token, const char *line)
76 const char *b, *be = line;
77 size_t toklen = strlen(token);
80 while ( *(b = be) != '\0' ) {
86 if (toklen != (be-b) || xstrncasecmp(token, b, (be-b)))
95 void handleComments(char *s)
104 static void forceIncludeFile(rpmSpec spec, const char * fileName)
108 ofi = newOpenFileInfo();
109 ofi->fileName = xstrdup(fileName);
110 ofi->next = spec->fileStack;
111 spec->fileStack = ofi;
116 static int copyNextLine(rpmSpec spec, OFI_t *ofi, int strip)
121 /* Restore 1st char in (possible) next line */
122 if (spec->nextline != NULL && spec->nextpeekc != '\0') {
123 *spec->nextline = spec->nextpeekc;
124 spec->nextpeekc = '\0';
126 /* Expand next line from file into line buffer */
127 if (!(spec->nextline && *spec->nextline)) {
128 int pc = 0, bc = 0, nc = 0;
130 to = spec->lbufPtr ? spec->lbufPtr : spec->lbuf;
133 while (*from && ch != '\n')
134 ch = *to++ = *from++;
139 /* Check if we need another line before expanding the buffer. */
140 for (p = spec->lbuf; *p; p++) {
144 case '\n': p++, nc = 1; break;
149 case '\n': nc = 0; break;
152 case '{': p++, bc++; break;
153 case '(': p++, pc++; break;
154 case '%': p++; break;
157 case '{': if (bc > 0) bc++; break;
158 case '}': if (bc > 0) bc--; break;
159 case '(': if (pc > 0) pc++; break;
160 case ')': if (pc > 0) pc--; break;
164 /* If it doesn't, ask for one more line. We need a better
165 * error code for this. */
166 if (pc || bc || nc ) {
168 return RPMERR_UNMATCHEDIF;
170 spec->lbufPtr = spec->lbuf;
172 /* Don't expand macros (eg. %define) in false branch of %if clause */
173 if (spec->readStack->reading &&
174 expandMacros(spec, spec->macros, spec->lbuf, sizeof(spec->lbuf))) {
175 rpmlog(RPMERR_BADSPEC, _("line %d: %s\n"),
176 spec->lineNum, spec->lbuf);
177 return RPMERR_BADSPEC;
179 spec->nextline = spec->lbuf;
182 /* Find next line in expanded line buffer */
183 spec->line = last = spec->nextline;
185 while (*spec->nextline && ch != '\n') {
186 ch = *spec->nextline++;
188 last = spec->nextline;
191 /* Save 1st char of next line in order to terminate current line. */
192 if (*spec->nextline != '\0') {
193 spec->nextpeekc = *spec->nextline;
194 *spec->nextline = '\0';
197 if (strip & STRIP_COMMENTS)
198 handleComments(spec->line);
200 if (strip & STRIP_TRAILINGSPACE)
206 int readLine(rpmSpec spec, int strip)
214 struct ReadLevelEntry *rl;
215 OFI_t *ofi = spec->fileStack;
219 /* Make sure the current file is open */
220 if (ofi->fd == NULL) {
221 ofi->fd = Fopen(ofi->fileName, "r.fpio");
222 if (ofi->fd == NULL || Ferror(ofi->fd)) {
224 rpmlog(RPMERR_BADSPEC, _("Unable to open %s: %s\n"),
225 ofi->fileName, Fstrerror(ofi->fd));
226 return RPMERR_BADSPEC;
228 spec->lineNum = ofi->lineNum = 0;
231 /* Make sure we have something in the read buffer */
232 if (!(ofi->readPtr && *(ofi->readPtr))) {
234 FILE * f = fdGetFp(ofi->fd);
235 if (f == NULL || !fgets(ofi->readBuf, BUFSIZ, f)) {
237 if (spec->readStack->next) {
238 rpmlog(RPMERR_UNMATCHEDIF, _("Unclosed %%if\n"));
239 return RPMERR_UNMATCHEDIF;
242 /* remove this file from the stack */
243 spec->fileStack = ofi->next;
244 (void) Fclose(ofi->fd);
245 ofi->fileName = _free(ofi->fileName);
248 /* only on last file do we signal EOF to caller */
249 ofi = spec->fileStack;
253 /* otherwise, go back and try the read again. */
256 ofi->readPtr = ofi->readBuf;
258 spec->lineNum = ofi->lineNum;
260 speclines sl = spec->sl;
261 if (sl->sl_nlines == sl->sl_nalloc) {
262 sl->sl_nalloc += 100;
263 sl->sl_lines = (char **) xrealloc(sl->sl_lines,
264 sl->sl_nalloc * sizeof(*(sl->sl_lines)));
266 sl->sl_lines[sl->sl_nlines++] = xstrdup(ofi->readBuf);
272 rpmGetArchInfo(&arch, NULL);
274 rpmGetOsInfo(&os, NULL);
277 /* Copy next file line into the spec line buffer */
278 if ((rc = copyNextLine(spec, ofi, strip)) != 0) {
279 if (rc == RPMERR_UNMATCHEDIF)
288 if (!spec->readStack->reading && !strncmp("%if", s, sizeof("%if")-1)) {
290 } else if (! strncmp("%ifarch", s, sizeof("%ifarch")-1)) {
291 const char *arch = rpmExpand("%{_target_cpu}", NULL);
293 match = matchTok(arch, s);
295 } else if (! strncmp("%ifnarch", s, sizeof("%ifnarch")-1)) {
296 const char *arch = rpmExpand("%{_target_cpu}", NULL);
298 match = !matchTok(arch, s);
300 } else if (! strncmp("%ifos", s, sizeof("%ifos")-1)) {
301 const char *os = rpmExpand("%{_target_os}", NULL);
303 match = matchTok(os, s);
305 } else if (! strncmp("%ifnos", s, sizeof("%ifnos")-1)) {
306 const char *os = rpmExpand("%{_target_os}", NULL);
308 match = !matchTok(os, s);
310 } else if (! strncmp("%if", s, sizeof("%if")-1)) {
312 match = parseExpressionBoolean(spec, s);
314 rpmlog(RPMERR_UNMATCHEDIF,
315 _("%s:%d: parseExpressionBoolean returns %d\n"),
316 ofi->fileName, ofi->lineNum, match);
317 return RPMERR_BADSPEC;
319 } else if (! strncmp("%else", s, sizeof("%else")-1)) {
321 if (! spec->readStack->next) {
322 /* Got an else with no %if ! */
323 rpmlog(RPMERR_UNMATCHEDIF,
324 _("%s:%d: Got a %%else with no %%if\n"),
325 ofi->fileName, ofi->lineNum);
326 return RPMERR_UNMATCHEDIF;
328 spec->readStack->reading =
329 spec->readStack->next->reading && ! spec->readStack->reading;
330 spec->line[0] = '\0';
331 } else if (! strncmp("%endif", s, sizeof("%endif")-1)) {
333 if (! spec->readStack->next) {
334 /* Got an end with no %if ! */
335 rpmlog(RPMERR_UNMATCHEDIF,
336 _("%s:%d: Got a %%endif with no %%if\n"),
337 ofi->fileName, ofi->lineNum);
338 return RPMERR_UNMATCHEDIF;
340 rl = spec->readStack;
341 spec->readStack = spec->readStack->next;
343 spec->line[0] = '\0';
344 } else if (! strncmp("%include", s, sizeof("%include")-1)) {
345 char *fileName, *endFileName, *p;
349 if (! xisspace(*fileName)) {
350 rpmlog(RPMERR_BADSPEC, _("malformed %%include statement\n"));
351 return RPMERR_BADSPEC;
354 endFileName = fileName;
355 SKIPNONSPACE(endFileName);
359 rpmlog(RPMERR_BADSPEC, _("malformed %%include statement\n"));
360 return RPMERR_BADSPEC;
364 forceIncludeFile(spec, fileName);
366 ofi = spec->fileStack;
371 rl = xmalloc(sizeof(*rl));
372 rl->reading = spec->readStack->reading && match;
373 rl->next = spec->readStack;
374 spec->readStack = rl;
375 spec->line[0] = '\0';
378 if (! spec->readStack->reading) {
379 spec->line[0] = '\0';
382 /* FIX: spec->readStack->next should be dependent */
386 void closeSpec(rpmSpec spec)
390 while (spec->fileStack) {
391 ofi = spec->fileStack;
392 spec->fileStack = spec->fileStack->next;
393 if (ofi->fd) (void) Fclose(ofi->fd);
394 ofi->fileName = _free(ofi->fileName);
399 extern int noLang; /* XXX FIXME: pass as arg */
401 int parseSpec(rpmts ts, const char *specFile, const char *rootURL,
402 const char *buildRootURL, int recursing, const char *passPhrase,
403 const char *cookie, int anyarch, int force)
405 rpmParseState parsePart = PART_PREAMBLE;
406 int initialPackage = 1;
408 const char *saveArch;
413 /* Set up a new Spec structure with no packages. */
417 * Note: rpmGetPath should guarantee a "canonical" path. That means
418 * that the following pathologies should be weeded out:
421 * /.././../usr/../bin//./sh (XXX FIXME: dots not handled yet)
423 spec->specFile = rpmGetPath(specFile, NULL);
424 spec->fileStack = newOpenFileInfo();
425 spec->fileStack->fileName = xstrdup(spec->specFile);
427 const char * buildRoot;
428 (void) urlPath(buildRootURL, &buildRoot);
429 if (*buildRoot == '\0') buildRoot = "/";
430 if (!strcmp(buildRoot, "/")) {
431 rpmlog(RPMERR_BADSPEC,
432 _("BuildRoot can not be \"/\": %s\n"), buildRootURL);
433 return RPMERR_BADSPEC;
435 spec->gotBuildRootURL = 1;
436 spec->buildRootURL = xstrdup(buildRootURL);
437 addMacro(spec->macros, "buildroot", NULL, buildRoot, RMIL_SPEC);
439 addMacro(NULL, "_docdir", NULL, "%{_defaultdocdir}", RMIL_SPEC);
440 spec->recursing = recursing;
441 spec->anyarch = anyarch;
445 spec->rootURL = xstrdup(rootURL);
447 spec->passPhrase = xstrdup(passPhrase);
449 spec->cookie = xstrdup(cookie);
451 spec->timeCheck = rpmExpandNumeric("%{_timecheck}");
453 /* All the parse*() functions expect to have a line pre-read */
454 /* in the spec's line buffer. Except for parsePreamble(), */
455 /* which handles the initial entry into a spec file. */
457 /* LCL: parsePart is modified @*/
458 while (parsePart < PART_LAST && parsePart != PART_NONE) {
461 parsePart = parsePreamble(spec, initialPackage);
465 parsePart = parsePrep(spec);
471 parsePart = parseBuildInstallClean(spec, parsePart);
474 parsePart = parseChangelog(spec);
476 case PART_DESCRIPTION:
477 parsePart = parseDescription(spec);
486 case PART_VERIFYSCRIPT:
489 case PART_TRIGGERPOSTUN:
490 parsePart = parseScript(spec, parsePart);
494 parsePart = parseFiles(spec);
497 case PART_NONE: /* XXX avoid gcc whining */
499 case PART_BUILDARCHITECTURES:
503 if (parsePart >= PART_LAST) {
504 spec = freeSpec(spec);
508 if (parsePart == PART_BUILDARCHITECTURES) {
514 /* LCL: sizeof(spec->BASpecs[0]) -nullderef whine here */
515 spec->BASpecs = xcalloc(spec->BACount, sizeof(*spec->BASpecs));
517 if (spec->BANames != NULL)
518 for (x = 0; x < spec->BACount; x++) {
520 /* Skip if not arch is not compatible. */
521 if (!rpmMachineScore(RPM_MACHTABLE_BUILDARCH, spec->BANames[x]))
524 rpmGetMachine(&saveArch, NULL);
525 saveArch = xstrdup(saveArch);
526 rpmSetMachine(spec->BANames[x], NULL);
528 addMacro(NULL, "_target_cpu", NULL, spec->BANames[x], RMIL_RPMRC);
530 spec->BASpecs[index] = NULL;
531 if (parseSpec(ts, specFile, spec->rootURL, buildRootURL, 1,
532 passPhrase, cookie, anyarch, force)
533 || (spec->BASpecs[index] = rpmtsSetSpec(ts, NULL)) == NULL)
535 spec->BACount = index;
536 spec = freeSpec(spec);
537 return RPMERR_BADSPEC;
540 rpmSetMachine(saveArch, NULL);
541 saveArch = _free(saveArch);
543 delMacro(NULL, "_target_cpu");
548 spec->BACount = index;
550 rpmlog(RPMERR_BADSPEC,
551 _("No compatible architectures found for build\n"));
552 spec = freeSpec(spec);
553 return RPMERR_BADSPEC;
557 * Return the 1st child's fully parsed Spec structure.
558 * The restart of the parse when encountering BuildArch
559 * causes problems for "rpm -q --specfile". This is
560 * still a hack because there may be more than 1 arch
561 * specified (unlikely but possible.) There's also the
562 * further problem that the macro context, particularly
563 * %{_target_cpu}, disagrees with the info in the header.
565 if (spec->BACount >= 1) {
566 rpmSpec nspec = spec->BASpecs[0];
567 spec->BASpecs = _free(spec->BASpecs);
568 spec = freeSpec(spec);
572 (void) rpmtsSetSpec(ts, spec);
576 /* LCL: parsePart is modified @*/
578 /* Check for description in each package and add arch and os */
581 const char *arch = NULL;
582 const char *os = NULL;
585 rpmGetArchInfo(&arch, NULL);
586 rpmGetOsInfo(&os, NULL);
588 * XXX Capitalizing the 'L' is needed to insure that old
589 * XXX os-from-uname (e.g. "Linux") is compatible with the new
590 * XXX os-from-platform (e.g "linux" from "sparc-*-linux").
591 * XXX A copy of this string is embedded in headers.
593 if (!strcmp(os, "linux")) {
599 const char *platform = rpmExpand("%{_target_platform}", NULL);
600 const char *arch = rpmExpand("%{_target_cpu}", NULL);
601 const char *os = rpmExpand("%{_target_os}", NULL);
604 for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
605 if (!headerIsEntry(pkg->header, RPMTAG_DESCRIPTION)) {
607 (void) headerNVR(pkg->header, &name, NULL, NULL);
608 rpmlog(RPMERR_BADSPEC, _("Package has no %%description: %s\n"),
610 spec = freeSpec(spec);
611 return RPMERR_BADSPEC;
614 (void) headerAddEntry(pkg->header, RPMTAG_OS, RPM_STRING_TYPE, os, 1);
615 (void) headerAddEntry(pkg->header, RPMTAG_ARCH,
616 RPM_STRING_TYPE, arch, 1);
617 (void) headerAddEntry(pkg->header, RPMTAG_PLATFORM,
618 RPM_STRING_TYPE, platform, 1);
620 pkg->ds = rpmdsThis(pkg->header, RPMTAG_REQUIRENAME, RPMSENSE_EQUAL);
627 platform = _free(platform);
634 (void) rpmtsSetSpec(ts, spec);