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