rpm  5.2.1
parseSpec.c
Go to the documentation of this file.
1 
6 #include "system.h"
7 
8 #include <rpmio_internal.h> /* XXX fdGetFp */
9 #include <rpmcb.h>
10 #include <argv.h>
11 
12 #define _RPMTAG_INTERNAL /* XXX rpmTags->aTags */
13 #include <rpmbuild.h>
14 #include "rpmds.h"
15 #include "rpmts.h"
16 #include "debug.h"
17 
18 /*@access headerTagIndices @*/
19 /*@access FD_t @*/ /* compared with NULL */
20 
23 /*@unchecked@*/
24 static struct PartRec {
26  size_t len;
27 /*@observer@*/ /*@null@*/
28  const char * token;
29 } partList[] = {
30  { PART_PREAMBLE, 0, "%package"},
31  { PART_PREP, 0, "%prep"},
32  { PART_BUILD, 0, "%build"},
33  { PART_INSTALL, 0, "%install"},
34  { PART_CHECK, 0, "%check"},
35  { PART_CLEAN, 0, "%clean"},
36  { PART_PREUN, 0, "%preun"},
37  { PART_POSTUN, 0, "%postun"},
38  { PART_PRETRANS, 0, "%pretrans"},
39  { PART_POSTTRANS, 0, "%posttrans"},
40  { PART_PRE, 0, "%pre"},
41  { PART_POST, 0, "%post"},
42  { PART_FILES, 0, "%files"},
43  { PART_CHANGELOG, 0, "%changelog"},
44  { PART_DESCRIPTION, 0, "%description"},
45  { PART_TRIGGERPOSTUN, 0, "%triggerpostun"},
46  { PART_TRIGGERPREIN, 0, "%triggerprein"},
47  { PART_TRIGGERUN, 0, "%triggerun"},
48  { PART_TRIGGERIN, 0, "%triggerin"},
49  { PART_TRIGGERIN, 0, "%trigger"},
50  { PART_VERIFYSCRIPT, 0, "%verifyscript"},
51  { PART_SANITYCHECK, 0, "%sanitycheck"}, /* support "%sanitycheck" scriptlet */
52  {0, 0, 0}
53 };
54 
57 static inline void initParts(struct PartRec *p)
58  /*@modifies p->len @*/
59 {
60  for (; p->token != NULL; p++)
61  p->len = strlen(p->token);
62 }
63 
65 {
66  const char * line = spec->line;
67  struct PartRec *p;
68  rpmParseState nextPart = PART_NONE; /* assume failure */
69 
70  if (partList[0].len == 0)
72 
73  for (p = partList; p->token != NULL; p++) {
74  char c;
75  if (xstrncasecmp(line, p->token, p->len))
76  continue;
77  c = *(line + p->len);
78  if (c == '\0' || xisspace(c)) {
79  nextPart = p->part;
80  break;
81  }
82  }
83 
84  /* If %foo is not found explictly, check for an arbitrary %foo tag. */
85  if (nextPart == PART_NONE) {
86  ARGV_t aTags = NULL;
87  const char * s;
88 /*@-noeffect@*/
89  (void) tagName(0); /* XXX force arbitrary tags to be initialized. */
90 /*@=noeffect@*/
91  aTags = rpmTags->aTags;
92  if (aTags != NULL && aTags[0] != NULL) {
93  ARGV_t av;
94  s = tagCanonicalize(line+1); /* XXX +1 to skip leading '%' */
95 #if defined(RPM_VENDOR_OPENPKG) /* wildcard-matching-arbitrary-tagnames */
96  av = argvSearchLinear(aTags, s, argvFnmatchCasefold);
97 #else
98  av = argvSearch(aTags, s, argvStrcasecmp);
99 #endif
100  if (av != NULL) {
101  spec->foo = xrealloc(spec->foo, (spec->nfoo + 1) * sizeof(*spec->foo));
102  spec->foo[spec->nfoo].str = xstrdup(s);
103  spec->foo[spec->nfoo].tag = tagGenerate(s);
104  spec->foo[spec->nfoo].iob = NULL;
105  spec->nfoo++;
106  nextPart = PART_ARBITRARY;
107  }
108  s = _free(s);
109  }
110  }
111 
112  return nextPart;
113 }
114 
117 static int matchTok(const char *token, const char *line)
118  /*@*/
119 {
120  const char *b, *be = line;
121  size_t toklen = strlen(token);
122  int rc = 0;
123 
124  while ( *(b = be) != '\0' ) {
125  SKIPSPACE(b);
126  be = b;
127  SKIPNONSPACE(be);
128  if (be == b)
129  break;
130  if (toklen != (size_t)(be-b) || xstrncasecmp(token, b, (be-b)))
131  continue;
132  rc = 1;
133  break;
134  }
135 
136  return rc;
137 }
138 
139 void handleComments(char *s)
140 {
141  SKIPSPACE(s);
142  if (*s == '#')
143  *s = '\0';
144 }
145 
148 static void forceIncludeFile(Spec spec, const char * fileName)
149  /*@modifies spec->fileStack @*/
150 {
151  OFI_t * ofi;
152 
153  ofi = newOpenFileInfo();
154  ofi->fileName = xstrdup(fileName);
155  ofi->next = spec->fileStack;
156  spec->fileStack = ofi;
157 }
158 
161 static int restoreFirstChar(Spec spec)
162  /*@modifies spec->nextline, spec->nextpeekc @*/
163 {
164  /* Restore 1st char in (possible) next line */
165  if (spec->nextline != NULL && spec->nextpeekc != '\0') {
166  *spec->nextline = spec->nextpeekc;
167  spec->nextpeekc = '\0';
168  return 1;
169  }
170  return 0;
171 }
172 
175 static int copyNextLineFromOFI(Spec spec, OFI_t * ofi, rpmStripFlags strip)
176  /*@globals rpmGlobalMacroContext, h_errno,
177  fileSystem, internalState @*/
178  /*@modifies spec->nextline, spec->lbuf, spec->lbufPtr,
179  ofi->readPtr,
180  rpmGlobalMacroContext, fileSystem, internalState @*/
181 {
182  char ch;
183 
184  /* Expand next line from file into line buffer */
185  if (!(spec->nextline && *spec->nextline)) {
186  int pc = 0, bc = 0, nc = 0;
187  char *from, *to, *p;
188  to = spec->lbufPtr ? spec->lbufPtr : spec->lbuf;
189  from = ofi->readPtr;
190  ch = ' ';
191  while (*from && ch != '\n')
192  ch = *to++ = *from++;
193 /*@-mods@*/
194  spec->lbufPtr = to;
195 /*@=mods@*/
196  *to++ = '\0';
197  ofi->readPtr = from;
198 
199  /* Check if we need another line before expanding the buffer. */
200  for (p = spec->lbuf; *p; p++) {
201  switch (*p) {
202  case '\\':
203  switch (*(p+1)) {
204  case '\n': p++, nc = 1; /*@innerbreak@*/ break;
205  case '\0': /*@innerbreak@*/ break;
206  default: p++; /*@innerbreak@*/ break;
207  }
208  /*@switchbreak@*/ break;
209  case '\n': nc = 0; /*@switchbreak@*/ break;
210  case '%':
211  switch (*(p+1)) {
212  case '{': p++, bc++; /*@innerbreak@*/ break;
213  case '(': p++, pc++; /*@innerbreak@*/ break;
214  case '%': p++; /*@innerbreak@*/ break;
215  }
216  /*@switchbreak@*/ break;
217  case '{': if (bc > 0) bc++; /*@switchbreak@*/ break;
218  case '}': if (bc > 0) bc--; /*@switchbreak@*/ break;
219  case '(': if (pc > 0) pc++; /*@switchbreak@*/ break;
220  case ')': if (pc > 0) pc--; /*@switchbreak@*/ break;
221  }
222  }
223 
224  /* If it doesn't, ask for one more line. We need a better
225  * error code for this. */
226  if (pc || bc || nc ) {
227 /*@-observertrans -readonlytrans@*/
228  spec->nextline = "";
229 /*@=observertrans =readonlytrans@*/
230  return RPMRC_FAIL;
231  }
232 /*@-mods@*/
233  spec->lbufPtr = spec->lbuf;
234 /*@=mods@*/
235 
236  /* Don't expand macros (eg. %define) in false branch of %if clause */
237  /* Also don't expand macros in %changelog if STRIP_NOEXPAND is set */
238  /* (first line is omitted, so %date macro will be expanded */
239  if (!(strip & STRIP_NOEXPAND)) {
240  if (spec->readStack->reading &&
241  expandMacros(spec, spec->macros, spec->lbuf, spec->lbuf_len)) {
242  rpmlog(RPMLOG_ERR, _("line %d: %s\n"),
243  spec->lineNum, spec->lbuf);
244  return RPMRC_FAIL;
245  }
246  }
247  spec->nextline = spec->lbuf;
248  }
249  return 0;
250 }
251 
254 static int copyNextLineFinish(Spec spec, int strip)
255  /*@modifies spec->line, spec->nextline, spec->nextpeekc @*/
256 {
257  char *last;
258  char ch;
259 
260  /* Find next line in expanded line buffer */
261  spec->line = last = spec->nextline;
262  ch = ' ';
263  while (*spec->nextline && ch != '\n') {
264  ch = *spec->nextline++;
265  if (!xisspace(ch))
266  last = spec->nextline;
267  }
268 
269  /* Save 1st char of next line in order to terminate current line. */
270  if (*spec->nextline != '\0') {
271  spec->nextpeekc = *spec->nextline;
272  *spec->nextline = '\0';
273  }
274 
275  if (strip & STRIP_COMMENTS)
276  handleComments(spec->line);
277 
278  if (strip & STRIP_TRAILINGSPACE)
279  *last = '\0';
280 
281  return 0;
282 }
283 
286 static int readLineFromOFI(Spec spec, OFI_t *ofi)
287  /*@globals h_errno, fileSystem, internalState @*/
288  /*@modifies ofi, spec->fileStack, spec->lineNum, spec->sl,
289  fileSystem, internalState @*/
290 {
291 retry:
292  /* Make sure the current file is open */
293  if (ofi->fd == NULL) {
294  ofi->fd = Fopen(ofi->fileName, "r.fpio");
295  if (ofi->fd == NULL || Ferror(ofi->fd)) {
296  /* XXX Fstrerror */
297  rpmlog(RPMLOG_ERR, _("Unable to open %s: %s\n"),
298  ofi->fileName, Fstrerror(ofi->fd));
299  return RPMRC_FAIL;
300  }
301  spec->lineNum = ofi->lineNum = 0;
302  }
303 
304  /* Make sure we have something in the read buffer */
305  if (!(ofi->readPtr && *(ofi->readPtr))) {
306  /*@-type@*/ /* FIX: cast? */
307  FILE * f = fdGetFp(ofi->fd);
308  /*@=type@*/
309  if (f == NULL || !fgets(ofi->readBuf, (int)sizeof(ofi->readBuf), f)) {
310  /* EOF */
311  if (spec->readStack->next) {
312  rpmlog(RPMLOG_ERR, _("Unclosed %%if\n"));
313  return RPMRC_FAIL;
314  }
315 
316  /* remove this file from the stack */
317  spec->fileStack = ofi->next;
318  (void) Fclose(ofi->fd);
319  ofi->fileName = _free(ofi->fileName);
320 /*@-temptrans@*/
321  ofi = _free(ofi);
322 /*@=temptrans@*/
323 
324  /* only on last file do we signal EOF to caller */
325  ofi = spec->fileStack;
326  if (ofi == NULL)
327  return 1;
328 
329  /* otherwise, go back and try the read again. */
330  goto retry;
331  }
332  ofi->readPtr = ofi->readBuf;
333  ofi->lineNum++;
334  spec->lineNum = ofi->lineNum;
335  if (spec->sl) {
336  speclines sl = spec->sl;
337  if (sl->sl_nlines == sl->sl_nalloc) {
338  sl->sl_nalloc += 100;
339  sl->sl_lines = (char **) xrealloc(sl->sl_lines,
340  sl->sl_nalloc * sizeof(*(sl->sl_lines)));
341  }
342  sl->sl_lines[sl->sl_nlines++] = xstrdup(ofi->readBuf);
343  }
344  }
345  return 0;
346 }
347 
348 int readLine(Spec spec, rpmStripFlags strip)
349 {
350  char *s;
351  int match;
352  struct ReadLevelEntry *rl;
353  OFI_t *ofi = spec->fileStack;
354  int rc;
355 
356  if (!restoreFirstChar(spec)) {
357  retry:
358  if ((rc = readLineFromOFI(spec, ofi)) != 0)
359  return rc;
360 
361  /* Copy next file line into the spec line buffer */
362 
363  if ((rc = copyNextLineFromOFI(spec, ofi, strip)) != 0) {
364  if (rc == RPMRC_FAIL)
365  goto retry;
366  return rc;
367  }
368  }
369 
370  (void) copyNextLineFinish(spec, strip);
371 
372  s = spec->line;
373  SKIPSPACE(s);
374 
375  match = -1;
376  if (!(strip & STRIP_NOEXPAND)) {
377  if (!spec->readStack->reading && !strncmp("%if", s, sizeof("%if")-1)) {
378  match = 0;
379  } else if (! strncmp("%ifarch", s, sizeof("%ifarch")-1)) {
380  const char *arch = rpmExpand("%{_target_cpu}", NULL);
381  s += 7;
382  match = matchTok(arch, s);
383  arch = _free(arch);
384  } else if (! strncmp("%ifnarch", s, sizeof("%ifnarch")-1)) {
385  const char *arch = rpmExpand("%{_target_cpu}", NULL);
386  s += 8;
387  match = !matchTok(arch, s);
388  arch = _free(arch);
389  } else if (! strncmp("%ifos", s, sizeof("%ifos")-1)) {
390  const char *os = rpmExpand("%{_target_os}", NULL);
391  s += 5;
392  match = matchTok(os, s);
393  os = _free(os);
394  } else if (! strncmp("%ifnos", s, sizeof("%ifnos")-1)) {
395  const char *os = rpmExpand("%{_target_os}", NULL);
396  s += 6;
397  match = !matchTok(os, s);
398  os = _free(os);
399  } else if (! strncmp("%if", s, sizeof("%if")-1)) {
400  s += 3;
401  match = parseExpressionBoolean(spec, s);
402  if (match < 0) {
404  _("%s:%d: parseExpressionBoolean returns %d\n"),
405  ofi->fileName, ofi->lineNum, match);
406  return RPMRC_FAIL;
407  }
408  } else if (! strncmp("%else", s, sizeof("%else")-1)) {
409  s += 5;
410  if (! spec->readStack->next) {
411  /* Got an else with no %if ! */
413  _("%s:%d: Got a %%else with no %%if\n"),
414  ofi->fileName, ofi->lineNum);
415  return RPMRC_FAIL;
416  }
417  spec->readStack->reading =
418  spec->readStack->next->reading && ! spec->readStack->reading;
419  spec->line[0] = '\0';
420  } else if (! strncmp("%endif", s, sizeof("%endif")-1)) {
421  s += 6;
422  if (! spec->readStack->next) {
423  /* Got an end with no %if ! */
425  _("%s:%d: Got a %%endif with no %%if\n"),
426  ofi->fileName, ofi->lineNum);
427  return RPMRC_FAIL;
428  }
429  rl = spec->readStack;
430  spec->readStack = spec->readStack->next;
431  free(rl);
432  spec->line[0] = '\0';
433  } else if (! strncmp("%include", s, sizeof("%include")-1)) {
434  char *fileName, *endFileName, *p;
435 
436  s += 8;
437  fileName = s;
438  if (! xisspace(*fileName)) {
439  rpmlog(RPMLOG_ERR, _("malformed %%include statement\n"));
440  return RPMRC_FAIL;
441  }
442  SKIPSPACE(fileName);
443  endFileName = fileName;
444  SKIPNONSPACE(endFileName);
445  p = endFileName;
446  SKIPSPACE(p);
447  if (*p != '\0') {
448  rpmlog(RPMLOG_ERR, _("malformed %%include statement\n"));
449  return RPMRC_FAIL;
450  }
451  *endFileName = '\0';
452 
453  forceIncludeFile(spec, fileName);
454 
455  ofi = spec->fileStack;
456  goto retry;
457  }
458  }
459 
460  if (match != -1) {
461  rl = xmalloc(sizeof(*rl));
462  rl->reading = spec->readStack->reading && match;
463  rl->next = spec->readStack;
464  spec->readStack = rl;
465  spec->line[0] = '\0';
466  }
467 
468  if (! spec->readStack->reading) {
469  spec->line[0] = '\0';
470  }
471 
472  /*@-compmempass@*/ /* FIX: spec->readStack->next should be dependent */
473  return 0;
474  /*@=compmempass@*/
475 }
476 
477 void closeSpec(Spec spec)
478 {
479  OFI_t *ofi;
480 
481  while (spec->fileStack) {
482  ofi = spec->fileStack;
483  spec->fileStack = spec->fileStack->next;
484  if (ofi->fd) (void) Fclose(ofi->fd);
485  ofi->fileName = _free(ofi->fileName);
486  ofi = _free(ofi);
487  }
488 }
489 
492 static inline int genSourceRpmName(Spec spec)
493  /*@globals internalState @*/
494  /*@modifies spec->sourceRpmName, spec->packages->header,
495  internalState @*/
496 {
497  if (spec->sourceRpmName == NULL) {
498  const char *N, *V, *R;
499  char fileName[BUFSIZ];
500 
501  (void) headerNEVRA(spec->packages->header, &N, NULL, &V, &R, NULL);
502  (void) snprintf(fileName, sizeof(fileName), "%s-%s-%s.%ssrc.rpm",
503  N, V, R, spec->noSource ? "no" : "");
504  fileName[sizeof(fileName)-1] = '\0';
505  N = _free(N);
506  V = _free(V);
507  R = _free(R);
508  spec->sourceRpmName = xstrdup(fileName);
509  }
510 
511  return 0;
512 }
513 
514 /*@-redecl@*/
515 /*@unchecked@*/
516 extern int noLang; /* XXX FIXME: pass as arg */
517 /*@=redecl@*/
518 
519 /*@todo Skip parse recursion if os is not compatible. @*/
520 int parseSpec(rpmts ts, const char *specFile, const char *rootURL,
521  int recursing, const char *passPhrase,
522  const char *cookie, int anyarch, int force, int verify)
523 {
524  HE_t he = memset(alloca(sizeof(*he)), 0, sizeof(*he));
525  rpmParseState parsePart = PART_PREAMBLE;
526  int initialPackage = 1;
527  Package pkg;
528  Spec spec;
529  int xx;
530 
531  /* Set up a new Spec structure with no packages. */
532  spec = newSpec();
533 
534  /*
535  * Note: rpmGetPath should guarantee a "canonical" path. That means
536  * that the following pathologies should be weeded out:
537  * //bin//sh
538  * //usr//bin/
539  * /.././../usr/../bin//./sh (XXX FIXME: dots not handled yet)
540  */
541  spec->specFile = rpmGetPath(specFile, NULL);
542  spec->fileStack = newOpenFileInfo();
543  spec->fileStack->fileName = xstrdup(spec->specFile);
544 
545  spec->recursing = recursing;
546  spec->toplevel = (!recursing ? 1 : 0);
547  spec->anyarch = anyarch;
548  spec->force = force;
549 
550  if (rootURL)
551  spec->rootURL = xstrdup(rootURL);
552  if (passPhrase)
553  spec->passPhrase = xstrdup(passPhrase);
554  if (cookie)
555  spec->cookie = xstrdup(cookie);
556 
557  spec->timeCheck = rpmExpandNumeric("%{_timecheck}");
558 
559  /* XXX %_docdir should be set somewhere else. */
560  addMacro(NULL, "_docdir", NULL, "%{_defaultdocdir}", RMIL_SPEC);
561 
562  /* All the parse*() functions expect to have a line pre-read */
563  /* in the spec's line buffer. Except for parsePreamble(), */
564  /* which handles the initial entry into a spec file. */
565 
566  /*@-infloops@*/ /* LCL: parsePart is modified @*/
567  while (parsePart > PART_NONE) {
568  int goterror = 0;
569 
570  switch (parsePart) {
571  default:
572  goterror = 1;
573  /*@switchbreak@*/ break;
574  case PART_PREAMBLE:
575  parsePart = parsePreamble(spec, initialPackage);
576  initialPackage = 0;
577  /*@switchbreak@*/ break;
578  case PART_PREP:
579  parsePart = parsePrep(spec, verify);
580  /*@switchbreak@*/ break;
581  case PART_BUILD:
582  case PART_INSTALL:
583  case PART_CHECK:
584  case PART_CLEAN:
585  case PART_ARBITRARY:
586  parsePart = parseBuildInstallClean(spec, parsePart);
587  /*@switchbreak@*/ break;
588  case PART_CHANGELOG:
589  parsePart = parseChangelog(spec);
590  /*@switchbreak@*/ break;
591  case PART_DESCRIPTION:
592  parsePart = parseDescription(spec);
593  /*@switchbreak@*/ break;
594 
595  case PART_PRE:
596  case PART_POST:
597  case PART_PREUN:
598  case PART_POSTUN:
599  case PART_PRETRANS:
600  case PART_POSTTRANS:
601  case PART_VERIFYSCRIPT:
602  case PART_SANITYCHECK:
603  case PART_TRIGGERPREIN:
604  case PART_TRIGGERIN:
605  case PART_TRIGGERUN:
606  case PART_TRIGGERPOSTUN:
607  parsePart = parseScript(spec, parsePart);
608  /*@switchbreak@*/ break;
609 
610  case PART_FILES:
611  parsePart = parseFiles(spec);
612  /*@switchbreak@*/ break;
613 
614  case PART_NONE: /* XXX avoid gcc whining */
615  case PART_LAST:
617  /*@switchbreak@*/ break;
618  }
619 
620  if (goterror || parsePart >= PART_LAST) {
621  spec = freeSpec(spec);
622  return parsePart;
623  }
624 
625  /* Detect whether BuildArch: is toplevel or within %package. */
626  if (spec->toplevel && parsePart != PART_BUILDARCHITECTURES)
627  spec->toplevel = 0;
628 
629  /* Restart parse iff toplevel BuildArch: is encountered. */
630  if (spec->toplevel && parsePart == PART_BUILDARCHITECTURES) {
631  int index;
632  int x;
633 
634  closeSpec(spec);
635 
636  /* LCL: sizeof(spec->BASpecs[0]) -nullderef whine here */
637  spec->BASpecs = xcalloc(spec->BACount, sizeof(*spec->BASpecs));
638  index = 0;
639  if (spec->BANames != NULL)
640  for (x = 0; x < spec->BACount; x++) {
641 
642  /* XXX DIEDIEDIE: filter irrelevant platforms here. */
643 
644  /* XXX there's more to do than set the macro. */
645  addMacro(NULL, "_target_cpu", NULL, spec->BANames[x], RMIL_RPMRC);
646  spec->BASpecs[index] = NULL;
647  if (parseSpec(ts, specFile, spec->rootURL, 1,
648  passPhrase, cookie, anyarch, force, verify)
649  || (spec->BASpecs[index] = rpmtsSetSpec(ts, NULL)) == NULL)
650  {
651  spec->BACount = index;
652 /*@-nullstate@*/
653  spec = freeSpec(spec);
654  return RPMRC_FAIL;
655 /*@=nullstate@*/
656  }
657 
658  /* XXX there's more to do than delete the macro. */
659  delMacro(NULL, "_target_cpu");
660  index++;
661  }
662 
663  spec->BACount = index;
664  if (! index) {
666  _("No compatible architectures found for build\n"));
667 /*@-nullstate@*/
668  spec = freeSpec(spec);
669  return RPMRC_FAIL;
670 /*@=nullstate@*/
671  }
672 
673  /*
674  * Return the 1st child's fully parsed Spec structure.
675  * The restart of the parse when encountering BuildArch
676  * causes problems for "rpm -q --specfile". This is
677  * still a hack because there may be more than 1 arch
678  * specified (unlikely but possible.) There's also the
679  * further problem that the macro context, particularly
680  * %{_target_cpu}, disagrees with the info in the header.
681  */
682  if (spec->BACount >= 1) {
683  Spec nspec = spec->BASpecs[0];
684  spec->BASpecs = _free(spec->BASpecs);
685  spec = freeSpec(spec);
686  spec = nspec;
687  }
688 
689  (void) rpmtsSetSpec(ts, spec);
690  return 0;
691  }
692  }
693  /*@=infloops@*/ /* LCL: parsePart is modified @*/
694 
695  /* Initialize source RPM name. */
696  (void) genSourceRpmName(spec);
697 
698  /* Check for description in each package and add arch and os */
699  {
700  const char *platform = rpmExpand("%{_target_platform}", NULL);
701  const char *arch = rpmExpand("%{_target_cpu}", NULL);
702  const char *os = rpmExpand("%{_target_os}", NULL);
703 
704  for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
705  he->tag = RPMTAG_OS;
706  he->t = RPM_STRING_TYPE;
707  /* XXX todo: really need "noos" like pkg->noarch somewhen. */
708  he->p.str = os;
709  he->c = 1;
710  xx = headerPut(pkg->header, he, 0);
711 
712  he->tag = RPMTAG_ARCH;
713  he->t = RPM_STRING_TYPE;
714  he->p.str = (pkg->noarch ? "noarch" : arch);
715  he->c = 1;
716  xx = headerPut(pkg->header, he, 0);
717 
718  he->tag = RPMTAG_PLATFORM;
719  he->t = RPM_STRING_TYPE;
720  he->p.str = platform;
721  he->c = 1;
722  xx = headerPut(pkg->header, he, 0);
723 
724  he->tag = RPMTAG_SOURCERPM;
725  he->t = RPM_STRING_TYPE;
726  he->p.str = spec->sourceRpmName;
727  he->c = 1;
728  xx = headerPut(pkg->header, he, 0);
729 
731  he->tag = RPMTAG_NVRA;
732  xx = headerGet(pkg->header, he, 0);
733  rpmlog(RPMLOG_ERR, _("Package has no %%description: %s\n"),
734  he->p.str);
735  he->p.ptr = _free(he->p.ptr);
736  spec = freeSpec(spec);
737  return RPMRC_FAIL;
738  }
739 
741 
742  }
743 
744  platform = _free(platform);
745  arch = _free(arch);
746  os = _free(os);
747  }
748 
749  closeSpec(spec);
750  (void) rpmtsSetSpec(ts, spec);
751 
752  return 0;
753 }