Allow noarch subpackages
[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_LICENSE,
46     0
47 };
48
49 /**
50  */
51 static void addOrAppendListEntry(Header h, rpmTag tag, const char * line)
52 {
53     int xx;
54     int argc;
55     const char **argv;
56
57     xx = poptParseArgvString(line, &argc, &argv);
58     if (argc) 
59         headerPutStringArray(h, tag, argv, argc);
60     argv = _free(argv);
61 }
62
63 /* Parse a simple part line that only take -n <pkg> or <pkg> */
64 /* <pkg> is returned in name as a pointer into a dynamic buffer */
65
66 /**
67  */
68 static int parseSimplePart(const char *line, char **name, int *flag)
69 {
70     char *tok;
71     char *linebuf = xstrdup(line);
72     int rc;
73
74     /* Throw away the first token (the %xxxx) */
75     (void)strtok(linebuf, " \t\n");
76     *name = NULL;
77
78     if (!(tok = strtok(NULL, " \t\n"))) {
79         rc = 0;
80         goto exit;
81     }
82     
83     if (!strcmp(tok, "-n")) {
84         if (!(tok = strtok(NULL, " \t\n"))) {
85             rc = 1;
86             goto exit;
87         }
88         *flag = PART_NAME;
89     } else {
90         *flag = PART_SUBNAME;
91     }
92     *name = xstrdup(tok);
93     rc = strtok(NULL, " \t\n") ? 1 : 0;
94
95 exit:
96     free(linebuf);
97     return rc;
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     struct rpmtd_s td;
193     int found = 0;
194     const char *str;
195
196     if (!headerGet(h, tag, &td, HEADERGET_MINMEM))
197         return -1;
198
199     while ((str = rpmtdNextString(&td))) {
200         if (!rstrcasecmp(str, name)) {
201             found = 1;
202             break;
203         }
204     }
205     rpmtdFreeData(&td);
206
207     return found;
208 }
209
210 /**
211  */
212 static rpmRC checkForValidArchitectures(rpmSpec spec)
213 {
214     char *arch = rpmExpand("%{_target_cpu}", NULL);
215     char *os = rpmExpand("%{_target_os}", NULL);
216     rpmRC rc = RPMRC_FAIL; /* assume failure */
217     
218     if (isMemberInEntry(spec->buildRestrictions,
219                         arch, RPMTAG_EXCLUDEARCH) == 1) {
220         rpmlog(RPMLOG_ERR, _("Architecture is excluded: %s\n"), arch);
221         goto exit;
222     }
223     if (isMemberInEntry(spec->buildRestrictions,
224                         arch, RPMTAG_EXCLUSIVEARCH) == 0) {
225         rpmlog(RPMLOG_ERR, _("Architecture is not included: %s\n"), arch);
226         goto exit;
227     }
228     if (isMemberInEntry(spec->buildRestrictions,
229                         os, RPMTAG_EXCLUDEOS) == 1) {
230         rpmlog(RPMLOG_ERR, _("OS is excluded: %s\n"), os);
231         goto exit;
232     }
233     if (isMemberInEntry(spec->buildRestrictions,
234                         os, RPMTAG_EXCLUSIVEOS) == 0) {
235         rpmlog(RPMLOG_ERR, _("OS is not included: %s\n"), os);
236         goto exit;
237     }
238     rc = RPMRC_OK;
239
240 exit:
241     arch = _free(arch);
242     os = _free(os);
243
244     return rc;
245 }
246
247 /**
248  * Check that required tags are present in header.
249  * @param h             header
250  * @param NVR           package name-version-release
251  * @return              RPMRC_OK if OK
252  */
253 static int checkForRequired(Header h, const char * NVR)
254         /* LCL: parse error here with modifies */
255 {
256     int res = RPMRC_OK;
257     const rpmTag * p;
258
259     for (p = requiredTags; *p != 0; p++) {
260         if (!headerIsEntry(h, *p)) {
261             rpmlog(RPMLOG_ERR,
262                         _("%s field must be present in package: %s\n"),
263                         rpmTagGetName(*p), NVR);
264             res = RPMRC_FAIL;
265         }
266     }
267
268     return res;
269 }
270
271 /**
272  * Check that no duplicate tags are present in header.
273  * @param h             header
274  * @param NVR           package name-version-release
275  * @return              RPMRC_OK if OK
276  */
277 static int checkForDuplicates(Header h, const char * NVR)
278 {
279     int res = RPMRC_OK;
280     rpmTag lastTag, tag;
281     HeaderIterator hi;
282     struct rpmtd_s td;
283     
284     for (hi = headerInitIterator(h), lastTag = 0;
285         headerNext(hi, &td), tag = rpmtdTag(&td);
286         lastTag = tag)
287     {
288         if (tag == lastTag) {
289             rpmlog(RPMLOG_ERR, _("Duplicate %s entries in package: %s\n"),
290                      rpmTagGetName(tag), NVR);
291             res = RPMRC_FAIL;
292         }
293         rpmtdFreeData(&td);
294     }
295     hi = headerFreeIterator(hi);
296
297     return res;
298 }
299
300 /**
301  */
302 static struct optionalTag {
303     rpmTag      ot_tag;
304     const char * ot_mac;
305 } const optionalTags[] = {
306     { RPMTAG_VENDOR,            "%{vendor}" },
307     { RPMTAG_PACKAGER,          "%{packager}" },
308     { RPMTAG_DISTRIBUTION,      "%{distribution}" },
309     { RPMTAG_DISTURL,           "%{disturl}" },
310     { -1, NULL }
311 };
312
313 /**
314  */
315 static void fillOutMainPackage(Header h)
316 {
317     const struct optionalTag *ot;
318
319     for (ot = optionalTags; ot->ot_mac != NULL; ot++) {
320         if (!headerIsEntry(h, ot->ot_tag)) {
321             char *val = rpmExpand(ot->ot_mac, NULL);
322             if (val && *val != '%') {
323                 headerPutString(h, ot->ot_tag, val);
324             }
325             val = _free(val);
326         }
327     }
328 }
329
330 static int getSpecialDocDir(Package pkg)
331 {
332     const char *errstr, *docdir_fmt = "%{NAME}-%{VERSION}";
333     char *fmt_macro, *fmt; 
334     int rc = -1;
335
336     fmt_macro = rpmExpand("%{?_docdir_fmt}", NULL);
337     if (fmt_macro && strlen(fmt_macro) > 0) {
338         docdir_fmt = fmt_macro;
339     }
340     fmt = headerFormat(pkg->header, docdir_fmt, &errstr);
341     if (!fmt) {
342         rpmlog(RPMLOG_ERR, _("illegal _docdir_fmt: %s\n"), errstr);
343         goto exit;
344     }
345     pkg->specialDocDir = rpmGetPath("%{_docdir}/", fmt, NULL);
346     rc = 0;
347
348 exit:
349     free(fmt);
350     free(fmt_macro);
351     return rc;
352 }
353
354 /**
355  */
356 static rpmRC readIcon(Header h, const char * file)
357 {
358     char *fn = NULL;
359     uint8_t *icon;
360     FD_t fd;
361     rpmRC rc = RPMRC_OK;
362     off_t size;
363     size_t nb, iconsize;
364
365     /* XXX use rpmGenPath(rootdir, "%{_sourcedir}/", file) for icon path. */
366     fn = rpmGetPath("%{_sourcedir}/", file, NULL);
367
368     fd = Fopen(fn, "r.ufdio");
369     if (fd == NULL || Ferror(fd)) {
370         rpmlog(RPMLOG_ERR, _("Unable to open icon %s: %s\n"),
371                 fn, Fstrerror(fd));
372         rc = RPMRC_FAIL;
373         goto exit;
374     }
375     size = fdSize(fd);
376     iconsize = (size >= 0 ? size : (8 * BUFSIZ));
377     if (iconsize == 0) {
378         (void) Fclose(fd);
379         rc = RPMRC_OK;
380         goto exit;
381     }
382
383     icon = xmalloc(iconsize + 1);
384     *icon = '\0';
385
386     nb = Fread(icon, sizeof(icon[0]), iconsize, fd);
387     if (Ferror(fd) || (size >= 0 && nb != size)) {
388         rpmlog(RPMLOG_ERR, _("Unable to read icon %s: %s\n"),
389                 fn, Fstrerror(fd));
390         rc = RPMRC_FAIL;
391     }
392     (void) Fclose(fd);
393     if (rc != RPMRC_OK)
394         goto exit;
395
396     if (! strncmp((char*)icon, "GIF", sizeof("GIF")-1)) {
397         headerPutBin(h, RPMTAG_GIF, icon, iconsize);
398     } else if (! strncmp((char*)icon, "/* XPM", sizeof("/* XPM")-1)) {
399         headerPutBin(h, RPMTAG_XPM, icon, iconsize);
400     } else {
401         rpmlog(RPMLOG_ERR, _("Unknown icon type: %s\n"), file);
402         rc = RPMRC_FAIL;
403         goto exit;
404     }
405     icon = _free(icon);
406     
407 exit:
408     fn = _free(fn);
409     return rc;
410 }
411
412 spectag stashSt(rpmSpec spec, Header h, rpmTag tag, const char * lang)
413 {
414     spectag t = NULL;
415
416     if (spec->st) {
417         spectags st = spec->st;
418         if (st->st_ntags == st->st_nalloc) {
419             st->st_nalloc += 10;
420             st->st_t = xrealloc(st->st_t, st->st_nalloc * sizeof(*(st->st_t)));
421         }
422         t = st->st_t + st->st_ntags++;
423         t->t_tag = tag;
424         t->t_startx = spec->lineNum - 1;
425         t->t_nlines = 1;
426         t->t_lang = xstrdup(lang);
427         t->t_msgid = NULL;
428         if (!(t->t_lang && strcmp(t->t_lang, RPMBUILD_DEFAULT_LANG))) {
429             struct rpmtd_s td;
430             if (headerGet(h, RPMTAG_NAME, &td, HEADERGET_MINMEM)) {
431                 rasprintf(&t->t_msgid, "%s(%s)", 
432                          rpmtdGetString(&td), rpmTagGetName(tag));
433                 rpmtdFreeData(&td);
434             }
435         }
436     }
437     return t;
438 }
439
440 #define SINGLE_TOKEN_ONLY \
441 if (multiToken) { \
442     rpmlog(RPMLOG_ERR, _("line %d: Tag takes single token only: %s\n"), \
443              spec->lineNum, spec->line); \
444     return RPMRC_FAIL; \
445 }
446
447 extern int noLang;
448
449 /**
450  */
451 static int handlePreambleTag(rpmSpec spec, Package pkg, rpmTag tag,
452                 const char *macro, const char *lang)
453 {
454     char * field = spec->line;
455     char * end;
456     int multiToken = 0;
457     rpmsenseFlags tagflags;
458     int rc;
459     int xx;
460     
461     if (field == NULL) return RPMRC_FAIL;       /* XXX can't happen */
462     /* Find the start of the "field" and strip trailing space */
463     while ((*field) && (*field != ':'))
464         field++;
465     if (*field != ':') {
466         rpmlog(RPMLOG_ERR, _("line %d: Malformed tag: %s\n"),
467                  spec->lineNum, spec->line);
468         return RPMRC_FAIL;
469     }
470     field++;
471     SKIPSPACE(field);
472     if (!*field) {
473         /* Empty field */
474         rpmlog(RPMLOG_ERR, _("line %d: Empty tag: %s\n"),
475                  spec->lineNum, spec->line);
476         return RPMRC_FAIL;
477     }
478     end = findLastChar(field);
479     *(end+1) = '\0';
480
481     /* See if this is multi-token */
482     end = field;
483     SKIPNONSPACE(end);
484     if (*end != '\0')
485         multiToken = 1;
486
487     switch (tag) {
488     case RPMTAG_NAME:
489     case RPMTAG_VERSION:
490     case RPMTAG_RELEASE:
491     case RPMTAG_URL:
492     case RPMTAG_DISTTAG:
493         SINGLE_TOKEN_ONLY;
494         /* These macros are for backward compatibility */
495         if (tag == RPMTAG_VERSION) {
496             if (strchr(field, '-') != NULL) {
497                 rpmlog(RPMLOG_ERR, _("line %d: Illegal char '-' in %s: %s\n"),
498                     spec->lineNum, "version", spec->line);
499                 return RPMRC_FAIL;
500             }
501             addMacro(spec->macros, "PACKAGE_VERSION", NULL, field, RMIL_OLDSPEC);
502         } else if (tag == RPMTAG_RELEASE) {
503             if (strchr(field, '-') != NULL) {
504                 rpmlog(RPMLOG_ERR, _("line %d: Illegal char '-' in %s: %s\n"),
505                     spec->lineNum, "release", spec->line);
506                 return RPMRC_FAIL;
507             }
508             addMacro(spec->macros, "PACKAGE_RELEASE", NULL, field, RMIL_OLDSPEC-1);
509         }
510         headerPutString(pkg->header, tag, field);
511         break;
512     case RPMTAG_GROUP:
513     case RPMTAG_SUMMARY:
514         (void) stashSt(spec, pkg->header, tag, lang);
515     case RPMTAG_DISTRIBUTION:
516     case RPMTAG_VENDOR:
517     case RPMTAG_LICENSE:
518     case RPMTAG_PACKAGER:
519         if (!*lang) {
520             headerPutString(pkg->header, tag, field);
521         } else if (!(noLang && strcmp(lang, RPMBUILD_DEFAULT_LANG)))
522             (void) headerAddI18NString(pkg->header, tag, field, lang);
523         break;
524     case RPMTAG_BUILDROOT:
525         /* just silently ignore BuildRoot */
526         macro = NULL;
527         break;
528     case RPMTAG_PREFIXES: {
529         struct rpmtd_s td;
530         const char *str;
531         addOrAppendListEntry(pkg->header, tag, field);
532         xx = headerGet(pkg->header, tag, &td, HEADERGET_MINMEM);
533         while ((str = rpmtdNextString(&td))) {
534             size_t len = strlen(str);
535             if (len > 1 && str[len-1] == '/') {
536                 rpmlog(RPMLOG_ERR,
537                          _("line %d: Prefixes must not end with \"/\": %s\n"),
538                          spec->lineNum, spec->line);
539                 rpmtdFreeData(&td);
540                 return RPMRC_FAIL;
541             }
542         }
543         rpmtdFreeData(&td);
544         break;
545     }
546     case RPMTAG_DOCDIR:
547         SINGLE_TOKEN_ONLY;
548         if (field[0] != '/') {
549             rpmlog(RPMLOG_ERR,
550                      _("line %d: Docdir must begin with '/': %s\n"),
551                      spec->lineNum, spec->line);
552             return RPMRC_FAIL;
553         }
554         macro = NULL;
555         delMacro(NULL, "_docdir");
556         addMacro(NULL, "_docdir", NULL, field, RMIL_SPEC);
557         break;
558     case RPMTAG_EPOCH: {
559         SINGLE_TOKEN_ONLY;
560         uint32_t epoch;
561         if (parseUnsignedNum(field, &epoch)) {
562             rpmlog(RPMLOG_ERR,
563                      _("line %d: Epoch field must be an unsigned number: %s\n"),
564                      spec->lineNum, spec->line);
565             return RPMRC_FAIL;
566         }
567         headerPutUint32(pkg->header, tag, &epoch, 1);
568         break;
569     }
570     case RPMTAG_AUTOREQPROV:
571         pkg->autoReq = parseYesNo(field);
572         pkg->autoProv = pkg->autoReq;
573         break;
574     case RPMTAG_AUTOREQ:
575         pkg->autoReq = parseYesNo(field);
576         break;
577     case RPMTAG_AUTOPROV:
578         pkg->autoProv = parseYesNo(field);
579         break;
580     case RPMTAG_SOURCE:
581     case RPMTAG_PATCH:
582         macro = NULL;
583         if ((rc = addSource(spec, pkg, field, tag)))
584             return rc;
585         break;
586     case RPMTAG_ICON:
587         SINGLE_TOKEN_ONLY;
588         if ((rc = addSource(spec, pkg, field, tag)))
589             return rc;
590         if ((rc = readIcon(pkg->header, field)))
591             return RPMRC_FAIL;
592         break;
593     case RPMTAG_NOSOURCE:
594     case RPMTAG_NOPATCH:
595         spec->noSource = 1;
596         if ((rc = parseNoSource(spec, field, tag)))
597             return rc;
598         break;
599     case RPMTAG_BUILDPREREQ:
600     case RPMTAG_BUILDREQUIRES:
601         if ((rc = parseBits(lang, buildScriptBits, &tagflags))) {
602             rpmlog(RPMLOG_ERR,
603                      _("line %d: Bad %s: qualifiers: %s\n"),
604                      spec->lineNum, rpmTagGetName(tag), spec->line);
605             return rc;
606         }
607         if ((rc = parseRCPOT(spec, pkg, field, tag, 0, tagflags)))
608             return rc;
609         break;
610     case RPMTAG_REQUIREFLAGS:
611     case RPMTAG_PREREQ:
612         if ((rc = parseBits(lang, installScriptBits, &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_BUILDCONFLICTS:
622     case RPMTAG_CONFLICTFLAGS:
623     case RPMTAG_OBSOLETEFLAGS:
624     case RPMTAG_PROVIDEFLAGS:
625         tagflags = RPMSENSE_ANY;
626         if ((rc = parseRCPOT(spec, pkg, field, tag, 0, tagflags)))
627             return rc;
628         break;
629     case RPMTAG_EXCLUDEARCH:
630     case RPMTAG_EXCLUSIVEARCH:
631     case RPMTAG_EXCLUDEOS:
632     case RPMTAG_EXCLUSIVEOS:
633         addOrAppendListEntry(spec->buildRestrictions, tag, field);
634         break;
635     case RPMTAG_BUILDARCHS: {
636         int BACount;
637         const char **BANames = NULL;
638         if ((rc = poptParseArgvString(field, &BACount, &BANames))) {
639             rpmlog(RPMLOG_ERR,
640                      _("line %d: Bad BuildArchitecture format: %s\n"),
641                      spec->lineNum, spec->line);
642             return RPMRC_FAIL;
643         }
644         if (spec->packages == pkg) {
645             spec->BACount = BACount;
646             spec->BANames = BANames;
647         } else {
648             if (BACount != 1 || strcmp(BANames[0], "noarch")) {
649                 rpmlog(RPMLOG_ERR,
650                          _("line %d: Only noarch subpackages are supported: %s\n"),
651                         spec->lineNum, spec->line);
652                 BANames = _free(BANames);
653                 return RPMRC_FAIL;
654             }
655             headerAddEntry(pkg->header, RPMTAG_ARCH, RPM_STRING_TYPE, "noarch", 1);
656         }
657         if (!BACount)
658             spec->BANames = _free(spec->BANames);
659         break;
660     }
661     default:
662         rpmlog(RPMLOG_ERR, _("Internal error: Bogus tag %d\n"), tag);
663         return RPMRC_FAIL;
664     }
665
666     if (macro)
667         addMacro(spec->macros, macro, NULL, field, RMIL_SPEC);
668     
669     return RPMRC_OK;
670 }
671
672 /* This table has to be in a peculiar order.  If one tag is the */
673 /* same as another, plus a few letters, it must come first.     */
674
675 /**
676  */
677 typedef struct PreambleRec_s {
678     rpmTag tag;
679     size_t len;
680     int multiLang;
681     int obsolete;
682     const char * token;
683 } * PreambleRec;
684
685 /* XXX FIXME: strlen for these is calculated at runtime, preventing const */
686 static struct PreambleRec_s preambleList[] = {
687     {RPMTAG_NAME,               0, 0, 0, "name"},
688     {RPMTAG_VERSION,            0, 0, 0, "version"},
689     {RPMTAG_RELEASE,            0, 0, 0, "release"},
690     {RPMTAG_EPOCH,              0, 0, 0, "epoch"},
691     {RPMTAG_SUMMARY,            0, 1, 0, "summary"},
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_DISTTAG,            0, 0, 0, "disttag"},
726         /* LCL: can't add null annotation */
727     {0, 0, 0, 0, 0}
728 };
729
730 /**
731  */
732 static inline void initPreambleList(void)
733 {
734     PreambleRec p;
735     for (p = preambleList; p->token != NULL; p++)
736         if (p->token) p->len = strlen(p->token);
737 }
738
739 /**
740  */
741 static int findPreambleTag(rpmSpec spec,rpmTag * tag,
742                 const char ** macro, char * lang)
743 {
744     PreambleRec p;
745     char *s;
746
747     if (preambleList[0].len == 0)
748         initPreambleList();
749
750     for (p = preambleList; p->token != NULL; p++) {
751         if (!(p->token && !rstrncasecmp(spec->line, p->token, p->len)))
752             continue;
753         if (p->obsolete) {
754             rpmlog(RPMLOG_ERR, _("Legacy syntax is unsupported: %s\n"),
755                         p->token);
756             p = NULL;
757         }
758         break;
759     }
760     if (p == NULL || p->token == NULL)
761         return 1;
762
763     s = spec->line + p->len;
764     SKIPSPACE(s);
765
766     switch (p->multiLang) {
767     default:
768     case 0:
769         /* Unless this is a source or a patch, a ':' better be next */
770         if (p->tag != RPMTAG_SOURCE && p->tag != RPMTAG_PATCH) {
771             if (*s != ':') return 1;
772         }
773         *lang = '\0';
774         break;
775     case 1:     /* Parse optional ( <token> ). */
776         if (*s == ':') {
777             strcpy(lang, RPMBUILD_DEFAULT_LANG);
778             break;
779         }
780         if (*s != '(') return 1;
781         s++;
782         SKIPSPACE(s);
783         while (!risspace(*s) && *s != ')')
784             *lang++ = *s++;
785         *lang = '\0';
786         SKIPSPACE(s);
787         if (*s != ')') return 1;
788         s++;
789         SKIPSPACE(s);
790         if (*s != ':') return 1;
791         break;
792     }
793
794     *tag = p->tag;
795     if (macro)
796         *macro = p->token;
797     return 0;
798 }
799
800 int parsePreamble(rpmSpec spec, int initialPackage)
801 {
802     int nextPart = PART_ERROR;
803     int res = PART_ERROR; /* assume failure */
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             goto exit;
819         }
820         
821         if (!lookupPackage(spec, name, flag, NULL)) {
822             rpmlog(RPMLOG_ERR, _("Package already exists: %s\n"), spec->line);
823             free(name);
824             goto exit;
825         }
826         
827         /* Construct the package */
828         if (flag == PART_SUBNAME) {
829             const char * mainName;
830             xx = headerNVR(spec->packages->header, &mainName, NULL, NULL);
831             rasprintf(&NVR, "%s-%s", mainName, name);
832         } else
833             NVR = xstrdup(name);
834         free(name);
835         headerPutString(pkg->header, RPMTAG_NAME, NVR);
836     } else {
837         NVR = xstrdup("(main package)");
838     }
839
840     if ((rc = readLine(spec, STRIP_TRAILINGSPACE | STRIP_COMMENTS)) > 0) {
841         nextPart = PART_NONE;
842     } else if (rc < 0) {
843         goto exit;
844     } else {
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(RPMLOG_ERR, _("line %d: Unknown tag: %s\n"),
855                                 spec->lineNum, spec->line);
856                     goto exit;
857                 }
858                 if (handlePreambleTag(spec, pkg, tag, macro, lang)) {
859                     goto exit;
860                 }
861                 if (spec->BANames && !spec->recursing) {
862                     res = PART_BUILDARCHITECTURES;
863                     goto exit;
864                 }
865             }
866             if ((rc =
867                  readLine(spec, STRIP_TRAILINGSPACE | STRIP_COMMENTS)) > 0) {
868                 nextPart = PART_NONE;
869                 break;
870             }
871             if (rc) {
872                 goto exit;
873             }
874         }
875     }
876
877     /* XXX Skip valid arch check if not building binary package */
878     if (!spec->anyarch && checkForValidArchitectures(spec)) {
879         goto exit;
880     }
881
882     if (pkg == spec->packages)
883         fillOutMainPackage(pkg->header);
884
885     if (checkForDuplicates(pkg->header, NVR)) {
886         goto exit;
887     }
888
889     if (pkg != spec->packages)
890         headerCopyTags(spec->packages->header, pkg->header,
891                         (rpmTag *)copyTagsDuringParse);
892
893     if (checkForRequired(pkg->header, NVR)) {
894         goto exit;
895     }
896
897     if (getSpecialDocDir(pkg)) {
898         goto exit;
899     }
900         
901     /* if we get down here nextPart has been set to non-error */
902     res = nextPart;
903
904 exit:
905     free(NVR);
906     return res;
907 }