2 * \file build/parseSpec.c
3 * Top level dispatcher for spec file parsing.
8 #include <rpm/rpmtag.h>
9 #include <rpm/rpmlib.h> /* RPM_MACHTABLE & related */
10 #include <rpm/rpmbuild.h>
11 #include <rpm/rpmds.h>
12 #include <rpm/rpmts.h>
13 #include <rpm/rpmlog.h>
14 #include <rpm/rpmfileutil.h>
17 #define SKIPSPACE(s) { while (*(s) && risspace(*(s))) (s)++; }
18 #define SKIPNONSPACE(s) { while (*(s) && !risspace(*(s))) (s)++; }
20 /* XXX FIXME: strlen for these is calculated at runtime, preventing const */
21 static struct PartRec {
26 { PART_PREAMBLE, 0, "%package"},
27 { PART_PREP, 0, "%prep"},
28 { PART_BUILD, 0, "%build"},
29 { PART_INSTALL, 0, "%install"},
30 { PART_CHECK, 0, "%check"},
31 { PART_CLEAN, 0, "%clean"},
32 { PART_PREUN, 0, "%preun"},
33 { PART_POSTUN, 0, "%postun"},
34 { PART_PRETRANS, 0, "%pretrans"},
35 { PART_POSTTRANS, 0, "%posttrans"},
36 { PART_PRE, 0, "%pre"},
37 { PART_POST, 0, "%post"},
38 { PART_FILES, 0, "%files"},
39 { PART_CHANGELOG, 0, "%changelog"},
40 { PART_DESCRIPTION, 0, "%description"},
41 { PART_TRIGGERPOSTUN, 0, "%triggerpostun"},
42 { PART_TRIGGERPREIN, 0, "%triggerprein"},
43 { PART_TRIGGERUN, 0, "%triggerun"},
44 { PART_TRIGGERIN, 0, "%triggerin"},
45 { PART_TRIGGERIN, 0, "%trigger"},
46 { PART_VERIFYSCRIPT, 0, "%verifyscript"},
52 static inline void initParts(struct PartRec *p)
54 for (; p->token != NULL; p++)
55 p->len = strlen(p->token);
58 rpmParseState isPart(const char *line)
62 if (partList[0].len == 0)
65 for (p = partList; p->token != NULL; p++) {
67 if (rstrncasecmp(line, p->token, p->len))
70 if (c == '\0' || risspace(c))
74 return (p->token ? p->part : PART_NONE);
79 static int matchTok(const char *token, const char *line)
81 const char *b, *be = line;
82 size_t toklen = strlen(token);
85 while ( *(b = be) != '\0' ) {
91 if (toklen != (be-b) || rstrncasecmp(token, b, (be-b)))
100 void handleComments(char *s)
109 static void forceIncludeFile(rpmSpec spec, const char * fileName)
113 ofi = newOpenFileInfo();
114 ofi->fileName = xstrdup(fileName);
115 ofi->next = spec->fileStack;
116 spec->fileStack = ofi;
119 static int restoreFirstChar(rpmSpec spec)
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';
130 static int copyNextLineFromOFI(rpmSpec spec, OFI_t *ofi)
134 /* Expand next line from file into line buffer */
135 if (!(spec->nextline && *spec->nextline)) {
136 int pc = 0, bc = 0, nc = 0;
138 to = spec->lbufPtr ? spec->lbufPtr : spec->lbuf;
141 while (*from && ch != '\n')
142 ch = *to++ = *from++;
147 /* Check if we need another line before expanding the buffer. */
148 for (p = spec->lbuf; *p; p++) {
152 case '\n': p++, nc = 1; break;
157 case '\n': nc = 0; break;
160 case '{': p++, bc++; break;
161 case '(': p++, pc++; break;
162 case '%': p++; break;
165 case '{': if (bc > 0) bc++; break;
166 case '}': if (bc > 0) bc--; break;
167 case '(': if (pc > 0) pc++; break;
168 case ')': if (pc > 0) pc--; break;
172 /* If it doesn't, ask for one more line. We need a better
173 * error code for this. */
174 if (pc || bc || nc ) {
178 spec->lbufPtr = spec->lbuf;
180 /* Don't expand macros (eg. %define) in false branch of %if clause */
181 if (spec->readStack->reading &&
182 expandMacros(spec, spec->macros, spec->lbuf, sizeof(spec->lbuf))) {
183 rpmlog(RPMLOG_ERR, _("line %d: %s\n"),
184 spec->lineNum, spec->lbuf);
187 spec->nextline = spec->lbuf;
192 static void copyNextLineFinish(rpmSpec spec, int strip)
197 /* Find next line in expanded line buffer */
198 spec->line = last = spec->nextline;
200 while (*spec->nextline && ch != '\n') {
201 ch = *spec->nextline++;
203 last = spec->nextline;
206 /* Save 1st char of next line in order to terminate current line. */
207 if (*spec->nextline != '\0') {
208 spec->nextpeekc = *spec->nextline;
209 *spec->nextline = '\0';
212 if (strip & STRIP_COMMENTS)
213 handleComments(spec->line);
215 if (strip & STRIP_TRAILINGSPACE)
219 static int readLineFromOFI(rpmSpec spec, OFI_t *ofi)
222 /* Make sure the current file is open */
223 if (ofi->fd == NULL) {
224 ofi->fd = Fopen(ofi->fileName, "r.fpio");
225 if (ofi->fd == NULL || Ferror(ofi->fd)) {
227 rpmlog(RPMLOG_ERR, _("Unable to open %s: %s\n"),
228 ofi->fileName, Fstrerror(ofi->fd));
231 spec->lineNum = ofi->lineNum = 0;
234 /* Make sure we have something in the read buffer */
235 if (!(ofi->readPtr && *(ofi->readPtr))) {
236 FILE * f = fdGetFILE(ofi->fd);
237 if (f == NULL || !fgets(ofi->readBuf, BUFSIZ, f)) {
239 if (spec->readStack->next) {
240 rpmlog(RPMLOG_ERR, _("Unclosed %%if\n"));
244 /* remove this file from the stack */
245 spec->fileStack = ofi->next;
246 (void) Fclose(ofi->fd);
247 ofi->fileName = _free(ofi->fileName);
250 /* only on last file do we signal EOF to caller */
251 ofi = spec->fileStack;
255 /* otherwise, go back and try the read again. */
258 ofi->readPtr = ofi->readBuf;
260 spec->lineNum = ofi->lineNum;
262 speclines sl = spec->sl;
263 if (sl->sl_nlines == sl->sl_nalloc) {
264 sl->sl_nalloc += 100;
265 sl->sl_lines = (char **) xrealloc(sl->sl_lines,
266 sl->sl_nalloc * sizeof(*(sl->sl_lines)));
268 sl->sl_lines[sl->sl_nlines++] = xstrdup(ofi->readBuf);
274 int readLine(rpmSpec spec, int strip)
278 struct ReadLevelEntry *rl;
279 OFI_t *ofi = spec->fileStack;
282 if (!restoreFirstChar(spec)) {
284 if ((rc = readLineFromOFI(spec, ofi)) != 0)
287 /* Copy next file line into the spec line buffer */
289 if ((rc = copyNextLineFromOFI(spec, ofi)) != 0) {
290 if (rc == RPMRC_FAIL)
296 copyNextLineFinish(spec, strip);
302 if (!spec->readStack->reading && !strncmp("%if", s, sizeof("%if")-1)) {
304 } else if (! strncmp("%ifarch", s, sizeof("%ifarch")-1)) {
305 char *arch = rpmExpand("%{_target_cpu}", NULL);
307 match = matchTok(arch, s);
309 } else if (! strncmp("%ifnarch", s, sizeof("%ifnarch")-1)) {
310 char *arch = rpmExpand("%{_target_cpu}", NULL);
312 match = !matchTok(arch, s);
314 } else if (! strncmp("%ifos", s, sizeof("%ifos")-1)) {
315 char *os = rpmExpand("%{_target_os}", NULL);
317 match = matchTok(os, s);
319 } else if (! strncmp("%ifnos", s, sizeof("%ifnos")-1)) {
320 char *os = rpmExpand("%{_target_os}", NULL);
322 match = !matchTok(os, s);
324 } else if (! strncmp("%if", s, sizeof("%if")-1)) {
326 match = parseExpressionBoolean(spec, s);
329 _("%s:%d: parseExpressionBoolean returns %d\n"),
330 ofi->fileName, ofi->lineNum, match);
333 } else if (! strncmp("%else", s, sizeof("%else")-1)) {
335 if (! spec->readStack->next) {
336 /* Got an else with no %if ! */
338 _("%s:%d: Got a %%else with no %%if\n"),
339 ofi->fileName, ofi->lineNum);
342 spec->readStack->reading =
343 spec->readStack->next->reading && ! spec->readStack->reading;
344 spec->line[0] = '\0';
345 } else if (! strncmp("%endif", s, sizeof("%endif")-1)) {
347 if (! spec->readStack->next) {
348 /* Got an end with no %if ! */
350 _("%s:%d: Got a %%endif with no %%if\n"),
351 ofi->fileName, ofi->lineNum);
354 rl = spec->readStack;
355 spec->readStack = spec->readStack->next;
357 spec->line[0] = '\0';
358 } else if (! strncmp("%include", s, sizeof("%include")-1)) {
359 char *fileName, *endFileName, *p;
363 if (! risspace(*fileName)) {
364 rpmlog(RPMLOG_ERR, _("malformed %%include statement\n"));
368 endFileName = fileName;
369 SKIPNONSPACE(endFileName);
373 rpmlog(RPMLOG_ERR, _("malformed %%include statement\n"));
378 forceIncludeFile(spec, fileName);
380 ofi = spec->fileStack;
385 rl = xmalloc(sizeof(*rl));
386 rl->reading = spec->readStack->reading && match;
387 rl->next = spec->readStack;
388 spec->readStack = rl;
389 spec->line[0] = '\0';
392 if (! spec->readStack->reading) {
393 spec->line[0] = '\0';
396 /* FIX: spec->readStack->next should be dependent */
400 void closeSpec(rpmSpec spec)
404 while (spec->fileStack) {
405 ofi = spec->fileStack;
406 spec->fileStack = spec->fileStack->next;
407 if (ofi->fd) (void) Fclose(ofi->fd);
408 ofi->fileName = _free(ofi->fileName);
413 extern int noLang; /* XXX FIXME: pass as arg */
415 int parseSpec(rpmts ts, const char *specFile, const char *rootDir,
416 const char *buildRoot, int recursing, const char *passPhrase,
417 const char *cookie, int anyarch, int force)
419 rpmParseState parsePart = PART_PREAMBLE;
420 int initialPackage = 1;
424 /* Set up a new Spec structure with no packages. */
428 * Note: rpmGetPath should guarantee a "canonical" path. That means
429 * that the following pathologies should be weeded out:
432 * /.././../usr/../bin//./sh (XXX FIXME: dots not handled yet)
434 spec->specFile = rpmGetPath(specFile, NULL);
435 spec->fileStack = newOpenFileInfo();
436 spec->fileStack->fileName = xstrdup(spec->specFile);
438 if (*buildRoot == '\0') {
440 _("BuildRoot couldn't be empty\n"));
443 if (!strcmp(buildRoot, "/")) {
445 _("BuildRoot can not be \"/\"\n"));
448 spec->gotBuildRoot = 1;
449 spec->buildRoot = xstrdup(buildRoot);
450 addMacro(spec->macros, "buildroot", NULL, buildRoot, RMIL_SPEC);
452 addMacro(NULL, "_docdir", NULL, "%{_defaultdocdir}", RMIL_SPEC);
453 spec->recursing = recursing;
454 spec->anyarch = anyarch;
458 spec->rootDir = xstrdup(rootDir);
460 spec->passPhrase = xstrdup(passPhrase);
462 spec->cookie = xstrdup(cookie);
464 spec->timeCheck = rpmExpandNumeric("%{_timecheck}");
466 /* All the parse*() functions expect to have a line pre-read */
467 /* in the spec's line buffer. Except for parsePreamble(), */
468 /* which handles the initial entry into a spec file. */
470 /* LCL: parsePart is modified @*/
471 while (parsePart != PART_NONE) {
474 case PART_ERROR: /* fallthrough */
479 parsePart = parsePreamble(spec, initialPackage);
483 parsePart = parsePrep(spec);
489 parsePart = parseBuildInstallClean(spec, parsePart);
492 parsePart = parseChangelog(spec);
494 case PART_DESCRIPTION:
495 parsePart = parseDescription(spec);
504 case PART_VERIFYSCRIPT:
505 case PART_TRIGGERPREIN:
508 case PART_TRIGGERPOSTUN:
509 parsePart = parseScript(spec, parsePart);
513 parsePart = parseFiles(spec);
516 case PART_NONE: /* XXX avoid gcc whining */
518 case PART_BUILDARCHITECTURES:
522 if (goterror || parsePart >= PART_LAST) {
526 if (parsePart == PART_BUILDARCHITECTURES) {
532 /* LCL: sizeof(spec->BASpecs[0]) -nullderef whine here */
533 spec->BASpecs = xcalloc(spec->BACount, sizeof(*spec->BASpecs));
535 if (spec->BANames != NULL)
536 for (x = 0; x < spec->BACount; x++) {
538 /* Skip if not arch is not compatible. */
539 if (!rpmMachineScore(RPM_MACHTABLE_BUILDARCH, spec->BANames[x]))
541 addMacro(NULL, "_target_cpu", NULL, spec->BANames[x], RMIL_RPMRC);
542 spec->BASpecs[index] = NULL;
543 if (parseSpec(ts, specFile, spec->rootDir, buildRoot, 1,
544 passPhrase, cookie, anyarch, force)
545 || (spec->BASpecs[index] = rpmtsSetSpec(ts, NULL)) == NULL)
547 spec->BACount = index;
550 delMacro(NULL, "_target_cpu");
554 spec->BACount = index;
557 _("No compatible architectures found for build\n"));
562 * Return the 1st child's fully parsed Spec structure.
563 * The restart of the parse when encountering BuildArch
564 * causes problems for "rpm -q --specfile". This is
565 * still a hack because there may be more than 1 arch
566 * specified (unlikely but possible.) There's also the
567 * further problem that the macro context, particularly
568 * %{_target_cpu}, disagrees with the info in the header.
570 if (spec->BACount >= 1) {
571 rpmSpec nspec = spec->BASpecs[0];
572 spec->BASpecs = _free(spec->BASpecs);
573 spec = freeSpec(spec);
577 (void) rpmtsSetSpec(ts, spec);
581 /* LCL: parsePart is modified @*/
583 /* Check for description in each package and add arch and os */
585 char *platform = rpmExpand("%{_target_platform}", NULL);
586 char *arch = rpmExpand("%{_target_cpu}", NULL);
587 char *os = rpmExpand("%{_target_os}", NULL);
589 for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
590 if (!headerIsEntry(pkg->header, RPMTAG_DESCRIPTION)) {
592 (void) headerNVR(pkg->header, &name, NULL, NULL);
593 rpmlog(RPMLOG_ERR, _("Package has no %%description: %s\n"), name);
597 (void) headerAddEntry(pkg->header, RPMTAG_OS, RPM_STRING_TYPE, os, 1);
598 (void) headerAddEntry(pkg->header, RPMTAG_ARCH,
599 RPM_STRING_TYPE, arch, 1);
600 (void) headerAddEntry(pkg->header, RPMTAG_PLATFORM,
601 RPM_STRING_TYPE, platform, 1);
603 pkg->ds = rpmdsThis(pkg->header, RPMTAG_REQUIRENAME, RPMSENSE_EQUAL);
607 platform = _free(platform);
613 (void) rpmtsSetSpec(ts, spec);
618 spec = freeSpec(spec);