rpm
4.5
|
00001 00006 #include "system.h" 00007 00008 #include <rpmio_internal.h> 00009 #include <rpmbuild.h> 00010 #include "rpmds.h" 00011 #include "rpmts.h" 00012 #include "debug.h" 00013 00014 /*@access FD_t @*/ /* compared with NULL */ 00015 00018 /*@unchecked@*/ 00019 static struct PartRec { 00020 int part; 00021 int len; 00022 /*@observer@*/ /*@null@*/ 00023 const char * token; 00024 } partList[] = { 00025 { PART_PREAMBLE, 0, "%package"}, 00026 { PART_PREP, 0, "%prep"}, 00027 { PART_BUILD, 0, "%build"}, 00028 { PART_INSTALL, 0, "%install"}, 00029 { PART_CHECK, 0, "%check"}, 00030 { PART_CLEAN, 0, "%clean"}, 00031 { PART_PREUN, 0, "%preun"}, 00032 { PART_POSTUN, 0, "%postun"}, 00033 { PART_PRETRANS, 0, "%pretrans"}, 00034 { PART_POSTTRANS, 0, "%posttrans"}, 00035 { PART_PRE, 0, "%pre"}, 00036 { PART_POST, 0, "%post"}, 00037 { PART_FILES, 0, "%files"}, 00038 { PART_CHANGELOG, 0, "%changelog"}, 00039 { PART_DESCRIPTION, 0, "%description"}, 00040 { PART_TRIGGERPOSTUN, 0, "%triggerpostun"}, 00041 { PART_TRIGGERPREIN, 0, "%triggerprein"}, 00042 { PART_TRIGGERUN, 0, "%triggerun"}, 00043 { PART_TRIGGERIN, 0, "%triggerin"}, 00044 { PART_TRIGGERIN, 0, "%trigger"}, 00045 { PART_VERIFYSCRIPT, 0, "%verifyscript"}, 00046 {0, 0, 0} 00047 }; 00048 00051 static inline void initParts(struct PartRec *p) 00052 /*@modifies p->len @*/ 00053 { 00054 for (; p->token != NULL; p++) 00055 p->len = strlen(p->token); 00056 } 00057 00058 rpmParseState isPart(const char *line) 00059 { 00060 struct PartRec *p; 00061 00062 /*@-boundsread@*/ 00063 if (partList[0].len == 0) 00064 initParts(partList); 00065 /*@=boundsread@*/ 00066 00067 for (p = partList; p->token != NULL; p++) { 00068 char c; 00069 if (xstrncasecmp(line, p->token, p->len)) 00070 continue; 00071 /*@-boundsread@*/ 00072 c = *(line + p->len); 00073 /*@=boundsread@*/ 00074 if (c == '\0' || xisspace(c)) 00075 break; 00076 } 00077 00078 return (p->token ? p->part : PART_NONE); 00079 } 00080 00083 static int matchTok(const char *token, const char *line) 00084 /*@*/ 00085 { 00086 const char *b, *be = line; 00087 size_t toklen = strlen(token); 00088 int rc = 0; 00089 00090 /*@-boundsread@*/ 00091 while ( *(b = be) != '\0' ) { 00092 SKIPSPACE(b); 00093 be = b; 00094 SKIPNONSPACE(be); 00095 if (be == b) 00096 break; 00097 if (toklen != (be-b) || xstrncasecmp(token, b, (be-b))) 00098 continue; 00099 rc = 1; 00100 break; 00101 } 00102 /*@=boundsread@*/ 00103 00104 return rc; 00105 } 00106 00107 /*@-boundswrite@*/ 00108 void handleComments(char *s) 00109 { 00110 SKIPSPACE(s); 00111 if (*s == '#') 00112 *s = '\0'; 00113 } 00114 /*@=boundswrite@*/ 00115 00118 static void forceIncludeFile(Spec spec, const char * fileName) 00119 /*@modifies spec->fileStack @*/ 00120 { 00121 OFI_t * ofi; 00122 00123 ofi = newOpenFileInfo(); 00124 ofi->fileName = xstrdup(fileName); 00125 ofi->next = spec->fileStack; 00126 spec->fileStack = ofi; 00127 } 00128 00131 /*@-boundswrite@*/ 00132 static int copyNextLine(Spec spec, OFI_t *ofi, int strip) 00133 /*@globals rpmGlobalMacroContext, h_errno, 00134 fileSystem @*/ 00135 /*@modifies spec->nextline, spec->nextpeekc, spec->lbuf, spec->line, 00136 ofi->readPtr, 00137 rpmGlobalMacroContext, fileSystem @*/ 00138 { 00139 char *last; 00140 char ch; 00141 00142 /* Restore 1st char in (possible) next line */ 00143 if (spec->nextline != NULL && spec->nextpeekc != '\0') { 00144 *spec->nextline = spec->nextpeekc; 00145 spec->nextpeekc = '\0'; 00146 } 00147 /* Expand next line from file into line buffer */ 00148 if (!(spec->nextline && *spec->nextline)) { 00149 int pc = 0, bc = 0, nc = 0; 00150 char *from, *to, *p; 00151 to = spec->lbufPtr ? spec->lbufPtr : spec->lbuf; 00152 from = ofi->readPtr; 00153 ch = ' '; 00154 while (*from && ch != '\n') 00155 ch = *to++ = *from++; 00156 /*@-mods@*/ 00157 spec->lbufPtr = to; 00158 /*@=mods@*/ 00159 *to++ = '\0'; 00160 ofi->readPtr = from; 00161 00162 /* Check if we need another line before expanding the buffer. */ 00163 for (p = spec->lbuf; *p; p++) { 00164 switch (*p) { 00165 case '\\': 00166 switch (*(p+1)) { 00167 case '\n': p++, nc = 1; /*@innerbreak@*/ break; 00168 case '\0': /*@innerbreak@*/ break; 00169 default: p++; /*@innerbreak@*/ break; 00170 } 00171 /*@switchbreak@*/ break; 00172 case '\n': nc = 0; /*@switchbreak@*/ break; 00173 case '%': 00174 switch (*(p+1)) { 00175 case '{': p++, bc++; /*@innerbreak@*/ break; 00176 case '(': p++, pc++; /*@innerbreak@*/ break; 00177 case '%': p++; /*@innerbreak@*/ break; 00178 } 00179 /*@switchbreak@*/ break; 00180 case '{': if (bc > 0) bc++; /*@switchbreak@*/ break; 00181 case '}': if (bc > 0) bc--; /*@switchbreak@*/ break; 00182 case '(': if (pc > 0) pc++; /*@switchbreak@*/ break; 00183 case ')': if (pc > 0) pc--; /*@switchbreak@*/ break; 00184 } 00185 } 00186 00187 /* If it doesn't, ask for one more line. We need a better 00188 * error code for this. */ 00189 if (pc || bc || nc ) { 00190 /*@-observertrans -readonlytrans@*/ 00191 spec->nextline = ""; 00192 /*@=observertrans =readonlytrans@*/ 00193 return RPMERR_UNMATCHEDIF; 00194 } 00195 /*@-mods@*/ 00196 spec->lbufPtr = spec->lbuf; 00197 /*@=mods@*/ 00198 00199 /* Don't expand macros (eg. %define) in false branch of %if clause */ 00200 /* Also don't expand macros in %changelog where we set STRIP_NOEXPAND flag */ 00201 /* (first line is ommited, so if there is e.g. %date macro, it will be expanded */ 00202 if (!(strip & STRIP_NOEXPAND)) { 00203 if (spec->readStack->reading && 00204 expandMacros(spec, spec->macros, spec->lbuf, sizeof(spec->lbuf))) { 00205 rpmError(RPMERR_BADSPEC, _("line %d: %s\n"), 00206 spec->lineNum, spec->lbuf); 00207 return RPMERR_BADSPEC; 00208 } 00209 } 00210 spec->nextline = spec->lbuf; 00211 } 00212 00213 /* Find next line in expanded line buffer */ 00214 spec->line = last = spec->nextline; 00215 ch = ' '; 00216 while (*spec->nextline && ch != '\n') { 00217 ch = *spec->nextline++; 00218 if (!xisspace(ch)) 00219 last = spec->nextline; 00220 } 00221 00222 /* Save 1st char of next line in order to terminate current line. */ 00223 if (*spec->nextline != '\0') { 00224 spec->nextpeekc = *spec->nextline; 00225 *spec->nextline = '\0'; 00226 } 00227 00228 if (strip & STRIP_COMMENTS) 00229 handleComments(spec->line); 00230 00231 if (strip & STRIP_TRAILINGSPACE) 00232 *last = '\0'; 00233 00234 return 0; 00235 } 00236 /*@=boundswrite@*/ 00237 00238 static const char *getAlternateArch(const char *arch) 00239 { 00240 const char *alternate_arch = NULL; 00241 if (! strncmp("x86_64", arch, sizeof("x86_64")-1)) 00242 alternate_arch = "amd64"; 00243 else if (! strncmp("amd64", arch, sizeof("amd64")-1)) 00244 alternate_arch = "x86_64"; 00245 return alternate_arch; 00246 } 00247 00248 /*@-boundswrite@*/ 00249 int readLine(Spec spec, int strip) 00250 { 00251 #ifdef DYING 00252 const char *arch; 00253 const char *os; 00254 #endif 00255 char *s; 00256 int match; 00257 struct ReadLevelEntry *rl; 00258 OFI_t *ofi = spec->fileStack; 00259 int rc; 00260 00261 retry: 00262 /* Make sure the current file is open */ 00263 /*@-branchstate@*/ 00264 if (ofi->fd == NULL) { 00265 ofi->fd = Fopen(ofi->fileName, "r.fpio"); 00266 if (ofi->fd == NULL || Ferror(ofi->fd)) { 00267 /* XXX Fstrerror */ 00268 rpmError(RPMERR_BADSPEC, _("Unable to open %s: %s\n"), 00269 ofi->fileName, Fstrerror(ofi->fd)); 00270 return RPMERR_BADSPEC; 00271 } 00272 spec->lineNum = ofi->lineNum = 0; 00273 } 00274 /*@=branchstate@*/ 00275 00276 /* Make sure we have something in the read buffer */ 00277 if (!(ofi->readPtr && *(ofi->readPtr))) { 00278 /*@-type@*/ /* FIX: cast? */ 00279 FILE * f = fdGetFp(ofi->fd); 00280 /*@=type@*/ 00281 if (f == NULL || !fgets(ofi->readBuf, BUFSIZ, f)) { 00282 /* EOF */ 00283 if (spec->readStack->next) { 00284 rpmError(RPMERR_UNMATCHEDIF, _("Unclosed %%if\n")); 00285 return RPMERR_UNMATCHEDIF; 00286 } 00287 00288 /* remove this file from the stack */ 00289 spec->fileStack = ofi->next; 00290 (void) Fclose(ofi->fd); 00291 ofi->fileName = _free(ofi->fileName); 00292 ofi = _free(ofi); 00293 00294 /* only on last file do we signal EOF to caller */ 00295 ofi = spec->fileStack; 00296 if (ofi == NULL) 00297 return 1; 00298 00299 /* otherwise, go back and try the read again. */ 00300 goto retry; 00301 } 00302 ofi->readPtr = ofi->readBuf; 00303 ofi->lineNum++; 00304 spec->lineNum = ofi->lineNum; 00305 if (spec->sl) { 00306 speclines sl = spec->sl; 00307 if (sl->sl_nlines == sl->sl_nalloc) { 00308 sl->sl_nalloc += 100; 00309 sl->sl_lines = (char **) xrealloc(sl->sl_lines, 00310 sl->sl_nalloc * sizeof(*(sl->sl_lines))); 00311 } 00312 sl->sl_lines[sl->sl_nlines++] = xstrdup(ofi->readBuf); 00313 } 00314 } 00315 00316 /* Copy next file line into the spec line buffer */ 00317 if ((rc = copyNextLine(spec, ofi, strip)) != 0) { 00318 if (rc == RPMERR_UNMATCHEDIF) 00319 goto retry; 00320 return rc; 00321 } 00322 00323 s = spec->line; 00324 SKIPSPACE(s); 00325 00326 match = -1; 00327 if (! (strip & STRIP_NOEXPAND)) { 00328 if (!spec->readStack->reading && !strncmp("%if", s, sizeof("%if")-1)) { 00329 match = 0; 00330 } else if (! strncmp("%ifarch", s, sizeof("%ifarch")-1)) { 00331 const char *arch = rpmExpand("%{_target_cpu}", NULL); 00332 const char *alternate_arch = getAlternateArch(arch); 00333 s += 7; 00334 match = matchTok(arch, s) || (alternate_arch && matchTok(alternate_arch, s)); 00335 arch = _free(arch); 00336 } else if (! strncmp("%ifnarch", s, sizeof("%ifnarch")-1)) { 00337 const char *arch = rpmExpand("%{_target_cpu}", NULL); 00338 const char *alternate_arch = getAlternateArch(arch); 00339 s += 8; 00340 match = !matchTok(arch, s) && (!alternate_arch || !matchTok(alternate_arch, s)); 00341 arch = _free(arch); 00342 } else if (! strncmp("%ifos", s, sizeof("%ifos")-1)) { 00343 const char *os = rpmExpand("%{_target_os}", NULL); 00344 s += 5; 00345 match = matchTok(os, s); 00346 os = _free(os); 00347 } else if (! strncmp("%ifnos", s, sizeof("%ifnos")-1)) { 00348 const char *os = rpmExpand("%{_target_os}", NULL); 00349 s += 6; 00350 match = !matchTok(os, s); 00351 os = _free(os); 00352 } else if (! strncmp("%if", s, sizeof("%if")-1)) { 00353 s += 3; 00354 match = parseExpressionBoolean(spec, s); 00355 if (match < 0) { 00356 rpmError(RPMERR_UNMATCHEDIF, 00357 _("%s:%d: parseExpressionBoolean returns %d\n"), 00358 ofi->fileName, ofi->lineNum, match); 00359 return RPMERR_BADSPEC; 00360 } 00361 } else if (! strncmp("%else", s, sizeof("%else")-1)) { 00362 s += 5; 00363 if (! spec->readStack->next) { 00364 /* Got an else with no %if ! */ 00365 rpmError(RPMERR_UNMATCHEDIF, 00366 _("%s:%d: Got a %%else with no %%if\n"), 00367 ofi->fileName, ofi->lineNum); 00368 return RPMERR_UNMATCHEDIF; 00369 } 00370 spec->readStack->reading = 00371 spec->readStack->next->reading && ! spec->readStack->reading; 00372 spec->line[0] = '\0'; 00373 } else if (! strncmp("%endif", s, sizeof("%endif")-1)) { 00374 s += 6; 00375 if (! spec->readStack->next) { 00376 /* Got an end with no %if ! */ 00377 rpmError(RPMERR_UNMATCHEDIF, 00378 _("%s:%d: Got a %%endif with no %%if\n"), 00379 ofi->fileName, ofi->lineNum); 00380 return RPMERR_UNMATCHEDIF; 00381 } 00382 rl = spec->readStack; 00383 spec->readStack = spec->readStack->next; 00384 free(rl); 00385 spec->line[0] = '\0'; 00386 } else if (! strncmp("%include", s, sizeof("%include")-1)) { 00387 char *fileName, *endFileName, *p; 00388 00389 s += 8; 00390 fileName = s; 00391 if (! xisspace(*fileName)) { 00392 rpmError(RPMERR_BADSPEC, _("malformed %%include statement\n")); 00393 return RPMERR_BADSPEC; 00394 } 00395 SKIPSPACE(fileName); 00396 endFileName = fileName; 00397 SKIPNONSPACE(endFileName); 00398 p = endFileName; 00399 SKIPSPACE(p); 00400 if (*p != '\0') { 00401 rpmError(RPMERR_BADSPEC, _("malformed %%include statement\n")); 00402 return RPMERR_BADSPEC; 00403 } 00404 *endFileName = '\0'; 00405 00406 forceIncludeFile(spec, fileName); 00407 00408 ofi = spec->fileStack; 00409 goto retry; 00410 } 00411 } 00412 00413 if (match != -1) { 00414 rl = xmalloc(sizeof(*rl)); 00415 rl->reading = spec->readStack->reading && match; 00416 rl->next = spec->readStack; 00417 spec->readStack = rl; 00418 spec->line[0] = '\0'; 00419 } 00420 00421 if (! spec->readStack->reading) { 00422 spec->line[0] = '\0'; 00423 } 00424 00425 /*@-compmempass@*/ /* FIX: spec->readStack->next should be dependent */ 00426 return 0; 00427 /*@=compmempass@*/ 00428 } 00429 /*@=boundswrite@*/ 00430 00431 void closeSpec(Spec spec) 00432 { 00433 OFI_t *ofi; 00434 00435 while (spec->fileStack) { 00436 ofi = spec->fileStack; 00437 spec->fileStack = spec->fileStack->next; 00438 if (ofi->fd) (void) Fclose(ofi->fd); 00439 ofi->fileName = _free(ofi->fileName); 00440 ofi = _free(ofi); 00441 } 00442 } 00443 00444 /*@-redecl@*/ 00445 /*@unchecked@*/ 00446 extern int noLang; /* XXX FIXME: pass as arg */ 00447 /*@=redecl@*/ 00448 00449 /*@todo Skip parse recursion if os is not compatible. @*/ 00450 /*@-boundswrite@*/ 00451 int parseSpec(rpmts ts, const char *specFile, const char *rootURL, 00452 int recursing, const char *passPhrase, 00453 char *cookie, int anyarch, int force, int verify) 00454 { 00455 rpmParseState parsePart = PART_PREAMBLE; 00456 int initialPackage = 1; 00457 Package pkg; 00458 Spec spec; 00459 00460 /* Set up a new Spec structure with no packages. */ 00461 spec = newSpec(); 00462 00463 /* 00464 * Note: rpmGetPath should guarantee a "canonical" path. That means 00465 * that the following pathologies should be weeded out: 00466 * //bin//sh 00467 * //usr//bin/ 00468 * /.././../usr/../bin//./sh (XXX FIXME: dots not handled yet) 00469 */ 00470 spec->specFile = rpmGetPath(specFile, NULL); 00471 spec->fileStack = newOpenFileInfo(); 00472 spec->fileStack->fileName = xstrdup(spec->specFile); 00473 00474 spec->recursing = recursing; 00475 spec->anyarch = anyarch; 00476 spec->force = force; 00477 00478 if (rootURL) 00479 spec->rootURL = xstrdup(rootURL); 00480 if (passPhrase) 00481 spec->passPhrase = xstrdup(passPhrase); 00482 if (cookie) 00483 spec->cookie = xstrdup(cookie); 00484 00485 spec->timeCheck = rpmExpandNumeric("%{_timecheck}"); 00486 00487 /* XXX %_docdir should be set somewhere else. */ 00488 addMacro(NULL, "_docdir", NULL, "%{_defaultdocdir}", RMIL_SPEC); 00489 00490 /* All the parse*() functions expect to have a line pre-read */ 00491 /* in the spec's line buffer. Except for parsePreamble(), */ 00492 /* which handles the initial entry into a spec file. */ 00493 00494 /*@-infloops@*/ /* LCL: parsePart is modified @*/ 00495 while (parsePart < PART_LAST && parsePart != PART_NONE) { 00496 switch (parsePart) { 00497 case PART_PREAMBLE: 00498 parsePart = parsePreamble(spec, initialPackage); 00499 initialPackage = 0; 00500 /*@switchbreak@*/ break; 00501 case PART_PREP: 00502 parsePart = parsePrep(spec, verify); 00503 /*@switchbreak@*/ break; 00504 case PART_BUILD: 00505 case PART_INSTALL: 00506 case PART_CHECK: 00507 case PART_CLEAN: 00508 parsePart = parseBuildInstallClean(spec, parsePart); 00509 /*@switchbreak@*/ break; 00510 case PART_CHANGELOG: 00511 parsePart = parseChangelog(spec); 00512 /*@switchbreak@*/ break; 00513 case PART_DESCRIPTION: 00514 parsePart = parseDescription(spec); 00515 /*@switchbreak@*/ break; 00516 00517 case PART_PRE: 00518 case PART_POST: 00519 case PART_PREUN: 00520 case PART_POSTUN: 00521 case PART_PRETRANS: 00522 case PART_POSTTRANS: 00523 case PART_VERIFYSCRIPT: 00524 case PART_TRIGGERPREIN: 00525 case PART_TRIGGERIN: 00526 case PART_TRIGGERUN: 00527 case PART_TRIGGERPOSTUN: 00528 parsePart = parseScript(spec, parsePart); 00529 /*@switchbreak@*/ break; 00530 00531 case PART_FILES: 00532 parsePart = parseFiles(spec); 00533 /*@switchbreak@*/ break; 00534 00535 case PART_NONE: /* XXX avoid gcc whining */ 00536 case PART_LAST: 00537 case PART_BUILDARCHITECTURES: 00538 /*@switchbreak@*/ break; 00539 } 00540 00541 if (parsePart >= PART_LAST) { 00542 spec = freeSpec(spec); 00543 return parsePart; 00544 } 00545 00546 if (parsePart == PART_BUILDARCHITECTURES) { 00547 int index; 00548 int x; 00549 00550 closeSpec(spec); 00551 00552 /* LCL: sizeof(spec->BASpecs[0]) -nullderef whine here */ 00553 spec->BASpecs = xcalloc(spec->BACount, sizeof(*spec->BASpecs)); 00554 index = 0; 00555 if (spec->BANames != NULL) 00556 for (x = 0; x < spec->BACount; x++) { 00557 00558 /* XXX DIEDIEDIE: filter irrelevant platforms here. */ 00559 00560 /* XXX there's more to do than set the macro. */ 00561 addMacro(NULL, "_target_cpu", NULL, spec->BANames[x], RMIL_RPMRC); 00562 spec->BASpecs[index] = NULL; 00563 if (parseSpec(ts, specFile, spec->rootURL, 1, 00564 passPhrase, cookie, anyarch, force, verify) 00565 || (spec->BASpecs[index] = rpmtsSetSpec(ts, NULL)) == NULL) 00566 { 00567 spec->BACount = index; 00568 /*@-nullstate@*/ 00569 spec = freeSpec(spec); 00570 return RPMERR_BADSPEC; 00571 /*@=nullstate@*/ 00572 } 00573 00574 /* XXX there's more to do than delete the macro. */ 00575 delMacro(NULL, "_target_cpu"); 00576 index++; 00577 } 00578 00579 spec->BACount = index; 00580 if (! index) { 00581 rpmError(RPMERR_BADSPEC, 00582 _("No compatible architectures found for build\n")); 00583 /*@-nullstate@*/ 00584 spec = freeSpec(spec); 00585 return RPMERR_BADSPEC; 00586 /*@=nullstate@*/ 00587 } 00588 00589 /* 00590 * Return the 1st child's fully parsed Spec structure. 00591 * The restart of the parse when encountering BuildArch 00592 * causes problems for "rpm -q --specfile". This is 00593 * still a hack because there may be more than 1 arch 00594 * specified (unlikely but possible.) There's also the 00595 * further problem that the macro context, particularly 00596 * %{_target_cpu}, disagrees with the info in the header. 00597 */ 00598 /*@-branchstate@*/ 00599 if (spec->BACount >= 1) { 00600 Spec nspec = spec->BASpecs[0]; 00601 spec->BASpecs = _free(spec->BASpecs); 00602 spec = freeSpec(spec); 00603 spec = nspec; 00604 } 00605 /*@=branchstate@*/ 00606 00607 (void) rpmtsSetSpec(ts, spec); 00608 return 0; 00609 } 00610 } 00611 /*@=infloops@*/ /* LCL: parsePart is modified @*/ 00612 00613 /* Check for description in each package and add arch and os */ 00614 { 00615 const char *platform = rpmExpand("%{_target_platform}", NULL); 00616 const char *arch = rpmExpand("%{_target_cpu}", NULL); 00617 const char *os = rpmExpand("%{_target_os}", NULL); 00618 00619 for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) { 00620 if (!headerIsEntry(pkg->header, RPMTAG_DESCRIPTION)) { 00621 const char * name; 00622 (void) headerNVR(pkg->header, &name, NULL, NULL); 00623 rpmError(RPMERR_BADSPEC, _("Package has no %%description: %s\n"), 00624 name); 00625 spec = freeSpec(spec); 00626 return RPMERR_BADSPEC; 00627 } 00628 00629 (void) headerAddEntry(pkg->header, RPMTAG_OS, RPM_STRING_TYPE, os, 1); 00630 (void) headerAddEntry(pkg->header, RPMTAG_ARCH, 00631 RPM_STRING_TYPE, arch, 1); 00632 (void) headerAddEntry(pkg->header, RPMTAG_PLATFORM, 00633 RPM_STRING_TYPE, platform, 1); 00634 00635 pkg->ds = rpmdsThis(pkg->header, RPMTAG_REQUIRENAME, RPMSENSE_EQUAL); 00636 00637 } 00638 00639 platform = _free(platform); 00640 arch = _free(arch); 00641 os = _free(os); 00642 } 00643 00644 closeSpec(spec); 00645 (void) rpmtsSetSpec(ts, spec); 00646 00647 return 0; 00648 } 00649 /*@=boundswrite@*/