Plug memleak in parseSimplePart()
[platform/upstream/rpm.git] / build / parsePreamble.c
1 /** \ingroup rpmbuild
2  * \file build/parsePreamble.c
3  *  Parse tags in global section from spec file.
4  */
5
6 #include "system.h"
7
8 #include <rpm/header.h>
9 #include <rpm/rpmbuild.h>
10 #include <rpm/rpmlog.h>
11 #include <rpm/rpmfileutil.h>
12 #include "debug.h"
13
14 #define SKIPSPACE(s) { while (*(s) && risspace(*(s))) (s)++; }
15 #define SKIPNONSPACE(s) { while (*(s) && !risspace(*(s))) (s)++; }
16
17 /**
18  */
19 static const rpmTag copyTagsDuringParse[] = {
20     RPMTAG_EPOCH,
21     RPMTAG_VERSION,
22     RPMTAG_RELEASE,
23     RPMTAG_LICENSE,
24     RPMTAG_PACKAGER,
25     RPMTAG_DISTRIBUTION,
26     RPMTAG_DISTURL,
27     RPMTAG_VENDOR,
28     RPMTAG_ICON,
29     RPMTAG_URL,
30     RPMTAG_CHANGELOGTIME,
31     RPMTAG_CHANGELOGNAME,
32     RPMTAG_CHANGELOGTEXT,
33     RPMTAG_PREFIXES,
34     RPMTAG_DISTTAG,
35     0
36 };
37
38 /**
39  */
40 static const rpmTag requiredTags[] = {
41     RPMTAG_NAME,
42     RPMTAG_VERSION,
43     RPMTAG_RELEASE,
44     RPMTAG_SUMMARY,
45     RPMTAG_GROUP,
46     RPMTAG_LICENSE,
47     0
48 };
49
50 /**
51  */
52 static void addOrAppendListEntry(Header h, rpmTag tag, const char * line)
53 {
54     int xx;
55     int argc;
56     const char **argv;
57
58     xx = poptParseArgvString(line, &argc, &argv);
59     if (argc)
60         xx = headerAddOrAppendEntry(h, tag, RPM_STRING_ARRAY_TYPE, 
61                                     argv, (rpm_count_t) argc);
62     argv = _free(argv);
63 }
64
65 /* Parse a simple part line that only take -n <pkg> or <pkg> */
66 /* <pkg> is returned in name as a pointer into a dynamic buffer */
67
68 /**
69  */
70 static int parseSimplePart(const char *line, char **name, int *flag)
71 {
72     char *tok;
73     char *linebuf = xstrdup(line);
74     int rc;
75
76     /* Throw away the first token (the %xxxx) */
77     (void)strtok(linebuf, " \t\n");
78     *name = NULL;
79
80     if (!(tok = strtok(NULL, " \t\n"))) {
81         rc = 0;
82         goto exit;
83     }
84     
85     if (!strcmp(tok, "-n")) {
86         if (!(tok = strtok(NULL, " \t\n"))) {
87             rc = 1;
88             goto exit;
89         }
90         *flag = PART_NAME;
91     } else {
92         *flag = PART_SUBNAME;
93     }
94     *name = xstrdup(tok);
95     rc = strtok(NULL, " \t\n") ? 1 : 0;
96
97 exit:
98     free(linebuf);
99     return rc;
100 }
101
102 /**
103  */
104 static inline int parseYesNo(const char * s)
105 {
106     return ((!s || (s[0] == 'n' || s[0] == 'N' || s[0] == '0') ||
107         !rstrcasecmp(s, "false") || !rstrcasecmp(s, "off"))
108             ? 0 : 1);
109 }
110
111 typedef const struct tokenBits_s {
112     const char * name;
113     rpmsenseFlags bits;
114 } * tokenBits;
115
116 /**
117  */
118 static struct tokenBits_s const installScriptBits[] = {
119     { "interp",         RPMSENSE_INTERP },
120     { "prereq",         RPMSENSE_PREREQ },
121     { "preun",          RPMSENSE_SCRIPT_PREUN },
122     { "pre",            RPMSENSE_SCRIPT_PRE },
123     { "postun",         RPMSENSE_SCRIPT_POSTUN },
124     { "post",           RPMSENSE_SCRIPT_POST },
125     { "rpmlib",         RPMSENSE_RPMLIB },
126     { "verify",         RPMSENSE_SCRIPT_VERIFY },
127     { NULL, 0 }
128 };
129
130 /**
131  */
132 static const struct tokenBits_s const buildScriptBits[] = {
133     { "prep",           RPMSENSE_SCRIPT_PREP },
134     { "build",          RPMSENSE_SCRIPT_BUILD },
135     { "install",        RPMSENSE_SCRIPT_INSTALL },
136     { "clean",          RPMSENSE_SCRIPT_CLEAN },
137     { NULL, 0 }
138 };
139
140 /**
141  */
142 static int parseBits(const char * s, const tokenBits tokbits,
143                 rpmsenseFlags * bp)
144 {
145     tokenBits tb;
146     const char * se;
147     rpmsenseFlags bits = RPMSENSE_ANY;
148     int c = 0;
149
150     if (s) {
151         while (*s != '\0') {
152             while ((c = *s) && risspace(c)) s++;
153             se = s;
154             while ((c = *se) && risalpha(c)) se++;
155             if (s == se)
156                 break;
157             for (tb = tokbits; tb->name; tb++) {
158                 if (tb->name != NULL &&
159                     strlen(tb->name) == (se-s) && !strncmp(tb->name, s, (se-s)))
160                     break;
161             }
162             if (tb->name == NULL)
163                 break;
164             bits |= tb->bits;
165             while ((c = *se) && risspace(c)) se++;
166             if (c != ',')
167                 break;
168             s = ++se;
169         }
170     }
171     if (c == 0 && bp) *bp = bits;
172     return (c ? RPMRC_FAIL : RPMRC_OK);
173 }
174
175 /**
176  */
177 static inline char * findLastChar(char * s)
178 {
179     char *res = s;
180
181     while (*s != '\0') {
182         if (! risspace(*s))
183             res = s;
184         s++;
185     }
186
187     return res;
188 }
189
190 /**
191  */
192 static int isMemberInEntry(Header h, const char *name, rpmTag tag)
193 {
194     HGE_t hge = (HGE_t)headerGetEntryMinMemory;
195     HFD_t hfd = headerFreeData;
196     const char ** names;
197     rpmTagType type;
198     rpm_count_t count;
199     int found = 0;
200
201     if (!hge(h, tag, &type, (rpm_data_t *)&names, &count))
202         return -1;
203     while (count--) {
204         if (!rstrcasecmp(names[count], name)) {
205             found = 1;
206             break;
207         }
208     }
209     names = hfd(names, type);
210     return found;
211 }
212
213 /**
214  */
215 static rpmRC checkForValidArchitectures(rpmSpec spec)
216 {
217     char *arch = rpmExpand("%{_target_cpu}", NULL);
218     char *os = rpmExpand("%{_target_os}", NULL);
219     rpmRC rc = RPMRC_FAIL; /* assume failure */
220     
221     if (isMemberInEntry(spec->buildRestrictions,
222                         arch, RPMTAG_EXCLUDEARCH) == 1) {
223         rpmlog(RPMLOG_ERR, _("Architecture is excluded: %s\n"), arch);
224         goto exit;
225     }
226     if (isMemberInEntry(spec->buildRestrictions,
227                         arch, RPMTAG_EXCLUSIVEARCH) == 0) {
228         rpmlog(RPMLOG_ERR, _("Architecture is not included: %s\n"), arch);
229         goto exit;
230     }
231     if (isMemberInEntry(spec->buildRestrictions,
232                         os, RPMTAG_EXCLUDEOS) == 1) {
233         rpmlog(RPMLOG_ERR, _("OS is excluded: %s\n"), os);
234         goto exit;
235     }
236     if (isMemberInEntry(spec->buildRestrictions,
237                         os, RPMTAG_EXCLUSIVEOS) == 0) {
238         rpmlog(RPMLOG_ERR, _("OS is not included: %s\n"), os);
239         goto exit;
240     }
241     rc = RPMRC_OK;
242
243 exit:
244     arch = _free(arch);
245     os = _free(os);
246
247     return rc;
248 }
249
250 /**
251  * Check that required tags are present in header.
252  * @param h             header
253  * @param NVR           package name-version-release
254  * @return              RPMRC_OK if OK
255  */
256 static int checkForRequired(Header h, const char * NVR)
257         /* LCL: parse error here with modifies */
258 {
259     int res = RPMRC_OK;
260     const rpmTag * p;
261
262     for (p = requiredTags; *p != 0; p++) {
263         if (!headerIsEntry(h, *p)) {
264             rpmlog(RPMLOG_ERR,
265                         _("%s field must be present in package: %s\n"),
266                         rpmTagGetName(*p), NVR);
267             res = RPMRC_FAIL;
268         }
269     }
270
271     return res;
272 }
273
274 /**
275  * Check that no duplicate tags are present in header.
276  * @param h             header
277  * @param NVR           package name-version-release
278  * @return              RPMRC_OK if OK
279  */
280 static int checkForDuplicates(Header h, const char * NVR)
281 {
282     int res = RPMRC_OK;
283     rpmTag lastTag, tag;
284     HeaderIterator hi;
285     
286     for (hi = headerInitIterator(h), lastTag = 0;
287         headerNextIterator(hi, &tag, NULL, NULL, NULL);
288         lastTag = tag)
289     {
290         if (tag != lastTag)
291             continue;
292         rpmlog(RPMLOG_ERR, _("Duplicate %s entries in package: %s\n"),
293                      rpmTagGetName(tag), NVR);
294         res = RPMRC_FAIL;
295     }
296     hi = headerFreeIterator(hi);
297
298     return res;
299 }
300
301 /**
302  */
303 static struct optionalTag {
304     rpmTag      ot_tag;
305     const char * ot_mac;
306 } const optionalTags[] = {
307     { RPMTAG_VENDOR,            "%{vendor}" },
308     { RPMTAG_PACKAGER,          "%{packager}" },
309     { RPMTAG_DISTRIBUTION,      "%{distribution}" },
310     { RPMTAG_DISTURL,           "%{disturl}" },
311     { -1, NULL }
312 };
313
314 /**
315  */
316 static void fillOutMainPackage(Header h)
317 {
318     const struct optionalTag *ot;
319
320     for (ot = optionalTags; ot->ot_mac != NULL; ot++) {
321         if (!headerIsEntry(h, ot->ot_tag)) {
322             char *val = rpmExpand(ot->ot_mac, NULL);
323             if (val && *val != '%')
324                 (void) headerAddEntry(h, ot->ot_tag, RPM_STRING_TYPE, val, 1);
325             val = _free(val);
326         }
327     }
328 }
329
330 /**
331  */
332 static rpmRC readIcon(Header h, const char * file)
333 {
334     char *fn = NULL;
335     char *icon;
336     FD_t fd;
337     rpmRC rc = RPMRC_OK;
338     off_t size;
339     size_t nb, iconsize;
340
341     /* XXX use rpmGenPath(rootdir, "%{_sourcedir}/", file) for icon path. */
342     fn = rpmGetPath("%{_sourcedir}/", file, NULL);
343
344     fd = Fopen(fn, "r.ufdio");
345     if (fd == NULL || Ferror(fd)) {
346         rpmlog(RPMLOG_ERR, _("Unable to open icon %s: %s\n"),
347                 fn, Fstrerror(fd));
348         rc = RPMRC_FAIL;
349         goto exit;
350     }
351     size = fdSize(fd);
352     iconsize = (size >= 0 ? size : (8 * BUFSIZ));
353     if (iconsize == 0) {
354         (void) Fclose(fd);
355         rc = RPMRC_OK;
356         goto exit;
357     }
358
359     icon = xmalloc(iconsize + 1);
360     *icon = '\0';
361
362     nb = Fread(icon, sizeof(icon[0]), iconsize, fd);
363     if (Ferror(fd) || (size >= 0 && nb != size)) {
364         rpmlog(RPMLOG_ERR, _("Unable to read icon %s: %s\n"),
365                 fn, Fstrerror(fd));
366         rc = RPMRC_FAIL;
367     }
368     (void) Fclose(fd);
369     if (rc != RPMRC_OK)
370         goto exit;
371
372     if (! strncmp(icon, "GIF", sizeof("GIF")-1)) {
373         (void) headerAddEntry(h, RPMTAG_GIF, RPM_BIN_TYPE, icon, iconsize);
374     } else if (! strncmp(icon, "/* XPM", sizeof("/* XPM")-1)) {
375         (void) headerAddEntry(h, RPMTAG_XPM, RPM_BIN_TYPE, icon, iconsize);
376     } else {
377         rpmlog(RPMLOG_ERR, _("Unknown icon type: %s\n"), file);
378         rc = RPMRC_FAIL;
379         goto exit;
380     }
381     icon = _free(icon);
382     
383 exit:
384     fn = _free(fn);
385     return rc;
386 }
387
388 spectag stashSt(rpmSpec spec, Header h, rpmTag tag, const char * lang)
389 {
390     HGE_t hge = (HGE_t)headerGetEntryMinMemory;
391     spectag t = NULL;
392
393     if (spec->st) {
394         spectags st = spec->st;
395         if (st->st_ntags == st->st_nalloc) {
396             st->st_nalloc += 10;
397             st->st_t = xrealloc(st->st_t, st->st_nalloc * sizeof(*(st->st_t)));
398         }
399         t = st->st_t + st->st_ntags++;
400         t->t_tag = tag;
401         t->t_startx = spec->lineNum - 1;
402         t->t_nlines = 1;
403         t->t_lang = xstrdup(lang);
404         t->t_msgid = NULL;
405         if (!(t->t_lang && strcmp(t->t_lang, RPMBUILD_DEFAULT_LANG))) {
406             char *n;
407             if (hge(h, RPMTAG_NAME, NULL, (rpm_data_t *) &n, NULL)) {
408                 rasprintf(&t->t_msgid, "%s(%s)", n, rpmTagGetName(tag));
409             }
410         }
411     }
412     return t;
413 }
414
415 #define SINGLE_TOKEN_ONLY \
416 if (multiToken) { \
417     rpmlog(RPMLOG_ERR, _("line %d: Tag takes single token only: %s\n"), \
418              spec->lineNum, spec->line); \
419     return RPMRC_FAIL; \
420 }
421
422 extern int noLang;
423
424 /**
425  */
426 static int handlePreambleTag(rpmSpec spec, Package pkg, rpmTag tag,
427                 const char *macro, const char *lang)
428 {
429     HGE_t hge = (HGE_t)headerGetEntryMinMemory;
430     HFD_t hfd = headerFreeData;
431     char * field = spec->line;
432     char * end;
433     char ** array;
434     int multiToken = 0;
435     rpmsenseFlags tagflags;
436     rpmTagType type;
437     size_t len;
438     rpm_count_t num;
439     int rc;
440     int xx;
441     
442     if (field == NULL) return RPMRC_FAIL;       /* XXX can't happen */
443     /* Find the start of the "field" and strip trailing space */
444     while ((*field) && (*field != ':'))
445         field++;
446     if (*field != ':') {
447         rpmlog(RPMLOG_ERR, _("line %d: Malformed tag: %s\n"),
448                  spec->lineNum, spec->line);
449         return RPMRC_FAIL;
450     }
451     field++;
452     SKIPSPACE(field);
453     if (!*field) {
454         /* Empty field */
455         rpmlog(RPMLOG_ERR, _("line %d: Empty tag: %s\n"),
456                  spec->lineNum, spec->line);
457         return RPMRC_FAIL;
458     }
459     end = findLastChar(field);
460     *(end+1) = '\0';
461
462     /* See if this is multi-token */
463     end = field;
464     SKIPNONSPACE(end);
465     if (*end != '\0')
466         multiToken = 1;
467
468     switch (tag) {
469     case RPMTAG_NAME:
470     case RPMTAG_VERSION:
471     case RPMTAG_RELEASE:
472     case RPMTAG_URL:
473     case RPMTAG_DISTTAG:
474         SINGLE_TOKEN_ONLY;
475         /* These macros are for backward compatibility */
476         if (tag == RPMTAG_VERSION) {
477             if (strchr(field, '-') != NULL) {
478                 rpmlog(RPMLOG_ERR, _("line %d: Illegal char '-' in %s: %s\n"),
479                     spec->lineNum, "version", spec->line);
480                 return RPMRC_FAIL;
481             }
482             addMacro(spec->macros, "PACKAGE_VERSION", NULL, field, RMIL_OLDSPEC);
483         } else if (tag == RPMTAG_RELEASE) {
484             if (strchr(field, '-') != NULL) {
485                 rpmlog(RPMLOG_ERR, _("line %d: Illegal char '-' in %s: %s\n"),
486                     spec->lineNum, "release", spec->line);
487                 return RPMRC_FAIL;
488             }
489             addMacro(spec->macros, "PACKAGE_RELEASE", NULL, field, RMIL_OLDSPEC-1);
490         }
491         (void) headerAddEntry(pkg->header, tag, RPM_STRING_TYPE, field, 1);
492         break;
493     case RPMTAG_GROUP:
494     case RPMTAG_SUMMARY:
495         (void) stashSt(spec, pkg->header, tag, lang);
496     case RPMTAG_DISTRIBUTION:
497     case RPMTAG_VENDOR:
498     case RPMTAG_LICENSE:
499     case RPMTAG_PACKAGER:
500         if (!*lang)
501             (void) headerAddEntry(pkg->header, tag, RPM_STRING_TYPE, field, 1);
502         else if (!(noLang && strcmp(lang, RPMBUILD_DEFAULT_LANG)))
503             (void) headerAddI18NString(pkg->header, tag, field, lang);
504         break;
505     case RPMTAG_BUILDROOT:
506         SINGLE_TOKEN_ONLY;
507       { char * buildRoot = NULL;
508
509         /*
510          * Note: rpmGenPath should guarantee a "canonical" path. That means
511          * that the following pathologies should be weeded out:
512          *          //bin//sh
513          *          //usr//bin/
514          *          /.././../usr/../bin//./sh
515          */
516         if (spec->buildRoot == NULL) {
517             buildRoot = rpmGenPath(NULL, "%{?buildroot:%{buildroot}}", NULL);
518             if (strcmp(buildRoot, "/")) {
519                 spec->buildRoot = buildRoot;
520                 macro = NULL;
521             } else {
522                 const char * specPath = field;
523
524                 buildRoot = _free(buildRoot);
525                 if (*field == '\0') field = "/";
526                 buildRoot = rpmGenPath(spec->rootDir, specPath, NULL);
527                 spec->buildRoot = buildRoot;
528                 field = (char *) buildRoot;
529             }
530             spec->gotBuildRoot = 1;
531         } else {
532             macro = NULL;
533         }
534         buildRoot = rpmGenPath(NULL, spec->buildRoot, NULL);
535         if (*buildRoot == '\0') buildRoot = "/";
536         if (!strcmp(buildRoot, "/")) {
537             rpmlog(RPMLOG_ERR,
538                      _("BuildRoot can not be \"/\": %s\n"), spec->buildRoot);
539             free(buildRoot);
540             return RPMRC_FAIL;
541         }
542         free(buildRoot);
543       } break;
544     case RPMTAG_PREFIXES:
545         addOrAppendListEntry(pkg->header, tag, field);
546         xx = hge(pkg->header, tag, &type, (rpm_data_t *)&array, &num);
547         while (num--) {
548             len = strlen(array[num]);
549             if (array[num][len - 1] == '/' && len > 1) {
550                 rpmlog(RPMLOG_ERR,
551                          _("line %d: Prefixes must not end with \"/\": %s\n"),
552                          spec->lineNum, spec->line);
553                 array = hfd(array, type);
554                 return RPMRC_FAIL;
555             }
556         }
557         array = hfd(array, type);
558         break;
559     case RPMTAG_DOCDIR:
560         SINGLE_TOKEN_ONLY;
561         if (field[0] != '/') {
562             rpmlog(RPMLOG_ERR,
563                      _("line %d: Docdir must begin with '/': %s\n"),
564                      spec->lineNum, spec->line);
565             return RPMRC_FAIL;
566         }
567         macro = NULL;
568         delMacro(NULL, "_docdir");
569         addMacro(NULL, "_docdir", NULL, field, RMIL_SPEC);
570         break;
571     case RPMTAG_EPOCH: {
572         SINGLE_TOKEN_ONLY;
573         int epoch;
574         if (parseNum(field, &epoch)) {
575             rpmlog(RPMLOG_ERR,
576                      _("line %d: Epoch field must be a number: %s\n"),
577                      spec->lineNum, spec->line);
578             return RPMRC_FAIL;
579         }
580         xx = headerAddEntry(pkg->header, tag, RPM_INT32_TYPE, &epoch, 1);
581         break;
582     }
583     case RPMTAG_AUTOREQPROV:
584         pkg->autoReq = parseYesNo(field);
585         pkg->autoProv = pkg->autoReq;
586         break;
587     case RPMTAG_AUTOREQ:
588         pkg->autoReq = parseYesNo(field);
589         break;
590     case RPMTAG_AUTOPROV:
591         pkg->autoProv = parseYesNo(field);
592         break;
593     case RPMTAG_SOURCE:
594     case RPMTAG_PATCH:
595         SINGLE_TOKEN_ONLY;
596         macro = NULL;
597         if ((rc = addSource(spec, pkg, field, tag)))
598             return rc;
599         break;
600     case RPMTAG_ICON:
601         SINGLE_TOKEN_ONLY;
602         if ((rc = addSource(spec, pkg, field, tag)))
603             return rc;
604         if ((rc = readIcon(pkg->header, field)))
605             return RPMRC_FAIL;
606         break;
607     case RPMTAG_NOSOURCE:
608     case RPMTAG_NOPATCH:
609         spec->noSource = 1;
610         if ((rc = parseNoSource(spec, field, tag)))
611             return rc;
612         break;
613     case RPMTAG_BUILDPREREQ:
614     case RPMTAG_BUILDREQUIRES:
615         if ((rc = parseBits(lang, buildScriptBits, &tagflags))) {
616             rpmlog(RPMLOG_ERR,
617                      _("line %d: Bad %s: qualifiers: %s\n"),
618                      spec->lineNum, rpmTagGetName(tag), spec->line);
619             return rc;
620         }
621         if ((rc = parseRCPOT(spec, pkg, field, tag, 0, tagflags)))
622             return rc;
623         break;
624     case RPMTAG_REQUIREFLAGS:
625     case RPMTAG_PREREQ:
626         if ((rc = parseBits(lang, installScriptBits, &tagflags))) {
627             rpmlog(RPMLOG_ERR,
628                      _("line %d: Bad %s: qualifiers: %s\n"),
629                      spec->lineNum, rpmTagGetName(tag), spec->line);
630             return rc;
631         }
632         if ((rc = parseRCPOT(spec, pkg, field, tag, 0, tagflags)))
633             return rc;
634         break;
635     case RPMTAG_BUILDCONFLICTS:
636     case RPMTAG_CONFLICTFLAGS:
637     case RPMTAG_OBSOLETEFLAGS:
638     case RPMTAG_PROVIDEFLAGS:
639         tagflags = RPMSENSE_ANY;
640         if ((rc = parseRCPOT(spec, pkg, field, tag, 0, tagflags)))
641             return rc;
642         break;
643     case RPMTAG_EXCLUDEARCH:
644     case RPMTAG_EXCLUSIVEARCH:
645     case RPMTAG_EXCLUDEOS:
646     case RPMTAG_EXCLUSIVEOS:
647         addOrAppendListEntry(spec->buildRestrictions, tag, field);
648         break;
649     case RPMTAG_BUILDARCHS:
650         if ((rc = poptParseArgvString(field,
651                                       &(spec->BACount),
652                                       &(spec->BANames)))) {
653             rpmlog(RPMLOG_ERR,
654                      _("line %d: Bad BuildArchitecture format: %s\n"),
655                      spec->lineNum, spec->line);
656             return RPMRC_FAIL;
657         }
658         if (!spec->BACount)
659             spec->BANames = _free(spec->BANames);
660         break;
661
662     default:
663         rpmlog(RPMLOG_ERR, _("Internal error: Bogus tag %d\n"), tag);
664         return RPMRC_FAIL;
665     }
666
667     if (macro)
668         addMacro(spec->macros, macro, NULL, field, RMIL_SPEC);
669     
670     return RPMRC_OK;
671 }
672
673 /* This table has to be in a peculiar order.  If one tag is the */
674 /* same as another, plus a few letters, it must come first.     */
675
676 /**
677  */
678 typedef struct PreambleRec_s {
679     rpmTag tag;
680     size_t len;
681     int multiLang;
682     int obsolete;
683     const char * token;
684 } * PreambleRec;
685
686 /* XXX FIXME: strlen for these is calculated at runtime, preventing const */
687 static struct PreambleRec_s preambleList[] = {
688     {RPMTAG_NAME,               0, 0, 0, "name"},
689     {RPMTAG_VERSION,            0, 0, 0, "version"},
690     {RPMTAG_RELEASE,            0, 0, 0, "release"},
691     {RPMTAG_EPOCH,              0, 0, 0, "epoch"},
692     {RPMTAG_SUMMARY,            0, 1, 0, "summary"},
693     {RPMTAG_LICENSE,            0, 0, 0, "license"},
694     {RPMTAG_DISTRIBUTION,       0, 0, 0, "distribution"},
695     {RPMTAG_DISTURL,            0, 0, 0, "disturl"},
696     {RPMTAG_VENDOR,             0, 0, 0, "vendor"},
697     {RPMTAG_GROUP,              0, 1, 0, "group"},
698     {RPMTAG_PACKAGER,           0, 0, 0, "packager"},
699     {RPMTAG_URL,                0, 0, 0, "url"},
700     {RPMTAG_SOURCE,             0, 0, 0, "source"},
701     {RPMTAG_PATCH,              0, 0, 0, "patch"},
702     {RPMTAG_NOSOURCE,           0, 0, 0, "nosource"},
703     {RPMTAG_NOPATCH,            0, 0, 0, "nopatch"},
704     {RPMTAG_EXCLUDEARCH,        0, 0, 0, "excludearch"},
705     {RPMTAG_EXCLUSIVEARCH,      0, 0, 0, "exclusivearch"},
706     {RPMTAG_EXCLUDEOS,          0, 0, 0, "excludeos"},
707     {RPMTAG_EXCLUSIVEOS,        0, 0, 0, "exclusiveos"},
708     {RPMTAG_ICON,               0, 0, 0, "icon"},
709     {RPMTAG_PROVIDEFLAGS,       0, 0, 0, "provides"},
710     {RPMTAG_REQUIREFLAGS,       0, 1, 0, "requires"},
711     {RPMTAG_PREREQ,             0, 1, 0, "prereq"},
712     {RPMTAG_CONFLICTFLAGS,      0, 0, 0, "conflicts"},
713     {RPMTAG_OBSOLETEFLAGS,      0, 0, 0, "obsoletes"},
714     {RPMTAG_PREFIXES,           0, 0, 0, "prefixes"},
715     {RPMTAG_PREFIXES,           0, 0, 0, "prefix"},
716     {RPMTAG_BUILDROOT,          0, 0, 0, "buildroot"},
717     {RPMTAG_BUILDARCHS,         0, 0, 0, "buildarchitectures"},
718     {RPMTAG_BUILDARCHS,         0, 0, 0, "buildarch"},
719     {RPMTAG_BUILDCONFLICTS,     0, 0, 0, "buildconflicts"},
720     {RPMTAG_BUILDPREREQ,        0, 1, 0, "buildprereq"},
721     {RPMTAG_BUILDREQUIRES,      0, 1, 0, "buildrequires"},
722     {RPMTAG_AUTOREQPROV,        0, 0, 0, "autoreqprov"},
723     {RPMTAG_AUTOREQ,            0, 0, 0, "autoreq"},
724     {RPMTAG_AUTOPROV,           0, 0, 0, "autoprov"},
725     {RPMTAG_DOCDIR,             0, 0, 0, "docdir"},
726     {RPMTAG_DISTTAG,            0, 0, 0, "disttag"},
727         /* LCL: can't add null annotation */
728     {0, 0, 0, 0, 0}
729 };
730
731 /**
732  */
733 static inline void initPreambleList(void)
734 {
735     PreambleRec p;
736     for (p = preambleList; p->token != NULL; p++)
737         if (p->token) p->len = strlen(p->token);
738 }
739
740 /**
741  */
742 static int findPreambleTag(rpmSpec spec,rpmTag * tag,
743                 const char ** macro, char * lang)
744 {
745     PreambleRec p;
746     char *s;
747
748     if (preambleList[0].len == 0)
749         initPreambleList();
750
751     for (p = preambleList; p->token != NULL; p++) {
752         if (!(p->token && !rstrncasecmp(spec->line, p->token, p->len)))
753             continue;
754         if (p->obsolete) {
755             rpmlog(RPMLOG_ERR, _("Legacy syntax is unsupported: %s\n"),
756                         p->token);
757             p = NULL;
758         }
759         break;
760     }
761     if (p == NULL || p->token == NULL)
762         return 1;
763
764     s = spec->line + p->len;
765     SKIPSPACE(s);
766
767     switch (p->multiLang) {
768     default:
769     case 0:
770         /* Unless this is a source or a patch, a ':' better be next */
771         if (p->tag != RPMTAG_SOURCE && p->tag != RPMTAG_PATCH) {
772             if (*s != ':') return 1;
773         }
774         *lang = '\0';
775         break;
776     case 1:     /* Parse optional ( <token> ). */
777         if (*s == ':') {
778             strcpy(lang, RPMBUILD_DEFAULT_LANG);
779             break;
780         }
781         if (*s != '(') return 1;
782         s++;
783         SKIPSPACE(s);
784         while (!risspace(*s) && *s != ')')
785             *lang++ = *s++;
786         *lang = '\0';
787         SKIPSPACE(s);
788         if (*s != ')') return 1;
789         s++;
790         SKIPSPACE(s);
791         if (*s != ':') return 1;
792         break;
793     }
794
795     *tag = p->tag;
796     if (macro)
797         *macro = p->token;
798     return 0;
799 }
800
801 int parsePreamble(rpmSpec spec, int initialPackage)
802 {
803     int nextPart;
804     int rc, xx;
805     char *name, *linep;
806     int flag = 0;
807     Package pkg;
808     char *NVR = NULL;
809     char lang[BUFSIZ];
810
811     pkg = newPackage(spec);
812         
813     if (! initialPackage) {
814         /* There is one option to %package: <pkg> or -n <pkg> */
815         if (parseSimplePart(spec->line, &name, &flag)) {
816             rpmlog(RPMLOG_ERR, _("Bad package specification: %s\n"),
817                         spec->line);
818             return RPMRC_FAIL;
819         }
820         
821         if (!lookupPackage(spec, name, flag, NULL)) {
822             rpmlog(RPMLOG_ERR, _("Package already exists: %s\n"),
823                         spec->line);
824             free(name);
825             return RPMRC_FAIL;
826         }
827         
828         /* Construct the package */
829         if (flag == PART_SUBNAME) {
830             const char * mainName;
831             xx = headerNVR(spec->packages->header, &mainName, NULL, NULL);
832             rasprintf(&NVR, "%s-%s", mainName, name);
833         } else
834             NVR = xstrdup(name);
835         free(name);
836         xx = headerAddEntry(pkg->header, RPMTAG_NAME, RPM_STRING_TYPE, NVR, 1);
837     } else {
838         NVR = xstrdup("(main package)");
839     }
840
841     if ((rc = readLine(spec, STRIP_TRAILINGSPACE | STRIP_COMMENTS)) > 0) {
842         nextPart = PART_NONE;
843     } else {
844         if (rc) {
845             free(NVR);
846             return rc;
847         }
848         while (! (nextPart = isPart(spec->line))) {
849             const char * macro;
850             rpmTag tag;
851
852             /* Skip blank lines */
853             linep = spec->line;
854             SKIPSPACE(linep);
855             if (*linep != '\0') {
856                 if (findPreambleTag(spec, &tag, &macro, lang)) {
857                     rpmlog(RPMLOG_ERR, _("line %d: Unknown tag: %s\n"),
858                                 spec->lineNum, spec->line);
859                     free(NVR);
860                     return RPMRC_FAIL;
861                 }
862                 if (handlePreambleTag(spec, pkg, tag, macro, lang)) {
863                     free(NVR);
864                     return RPMRC_FAIL;
865                 }
866                 if (spec->BANames && !spec->recursing) {
867                     free(NVR);
868                     return PART_BUILDARCHITECTURES;
869                 }
870             }
871             if ((rc =
872                  readLine(spec, STRIP_TRAILINGSPACE | STRIP_COMMENTS)) > 0) {
873                 nextPart = PART_NONE;
874                 break;
875             }
876             if (rc) {
877                 free(NVR);
878                 return rc;
879             }
880         }
881     }
882
883     /* Do some final processing on the header */
884     
885     if (!spec->gotBuildRoot && spec->buildRoot) {
886         rpmlog(RPMLOG_ERR, _("Spec file can't use BuildRoot\n"));
887         free(NVR);
888         return RPMRC_FAIL;
889     }
890
891     /* XXX Skip valid arch check if not building binary package */
892     if (!spec->anyarch && checkForValidArchitectures(spec)) {
893         free(NVR);
894         return RPMRC_FAIL;
895     }
896
897     if (pkg == spec->packages)
898         fillOutMainPackage(pkg->header);
899
900     if (checkForDuplicates(pkg->header, NVR)) {
901         free(NVR);
902         return RPMRC_FAIL;
903     }
904
905     if (pkg != spec->packages)
906         headerCopyTags(spec->packages->header, pkg->header,
907                         (rpmTag *)copyTagsDuringParse);
908
909     if (checkForRequired(pkg->header, NVR)) {
910         free(NVR);
911         return RPMRC_FAIL;
912     }
913
914     return nextPart;
915 }