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