Start cleaning up the rpmerr removal mess of build/ return codes
[platform/upstream/rpm.git] / build / parseSpec.c
1 /** \ingroup rpmbuild
2  * \file build/parseSpec.c
3  *  Top level dispatcher for spec file parsing.
4  */
5
6 #include "system.h"
7
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>
15 #include "debug.h"
16
17 #define SKIPSPACE(s) { while (*(s) && risspace(*(s))) (s)++; }
18 #define SKIPNONSPACE(s) { while (*(s) && !risspace(*(s))) (s)++; }
19
20 /* XXX FIXME: strlen for these is calculated at runtime, preventing const */
21 static struct PartRec {
22     int part;
23     size_t len;
24     const char * token;
25 } partList[] = {
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"},
47     {0, 0, 0}
48 };
49
50 /**
51  */
52 static inline void initParts(struct PartRec *p)
53 {
54     for (; p->token != NULL; p++)
55         p->len = strlen(p->token);
56 }
57
58 rpmParseState isPart(const char *line)
59 {
60     struct PartRec *p;
61
62     if (partList[0].len == 0)
63         initParts(partList);
64     
65     for (p = partList; p->token != NULL; p++) {
66         char c;
67         if (rstrncasecmp(line, p->token, p->len))
68             continue;
69         c = *(line + p->len);
70         if (c == '\0' || risspace(c))
71             break;
72     }
73
74     return (p->token ? p->part : PART_NONE);
75 }
76
77 /**
78  */
79 static int matchTok(const char *token, const char *line)
80 {
81     const char *b, *be = line;
82     size_t toklen = strlen(token);
83     int rc = 0;
84
85     while ( *(b = be) != '\0' ) {
86         SKIPSPACE(b);
87         be = b;
88         SKIPNONSPACE(be);
89         if (be == b)
90             break;
91         if (toklen != (be-b) || rstrncasecmp(token, b, (be-b)))
92             continue;
93         rc = 1;
94         break;
95     }
96
97     return rc;
98 }
99
100 void handleComments(char *s)
101 {
102     SKIPSPACE(s);
103     if (*s == '#')
104         *s = '\0';
105 }
106
107 /**
108  */
109 static void forceIncludeFile(rpmSpec spec, const char * fileName)
110 {
111     OFI_t * ofi;
112
113     ofi = newOpenFileInfo();
114     ofi->fileName = xstrdup(fileName);
115     ofi->next = spec->fileStack;
116     spec->fileStack = ofi;
117 }
118
119 static int restoreFirstChar(rpmSpec spec)
120 {
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';
125         return 1;
126     }
127     return 0;
128 }
129
130 static int copyNextLineFromOFI(rpmSpec spec, OFI_t *ofi)
131 {
132     char ch;
133
134     /* Expand next line from file into line buffer */
135     if (!(spec->nextline && *spec->nextline)) {
136         int pc = 0, bc = 0, nc = 0;
137         char *from, *to, *p;
138         to = spec->lbufPtr ? spec->lbufPtr : spec->lbuf;
139         from = ofi->readPtr;
140         ch = ' ';
141         while (*from && ch != '\n')
142             ch = *to++ = *from++;
143         spec->lbufPtr = to;
144         *to++ = '\0';
145         ofi->readPtr = from;
146
147         /* Check if we need another line before expanding the buffer. */
148         for (p = spec->lbuf; *p; p++) {
149             switch (*p) {
150                 case '\\':
151                     switch (*(p+1)) {
152                         case '\n': p++, nc = 1; break;
153                         case '\0': break;
154                         default: p++; break;
155                     }
156                     break;
157                 case '\n': nc = 0; break;
158                 case '%':
159                     switch (*(p+1)) {
160                         case '{': p++, bc++; break;
161                         case '(': p++, pc++; break;
162                         case '%': p++; break;
163                     }
164                     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;
169             }
170         }
171         
172         /* If it doesn't, ask for one more line. We need a better
173          * error code for this. */
174         if (pc || bc || nc ) {
175             spec->nextline = "";
176             return RPMRC_FAIL;
177         }
178         spec->lbufPtr = spec->lbuf;
179
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);
185                 return RPMRC_FAIL;
186         }
187         spec->nextline = spec->lbuf;
188     }
189     return 0;
190 }
191
192 static void copyNextLineFinish(rpmSpec spec, int strip)
193 {
194     char *last;
195     char ch;
196
197     /* Find next line in expanded line buffer */
198     spec->line = last = spec->nextline;
199     ch = ' ';
200     while (*spec->nextline && ch != '\n') {
201         ch = *spec->nextline++;
202         if (!risspace(ch))
203             last = spec->nextline;
204     }
205
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';
210     }
211     
212     if (strip & STRIP_COMMENTS)
213         handleComments(spec->line);
214     
215     if (strip & STRIP_TRAILINGSPACE)
216         *last = '\0';
217 }
218
219 static int readLineFromOFI(rpmSpec spec, OFI_t *ofi)
220 {
221 retry:
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)) {
226             /* XXX Fstrerror */
227             rpmlog(RPMLOG_ERR, _("Unable to open %s: %s\n"),
228                      ofi->fileName, Fstrerror(ofi->fd));
229             return RPMRC_FAIL;
230         }
231         spec->lineNum = ofi->lineNum = 0;
232     }
233
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)) {
238             /* EOF */
239             if (spec->readStack->next) {
240                 rpmlog(RPMLOG_ERR, _("Unclosed %%if\n"));
241                 return RPMRC_FAIL;
242             }
243
244             /* remove this file from the stack */
245             spec->fileStack = ofi->next;
246             (void) Fclose(ofi->fd);
247             ofi->fileName = _free(ofi->fileName);
248             ofi = _free(ofi);
249
250             /* only on last file do we signal EOF to caller */
251             ofi = spec->fileStack;
252             if (ofi == NULL)
253                 return 1;
254
255             /* otherwise, go back and try the read again. */
256             goto retry;
257         }
258         ofi->readPtr = ofi->readBuf;
259         ofi->lineNum++;
260         spec->lineNum = ofi->lineNum;
261         if (spec->sl) {
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)));
267             }
268             sl->sl_lines[sl->sl_nlines++] = xstrdup(ofi->readBuf);
269         }
270     }
271     return 0;
272 }
273
274 int readLine(rpmSpec spec, int strip)
275 {
276     char  *s;
277     int match;
278     struct ReadLevelEntry *rl;
279     OFI_t *ofi = spec->fileStack;
280     int rc;
281
282     if (!restoreFirstChar(spec)) {
283     retry:
284       if ((rc = readLineFromOFI(spec, ofi)) != 0)
285         return rc;
286
287       /* Copy next file line into the spec line buffer */
288
289       if ((rc = copyNextLineFromOFI(spec, ofi)) != 0) {
290         if (rc == RPMRC_FAIL)
291             goto retry;
292         return rc;
293       }
294     }
295
296     copyNextLineFinish(spec, strip);
297
298     s = spec->line;
299     SKIPSPACE(s);
300
301     match = -1;
302     if (!spec->readStack->reading && !strncmp("%if", s, sizeof("%if")-1)) {
303         match = 0;
304     } else if (! strncmp("%ifarch", s, sizeof("%ifarch")-1)) {
305         char *arch = rpmExpand("%{_target_cpu}", NULL);
306         s += 7;
307         match = matchTok(arch, s);
308         arch = _free(arch);
309     } else if (! strncmp("%ifnarch", s, sizeof("%ifnarch")-1)) {
310         char *arch = rpmExpand("%{_target_cpu}", NULL);
311         s += 8;
312         match = !matchTok(arch, s);
313         arch = _free(arch);
314     } else if (! strncmp("%ifos", s, sizeof("%ifos")-1)) {
315         char *os = rpmExpand("%{_target_os}", NULL);
316         s += 5;
317         match = matchTok(os, s);
318         os = _free(os);
319     } else if (! strncmp("%ifnos", s, sizeof("%ifnos")-1)) {
320         char *os = rpmExpand("%{_target_os}", NULL);
321         s += 6;
322         match = !matchTok(os, s);
323         os = _free(os);
324     } else if (! strncmp("%if", s, sizeof("%if")-1)) {
325         s += 3;
326         match = parseExpressionBoolean(spec, s);
327         if (match < 0) {
328             rpmlog(RPMLOG_ERR,
329                         _("%s:%d: parseExpressionBoolean returns %d\n"),
330                         ofi->fileName, ofi->lineNum, match);
331             return RPMRC_FAIL;
332         }
333     } else if (! strncmp("%else", s, sizeof("%else")-1)) {
334         s += 5;
335         if (! spec->readStack->next) {
336             /* Got an else with no %if ! */
337             rpmlog(RPMLOG_ERR,
338                         _("%s:%d: Got a %%else with no %%if\n"),
339                         ofi->fileName, ofi->lineNum);
340             return RPMRC_FAIL;
341         }
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)) {
346         s += 6;
347         if (! spec->readStack->next) {
348             /* Got an end with no %if ! */
349             rpmlog(RPMLOG_ERR,
350                         _("%s:%d: Got a %%endif with no %%if\n"),
351                         ofi->fileName, ofi->lineNum);
352             return RPMRC_FAIL;
353         }
354         rl = spec->readStack;
355         spec->readStack = spec->readStack->next;
356         free(rl);
357         spec->line[0] = '\0';
358     } else if (! strncmp("%include", s, sizeof("%include")-1)) {
359         char *fileName, *endFileName, *p;
360
361         s += 8;
362         fileName = s;
363         if (! risspace(*fileName)) {
364             rpmlog(RPMLOG_ERR, _("malformed %%include statement\n"));
365             return RPMRC_FAIL;
366         }
367         SKIPSPACE(fileName);
368         endFileName = fileName;
369         SKIPNONSPACE(endFileName);
370         p = endFileName;
371         SKIPSPACE(p);
372         if (*p != '\0') {
373             rpmlog(RPMLOG_ERR, _("malformed %%include statement\n"));
374             return RPMRC_FAIL;
375         }
376         *endFileName = '\0';
377
378         forceIncludeFile(spec, fileName);
379
380         ofi = spec->fileStack;
381         goto retry;
382     }
383
384     if (match != -1) {
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';
390     }
391
392     if (! spec->readStack->reading) {
393         spec->line[0] = '\0';
394     }
395
396     /* FIX: spec->readStack->next should be dependent */
397     return 0;
398 }
399
400 void closeSpec(rpmSpec spec)
401 {
402     OFI_t *ofi;
403
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);
409         ofi = _free(ofi);
410     }
411 }
412
413 extern int noLang;              /* XXX FIXME: pass as arg */
414
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)
418 {
419     rpmParseState parsePart = PART_PREAMBLE;
420     int initialPackage = 1;
421     Package pkg;
422     rpmSpec spec;
423     
424     /* Set up a new Spec structure with no packages. */
425     spec = newSpec();
426
427     /*
428      * Note: rpmGetPath should guarantee a "canonical" path. That means
429      * that the following pathologies should be weeded out:
430      *          //bin//sh
431      *          //usr//bin/
432      *          /.././../usr/../bin//./sh (XXX FIXME: dots not handled yet)
433      */
434     spec->specFile = rpmGetPath(specFile, NULL);
435     spec->fileStack = newOpenFileInfo();
436     spec->fileStack->fileName = xstrdup(spec->specFile);
437     if (buildRoot) {
438         if (*buildRoot == '\0') {
439             rpmlog(RPMLOG_ERR,
440                      _("BuildRoot couldn't be empty\n"));
441             return RPMRC_FAIL;
442         }
443         if (!strcmp(buildRoot, "/")) {
444             rpmlog(RPMLOG_ERR,
445                      _("BuildRoot can not be \"/\"\n"));
446             return RPMRC_FAIL;
447         }
448         spec->gotBuildRoot = 1;
449         spec->buildRoot = xstrdup(buildRoot);
450         addMacro(spec->macros, "buildroot", NULL, buildRoot, RMIL_SPEC);
451     }
452     addMacro(NULL, "_docdir", NULL, "%{_defaultdocdir}", RMIL_SPEC);
453     spec->recursing = recursing;
454     spec->anyarch = anyarch;
455     spec->force = force;
456
457     if (rootDir)
458         spec->rootDir = xstrdup(rootDir);
459     if (passPhrase)
460         spec->passPhrase = xstrdup(passPhrase);
461     if (cookie)
462         spec->cookie = xstrdup(cookie);
463
464     spec->timeCheck = rpmExpandNumeric("%{_timecheck}");
465
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.         */
469     
470         /* LCL: parsePart is modified @*/
471     while (parsePart != PART_NONE) {
472         int goterror = 0;
473         switch (parsePart) {
474         case PART_ERROR: /* fallthrough */
475         default:
476             goterror = 1;
477             break;
478         case PART_PREAMBLE:
479             parsePart = parsePreamble(spec, initialPackage);
480             initialPackage = 0;
481             break;
482         case PART_PREP:
483             parsePart = parsePrep(spec);
484             break;
485         case PART_BUILD:
486         case PART_INSTALL:
487         case PART_CHECK:
488         case PART_CLEAN:
489             parsePart = parseBuildInstallClean(spec, parsePart);
490             break;
491         case PART_CHANGELOG:
492             parsePart = parseChangelog(spec);
493             break;
494         case PART_DESCRIPTION:
495             parsePart = parseDescription(spec);
496             break;
497
498         case PART_PRE:
499         case PART_POST:
500         case PART_PREUN:
501         case PART_POSTUN:
502         case PART_PRETRANS:
503         case PART_POSTTRANS:
504         case PART_VERIFYSCRIPT:
505         case PART_TRIGGERPREIN:
506         case PART_TRIGGERIN:
507         case PART_TRIGGERUN:
508         case PART_TRIGGERPOSTUN:
509             parsePart = parseScript(spec, parsePart);
510             break;
511
512         case PART_FILES:
513             parsePart = parseFiles(spec);
514             break;
515
516         case PART_NONE:         /* XXX avoid gcc whining */
517         case PART_LAST:
518         case PART_BUILDARCHITECTURES:
519             break;
520         }
521
522         if (goterror || parsePart >= PART_LAST) {
523             goto errxit;
524         }
525
526         if (parsePart == PART_BUILDARCHITECTURES) {
527             int index;
528             int x;
529
530             closeSpec(spec);
531
532             /* LCL: sizeof(spec->BASpecs[0]) -nullderef whine here */
533             spec->BASpecs = xcalloc(spec->BACount, sizeof(*spec->BASpecs));
534             index = 0;
535             if (spec->BANames != NULL)
536             for (x = 0; x < spec->BACount; x++) {
537
538                 /* Skip if not arch is not compatible. */
539                 if (!rpmMachineScore(RPM_MACHTABLE_BUILDARCH, spec->BANames[x]))
540                     continue;
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)
546                 {
547                         spec->BACount = index;
548                         goto errxit;
549                 }
550                 delMacro(NULL, "_target_cpu");
551                 index++;
552             }
553
554             spec->BACount = index;
555             if (! index) {
556                 rpmlog(RPMLOG_ERR,
557                         _("No compatible architectures found for build\n"));
558                 goto errxit;
559             }
560
561             /*
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.
569              */
570             if (spec->BACount >= 1) {
571                 rpmSpec nspec = spec->BASpecs[0];
572                 spec->BASpecs = _free(spec->BASpecs);
573                 spec = freeSpec(spec);
574                 spec = nspec;
575             }
576
577             (void) rpmtsSetSpec(ts, spec);
578             return 0;
579         }
580     }
581         /* LCL: parsePart is modified @*/
582
583     /* Check for description in each package and add arch and os */
584   {
585     char *platform = rpmExpand("%{_target_platform}", NULL);
586     char *arch = rpmExpand("%{_target_cpu}", NULL);
587     char *os = rpmExpand("%{_target_os}", NULL);
588
589     for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
590         if (!headerIsEntry(pkg->header, RPMTAG_DESCRIPTION)) {
591             const char * name;
592             (void) headerNVR(pkg->header, &name, NULL, NULL);
593             rpmlog(RPMLOG_ERR, _("Package has no %%description: %s\n"), name);
594             goto errxit;
595         }
596
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);
602
603         pkg->ds = rpmdsThis(pkg->header, RPMTAG_REQUIRENAME, RPMSENSE_EQUAL);
604
605     }
606
607     platform = _free(platform);
608     arch = _free(arch);
609     os = _free(os);
610   }
611
612     closeSpec(spec);
613     (void) rpmtsSetSpec(ts, spec);
614
615     return 0;
616
617 errxit:
618     spec = freeSpec(spec);
619     return PART_ERROR;
620 }