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