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