rpm  5.2.1
parsePreamble.c
Go to the documentation of this file.
1 
6 #include "system.h"
7 
8 #include <rpmio.h>
9 #include <rpmiotypes.h>
10 #include <rpmlog.h>
11 #include <rpmurl.h>
12 #include <argv.h>
13 #include <mire.h>
14 
15 #define _RPMEVR_INTERNAL
16 #define _RPMTAG_INTERNAL /* XXX rpmTags->aTags */
17 #include <rpmbuild.h>
18 #include "debug.h"
19 
20 /*@access FD_t @*/ /* compared with NULL */
21 /*@access headerTagIndices @*/ /* rpmTags->aTags */
22 
25 /*@observer@*/ /*@unchecked@*/
32  RPMTAG_GROUP, /* XXX permissive. */
33  RPMTAG_SUMMARY, /* XXX permissive. */
34  RPMTAG_DESCRIPTION, /* XXX permissive. */
40  RPMTAG_GIF,
41  RPMTAG_XPM,
42  RPMTAG_URL,
54  0
55 };
56 
59 /*@observer@*/ /*@unchecked@*/
60 static rpmTag requiredTags[] = {
67  0
68 };
69 
72 static void addOrAppendListEntry(Header h, rpmTag tag, char * line)
73  /*@modifies h @*/
74 {
75  HE_t he = memset(alloca(sizeof(*he)), 0, sizeof(*he));
76  int xx;
77  int argc;
78  const char **argv;
79 
80  xx = poptParseArgvString(line, &argc, &argv);
81  if (argc) {
82  he->tag = tag;
84  he->p.argv = argv;
85  he->c = argc;
86  he->append = 1;
87  xx = headerPut(h, he, 0);
88  he->append = 0;
89  }
90  argv = _free(argv);
91 }
92 
93 /* Parse a simple part line that only take -n <pkg> or <pkg> */
94 /* <pkg> is returned in name as a pointer into malloc'd storage. */
95 
98 static int parseSimplePart(Spec spec, /*@out@*/char ** Np,
99  /*@out@*/rpmParseState *flag)
100  /*@globals internalState@*/
101  /*@modifies *Np, *flag, internalState, spec->line @*/
102 {
103  char * s, * se;
104  int rc = 0; /* assume failure */
105 
106  if (Np)
107  *Np = NULL;
108 
109  se = strchr(spec->line, '#');
110  if (se) {
111  *se = '\0';
112  while (--se >= spec->line && strchr(" \t\n\r", *se) != NULL)
113  *se = '\0';
114  }
115 
116  s = xstrdup(spec->line);
117  /* Throw away the first token (the %xxxx) */
118  (void)strtok(s, " \t\n");
119 
120  if (!(se = strtok(NULL, " \t\n")))
121  goto exit;
122 
123  if (!strcmp(se, "-n")) {
124  if (!(se = strtok(NULL, " \t\n"))) {
125  rc = 1;
126  goto exit;
127  }
128  *flag = PART_NAME;
129  } else
130  *flag = PART_SUBNAME;
131 
132  if (Np)
133  *Np = xstrdup(se);
134 
135  rc = (strtok(NULL, " \t\n") ? 1 : 0);
136 
137 exit:
138  s = _free(s);
139  return rc;
140 }
141 
144 static inline int parseYesNo(const char * s)
145  /*@*/
146 {
147  return ((!s || (s[0] == 'n' || s[0] == 'N' || s[0] == '0') ||
148  !xstrcasecmp(s, "false") || !xstrcasecmp(s, "off"))
149  ? 0 : 1);
150 }
151 
152 typedef struct tokenBits_s {
153 /*@observer@*/ /*@null@*/
154  const char * name;
156 } * tokenBits;
157 
160 /*@observer@*/ /*@unchecked@*/
161 static struct tokenBits_s installScriptBits[] = {
162  { "interp", RPMSENSE_INTERP },
163  { "preun", RPMSENSE_SCRIPT_PREUN },
164  { "pre", RPMSENSE_SCRIPT_PRE },
165  { "postun", RPMSENSE_SCRIPT_POSTUN },
166  { "post", RPMSENSE_SCRIPT_POST },
167  { "rpmlib", RPMSENSE_RPMLIB },
168  { "verify", RPMSENSE_SCRIPT_VERIFY },
169  { "hint", RPMSENSE_MISSINGOK },
170  { NULL, 0 }
171 };
172 
175 /*@observer@*/ /*@unchecked@*/
176 static struct tokenBits_s buildScriptBits[] = {
177  { "prep", RPMSENSE_SCRIPT_PREP },
178  { "build", RPMSENSE_SCRIPT_BUILD },
179  { "install", RPMSENSE_SCRIPT_INSTALL },
180  { "clean", RPMSENSE_SCRIPT_CLEAN },
181  { "hint", RPMSENSE_MISSINGOK },
182  { NULL, 0 }
183 };
184 
187 static int parseBits(const char * s, const tokenBits tokbits,
188  /*@out@*/ rpmsenseFlags * bp)
189  /*@modifies *bp @*/
190 {
191  tokenBits tb;
192  const char * se;
193  rpmsenseFlags bits = RPMSENSE_ANY;
194  int c = 0;
195 
196  if (s) {
197  while (*s != '\0') {
198  while ((c = *s) && xisspace(c)) s++;
199  se = s;
200  while ((c = *se) && xisalpha(c)) se++;
201  if (s == se)
202  break;
203  for (tb = tokbits; tb->name; tb++) {
204  if (tb->name != NULL &&
205  strlen(tb->name) == (size_t)(se-s) && !strncmp(tb->name, s, (se-s)))
206  /*@innerbreak@*/ break;
207  }
208  if (tb->name == NULL)
209  break;
210  bits |= tb->bits;
211  while ((c = *se) && xisspace(c)) se++;
212  if (c != ',')
213  break;
214  s = ++se;
215  }
216  }
217  if (c == 0 && bp) *bp = bits;
218  return (c ? RPMRC_FAIL : RPMRC_OK);
219 }
220 
223 /*@null@*/
224 static inline char * findLastChar(char * s)
225  /*@modifies *s @*/
226 {
227  char *se = s + strlen(s);
228 
229  /* Right trim white space. */
230  while (--se > s && strchr(" \t\n\r", *se) != NULL)
231  *se = '\0';
232  /* Truncate comments. */
233  if ((se = strchr(s, '#')) != NULL) {
234  *se = '\0';
235  while (--se > s && strchr(" \t\n\r", *se) != NULL)
236  *se = '\0';
237  }
238 /*@-temptrans -retalias @*/
239  return se;
240 /*@=temptrans =retalias @*/
241 }
242 
245 static int isMemberInEntry(Header h, const char *name, rpmTag tag)
246  /*@globals internalState @*/
247  /*@modifies internalState @*/
248 {
249  HE_t he = memset(alloca(sizeof(*he)), 0, sizeof(*he));
250  int rc = -1;
251  int xx;
252 
253  he->tag = tag;
254  xx = headerGet(h, he, 0);
255  if (!xx)
256  return rc;
257  rc = 0;
258  while (he->c) {
259  he->c--;
260  if (xstrcasecmp(he->p.argv[he->c], name))
261  continue;
262  rc = 1;
263  break;
264  }
265  he->p.ptr = _free(he->p.ptr);
266  return rc;
267 }
268 
272  /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
273  /*@modifies rpmGlobalMacroContext, internalState @*/
274 {
275  const char *arch = rpmExpand("%{_target_cpu}", NULL);
276  const char *os = rpmExpand("%{_target_os}", NULL);
277  int rc = RPMRC_FAIL; /* assume failure. */
278 
279  if (isMemberInEntry(spec->sourceHeader, arch, RPMTAG_EXCLUDEARCH) == 1) {
280  rpmlog(RPMLOG_ERR, _("Architecture is excluded: %s\n"), arch);
281  goto exit;
282  }
283  if (isMemberInEntry(spec->sourceHeader, arch, RPMTAG_EXCLUSIVEARCH) == 0) {
284  rpmlog(RPMLOG_ERR, _("Architecture is not included: %s\n"), arch);
285  goto exit;
286  }
287  if (isMemberInEntry(spec->sourceHeader, os, RPMTAG_EXCLUDEOS) == 1) {
288  rpmlog(RPMLOG_ERR, _("OS is excluded: %s\n"), os);
289  goto exit;
290  }
291  if (isMemberInEntry(spec->sourceHeader, os, RPMTAG_EXCLUSIVEOS) == 0) {
292  rpmlog(RPMLOG_ERR, _("OS is not included: %s\n"), os);
293  goto exit;
294  }
295  rc = 0;
296 exit:
297  arch = _free(arch);
298  os = _free(os);
299  return rc;
300 }
301 
308 static rpmRC checkForRequired(Header h, const char * NVR)
309  /*@*/
310 {
311  rpmTag * p;
312  rpmRC rc = RPMRC_OK;
313 
314  for (p = requiredTags; *p != 0; p++) {
315  if (!headerIsEntry(h, *p)) {
317  _("%s field must be present in package: %s\n"),
318  tagName(*p), NVR);
319  rc = RPMRC_FAIL;
320  }
321  }
322 
323  return rc;
324 }
325 
332 static rpmRC checkForDuplicates(Header h, const char * NVR)
333  /*@globals internalState @*/
334  /*@modifies h, internalState @*/
335 {
336  HE_t he = memset(alloca(sizeof(*he)), 0, sizeof(*he));
337  HeaderIterator hi;
338  rpmTag lastTag = 0;
339  rpmRC rc = RPMRC_OK;
340 
341  for (hi = headerInit(h);
342  headerNext(hi, he, 0);
343  he->p.ptr = _free(he->p.ptr))
344  {
345  if (he->tag != lastTag) {
346  lastTag = he->tag;
347  continue;
348  }
349  rpmlog(RPMLOG_ERR, _("Duplicate %s entries in package: %s\n"),
350  tagName(he->tag), NVR);
351  rc = RPMRC_FAIL;
352  }
353  hi = headerFini(hi);
354 
355  return rc;
356 }
357 
360 /*@observer@*/ /*@unchecked@*/
361 static struct optionalTag {
363 /*@observer@*/ /*@null@*/
364  const char * ot_mac;
365 } optionalTags[] = {
366  { RPMTAG_VENDOR, "%{vendor}" },
367  { RPMTAG_PACKAGER, "%{packager}" },
368  { RPMTAG_DISTEPOCH, "%{distepoch}" },
369  { RPMTAG_DISTRIBUTION, "%{distribution}" },
370  { RPMTAG_DISTTAG, "%{disttag}" },
371  { RPMTAG_DISTURL, "%{disturl}" },
372  { 0xffffffff, "%{class}" },
373  { -1, NULL }
374 };
375 
379  /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
380  /*@modifies h, rpmGlobalMacroContext, internalState @*/
381 {
382  HE_t he = memset(alloca(sizeof(*he)), 0, sizeof(*he));
383  struct optionalTag *ot;
384  int xx;
385 
386  for (ot = optionalTags; ot->ot_mac != NULL; ot++) {
387  const char * val;
388  rpmTag tag;
389 
390  tag = ot->ot_tag;
391 
392  /* Generate arbitrary tag (if necessary). */
393  if (tag == 0xffffffff) {
394  val = tagCanonicalize(ot->ot_mac + (sizeof("%{")-1));
395  tag = tagGenerate(val);
396  val = _free(val);
397  }
398 
399  if (headerIsEntry(h, tag))
400  continue;
401  val = rpmExpand(ot->ot_mac, NULL);
402  if (val && *val != '%') {
403  he->tag = tag;
404  he->t = RPM_STRING_TYPE;
405  he->p.str = val;
406  he->c = 1;
407  xx = headerPut(h, he, 0);
408  }
409  val = _free(val);
410  }
411 }
412 
415 static int doIcon(Spec spec, Header h)
416  /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
417  /*@modifies h, rpmGlobalMacroContext, fileSystem, internalState @*/
418 {
419  static size_t iconsize = 0;
420  HE_t he = memset(alloca(sizeof(*he)), 0, sizeof(*he));
421  const char *fn, *Lurlfn = NULL;
422  struct Source *sp;
423  size_t nb;
424  rpmuint8_t * icon;
425  FD_t fd = NULL;
426  int rc = RPMRC_FAIL; /* assume error */
427  int urltype;
428  int xx;
429 
430  if (iconsize == 0) {
431  iconsize = rpmExpandNumeric("%{?_build_iconsize}");
432  if (iconsize < 2048)
433  iconsize = 2048;
434  }
435  icon = alloca(iconsize+1);
436 
437  for (sp = spec->sources; sp != NULL; sp = sp->next) {
438  if (sp->flags & RPMFILE_ICON)
439  break;
440  }
441  if (sp == NULL) {
442  rpmlog(RPMLOG_ERR, _("No icon file in sources\n"));
443  goto exit;
444  }
445 
446 #if defined(RPM_VENDOR_OPENPKG) /* splitted-source-directory */
447  /* support splitted source directories, i.e., source files which
448  are alternatively placed into the .spec directory and picked
449  up from there, too. */
450  Lurlfn = rpmGenPath(NULL, "%{_specdir}/", sp->source);
451  if (access(Lurlfn, F_OK) == -1) {
452  Lurlfn = _free(Lurlfn);
453  Lurlfn = rpmGenPath(NULL, "%{_icondir}/", sp->source);
454  }
455 #else
456  Lurlfn = rpmGenPath(NULL, "%{_icondir}/", sp->source);
457 #endif
458 
459  fn = NULL;
460  urltype = urlPath(Lurlfn, &fn);
461  switch (urltype) {
462  case URL_IS_HTTPS:
463  case URL_IS_HTTP:
464  case URL_IS_FTP:
465  case URL_IS_PATH:
466  case URL_IS_UNKNOWN:
467  break;
468  case URL_IS_DASH:
469  case URL_IS_HKP:
470  rpmlog(RPMLOG_ERR, _("Invalid icon URL: %s\n"), Lurlfn);
471  goto exit;
472  /*@notreached@*/ break;
473  }
474 
475  fd = Fopen(fn, "r%{?_rpmgio}");
476  if (fd == NULL || Ferror(fd)) {
477  rpmlog(RPMLOG_ERR, _("Unable to open icon %s: %s\n"),
478  fn, Fstrerror(fd));
479  rc = RPMRC_FAIL;
480  goto exit;
481  }
482 
483  *icon = '\0';
484  nb = Fread(icon, sizeof(icon[0]), iconsize, fd);
485  if (Ferror(fd) || nb == 0) {
486  rpmlog(RPMLOG_ERR, _("Unable to read icon %s: %s\n"),
487  fn, Fstrerror(fd));
488  goto exit;
489  }
490  if (nb >= iconsize) {
491  rpmlog(RPMLOG_ERR, _("Icon %s is too big (max. %d bytes)\n"),
492  fn, (int)iconsize);
493  goto exit;
494  }
495 
496  if (icon[0] == 'G' && icon[1] == 'I' && icon[2] == 'F')
497  he->tag = RPMTAG_GIF;
498  else
499  if (icon[0] == '/' && icon[1] == '*' && icon[2] == ' '
500  && icon[3] == 'X' && icon[4] == 'P' && icon[5] == 'M')
501  he->tag = RPMTAG_XPM;
502  else
503  he->tag = tagValue("Icon");
504  he->t = RPM_BIN_TYPE;
505  he->p.ui8p = icon;
506  he->c = (rpmTagCount)nb;
507  xx = headerPut(h, he, 0);
508  rc = 0;
509 
510 exit:
511  if (fd) {
512  (void) Fclose(fd);
513  fd = NULL;
514  }
515  Lurlfn = _free(Lurlfn);
516  return rc;
517 }
518 
519 spectag stashSt(Spec spec, Header h, rpmTag tag, const char * lang)
520 {
521  HE_t he = memset(alloca(sizeof(*he)), 0, sizeof(*he));
522  spectag t = NULL;
523  int xx;
524 
525  if (spec->st) {
526  spectags st = spec->st;
527  if (st->st_ntags == st->st_nalloc) {
528  st->st_nalloc += 10;
529  st->st_t = xrealloc(st->st_t, st->st_nalloc * sizeof(*(st->st_t)));
530  }
531  t = st->st_t + st->st_ntags++;
532  t->t_tag = tag;
533  t->t_startx = spec->lineNum - 1;
534  t->t_nlines = 1;
535  t->t_lang = xstrdup(lang);
536  t->t_msgid = NULL;
537  if (!(t->t_lang && strcmp(t->t_lang, RPMBUILD_DEFAULT_LANG))) {
538  he->tag = RPMTAG_NAME;
539  xx = headerGet(h, he, 0);
540  if (xx) {
541  char buf[1024];
542  sprintf(buf, "%s(%s)", he->p.str, tagName(tag));
543  t->t_msgid = xstrdup(buf);
544  }
545  he->p.ptr = _free(he->p.ptr);
546  }
547  }
548  /*@-usereleased -compdef@*/
549  return t;
550  /*@=usereleased =compdef@*/
551 }
552 
553 #define SINGLE_TOKEN_ONLY \
554 if (multiToken) { \
555  rpmlog(RPMLOG_ERR, _("line %d: Tag takes single token only: %s\n"), \
556  spec->lineNum, spec->line); \
557  return RPMRC_FAIL; \
558 }
559 
560 /*@-redecl@*/
561 extern int noLang;
562 /*@=redecl@*/
563 
564 static rpmRC tagValidate(Spec spec, rpmTag tag, const char * value)
565  /*@*/
566 {
567  const char * tagN = tagName(tag);
568  const char * pattern = rpmExpand("%{?pattern_", tagN, "}", NULL);
569  rpmRC ec = RPMRC_OK;
570 
571  if (pattern && *pattern) {
572  miRE mire;
573  int xx;
574 
575  mire = mireNew(RPMMIRE_REGEX, tag);
576  xx = mireSetCOptions(mire, RPMMIRE_REGEX, 0, 0, NULL);
577  if (!xx)
578  xx = mireRegcomp(mire, pattern);
579  if (!xx)
580  xx = mireRegexec(mire, value, strlen(value));
581  if (!xx)
582  ec = RPMRC_OK;
583  else {
584  rpmlog(RPMLOG_ERR, _("line %d: invalid tag value(\"%s\") %s: %s\n"),
585  spec->lineNum, pattern, tagN, spec->line);
586  ec = RPMRC_FAIL;
587  }
588 
589  mire = mireFree(mire);
590  }
591 
592  pattern = _free(pattern);
593 
594  return ec;
595 }
596 
599 static rpmRC handlePreambleTag(Spec spec, Package pkg, rpmTag tag,
600  const char *macro, const char *lang)
601  /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
602  /*@modifies spec->macros, spec->st,
603  spec->sources, spec->numSources, spec->noSource,
604  spec->sourceHeader, spec->BANames, spec->BACount,
605  spec->line,
606  pkg->header, pkg->autoProv, pkg->autoReq, pkg->noarch,
607  rpmGlobalMacroContext, fileSystem, internalState @*/
608 {
609  HE_t he = memset(alloca(sizeof(*he)), 0, sizeof(*he));
610  char * field = spec->line;
611  char * end;
612  int multiToken = 0;
613  rpmsenseFlags tagflags;
614  int len;
616  int rc;
617  int xx;
618 #if defined(RPM_VENDOR_MANDRIVA) /* build-expand-field-for-single-token */
619  char * expand_field = NULL;
620 #endif
621 
622  if (field == NULL) return RPMRC_FAIL; /* XXX can't happen */
623  /* Find the start of the "field" and strip trailing space */
624  while ((*field) && (*field != ':'))
625  field++;
626  if (*field != ':') {
627  rpmlog(RPMLOG_ERR, _("line %d: Malformed tag: %s\n"),
628  spec->lineNum, spec->line);
629  return RPMRC_FAIL;
630  }
631  field++;
632  SKIPSPACE(field);
633  if (!*field) {
634  /* Empty field */
635  rpmlog(RPMLOG_ERR, _("line %d: Empty tag: %s\n"),
636  spec->lineNum, spec->line);
637  return RPMRC_FAIL;
638  }
639 #if defined(RPM_VENDOR_MANDRIVA) /* build-expand-field-for-single-token */
640  expand_field = rpmExpand(field, NULL);
641 #else
642  end = findLastChar(field);
643 #endif
644 
645  /* Validate tag data content. */
646  if (tagValidate(spec, tag, field) != RPMRC_OK)
647  return RPMRC_FAIL;
648 
649  /* See if this is multi-token */
650 #if defined(RPM_VENDOR_MANDRIVA) /* build-expand-field-for-single-token */
651  end = expand_field;
652 #else
653  end = field;
654 #endif
655  SKIPNONSPACE(end);
656  if (*end != '\0')
657  multiToken = 1;
658 #if defined(RPM_VENDOR_MANDRIVA) /* build-expand-field-for-single-token */
659  _free(expand_field);
660 #endif
661 
662  switch (tag) {
663  case RPMTAG_NAME:
664  case RPMTAG_VERSION:
665  case RPMTAG_RELEASE:
666  case RPMTAG_DISTEPOCH:
667  case RPMTAG_URL:
668  case RPMTAG_DISTTAG:
669  case RPMTAG_REPOTAG:
670  case RPMTAG_CVSID:
672  /* These macros are for backward compatibility */
673  if (tag == RPMTAG_VERSION) {
674  if (strchr(field, '-') != NULL) {
675  rpmlog(RPMLOG_ERR, _("line %d: Illegal char '-' in %s: %s\n"),
676  spec->lineNum, "version", spec->line);
677  return RPMRC_FAIL;
678  }
679  addMacro(spec->macros, "PACKAGE_VERSION", NULL, field, RMIL_OLDSPEC);
680  } else if (tag == RPMTAG_RELEASE) {
681  if (strchr(field, '-') != NULL) {
682  rpmlog(RPMLOG_ERR, _("line %d: Illegal char '-' in %s: %s\n"),
683  spec->lineNum, "release", spec->line);
684  return RPMRC_FAIL;
685  }
686  addMacro(spec->macros, "PACKAGE_RELEASE", NULL, field, RMIL_OLDSPEC-1);
687  }
688  he->tag = tag;
689  he->t = RPM_STRING_TYPE;
690  he->p.str = field;
691  he->c = 1;
692  xx = headerPut(pkg->header, he, 0);
693  break;
694  case RPMTAG_GROUP:
695  case RPMTAG_SUMMARY:
696 #if defined(RPM_VENDOR_OPENPKG) /* make-class-available-as-macro */
697  case RPMTAG_CLASS:
698 #endif
699  (void) stashSt(spec, pkg->header, tag, lang);
700  /*@fallthrough@*/
701  case RPMTAG_DISTRIBUTION:
702  case RPMTAG_VENDOR:
703  case RPMTAG_LICENSE:
704  case RPMTAG_PACKAGER:
705  if (!*lang) {
706  he->tag = tag;
707  he->t = RPM_STRING_TYPE;
708  he->p.str = field;
709  he->c = 1;
710  xx = headerPut(pkg->header, he, 0);
711  } else if (!(noLang && strcmp(lang, RPMBUILD_DEFAULT_LANG))) {
712  (void) headerAddI18NString(pkg->header, tag, field, lang);
713  }
714  break;
715  /* XXX silently ignore BuildRoot: */
716  case RPMTAG_BUILDROOT:
718  macro = NULL;
719 #ifdef DYING
720  buildRootURL = rpmGenPath(spec->rootURL, "%{?buildroot}", NULL);
721  (void) urlPath(buildRootURL, &buildRoot);
722  if (*buildRoot == '\0') buildRoot = "/";
723  if (!strcmp(buildRoot, "/")) {
725  _("BuildRoot can not be \"/\": %s\n"), spec->buildRootURL);
726  buildRootURL = _free(buildRootURL);
727  return RPMRC_FAIL;
728  }
729  buildRootURL = _free(buildRootURL);
730 #endif
731  break;
732  case RPMTAG_KEYWORDS:
733  case RPMTAG_VARIANTS:
734  case RPMTAG_PREFIXES:
735  addOrAppendListEntry(pkg->header, tag, field);
736  he->tag = tag;
737  xx = headerGet(pkg->header, he, 0);
738  if (tag == RPMTAG_PREFIXES)
739  while (he->c--) {
740  if (he->p.argv[he->c][0] != '/') {
742  _("line %d: Prefixes must begin with \"/\": %s\n"),
743  spec->lineNum, spec->line);
744  he->p.ptr = _free(he->p.ptr);
745  return RPMRC_FAIL;
746  }
747  len = (int)strlen(he->p.argv[he->c]);
748  if (he->p.argv[he->c][len - 1] == '/' && len > 1) {
750  _("line %d: Prefixes must not end with \"/\": %s\n"),
751  spec->lineNum, spec->line);
752  he->p.ptr = _free(he->p.ptr);
753  return RPMRC_FAIL;
754  }
755  }
756  he->p.ptr = _free(he->p.ptr);
757  break;
758  case RPMTAG_DOCDIR:
760  if (field[0] != '/') {
762  _("line %d: Docdir must begin with '/': %s\n"),
763  spec->lineNum, spec->line);
764  return RPMRC_FAIL;
765  }
766  macro = NULL;
767  delMacro(NULL, "_docdir");
768  addMacro(NULL, "_docdir", NULL, field, RMIL_SPEC);
769  break;
770  case RPMTAG_XMAJOR:
771  case RPMTAG_XMINOR:
772  case RPMTAG_EPOCH:
774  if (parseNum(field, &num)) {
776  _("line %d: %s takes an integer value: %s\n"),
777  spec->lineNum, tagName(tag), spec->line);
778  return RPMRC_FAIL;
779  }
780  he->tag = tag;
781  he->t = RPM_UINT32_TYPE;
782  he->p.ui32p = &num;
783  he->c = 1;
784  xx = headerPut(pkg->header, he, 0);
785  break;
786  case RPMTAG_AUTOREQPROV:
787  pkg->autoReq = parseYesNo(field);
788  pkg->autoProv = pkg->autoReq;
789  break;
790  case RPMTAG_AUTOREQ:
791  pkg->autoReq = parseYesNo(field);
792  break;
793  case RPMTAG_AUTOPROV:
794  pkg->autoProv = parseYesNo(field);
795  break;
796  case RPMTAG_SOURCE:
797  case RPMTAG_PATCH:
799  macro = NULL;
800  if ((rc = addSource(spec, pkg, field, tag)))
801  return rc;
802  break;
803  case RPMTAG_ICON:
805  macro = NULL;
806  if ((rc = addSource(spec, pkg, field, tag)))
807  return rc;
808  /* XXX the fetch/load of icon needs to be elsewhere. */
809  if ((rc = doIcon(spec, pkg->header)))
810  return rc;
811  break;
812  case RPMTAG_NOSOURCE:
813  case RPMTAG_NOPATCH:
814  spec->noSource = 1;
815  if ((rc = parseNoSource(spec, field, tag)))
816  return rc;
817  break;
818  case RPMTAG_BUILDPREREQ:
820  if ((rc = parseBits(lang, buildScriptBits, &tagflags))) {
822  _("line %d: Bad %s: qualifiers: %s\n"),
823  spec->lineNum, tagName(tag), spec->line);
824  return rc;
825  }
826  if ((rc = parseRCPOT(spec, pkg, field, tag, 0, tagflags)))
827  return rc;
828  break;
829  case RPMTAG_PREREQ:
830  case RPMTAG_REQUIREFLAGS:
831  if ((rc = parseBits(lang, installScriptBits, &tagflags))) {
833  _("line %d: Bad %s: qualifiers: %s\n"),
834  spec->lineNum, tagName(tag), spec->line);
835  return rc;
836  }
837  if ((rc = parseRCPOT(spec, pkg, field, tag, 0, tagflags)))
838  return rc;
839  break;
840  /* Aliases for BuildRequires(hint): */
843  tagflags = RPMSENSE_MISSINGOK;
844  if ((rc = parseRCPOT(spec, pkg, field, tag, 0, tagflags)))
845  return rc;
846  break;
847  /* Aliases for Requires(hint): */
850  tag = RPMTAG_REQUIREFLAGS;
851  tagflags = RPMSENSE_MISSINGOK;
852  if ((rc = parseRCPOT(spec, pkg, field, tag, 0, tagflags)))
853  return rc;
854  break;
860  case RPMTAG_PROVIDEFLAGS:
861  tagflags = RPMSENSE_ANY;
862  if ((rc = parseRCPOT(spec, pkg, field, tag, 0, tagflags)))
863  return rc;
864  break;
865  case RPMTAG_BUILDPLATFORMS: /* XXX needs pattern parsing */
866  case RPMTAG_EXCLUDEARCH:
868  case RPMTAG_EXCLUDEOS:
869  case RPMTAG_EXCLUSIVEOS:
870  addOrAppendListEntry(spec->sourceHeader, tag, field);
871  break;
872 
873  case RPMTAG_BUILDARCHS:
874  { const char ** BANames = NULL;
875  int BACount = 0;
876  if ((rc = poptParseArgvString(field, &BACount, &BANames))) {
878  _("line %d: Bad BuildArchitecture format: %s\n"),
879  spec->lineNum, spec->line);
880  return RPMRC_FAIL;
881  }
882  if (spec->toplevel) {
883  if (BACount > 0 && BANames != NULL) {
884  spec->BACount = BACount;
885  spec->BANames = BANames;
886  BANames = NULL; /* XXX don't free. */
887  }
888  } else {
889  if (BACount != 1 || strcmp(BANames[0], "noarch")) {
891  _("line %d: Only \"noarch\" sub-packages are supported: %s\n"),
892  spec->lineNum, spec->line);
893  BANames = _free(BANames);
894  return RPMRC_FAIL;
895  }
896  pkg->noarch = 1;
897  }
898  BANames = _free(BANames);
899  } break;
900 
901  default:
902  macro = NULL;
903  he->tag = tag;
904  he->t = RPM_STRING_ARRAY_TYPE;
905  he->p.argv= (const char **) &field; /* XXX NOCAST */
906  he->c = 1;
907  he->append = 1;
908  xx = headerPut(pkg->header, he, 0);
909  he->append = 0;
910  break;
911  }
912 
913 /*@-usereleased@*/
914  if (macro)
915  addMacro(spec->macros, macro, NULL, field, RMIL_SPEC);
916 /*@=usereleased@*/
917 
918  return RPMRC_OK;
919 }
920 
921 /* This table has to be in a peculiar order. If one tag is the */
922 /* same as another, plus a few letters, it must come first. */
923 
926 typedef struct PreambleRec_s {
929  int obsolete;
930 /*@observer@*/ /*@null@*/
931  const char * token;
932 } * PreambleRec;
933 
934 /*@unchecked@*/
935 static struct PreambleRec_s preambleList[] = {
936  {RPMTAG_NAME, 0, 0, "name"},
937  {RPMTAG_VERSION, 0, 0, "version"},
938  {RPMTAG_RELEASE, 0, 0, "release"},
939  {RPMTAG_DISTEPOCH, 0, 0, "distepoch"},
940  {RPMTAG_EPOCH, 0, 0, "epoch"},
941  {RPMTAG_EPOCH, 0, 1, "serial"},
942  {RPMTAG_SUMMARY, 1, 0, "summary"},
943  {RPMTAG_LICENSE, 0, 0, "copyright"},
944  {RPMTAG_LICENSE, 0, 0, "license"},
945  {RPMTAG_DISTRIBUTION, 0, 0, "distribution"},
946  {RPMTAG_DISTURL, 0, 0, "disturl"},
947  {RPMTAG_VENDOR, 0, 0, "vendor"},
948  {RPMTAG_GROUP, 1, 0, "group"},
949  {RPMTAG_PACKAGER, 0, 0, "packager"},
950  {RPMTAG_URL, 0, 0, "url"},
951  {RPMTAG_SOURCE, 0, 0, "source"},
952  {RPMTAG_PATCH, 0, 0, "patch"},
953  {RPMTAG_NOSOURCE, 0, 0, "nosource"},
954  {RPMTAG_NOPATCH, 0, 0, "nopatch"},
955  {RPMTAG_EXCLUDEARCH, 0, 0, "excludearch"},
956  {RPMTAG_EXCLUSIVEARCH, 0, 0, "exclusivearch"},
957  {RPMTAG_EXCLUDEOS, 0, 0, "excludeos"},
958  {RPMTAG_EXCLUSIVEOS, 0, 0, "exclusiveos"},
959  {RPMTAG_ICON, 0, 0, "icon"},
960  {RPMTAG_PROVIDEFLAGS, 0, 0, "provides"},
961  {RPMTAG_REQUIREFLAGS, 1, 0, "requires"},
962  {RPMTAG_PREREQ, 1, 0, "prereq"},
963  {RPMTAG_CONFLICTFLAGS, 0, 0, "conflicts"},
964  {RPMTAG_OBSOLETEFLAGS, 0, 0, "obsoletes"},
965  {RPMTAG_PREFIXES, 0, 0, "prefixes"},
966  {RPMTAG_PREFIXES, 0, 0, "prefix"},
967  {RPMTAG_BUILDROOT, 0, 0, "buildroot"},
968  {RPMTAG_BUILDARCHS, 0, 0, "buildarchitectures"},
969  {RPMTAG_BUILDARCHS, 0, 0, "buildarch"},
970  {RPMTAG_BUILDCONFLICTS, 0, 0, "buildconflicts"},
971  {RPMTAG_BUILDOBSOLETES, 0, 0, "buildobsoletes"},
972  {RPMTAG_BUILDPREREQ, 1, 0, "buildprereq"},
973  {RPMTAG_BUILDPROVIDES, 0, 0, "buildprovides"},
974  {RPMTAG_BUILDREQUIRES, 1, 0, "buildrequires"},
975  {RPMTAG_AUTOREQPROV, 0, 0, "autoreqprov"},
976  {RPMTAG_AUTOREQ, 0, 0, "autoreq"},
977  {RPMTAG_AUTOPROV, 0, 0, "autoprov"},
978  {RPMTAG_DOCDIR, 0, 0, "docdir"},
979  {RPMTAG_DISTTAG, 0, 0, "disttag"},
980  {RPMTAG_CVSID, 0, 0, "cvsid"},
981  {RPMTAG_SVNID, 0, 0, "svnid"},
982  {RPMTAG_SUGGESTSFLAGS, 0, 0, "suggests"},
983  {RPMTAG_ENHANCESFLAGS, 0, 0, "enhances"},
984  {RPMTAG_BUILDSUGGESTS, 0, 0, "buildsuggests"},
985  {RPMTAG_BUILDENHANCES, 0, 0, "buildenhances"},
986  {RPMTAG_VARIANTS, 0, 0, "variants"},
987  {RPMTAG_VARIANTS, 0, 0, "variant"},
988  {RPMTAG_XMAJOR, 0, 0, "xmajor"},
989  {RPMTAG_XMINOR, 0, 0, "xminor"},
990  {RPMTAG_REPOTAG, 0, 0, "repotag"},
991  {RPMTAG_KEYWORDS, 0, 0, "keywords"},
992  {RPMTAG_KEYWORDS, 0, 0, "keyword"},
993  {RPMTAG_BUILDPLATFORMS, 0, 0, "buildplatforms"},
994 #if defined(RPM_VENDOR_OPENPKG) /* make-class-available-as-macro */
995  {RPMTAG_CLASS, 0, 0, "class"},
996 #endif
997  /*@-nullassign@*/ /* LCL: can't add null annotation */
998  {0, 0, 0, 0}
999  /*@=nullassign@*/
1000 };
1001 
1004 static int findPreambleTag(Spec spec, /*@out@*/rpmTag * tagp,
1005  /*@null@*/ /*@out@*/ const char ** macro, /*@out@*/ char * lang)
1006  /*@modifies *tagp, *macro, *lang @*/
1007 {
1008  PreambleRec p;
1009  char *s;
1010  size_t len = 0;
1011 
1012  /* Search for defined tags. */
1013  for (p = preambleList; p->token != NULL; p++) {
1014  len = strlen(p->token);
1015  if (!(p->token && !xstrncasecmp(spec->line, p->token, len)))
1016  continue;
1017  if (p->obsolete) {
1018  rpmlog(RPMLOG_ERR, _("Legacy syntax is unsupported: %s\n"),
1019  p->token);
1020  p = NULL;
1021  }
1022  break;
1023  }
1024  if (p == NULL)
1025  return 1;
1026 
1027  /* Search for arbitrary tags. */
1028  if (tagp && p->token == NULL) {
1029  ARGV_t aTags = NULL;
1030  int rc = 1; /* assume failure */
1031 
1032 /*@-noeffect@*/
1033  (void) tagName(0); /* XXX force arbitrary tags to be initialized. */
1034 /*@=noeffect@*/
1035  aTags = rpmTags->aTags;
1036  if (aTags != NULL && aTags[0] != NULL) {
1037  ARGV_t av;
1038  s = tagCanonicalize(spec->line);
1039 #if defined(RPM_VENDOR_OPENPKG) /* wildcard-matching-arbitrary-tagnames */
1040  av = argvSearchLinear(aTags, s, argvFnmatchCasefold);
1041 #else
1042  av = argvSearch(aTags, s, argvStrcasecmp);
1043 #endif
1044  if (av != NULL) {
1045  *tagp = tagGenerate(s);
1046  rc = 0;
1047  }
1048  s = _free(s);
1049  }
1050  return rc;
1051  }
1052 
1053  s = spec->line + len;
1054  SKIPSPACE(s);
1055 
1056  switch (p->multiLang) {
1057  default:
1058  case 0:
1059  /* Unless this is a source or a patch, a ':' better be next */
1060  if (p->tag != RPMTAG_SOURCE && p->tag != RPMTAG_PATCH) {
1061  if (*s != ':') return 1;
1062  }
1063  *lang = '\0';
1064  break;
1065  case 1: /* Parse optional ( <token> ). */
1066  if (*s == ':') {
1067  strcpy(lang, RPMBUILD_DEFAULT_LANG);
1068  break;
1069  }
1070  if (*s != '(') return 1;
1071  s++;
1072  SKIPSPACE(s);
1073  while (!xisspace(*s) && *s != ')')
1074  *lang++ = *s++;
1075  *lang = '\0';
1076  SKIPSPACE(s);
1077  if (*s != ')') return 1;
1078  s++;
1079  SKIPSPACE(s);
1080  if (*s != ':') return 1;
1081  break;
1082  }
1083 
1084  if (tagp)
1085  *tagp = p->tag;
1086  if (macro)
1087  /*@-onlytrans -observertrans -dependenttrans@*/ /* FIX: double indirection. */
1088  *macro = p->token;
1089  /*@=onlytrans =observertrans =dependenttrans@*/
1090  return 0;
1091 }
1092 
1093 /* XXX should return rpmParseState, but RPMRC_FAIL forces int return. */
1094 int parsePreamble(Spec spec, int initialPackage)
1095 {
1096  HE_t he = memset(alloca(sizeof(*he)), 0, sizeof(*he));
1097  rpmParseState nextPart;
1098  int xx;
1099  char *linep;
1100  Package pkg;
1101  char NVR[BUFSIZ];
1102  char lang[BUFSIZ];
1103  rpmRC rc;
1104 
1105  strcpy(NVR, "(main package)");
1106 
1107  pkg = newPackage(spec);
1108  if (spec->packages == NULL) {
1109  spec->packages = pkg;
1110 assert(initialPackage);
1111  } else if (! initialPackage) {
1112  char *name = NULL;
1113  rpmParseState flag;
1114  Package lastpkg;
1115 
1116  /* There is one option to %package: <pkg> or -n <pkg> */
1117  flag = PART_NONE;
1118  if (parseSimplePart(spec, &name, &flag)) {
1119  rpmlog(RPMLOG_ERR, _("Bad package specification: %s\n"),
1120  spec->line);
1121  return RPMRC_FAIL;
1122  }
1123 
1124  lastpkg = NULL;
1125  if (lookupPackage(spec, name, flag, &lastpkg) == RPMRC_OK) {
1126  pkg->next = lastpkg->next;
1127  } else {
1128  /* Add package to end of list */
1129  for (lastpkg = spec->packages; lastpkg->next != NULL; lastpkg = lastpkg->next)
1130  {};
1131  }
1132 assert(lastpkg != NULL);
1133  lastpkg->next = pkg;
1134 
1135  /* Construct the package */
1136  if (flag == PART_SUBNAME) {
1137  he->tag = RPMTAG_NAME;
1138  xx = headerGet(spec->packages->header, he, 0);
1139  sprintf(NVR, "%s-%s", he->p.str, name);
1140  he->p.ptr = _free(he->p.ptr);
1141  } else
1142  strcpy(NVR, name);
1143  name = _free(name);
1144  he->tag = RPMTAG_NAME;
1145  he->t = RPM_STRING_TYPE;
1146  he->p.str = NVR;
1147  he->c = 1;
1148  xx = headerPut(pkg->header, he, 0);
1149  }
1150 
1151  if ((rc = readLine(spec, STRIP_TRAILINGSPACE | STRIP_COMMENTS)) > 0) {
1152  nextPart = PART_NONE;
1153  } else {
1154  if (rc)
1155  return rc;
1156  while ((nextPart = isPart(spec)) == PART_NONE) {
1157  const char * macro = NULL;
1158  rpmTag tag;
1159 
1160  /* Skip blank lines */
1161  linep = spec->line;
1162  SKIPSPACE(linep);
1163  if (*linep != '\0') {
1164  if (findPreambleTag(spec, &tag, &macro, lang)) {
1165  rpmlog(RPMLOG_ERR, _("line %d: Unknown tag: %s\n"),
1166  spec->lineNum, spec->line);
1167  return RPMRC_FAIL;
1168  }
1169  if (handlePreambleTag(spec, pkg, tag, macro, lang))
1170  return RPMRC_FAIL;
1171  if (spec->BANames && !spec->recursing && spec->toplevel)
1172  return PART_BUILDARCHITECTURES;
1173  }
1174  if ((rc =
1176  nextPart = PART_NONE;
1177  break;
1178  }
1179  if (rc)
1180  return rc;
1181  }
1182  }
1183 
1184  /* Do some final processing on the header */
1185 
1186  /*
1187  * Expand buildroot one more time to get %{version} and the like
1188  * from the main package.
1189  */
1190  if (initialPackage) {
1191  const char *s = rpmExpand("%{?buildroot}", NULL);
1192  if (s && *s)
1193  (void) addMacro(NULL, "buildroot", NULL, s, -1);
1194  s = _free(s);
1195  }
1196 
1197  /* XXX Skip valid arch check if not building binary package */
1198  if (!spec->anyarch && checkForValidArchitectures(spec))
1199  return RPMRC_FAIL;
1200 
1201  if (pkg == spec->packages)
1202  fillOutMainPackage(pkg->header);
1203 
1204  if (checkForDuplicates(pkg->header, NVR) != RPMRC_OK)
1205  return RPMRC_FAIL;
1206 
1207  if (pkg != spec->packages)
1208  headerCopyTags(spec->packages->header, pkg->header,
1210 
1211 #ifdef RPM_VENDOR_PLD /* rpm-epoch0 */
1212  /* Add Epoch: 0 to package header if it was not set by spec */
1213  he->tag = RPMTAG_NAME;
1214  if (headerGet(spec->packages->header, he, 0) == 0) {
1215  rpmuint32_t num = 0;
1216 
1217  he->tag = RPMTAG_EPOCH;
1218  he->t = RPM_UINT32_TYPE;
1219  he->p.ui32p = &num;
1220  he->c = 1;
1221  xx = headerPut(pkg->header, he, 0);
1222 
1223  /* also declare %{epoch} to be same */
1224  addMacro(spec->macros, "epoch", NULL, "0", RMIL_SPEC);
1225  }
1226 #endif /* RPM_VENDOR_PLD rpm-epoch0 */
1227 
1228  if (checkForRequired(pkg->header, NVR) != RPMRC_OK)
1229  return RPMRC_FAIL;
1230 
1231  return nextPart;
1232 }