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