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