Permit file objects in python header constructor
[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/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>
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 #define LEN_AND_STR(_tag) (sizeof(_tag)-1), (_tag)
21
22 static const struct PartRec {
23     int part;
24     size_t len;
25     const char * token;
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")},
48     {0, 0, 0}
49 };
50
51 rpmParseState isPart(const char *line)
52 {
53     const struct PartRec *p;
54
55     for (p = partList; p->token != NULL; p++) {
56         char c;
57         if (rstrncasecmp(line, p->token, p->len))
58             continue;
59         c = *(line + p->len);
60         if (c == '\0' || risspace(c))
61             break;
62     }
63
64     return (p->token ? p->part : PART_NONE);
65 }
66
67 /**
68  */
69 static int matchTok(const char *token, const char *line)
70 {
71     const char *b, *be = line;
72     size_t toklen = strlen(token);
73     int rc = 0;
74
75     while ( *(b = be) != '\0' ) {
76         SKIPSPACE(b);
77         be = b;
78         SKIPNONSPACE(be);
79         if (be == b)
80             break;
81         if (toklen != (be-b) || rstrncasecmp(token, b, (be-b)))
82             continue;
83         rc = 1;
84         break;
85     }
86
87     return rc;
88 }
89
90 void handleComments(char *s)
91 {
92     SKIPSPACE(s);
93     if (*s == '#')
94         *s = '\0';
95 }
96
97 /**
98  */
99 static void forceIncludeFile(rpmSpec spec, const char * fileName)
100 {
101     OFI_t * ofi;
102
103     ofi = newOpenFileInfo();
104     ofi->fileName = xstrdup(fileName);
105     ofi->next = spec->fileStack;
106     spec->fileStack = ofi;
107 }
108
109 static int restoreFirstChar(rpmSpec spec)
110 {
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';
115         return 1;
116     }
117     return 0;
118 }
119
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)
122 {
123     char ch;
124
125     /* Expand next line from file into line buffer */
126     if (!(spec->nextline && *spec->nextline)) {
127         int pc = 0, bc = 0, nc = 0;
128         char *from, *to, *p;
129         to = spec->lbufPtr ? spec->lbufPtr : spec->lbuf;
130         from = ofi->readPtr;
131         ch = ' ';
132         while (from && *from && ch != '\n')
133             ch = *to++ = *from++;
134         spec->lbufPtr = to;
135         *to++ = '\0';
136         ofi->readPtr = from;
137
138         /* Check if we need another line before expanding the buffer. */
139         for (p = spec->lbuf; *p; p++) {
140             switch (*p) {
141                 case '\\':
142                     switch (*(p+1)) {
143                         case '\n': p++, nc = 1; break;
144                         case '\0': break;
145                         default: p++; break;
146                     }
147                     break;
148                 case '\n': nc = 0; break;
149                 case '%':
150                     switch (*(p+1)) {
151                         case '{': p++, bc++; break;
152                         case '(': p++, pc++; break;
153                         case '%': p++; break;
154                     }
155                     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;
160             }
161         }
162         
163         /* If it doesn't, ask for one more line. */
164         if (pc || bc || nc ) {
165             spec->nextline = "";
166             return 1;
167         }
168         spec->lbufPtr = spec->lbuf;
169
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);
175                 return -1;
176         }
177         spec->nextline = spec->lbuf;
178     }
179     return 0;
180 }
181
182 static void copyNextLineFinish(rpmSpec spec, int strip)
183 {
184     char *last;
185     char ch;
186
187     /* Find next line in expanded line buffer */
188     spec->line = last = spec->nextline;
189     ch = ' ';
190     while (*spec->nextline && ch != '\n') {
191         ch = *spec->nextline++;
192         if (!risspace(ch))
193             last = spec->nextline;
194     }
195
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';
200     }
201     
202     if (strip & STRIP_COMMENTS)
203         handleComments(spec->line);
204     
205     if (strip & STRIP_TRAILINGSPACE)
206         *last = '\0';
207 }
208
209 static int readLineFromOFI(rpmSpec spec, OFI_t *ofi)
210 {
211 retry:
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)) {
216             /* XXX Fstrerror */
217             rpmlog(RPMLOG_ERR, _("Unable to open %s: %s\n"),
218                      ofi->fileName, Fstrerror(ofi->fd));
219             return PART_ERROR;
220         }
221         spec->lineNum = ofi->lineNum = 0;
222     }
223
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)) {
228             /* EOF */
229             if (spec->readStack->next) {
230                 rpmlog(RPMLOG_ERR, _("Unclosed %%if\n"));
231                 return PART_ERROR;
232             }
233
234             /* remove this file from the stack */
235             spec->fileStack = ofi->next;
236             (void) Fclose(ofi->fd);
237             ofi->fileName = _free(ofi->fileName);
238             ofi = _free(ofi);
239
240             /* only on last file do we signal EOF to caller */
241             ofi = spec->fileStack;
242             if (ofi == NULL)
243                 return 1;
244
245             /* otherwise, go back and try the read again. */
246             goto retry;
247         }
248         ofi->readPtr = ofi->readBuf;
249         ofi->lineNum++;
250         spec->lineNum = ofi->lineNum;
251         if (spec->sl) {
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)));
257             }
258             sl->sl_lines[sl->sl_nlines++] = xstrdup(ofi->readBuf);
259         }
260     }
261     return 0;
262 }
263
264 int readLine(rpmSpec spec, int strip)
265 {
266     char  *s;
267     int match;
268     struct ReadLevelEntry *rl;
269     OFI_t *ofi = spec->fileStack;
270     int rc;
271
272     if (!restoreFirstChar(spec)) {
273     retry:
274         if ((rc = readLineFromOFI(spec, ofi)) != 0)
275             return rc;
276
277         /* Copy next file line into the spec line buffer */
278         rc = copyNextLineFromOFI(spec, ofi);
279         if (rc > 0) {
280             goto retry;
281         } else if (rc < 0) {
282             return PART_ERROR;
283         }
284     }
285
286     copyNextLineFinish(spec, strip);
287
288     s = spec->line;
289     SKIPSPACE(s);
290
291     match = -1;
292     if (!spec->readStack->reading && rstreqn("%if", s, sizeof("%if")-1)) {
293         match = 0;
294     } else if (rstreqn("%ifarch", s, sizeof("%ifarch")-1)) {
295         char *arch = rpmExpand("%{_target_cpu}", NULL);
296         s += 7;
297         match = matchTok(arch, s);
298         arch = _free(arch);
299     } else if (rstreqn("%ifnarch", s, sizeof("%ifnarch")-1)) {
300         char *arch = rpmExpand("%{_target_cpu}", NULL);
301         s += 8;
302         match = !matchTok(arch, s);
303         arch = _free(arch);
304     } else if (rstreqn("%ifos", s, sizeof("%ifos")-1)) {
305         char *os = rpmExpand("%{_target_os}", NULL);
306         s += 5;
307         match = matchTok(os, s);
308         os = _free(os);
309     } else if (rstreqn("%ifnos", s, sizeof("%ifnos")-1)) {
310         char *os = rpmExpand("%{_target_os}", NULL);
311         s += 6;
312         match = !matchTok(os, s);
313         os = _free(os);
314     } else if (rstreqn("%if", s, sizeof("%if")-1)) {
315         s += 3;
316         match = parseExpressionBoolean(spec, s);
317         if (match < 0) {
318             rpmlog(RPMLOG_ERR,
319                         _("%s:%d: parseExpressionBoolean returns %d\n"),
320                         ofi->fileName, ofi->lineNum, match);
321             return PART_ERROR;
322         }
323     } else if (rstreqn("%else", s, sizeof("%else")-1)) {
324         s += 5;
325         if (! spec->readStack->next) {
326             /* Got an else with no %if ! */
327             rpmlog(RPMLOG_ERR,
328                         _("%s:%d: Got a %%else with no %%if\n"),
329                         ofi->fileName, ofi->lineNum);
330             return PART_ERROR;
331         }
332         spec->readStack->reading =
333             spec->readStack->next->reading && ! spec->readStack->reading;
334         spec->line[0] = '\0';
335     } else if (rstreqn("%endif", s, sizeof("%endif")-1)) {
336         s += 6;
337         if (! spec->readStack->next) {
338             /* Got an end with no %if ! */
339             rpmlog(RPMLOG_ERR,
340                         _("%s:%d: Got a %%endif with no %%if\n"),
341                         ofi->fileName, ofi->lineNum);
342             return PART_ERROR;
343         }
344         rl = spec->readStack;
345         spec->readStack = spec->readStack->next;
346         free(rl);
347         spec->line[0] = '\0';
348     } else if (rstreqn("%include", s, sizeof("%include")-1)) {
349         char *fileName, *endFileName, *p;
350
351         s += 8;
352         fileName = s;
353         if (! risspace(*fileName)) {
354             rpmlog(RPMLOG_ERR, _("malformed %%include statement\n"));
355             return PART_ERROR;
356         }
357         SKIPSPACE(fileName);
358         endFileName = fileName;
359         SKIPNONSPACE(endFileName);
360         p = endFileName;
361         SKIPSPACE(p);
362         if (*p != '\0') {
363             rpmlog(RPMLOG_ERR, _("malformed %%include statement\n"));
364             return PART_ERROR;
365         }
366         *endFileName = '\0';
367
368         forceIncludeFile(spec, fileName);
369
370         ofi = spec->fileStack;
371         goto retry;
372     }
373
374     if (match != -1) {
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';
380     }
381
382     if (! spec->readStack->reading) {
383         spec->line[0] = '\0';
384     }
385
386     /* FIX: spec->readStack->next should be dependent */
387     return 0;
388 }
389
390 void closeSpec(rpmSpec spec)
391 {
392     OFI_t *ofi;
393
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);
399         ofi = _free(ofi);
400     }
401 }
402
403 extern int noLang;              /* XXX FIXME: pass as arg */
404
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)
408 {
409     rpmParseState parsePart = PART_PREAMBLE;
410     int initialPackage = 1;
411     Package pkg;
412     rpmSpec spec;
413     
414     /* Set up a new Spec structure with no packages. */
415     spec = newSpec();
416
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} */
421     if (buildRoot) {
422         spec->buildRoot = xstrdup(buildRoot);
423     } else {
424         spec->buildRoot = rpmGetPath("%{?buildroot:%{buildroot}}", NULL);
425     }
426     addMacro(NULL, "_docdir", NULL, "%{_defaultdocdir}", RMIL_SPEC);
427     spec->recursing = recursing;
428     spec->anyarch = anyarch;
429     spec->force = force;
430
431     if (rootDir)
432         spec->rootDir = xstrdup(rootDir);
433     if (passPhrase)
434         spec->passPhrase = xstrdup(passPhrase);
435     if (cookie)
436         spec->cookie = xstrdup(cookie);
437
438     spec->timeCheck = rpmExpandNumeric("%{_timecheck}");
439
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.         */
443     
444     while (parsePart != PART_NONE) {
445         int goterror = 0;
446         switch (parsePart) {
447         /* XXX Trap unexpected RPMRC_FAIL returns for now */
448         case RPMRC_FAIL:
449             rpmlog(RPMLOG_ERR, "FIXME: got RPMRC_FAIL from spec parse\n");
450             abort();
451         case PART_ERROR: /* fallthrough */
452         default:
453             goterror = 1;
454             break;
455         case PART_PREAMBLE:
456             parsePart = parsePreamble(spec, initialPackage);
457             initialPackage = 0;
458             break;
459         case PART_PREP:
460             parsePart = parsePrep(spec);
461             break;
462         case PART_BUILD:
463         case PART_INSTALL:
464         case PART_CHECK:
465         case PART_CLEAN:
466             parsePart = parseBuildInstallClean(spec, parsePart);
467             break;
468         case PART_CHANGELOG:
469             parsePart = parseChangelog(spec);
470             break;
471         case PART_DESCRIPTION:
472             parsePart = parseDescription(spec);
473             break;
474
475         case PART_PRE:
476         case PART_POST:
477         case PART_PREUN:
478         case PART_POSTUN:
479         case PART_PRETRANS:
480         case PART_POSTTRANS:
481         case PART_VERIFYSCRIPT:
482         case PART_TRIGGERPREIN:
483         case PART_TRIGGERIN:
484         case PART_TRIGGERUN:
485         case PART_TRIGGERPOSTUN:
486             parsePart = parseScript(spec, parsePart);
487             break;
488
489         case PART_FILES:
490             parsePart = parseFiles(spec);
491             break;
492
493         case PART_NONE:         /* XXX avoid gcc whining */
494         case PART_LAST:
495         case PART_BUILDARCHITECTURES:
496             break;
497         }
498
499         if (goterror || parsePart >= PART_LAST) {
500             goto errxit;
501         }
502
503         if (parsePart == PART_BUILDARCHITECTURES) {
504             int index;
505             int x;
506
507             closeSpec(spec);
508
509             spec->BASpecs = xcalloc(spec->BACount, sizeof(*spec->BASpecs));
510             index = 0;
511             if (spec->BANames != NULL)
512             for (x = 0; x < spec->BACount; x++) {
513
514                 /* Skip if not arch is not compatible. */
515                 if (!rpmMachineScore(RPM_MACHTABLE_BUILDARCH, spec->BANames[x]))
516                     continue;
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)
522                 {
523                         spec->BACount = index;
524                         goto errxit;
525                 }
526                 delMacro(NULL, "_target_cpu");
527                 index++;
528             }
529
530             spec->BACount = index;
531             if (! index) {
532                 rpmlog(RPMLOG_ERR,
533                         _("No compatible architectures found for build\n"));
534                 goto errxit;
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
558     if (spec->clean == NULL) {
559         char *body = rpmExpand("%{?buildroot: %{__rm} -rf %{buildroot}}", NULL);
560         spec->clean = newStringBuf();
561         appendLineStringBuf(spec->clean, body);
562         free(body);
563     }
564
565     /* Check for description in each package and add arch and os */
566   {
567     char *platform = rpmExpand("%{_target_platform}", NULL);
568     char *arch = rpmExpand("%{_target_cpu}", NULL);
569     char *os = rpmExpand("%{_target_os}", NULL);
570
571     for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
572         if (!headerIsEntry(pkg->header, RPMTAG_DESCRIPTION)) {
573             rpmlog(RPMLOG_ERR, _("Package has no %%description: %s\n"),
574                    headerGetString(pkg->header, RPMTAG_NAME));
575             goto errxit;
576         }
577
578         headerPutString(pkg->header, RPMTAG_OS, os);
579         /* noarch subpackages already have arch set here, leave it alone */
580         if (!headerIsEntry(pkg->header, RPMTAG_ARCH)) {
581             headerPutString(pkg->header, RPMTAG_ARCH, arch);
582         }
583         headerPutString(pkg->header, RPMTAG_PLATFORM, platform);
584
585         pkg->ds = rpmdsThis(pkg->header, RPMTAG_REQUIRENAME, RPMSENSE_EQUAL);
586
587     }
588
589     platform = _free(platform);
590     arch = _free(arch);
591     os = _free(os);
592   }
593
594     closeSpec(spec);
595     (void) rpmtsSetSpec(ts, spec);
596
597     return 0;
598
599 errxit:
600     spec = freeSpec(spec);
601     return PART_ERROR;
602 }