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