Make bunch of build internal code take const parameters
[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/rpmbuild.h>
9 #include <rpm/rpmlog.h>
10 #include <rpm/rpmfileutil.h>
11 #include "debug.h"
12
13 #define SKIPSPACE(s) { while (*(s) && xisspace(*(s))) (s)++; }
14 #define SKIPNONSPACE(s) { while (*(s) && !xisspace(*(s))) (s)++; }
15
16 /**
17  */
18 static rpm_tag_t copyTagsDuringParse[] = {
19     RPMTAG_EPOCH,
20     RPMTAG_VERSION,
21     RPMTAG_RELEASE,
22     RPMTAG_LICENSE,
23     RPMTAG_PACKAGER,
24     RPMTAG_DISTRIBUTION,
25     RPMTAG_DISTURL,
26     RPMTAG_VENDOR,
27     RPMTAG_ICON,
28     RPMTAG_URL,
29     RPMTAG_CHANGELOGTIME,
30     RPMTAG_CHANGELOGNAME,
31     RPMTAG_CHANGELOGTEXT,
32     RPMTAG_PREFIXES,
33     RPMTAG_RHNPLATFORM,
34     RPMTAG_DISTTAG,
35     RPMTAG_CVSID,
36     0
37 };
38
39 /**
40  */
41 static rpm_tag_t requiredTags[] = {
42     RPMTAG_NAME,
43     RPMTAG_VERSION,
44     RPMTAG_RELEASE,
45     RPMTAG_SUMMARY,
46     RPMTAG_GROUP,
47     RPMTAG_LICENSE,
48     0
49 };
50
51 /**
52  */
53 static void addOrAppendListEntry(Header h, rpm_tag_t tag, const char * line)
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, 
62                                     argv, (rpm_count_t) argc);
63     argv = _free(argv);
64 }
65
66 /* Parse a simple part line that only take -n <pkg> or <pkg> */
67 /* <pkg> is return in name as a pointer into a static buffer */
68
69 /**
70  */
71 static int parseSimplePart(const char *line, char **name, int *flag)
72 {
73     char *tok;
74     char linebuf[BUFSIZ];
75     static char buf[BUFSIZ];
76
77     strcpy(linebuf, line);
78
79     /* Throw away the first token (the %xxxx) */
80     (void)strtok(linebuf, " \t\n");
81     
82     if (!(tok = strtok(NULL, " \t\n"))) {
83         *name = NULL;
84         return 0;
85     }
86     
87     if (!strcmp(tok, "-n")) {
88         if (!(tok = strtok(NULL, " \t\n")))
89             return 1;
90         *flag = PART_NAME;
91     } else {
92         *flag = PART_SUBNAME;
93     }
94     strcpy(buf, tok);
95     *name = buf;
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         !xstrcasecmp(s, "false") || !xstrcasecmp(s, "off"))
106             ? 0 : 1);
107 }
108
109 typedef struct tokenBits_s {
110     const char * name;
111     rpmsenseFlags bits;
112 } * tokenBits;
113
114 /**
115  */
116 static struct tokenBits_s 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 struct tokenBits_s 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) && xisspace(c)) s++;
151             se = s;
152             while ((c = *se) && xisalpha(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) && xisspace(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 (! xisspace(*s))
181             res = s;
182         s++;
183     }
184
185     return res;
186 }
187
188 /**
189  */
190 static int isMemberInEntry(Header h, const char *name, rpm_tag_t tag)
191 {
192     HGE_t hge = (HGE_t)headerGetEntryMinMemory;
193     HFD_t hfd = headerFreeData;
194     const char ** names;
195     rpm_tagtype_t type;
196     rpm_count_t count;
197
198     if (!hge(h, tag, &type, (void **)&names, &count))
199         return -1;
200     while (count--) {
201         if (!xstrcasecmp(names[count], name))
202             break;
203     }
204     names = hfd(names, type);
205     return (count >= 0 ? 1 : 0);
206 }
207
208 /**
209  */
210 static int checkForValidArchitectures(rpmSpec spec)
211 {
212     char *arch = rpmExpand("%{_target_cpu}", NULL);
213     char *os = rpmExpand("%{_target_os}", NULL);
214     
215     if (isMemberInEntry(spec->buildRestrictions,
216                         arch, RPMTAG_EXCLUDEARCH) == 1) {
217         rpmlog(RPMLOG_ERR, _("Architecture is excluded: %s\n"), arch);
218         return RPMRC_FAIL;
219     }
220     if (isMemberInEntry(spec->buildRestrictions,
221                         arch, RPMTAG_EXCLUSIVEARCH) == 0) {
222         rpmlog(RPMLOG_ERR, _("Architecture is not included: %s\n"), arch);
223         return RPMRC_FAIL;
224     }
225     if (isMemberInEntry(spec->buildRestrictions,
226                         os, RPMTAG_EXCLUDEOS) == 1) {
227         rpmlog(RPMLOG_ERR, _("OS is excluded: %s\n"), os);
228         return RPMRC_FAIL;
229     }
230     if (isMemberInEntry(spec->buildRestrictions,
231                         os, RPMTAG_EXCLUSIVEOS) == 0) {
232         rpmlog(RPMLOG_ERR, _("OS is not included: %s\n"), os);
233         return RPMRC_FAIL;
234     }
235
236     arch = _free(arch);
237     os = _free(os);
238
239     return RPMRC_OK;
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              RPMRC_OK if OK
247  */
248 static int checkForRequired(Header h, const char * NVR)
249         /* LCL: parse error here with modifies */
250 {
251     int res = RPMRC_OK;
252     rpm_tag_t * p;
253
254     for (p = requiredTags; *p != 0; p++) {
255         if (!headerIsEntry(h, *p)) {
256             rpmlog(RPMLOG_ERR,
257                         _("%s field must be present in package: %s\n"),
258                         rpmTagGetName(*p), NVR);
259             res = RPMRC_FAIL;
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              RPMRC_OK if OK
271  */
272 static int checkForDuplicates(Header h, const char * NVR)
273 {
274     int res = RPMRC_OK;
275     rpm_tag_t 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(RPMLOG_ERR, _("Duplicate %s entries in package: %s\n"),
285                      rpmTagGetName(tag), NVR);
286         res = RPMRC_FAIL;
287     }
288     hi = headerFreeIterator(hi);
289
290     return res;
291 }
292
293 /**
294  */
295 static struct optionalTag {
296     rpm_tag_t   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             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 rpmRC readIcon(Header h, const char * file)
325 {
326     char *fn = NULL;
327     char *icon;
328     FD_t fd;
329     rpmRC rc = RPMRC_OK;
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(RPMLOG_ERR, _("Unable to open icon %s: %s\n"),
339                 fn, Fstrerror(fd));
340         rc = RPMRC_FAIL;
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 = RPMRC_OK;
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(RPMLOG_ERR, _("Unable to read icon %s: %s\n"),
357                 fn, Fstrerror(fd));
358         rc = RPMRC_FAIL;
359     }
360     (void) Fclose(fd);
361     if (rc != RPMRC_OK)
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(RPMLOG_ERR, _("Unknown icon type: %s\n"), file);
370         rc = RPMRC_FAIL;
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, rpm_tag_t 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(RPMLOG_ERR, _("line %d: Tag takes single token only: %s\n"), \
412              spec->lineNum, spec->line); \
413     return RPMRC_FAIL; \
414 }
415
416 extern int noLang;
417
418 /**
419  */
420 static int handlePreambleTag(rpmSpec spec, Package pkg, rpm_tag_t 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     rpm_tagtype_t type;
431     int len;
432     rpm_count_t num;
433     int rc;
434     int xx;
435     
436     if (field == NULL) return RPMRC_FAIL;       /* 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(RPMLOG_ERR, _("line %d: Malformed tag: %s\n"),
442                  spec->lineNum, spec->line);
443         return RPMRC_FAIL;
444     }
445     field++;
446     SKIPSPACE(field);
447     if (!*field) {
448         /* Empty field */
449         rpmlog(RPMLOG_ERR, _("line %d: Empty tag: %s\n"),
450                  spec->lineNum, spec->line);
451         return RPMRC_FAIL;
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(RPMLOG_ERR, _("line %d: Illegal char '-' in %s: %s\n"),
475                     spec->lineNum, "version", spec->line);
476                 return RPMRC_FAIL;
477             }
478             addMacro(spec->macros, "PACKAGE_VERSION", NULL, field, RMIL_OLDSPEC);
479         } else if (tag == RPMTAG_RELEASE) {
480             if (strchr(field, '-') != NULL) {
481                 rpmlog(RPMLOG_ERR, _("line %d: Illegal char '-' in %s: %s\n"),
482                     spec->lineNum, "release", spec->line);
483                 return RPMRC_FAIL;
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         char * 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 (spec->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(RPMLOG_ERR,
537                      _("BuildRoot can not be \"/\": %s\n"), spec->buildRootURL);
538             buildRootURL = _free(buildRootURL);
539             return RPMRC_FAIL;
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(RPMLOG_ERR,
550                          _("line %d: Prefixes must not end with \"/\": %s\n"),
551                          spec->lineNum, spec->line);
552                 array = hfd(array, type);
553                 return RPMRC_FAIL;
554             }
555         }
556         array = hfd(array, type);
557         break;
558     case RPMTAG_DOCDIR:
559         SINGLE_TOKEN_ONLY;
560         if (field[0] != '/') {
561             rpmlog(RPMLOG_ERR,
562                      _("line %d: Docdir must begin with '/': %s\n"),
563                      spec->lineNum, spec->line);
564             return RPMRC_FAIL;
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         int epoch;
573         if (parseNum(field, &epoch)) {
574             rpmlog(RPMLOG_ERR,
575                      _("line %d: Epoch/Serial field must be a number: %s\n"),
576                      spec->lineNum, spec->line);
577             return RPMRC_FAIL;
578         }
579         xx = headerAddEntry(pkg->header, tag, RPM_INT32_TYPE, &epoch, 1);
580         break;
581     }
582     case RPMTAG_AUTOREQPROV:
583         pkg->autoReq = parseYesNo(field);
584         pkg->autoProv = pkg->autoReq;
585         break;
586     case RPMTAG_AUTOREQ:
587         pkg->autoReq = parseYesNo(field);
588         break;
589     case RPMTAG_AUTOPROV:
590         pkg->autoProv = parseYesNo(field);
591         break;
592     case RPMTAG_SOURCE:
593     case RPMTAG_PATCH:
594         SINGLE_TOKEN_ONLY;
595         macro = NULL;
596         if ((rc = addSource(spec, pkg, field, tag)))
597             return rc;
598         break;
599     case RPMTAG_ICON:
600         SINGLE_TOKEN_ONLY;
601         if ((rc = addSource(spec, pkg, field, tag)))
602             return rc;
603         if ((rc = readIcon(pkg->header, field)))
604             return RPMRC_FAIL;
605         break;
606     case RPMTAG_NOSOURCE:
607     case RPMTAG_NOPATCH:
608         spec->noSource = 1;
609         if ((rc = parseNoSource(spec, field, tag)))
610             return rc;
611         break;
612     case RPMTAG_BUILDPREREQ:
613     case RPMTAG_BUILDREQUIRES:
614         if ((rc = parseBits(lang, buildScriptBits, &tagflags))) {
615             rpmlog(RPMLOG_ERR,
616                      _("line %d: Bad %s: qualifiers: %s\n"),
617                      spec->lineNum, rpmTagGetName(tag), spec->line);
618             return rc;
619         }
620         if ((rc = parseRCPOT(spec, pkg, field, tag, 0, tagflags)))
621             return rc;
622         break;
623     case RPMTAG_REQUIREFLAGS:
624     case RPMTAG_PREREQ:
625         if ((rc = parseBits(lang, installScriptBits, &tagflags))) {
626             rpmlog(RPMLOG_ERR,
627                      _("line %d: Bad %s: qualifiers: %s\n"),
628                      spec->lineNum, rpmTagGetName(tag), spec->line);
629             return rc;
630         }
631         if ((rc = parseRCPOT(spec, pkg, field, tag, 0, tagflags)))
632             return rc;
633         break;
634     case RPMTAG_BUILDCONFLICTS:
635     case RPMTAG_CONFLICTFLAGS:
636     case RPMTAG_OBSOLETEFLAGS:
637     case RPMTAG_PROVIDEFLAGS:
638         tagflags = RPMSENSE_ANY;
639         if ((rc = parseRCPOT(spec, pkg, field, tag, 0, tagflags)))
640             return rc;
641         break;
642     case RPMTAG_EXCLUDEARCH:
643     case RPMTAG_EXCLUSIVEARCH:
644     case RPMTAG_EXCLUDEOS:
645     case RPMTAG_EXCLUSIVEOS:
646         addOrAppendListEntry(spec->buildRestrictions, tag, field);
647         break;
648     case RPMTAG_BUILDARCHS:
649         if ((rc = poptParseArgvString(field,
650                                       &(spec->BACount),
651                                       &(spec->BANames)))) {
652             rpmlog(RPMLOG_ERR,
653                      _("line %d: Bad BuildArchitecture format: %s\n"),
654                      spec->lineNum, spec->line);
655             return RPMRC_FAIL;
656         }
657         if (!spec->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     rpm_tag_t tag;
679     size_t len;
680     int multiLang;
681     int obsolete;
682     const char * token;
683 } * PreambleRec;
684
685 static struct PreambleRec_s preambleList[] = {
686     {RPMTAG_NAME,               0, 0, 0, "name"},
687     {RPMTAG_VERSION,            0, 0, 0, "version"},
688     {RPMTAG_RELEASE,            0, 0, 0, "release"},
689     {RPMTAG_EPOCH,              0, 0, 0, "epoch"},
690     {RPMTAG_EPOCH,              0, 0, 1, "serial"},
691     {RPMTAG_SUMMARY,            0, 1, 0, "summary"},
692     {RPMTAG_LICENSE,            0, 0, 1, "copyright"},
693     {RPMTAG_LICENSE,            0, 0, 0, "license"},
694     {RPMTAG_DISTRIBUTION,       0, 0, 0, "distribution"},
695     {RPMTAG_DISTURL,            0, 0, 0, "disturl"},
696     {RPMTAG_VENDOR,             0, 0, 0, "vendor"},
697     {RPMTAG_GROUP,              0, 1, 0, "group"},
698     {RPMTAG_PACKAGER,           0, 0, 0, "packager"},
699     {RPMTAG_URL,                0, 0, 0, "url"},
700     {RPMTAG_SOURCE,             0, 0, 0, "source"},
701     {RPMTAG_PATCH,              0, 0, 0, "patch"},
702     {RPMTAG_NOSOURCE,           0, 0, 0, "nosource"},
703     {RPMTAG_NOPATCH,            0, 0, 0, "nopatch"},
704     {RPMTAG_EXCLUDEARCH,        0, 0, 0, "excludearch"},
705     {RPMTAG_EXCLUSIVEARCH,      0, 0, 0, "exclusivearch"},
706     {RPMTAG_EXCLUDEOS,          0, 0, 0, "excludeos"},
707     {RPMTAG_EXCLUSIVEOS,        0, 0, 0, "exclusiveos"},
708     {RPMTAG_ICON,               0, 0, 0, "icon"},
709     {RPMTAG_PROVIDEFLAGS,       0, 0, 0, "provides"},
710     {RPMTAG_REQUIREFLAGS,       0, 1, 0, "requires"},
711     {RPMTAG_PREREQ,             0, 1, 0, "prereq"},
712     {RPMTAG_CONFLICTFLAGS,      0, 0, 0, "conflicts"},
713     {RPMTAG_OBSOLETEFLAGS,      0, 0, 0, "obsoletes"},
714     {RPMTAG_PREFIXES,           0, 0, 0, "prefixes"},
715     {RPMTAG_PREFIXES,           0, 0, 0, "prefix"},
716     {RPMTAG_BUILDROOT,          0, 0, 0, "buildroot"},
717     {RPMTAG_BUILDARCHS,         0, 0, 0, "buildarchitectures"},
718     {RPMTAG_BUILDARCHS,         0, 0, 0, "buildarch"},
719     {RPMTAG_BUILDCONFLICTS,     0, 0, 0, "buildconflicts"},
720     {RPMTAG_BUILDPREREQ,        0, 1, 0, "buildprereq"},
721     {RPMTAG_BUILDREQUIRES,      0, 1, 0, "buildrequires"},
722     {RPMTAG_AUTOREQPROV,        0, 0, 0, "autoreqprov"},
723     {RPMTAG_AUTOREQ,            0, 0, 0, "autoreq"},
724     {RPMTAG_AUTOPROV,           0, 0, 0, "autoprov"},
725     {RPMTAG_DOCDIR,             0, 0, 0, "docdir"},
726     {RPMTAG_RHNPLATFORM,        0, 0, 1, "rhnplatform"},
727     {RPMTAG_DISTTAG,            0, 0, 0, "disttag"},
728     {RPMTAG_CVSID,              0, 0, 0, "cvsid"},
729     {RPMTAG_SVNID,              0, 0, 0, "svnid"},
730         /* LCL: can't add null annotation */
731     {0, 0, 0, 0, 0}
732 };
733
734 /**
735  */
736 static inline void initPreambleList(void)
737 {
738     PreambleRec p;
739     for (p = preambleList; p->token != NULL; p++)
740         if (p->token) p->len = strlen(p->token);
741 }
742
743 /**
744  */
745 static int findPreambleTag(rpmSpec spec,rpm_tag_t * tag,
746                 const char ** macro, char * lang)
747 {
748     PreambleRec p;
749     char *s;
750
751     if (preambleList[0].len == 0)
752         initPreambleList();
753
754     for (p = preambleList; p->token != NULL; p++) {
755         if (!(p->token && !xstrncasecmp(spec->line, p->token, p->len)))
756             continue;
757         if (p->obsolete) {
758             rpmlog(RPMLOG_ERR, _("Legacy syntax is unsupported: %s\n"),
759                         p->token);
760             p = NULL;
761         }
762         break;
763     }
764     if (p == NULL || p->token == NULL)
765         return 1;
766
767     s = spec->line + p->len;
768     SKIPSPACE(s);
769
770     switch (p->multiLang) {
771     default:
772     case 0:
773         /* Unless this is a source or a patch, a ':' better be next */
774         if (p->tag != RPMTAG_SOURCE && p->tag != RPMTAG_PATCH) {
775             if (*s != ':') return 1;
776         }
777         *lang = '\0';
778         break;
779     case 1:     /* Parse optional ( <token> ). */
780         if (*s == ':') {
781             strcpy(lang, RPMBUILD_DEFAULT_LANG);
782             break;
783         }
784         if (*s != '(') return 1;
785         s++;
786         SKIPSPACE(s);
787         while (!xisspace(*s) && *s != ')')
788             *lang++ = *s++;
789         *lang = '\0';
790         SKIPSPACE(s);
791         if (*s != ')') return 1;
792         s++;
793         SKIPSPACE(s);
794         if (*s != ':') return 1;
795         break;
796     }
797
798     *tag = p->tag;
799     if (macro)
800         *macro = p->token;
801     return 0;
802 }
803
804 int parsePreamble(rpmSpec spec, int initialPackage)
805 {
806     int nextPart;
807     int rc, xx;
808     char *name, *linep;
809     int flag = 0;
810     Package pkg;
811     char NVR[BUFSIZ];
812     char lang[BUFSIZ];
813
814     strcpy(NVR, "(main package)");
815
816     pkg = newPackage(spec);
817         
818     if (! initialPackage) {
819         /* There is one option to %package: <pkg> or -n <pkg> */
820         if (parseSimplePart(spec->line, &name, &flag)) {
821             rpmlog(RPMLOG_ERR, _("Bad package specification: %s\n"),
822                         spec->line);
823             return RPMRC_FAIL;
824         }
825         
826         if (!lookupPackage(spec, name, flag, NULL)) {
827             rpmlog(RPMLOG_ERR, _("Package already exists: %s\n"),
828                         spec->line);
829             return RPMRC_FAIL;
830         }
831         
832         /* Construct the package */
833         if (flag == PART_SUBNAME) {
834             const char * mainName;
835             xx = headerNVR(spec->packages->header, &mainName, NULL, NULL);
836             sprintf(NVR, "%s-%s", mainName, name);
837         } else
838             strcpy(NVR, name);
839         xx = headerAddEntry(pkg->header, RPMTAG_NAME, RPM_STRING_TYPE, NVR, 1);
840     }
841
842     if ((rc = readLine(spec, STRIP_TRAILINGSPACE | STRIP_COMMENTS)) > 0) {
843         nextPart = PART_NONE;
844     } else {
845         if (rc)
846             return rc;
847         while (! (nextPart = isPart(spec->line))) {
848             const char * macro;
849             rpm_tag_t tag;
850
851             /* Skip blank lines */
852             linep = spec->line;
853             SKIPSPACE(linep);
854             if (*linep != '\0') {
855                 if (findPreambleTag(spec, &tag, &macro, lang)) {
856                     rpmlog(RPMLOG_ERR, _("line %d: Unknown tag: %s\n"),
857                                 spec->lineNum, spec->line);
858                     return RPMRC_FAIL;
859                 }
860                 if (handlePreambleTag(spec, pkg, tag, macro, lang))
861                     return RPMRC_FAIL;
862                 if (spec->BANames && !spec->recursing)
863                     return PART_BUILDARCHITECTURES;
864             }
865             if ((rc =
866                  readLine(spec, STRIP_TRAILINGSPACE | STRIP_COMMENTS)) > 0) {
867                 nextPart = PART_NONE;
868                 break;
869             }
870             if (rc)
871                 return rc;
872         }
873     }
874
875     /* Do some final processing on the header */
876     
877     if (!spec->gotBuildRootURL && spec->buildRootURL) {
878         rpmlog(RPMLOG_ERR, _("Spec file can't use BuildRoot\n"));
879         return RPMRC_FAIL;
880     }
881
882     /* XXX Skip valid arch check if not building binary package */
883     if (!spec->anyarch && checkForValidArchitectures(spec))
884         return RPMRC_FAIL;
885
886     if (pkg == spec->packages)
887         fillOutMainPackage(pkg->header);
888
889     if (checkForDuplicates(pkg->header, NVR))
890         return RPMRC_FAIL;
891
892     if (pkg != spec->packages)
893         headerCopyTags(spec->packages->header, pkg->header,
894                         (rpm_tag_t *)copyTagsDuringParse);
895
896     if (checkForRequired(pkg->header, NVR))
897         return RPMRC_FAIL;
898
899     return nextPart;
900 }