2 * \file build/parseSpec.c
3 * Top level dispatcher for spec file parsing.
8 #include <rpm/rpmtypes.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 #define LEN_AND_STR(_tag) (sizeof(_tag)-1), (_tag)
22 static const struct PartRec {
26 } const partList[] = {
27 { PART_PREAMBLE, LEN_AND_STR("%package")},
28 { PART_PREP, LEN_AND_STR("%prep")},
29 { PART_BUILD, LEN_AND_STR("%build")},
30 { PART_INSTALL, LEN_AND_STR("%install")},
31 { PART_CHECK, LEN_AND_STR("%check")},
32 { PART_CLEAN, LEN_AND_STR("%clean")},
33 { PART_PREUN, LEN_AND_STR("%preun")},
34 { PART_POSTUN, LEN_AND_STR("%postun")},
35 { PART_PRETRANS, LEN_AND_STR("%pretrans")},
36 { PART_POSTTRANS, LEN_AND_STR("%posttrans")},
37 { PART_PRE, LEN_AND_STR("%pre")},
38 { PART_POST, LEN_AND_STR("%post")},
39 { PART_FILES, LEN_AND_STR("%files")},
40 { PART_CHANGELOG, LEN_AND_STR("%changelog")},
41 { PART_DESCRIPTION, LEN_AND_STR("%description")},
42 { PART_TRIGGERPOSTUN, LEN_AND_STR("%triggerpostun")},
43 { PART_TRIGGERPREIN, LEN_AND_STR("%triggerprein")},
44 { PART_TRIGGERUN, LEN_AND_STR("%triggerun")},
45 { PART_TRIGGERIN, LEN_AND_STR("%triggerin")},
46 { PART_TRIGGERIN, LEN_AND_STR("%trigger")},
47 { PART_VERIFYSCRIPT, LEN_AND_STR("%verifyscript")},
51 rpmParseState isPart(const char *line)
53 const struct PartRec *p;
55 for (p = partList; p->token != NULL; p++) {
57 if (rstrncasecmp(line, p->token, p->len))
60 if (c == '\0' || risspace(c))
64 return (p->token ? p->part : PART_NONE);
69 static int matchTok(const char *token, const char *line)
71 const char *b, *be = line;
72 size_t toklen = strlen(token);
75 while ( *(b = be) != '\0' ) {
81 if (toklen != (be-b) || rstrncasecmp(token, b, (be-b)))
90 void handleComments(char *s)
99 static void forceIncludeFile(rpmSpec spec, const char * fileName)
103 ofi = newOpenFileInfo();
104 ofi->fileName = xstrdup(fileName);
105 ofi->next = spec->fileStack;
106 spec->fileStack = ofi;
109 static int restoreFirstChar(rpmSpec spec)
111 /* Restore 1st char in (possible) next line */
112 if (spec->nextline != NULL && spec->nextpeekc != '\0') {
113 *spec->nextline = spec->nextpeekc;
114 spec->nextpeekc = '\0';
120 /* Return zero on success, 1 if we need to read more and -1 on errors. */
121 static int copyNextLineFromOFI(rpmSpec spec, OFI_t *ofi)
125 /* Expand next line from file into line buffer */
126 if (!(spec->nextline && *spec->nextline)) {
127 int pc = 0, bc = 0, nc = 0;
129 to = spec->lbufPtr ? spec->lbufPtr : spec->lbuf;
132 while (from && *from && ch != '\n')
133 ch = *to++ = *from++;
138 /* Check if we need another line before expanding the buffer. */
139 for (p = spec->lbuf; *p; p++) {
143 case '\n': p++, nc = 1; break;
148 case '\n': nc = 0; break;
151 case '{': p++, bc++; break;
152 case '(': p++, pc++; break;
153 case '%': p++; break;
156 case '{': if (bc > 0) bc++; break;
157 case '}': if (bc > 0) bc--; break;
158 case '(': if (pc > 0) pc++; break;
159 case ')': if (pc > 0) pc--; break;
163 /* If it doesn't, ask for one more line. */
164 if (pc || bc || nc ) {
168 spec->lbufPtr = spec->lbuf;
170 /* Don't expand macros (eg. %define) in false branch of %if clause */
171 if (spec->readStack->reading &&
172 expandMacros(spec, spec->macros, spec->lbuf, sizeof(spec->lbuf))) {
173 rpmlog(RPMLOG_ERR, _("line %d: %s\n"),
174 spec->lineNum, spec->lbuf);
177 spec->nextline = spec->lbuf;
182 static void copyNextLineFinish(rpmSpec spec, int strip)
187 /* Find next line in expanded line buffer */
188 spec->line = last = spec->nextline;
190 while (*spec->nextline && ch != '\n') {
191 ch = *spec->nextline++;
193 last = spec->nextline;
196 /* Save 1st char of next line in order to terminate current line. */
197 if (*spec->nextline != '\0') {
198 spec->nextpeekc = *spec->nextline;
199 *spec->nextline = '\0';
202 if (strip & STRIP_COMMENTS)
203 handleComments(spec->line);
205 if (strip & STRIP_TRAILINGSPACE)
209 static int readLineFromOFI(rpmSpec spec, OFI_t *ofi)
212 /* Make sure the current file is open */
213 if (ofi->fd == NULL) {
214 ofi->fd = Fopen(ofi->fileName, "r.fpio");
215 if (ofi->fd == NULL || Ferror(ofi->fd)) {
217 rpmlog(RPMLOG_ERR, _("Unable to open %s: %s\n"),
218 ofi->fileName, Fstrerror(ofi->fd));
221 spec->lineNum = ofi->lineNum = 0;
224 /* Make sure we have something in the read buffer */
225 if (!(ofi->readPtr && *(ofi->readPtr))) {
226 FILE * f = fdGetFILE(ofi->fd);
227 if (f == NULL || !fgets(ofi->readBuf, BUFSIZ, f)) {
229 if (spec->readStack->next) {
230 rpmlog(RPMLOG_ERR, _("Unclosed %%if\n"));
234 /* remove this file from the stack */
235 spec->fileStack = ofi->next;
236 (void) Fclose(ofi->fd);
237 ofi->fileName = _free(ofi->fileName);
240 /* only on last file do we signal EOF to caller */
241 ofi = spec->fileStack;
245 /* otherwise, go back and try the read again. */
248 ofi->readPtr = ofi->readBuf;
250 spec->lineNum = ofi->lineNum;
252 speclines sl = spec->sl;
253 if (sl->sl_nlines == sl->sl_nalloc) {
254 sl->sl_nalloc += 100;
255 sl->sl_lines = (char **) xrealloc(sl->sl_lines,
256 sl->sl_nalloc * sizeof(*(sl->sl_lines)));
258 sl->sl_lines[sl->sl_nlines++] = xstrdup(ofi->readBuf);
264 int readLine(rpmSpec spec, int strip)
268 struct ReadLevelEntry *rl;
269 OFI_t *ofi = spec->fileStack;
272 if (!restoreFirstChar(spec)) {
274 if ((rc = readLineFromOFI(spec, ofi)) != 0)
277 /* Copy next file line into the spec line buffer */
278 rc = copyNextLineFromOFI(spec, ofi);
286 copyNextLineFinish(spec, strip);
292 if (!spec->readStack->reading && !strncmp("%if", s, sizeof("%if")-1)) {
294 } else if (! strncmp("%ifarch", s, sizeof("%ifarch")-1)) {
295 char *arch = rpmExpand("%{_target_cpu}", NULL);
297 match = matchTok(arch, s);
299 } else if (! strncmp("%ifnarch", s, sizeof("%ifnarch")-1)) {
300 char *arch = rpmExpand("%{_target_cpu}", NULL);
302 match = !matchTok(arch, s);
304 } else if (! strncmp("%ifos", s, sizeof("%ifos")-1)) {
305 char *os = rpmExpand("%{_target_os}", NULL);
307 match = matchTok(os, s);
309 } else if (! strncmp("%ifnos", s, sizeof("%ifnos")-1)) {
310 char *os = rpmExpand("%{_target_os}", NULL);
312 match = !matchTok(os, s);
314 } else if (! strncmp("%if", s, sizeof("%if")-1)) {
316 match = parseExpressionBoolean(spec, s);
319 _("%s:%d: parseExpressionBoolean returns %d\n"),
320 ofi->fileName, ofi->lineNum, match);
323 } else if (! strncmp("%else", s, sizeof("%else")-1)) {
325 if (! spec->readStack->next) {
326 /* Got an else with no %if ! */
328 _("%s:%d: Got a %%else with no %%if\n"),
329 ofi->fileName, ofi->lineNum);
332 spec->readStack->reading =
333 spec->readStack->next->reading && ! spec->readStack->reading;
334 spec->line[0] = '\0';
335 } else if (! strncmp("%endif", s, sizeof("%endif")-1)) {
337 if (! spec->readStack->next) {
338 /* Got an end with no %if ! */
340 _("%s:%d: Got a %%endif with no %%if\n"),
341 ofi->fileName, ofi->lineNum);
344 rl = spec->readStack;
345 spec->readStack = spec->readStack->next;
347 spec->line[0] = '\0';
348 } else if (! strncmp("%include", s, sizeof("%include")-1)) {
349 char *fileName, *endFileName, *p;
353 if (! risspace(*fileName)) {
354 rpmlog(RPMLOG_ERR, _("malformed %%include statement\n"));
358 endFileName = fileName;
359 SKIPNONSPACE(endFileName);
363 rpmlog(RPMLOG_ERR, _("malformed %%include statement\n"));
368 forceIncludeFile(spec, fileName);
370 ofi = spec->fileStack;
375 rl = xmalloc(sizeof(*rl));
376 rl->reading = spec->readStack->reading && match;
377 rl->next = spec->readStack;
378 spec->readStack = rl;
379 spec->line[0] = '\0';
382 if (! spec->readStack->reading) {
383 spec->line[0] = '\0';
386 /* FIX: spec->readStack->next should be dependent */
390 void closeSpec(rpmSpec spec)
394 while (spec->fileStack) {
395 ofi = spec->fileStack;
396 spec->fileStack = spec->fileStack->next;
397 if (ofi->fd) (void) Fclose(ofi->fd);
398 ofi->fileName = _free(ofi->fileName);
403 extern int noLang; /* XXX FIXME: pass as arg */
405 int parseSpec(rpmts ts, const char *specFile, const char *rootDir,
406 const char *buildRoot, int recursing, const char *passPhrase,
407 const char *cookie, int anyarch, int force)
409 rpmParseState parsePart = PART_PREAMBLE;
410 int initialPackage = 1;
414 /* Set up a new Spec structure with no packages. */
417 spec->specFile = rpmGetPath(specFile, NULL);
418 spec->fileStack = newOpenFileInfo();
419 spec->fileStack->fileName = xstrdup(spec->specFile);
420 /* If buildRoot not specified, use default %{buildroot} */
422 spec->buildRoot = xstrdup(buildRoot);
424 spec->buildRoot = rpmGetPath("%{?buildroot:%{buildroot}}", NULL);
426 addMacro(NULL, "_docdir", NULL, "%{_defaultdocdir}", RMIL_SPEC);
427 spec->recursing = recursing;
428 spec->anyarch = anyarch;
432 spec->rootDir = xstrdup(rootDir);
434 spec->passPhrase = xstrdup(passPhrase);
436 spec->cookie = xstrdup(cookie);
438 spec->timeCheck = rpmExpandNumeric("%{_timecheck}");
440 /* All the parse*() functions expect to have a line pre-read */
441 /* in the spec's line buffer. Except for parsePreamble(), */
442 /* which handles the initial entry into a spec file. */
444 while (parsePart != PART_NONE) {
447 /* XXX Trap unexpected RPMRC_FAIL returns for now */
449 rpmlog(RPMLOG_ERR, "FIXME: got RPMRC_FAIL from spec parse\n");
451 case PART_ERROR: /* fallthrough */
456 parsePart = parsePreamble(spec, initialPackage);
460 parsePart = parsePrep(spec);
466 parsePart = parseBuildInstallClean(spec, parsePart);
469 parsePart = parseChangelog(spec);
471 case PART_DESCRIPTION:
472 parsePart = parseDescription(spec);
481 case PART_VERIFYSCRIPT:
482 case PART_TRIGGERPREIN:
485 case PART_TRIGGERPOSTUN:
486 parsePart = parseScript(spec, parsePart);
490 parsePart = parseFiles(spec);
493 case PART_NONE: /* XXX avoid gcc whining */
495 case PART_BUILDARCHITECTURES:
499 if (goterror || parsePart >= PART_LAST) {
503 if (parsePart == PART_BUILDARCHITECTURES) {
509 spec->BASpecs = xcalloc(spec->BACount, sizeof(*spec->BASpecs));
511 if (spec->BANames != NULL)
512 for (x = 0; x < spec->BACount; x++) {
514 /* Skip if not arch is not compatible. */
515 if (!rpmMachineScore(RPM_MACHTABLE_BUILDARCH, spec->BANames[x]))
517 addMacro(NULL, "_target_cpu", NULL, spec->BANames[x], RMIL_RPMRC);
518 spec->BASpecs[index] = NULL;
519 if (parseSpec(ts, specFile, spec->rootDir, buildRoot, 1,
520 passPhrase, cookie, anyarch, force)
521 || (spec->BASpecs[index] = rpmtsSetSpec(ts, NULL)) == NULL)
523 spec->BACount = index;
526 delMacro(NULL, "_target_cpu");
530 spec->BACount = index;
533 _("No compatible architectures found for build\n"));
538 * Return the 1st child's fully parsed Spec structure.
539 * The restart of the parse when encountering BuildArch
540 * causes problems for "rpm -q --specfile". This is
541 * still a hack because there may be more than 1 arch
542 * specified (unlikely but possible.) There's also the
543 * further problem that the macro context, particularly
544 * %{_target_cpu}, disagrees with the info in the header.
546 if (spec->BACount >= 1) {
547 rpmSpec nspec = spec->BASpecs[0];
548 spec->BASpecs = _free(spec->BASpecs);
549 spec = freeSpec(spec);
553 (void) rpmtsSetSpec(ts, spec);
558 /* Check for description in each package and add arch and os */
560 char *platform = rpmExpand("%{_target_platform}", NULL);
561 char *arch = rpmExpand("%{_target_cpu}", NULL);
562 char *os = rpmExpand("%{_target_os}", NULL);
564 for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
565 if (!headerIsEntry(pkg->header, RPMTAG_DESCRIPTION)) {
567 (void) headerNVR(pkg->header, &name, NULL, NULL);
568 rpmlog(RPMLOG_ERR, _("Package has no %%description: %s\n"), name);
572 headerPutString(pkg->header, RPMTAG_OS, os);
573 /* noarch subpackages already have arch set here, leave it alone */
574 if (!headerIsEntry(pkg->header, RPMTAG_ARCH)) {
575 headerPutString(pkg->header, RPMTAG_ARCH, arch);
577 headerPutString(pkg->header, RPMTAG_PLATFORM, platform);
579 pkg->ds = rpmdsThis(pkg->header, RPMTAG_REQUIRENAME, RPMSENSE_EQUAL);
583 platform = _free(platform);
589 (void) rpmtsSetSpec(ts, spec);
594 spec = freeSpec(spec);