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