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