rpm 5.2.1
|
00001 00006 #include "system.h" 00007 00008 #include <rpmio.h> 00009 #include <rpmiotypes.h> 00010 #include <rpmlog.h> 00011 #include <rpmurl.h> 00012 #include <argv.h> 00013 #include <mire.h> 00014 00015 #define _RPMEVR_INTERNAL 00016 #define _RPMTAG_INTERNAL /* XXX rpmTags->aTags */ 00017 #include <rpmbuild.h> 00018 #include "debug.h" 00019 00020 /*@access FD_t @*/ /* compared with NULL */ 00021 /*@access headerTagIndices @*/ /* rpmTags->aTags */ 00022 00025 /*@observer@*/ /*@unchecked@*/ 00026 static rpmTag copyTagsDuringParse[] = { 00027 RPMTAG_EPOCH, 00028 RPMTAG_VERSION, 00029 RPMTAG_RELEASE, 00030 RPMTAG_DISTEPOCH, 00031 RPMTAG_LICENSE, 00032 RPMTAG_GROUP, /* XXX permissive. */ 00033 RPMTAG_SUMMARY, /* XXX permissive. */ 00034 RPMTAG_DESCRIPTION, /* XXX permissive. */ 00035 RPMTAG_PACKAGER, 00036 RPMTAG_DISTRIBUTION, 00037 RPMTAG_DISTURL, 00038 RPMTAG_VENDOR, 00039 RPMTAG_ICON, 00040 RPMTAG_GIF, 00041 RPMTAG_XPM, 00042 RPMTAG_URL, 00043 RPMTAG_CHANGELOGTIME, 00044 RPMTAG_CHANGELOGNAME, 00045 RPMTAG_CHANGELOGTEXT, 00046 RPMTAG_PREFIXES, 00047 RPMTAG_DISTTAG, 00048 RPMTAG_CVSID, 00049 RPMTAG_VARIANTS, 00050 RPMTAG_XMAJOR, 00051 RPMTAG_XMINOR, 00052 RPMTAG_REPOTAG, 00053 RPMTAG_KEYWORDS, 00054 0 00055 }; 00056 00059 /*@observer@*/ /*@unchecked@*/ 00060 static rpmTag requiredTags[] = { 00061 RPMTAG_NAME, 00062 RPMTAG_VERSION, 00063 RPMTAG_RELEASE, 00064 RPMTAG_SUMMARY, 00065 RPMTAG_GROUP, 00066 RPMTAG_LICENSE, 00067 0 00068 }; 00069 00072 static void addOrAppendListEntry(Header h, rpmTag tag, char * line) 00073 /*@modifies h @*/ 00074 { 00075 HE_t he = memset(alloca(sizeof(*he)), 0, sizeof(*he)); 00076 int xx; 00077 int argc; 00078 const char **argv; 00079 00080 xx = poptParseArgvString(line, &argc, &argv); 00081 if (argc) { 00082 he->tag = tag; 00083 he->t = RPM_STRING_ARRAY_TYPE; 00084 he->p.argv = argv; 00085 he->c = argc; 00086 he->append = 1; 00087 xx = headerPut(h, he, 0); 00088 he->append = 0; 00089 } 00090 argv = _free(argv); 00091 } 00092 00093 /* Parse a simple part line that only take -n <pkg> or <pkg> */ 00094 /* <pkg> is returned in name as a pointer into malloc'd storage. */ 00095 00098 static int parseSimplePart(Spec spec, /*@out@*/char ** Np, 00099 /*@out@*/rpmParseState *flag) 00100 /*@globals internalState@*/ 00101 /*@modifies *Np, *flag, internalState, spec->line @*/ 00102 { 00103 char * s, * se; 00104 int rc = 0; /* assume failure */ 00105 00106 if (Np) 00107 *Np = NULL; 00108 00109 se = strchr(spec->line, '#'); 00110 if (se) { 00111 *se = '\0'; 00112 while (--se >= spec->line && strchr(" \t\n\r", *se) != NULL) 00113 *se = '\0'; 00114 } 00115 00116 s = xstrdup(spec->line); 00117 /* Throw away the first token (the %xxxx) */ 00118 (void)strtok(s, " \t\n"); 00119 00120 if (!(se = strtok(NULL, " \t\n"))) 00121 goto exit; 00122 00123 if (!strcmp(se, "-n")) { 00124 if (!(se = strtok(NULL, " \t\n"))) { 00125 rc = 1; 00126 goto exit; 00127 } 00128 *flag = PART_NAME; 00129 } else 00130 *flag = PART_SUBNAME; 00131 00132 if (Np) 00133 *Np = xstrdup(se); 00134 00135 rc = (strtok(NULL, " \t\n") ? 1 : 0); 00136 00137 exit: 00138 s = _free(s); 00139 return rc; 00140 } 00141 00144 static inline int parseYesNo(const char * s) 00145 /*@*/ 00146 { 00147 return ((!s || (s[0] == 'n' || s[0] == 'N' || s[0] == '0') || 00148 !xstrcasecmp(s, "false") || !xstrcasecmp(s, "off")) 00149 ? 0 : 1); 00150 } 00151 00152 typedef struct tokenBits_s { 00153 /*@observer@*/ /*@null@*/ 00154 const char * name; 00155 rpmsenseFlags bits; 00156 } * tokenBits; 00157 00160 /*@observer@*/ /*@unchecked@*/ 00161 static struct tokenBits_s installScriptBits[] = { 00162 { "interp", RPMSENSE_INTERP }, 00163 { "preun", RPMSENSE_SCRIPT_PREUN }, 00164 { "pre", RPMSENSE_SCRIPT_PRE }, 00165 { "postun", RPMSENSE_SCRIPT_POSTUN }, 00166 { "post", RPMSENSE_SCRIPT_POST }, 00167 { "rpmlib", RPMSENSE_RPMLIB }, 00168 { "verify", RPMSENSE_SCRIPT_VERIFY }, 00169 { "hint", RPMSENSE_MISSINGOK }, 00170 { NULL, 0 } 00171 }; 00172 00175 /*@observer@*/ /*@unchecked@*/ 00176 static struct tokenBits_s buildScriptBits[] = { 00177 { "prep", RPMSENSE_SCRIPT_PREP }, 00178 { "build", RPMSENSE_SCRIPT_BUILD }, 00179 { "install", RPMSENSE_SCRIPT_INSTALL }, 00180 { "clean", RPMSENSE_SCRIPT_CLEAN }, 00181 { "hint", RPMSENSE_MISSINGOK }, 00182 { NULL, 0 } 00183 }; 00184 00187 static int parseBits(const char * s, const tokenBits tokbits, 00188 /*@out@*/ rpmsenseFlags * bp) 00189 /*@modifies *bp @*/ 00190 { 00191 tokenBits tb; 00192 const char * se; 00193 rpmsenseFlags bits = RPMSENSE_ANY; 00194 int c = 0; 00195 00196 if (s) { 00197 while (*s != '\0') { 00198 while ((c = *s) && xisspace(c)) s++; 00199 se = s; 00200 while ((c = *se) && xisalpha(c)) se++; 00201 if (s == se) 00202 break; 00203 for (tb = tokbits; tb->name; tb++) { 00204 if (tb->name != NULL && 00205 strlen(tb->name) == (size_t)(se-s) && !strncmp(tb->name, s, (se-s))) 00206 /*@innerbreak@*/ break; 00207 } 00208 if (tb->name == NULL) 00209 break; 00210 bits |= tb->bits; 00211 while ((c = *se) && xisspace(c)) se++; 00212 if (c != ',') 00213 break; 00214 s = ++se; 00215 } 00216 } 00217 if (c == 0 && bp) *bp = bits; 00218 return (c ? RPMRC_FAIL : RPMRC_OK); 00219 } 00220 00223 /*@null@*/ 00224 static inline char * findLastChar(char * s) 00225 /*@modifies *s @*/ 00226 { 00227 char *se = s + strlen(s); 00228 00229 /* Right trim white space. */ 00230 while (--se > s && strchr(" \t\n\r", *se) != NULL) 00231 *se = '\0'; 00232 /* Truncate comments. */ 00233 if ((se = strchr(s, '#')) != NULL) { 00234 *se = '\0'; 00235 while (--se > s && strchr(" \t\n\r", *se) != NULL) 00236 *se = '\0'; 00237 } 00238 /*@-temptrans -retalias @*/ 00239 return se; 00240 /*@=temptrans =retalias @*/ 00241 } 00242 00245 static int isMemberInEntry(Header h, const char *name, rpmTag tag) 00246 /*@globals internalState @*/ 00247 /*@modifies internalState @*/ 00248 { 00249 HE_t he = memset(alloca(sizeof(*he)), 0, sizeof(*he)); 00250 int rc = -1; 00251 int xx; 00252 00253 he->tag = tag; 00254 xx = headerGet(h, he, 0); 00255 if (!xx) 00256 return rc; 00257 rc = 0; 00258 while (he->c) { 00259 he->c--; 00260 if (xstrcasecmp(he->p.argv[he->c], name)) 00261 continue; 00262 rc = 1; 00263 break; 00264 } 00265 he->p.ptr = _free(he->p.ptr); 00266 return rc; 00267 } 00268 00271 static int checkForValidArchitectures(Spec spec) 00272 /*@globals rpmGlobalMacroContext, h_errno, internalState @*/ 00273 /*@modifies rpmGlobalMacroContext, internalState @*/ 00274 { 00275 const char *arch = rpmExpand("%{_target_cpu}", NULL); 00276 const char *os = rpmExpand("%{_target_os}", NULL); 00277 int rc = RPMRC_FAIL; /* assume failure. */ 00278 00279 if (isMemberInEntry(spec->sourceHeader, arch, RPMTAG_EXCLUDEARCH) == 1) { 00280 rpmlog(RPMLOG_ERR, _("Architecture is excluded: %s\n"), arch); 00281 goto exit; 00282 } 00283 if (isMemberInEntry(spec->sourceHeader, arch, RPMTAG_EXCLUSIVEARCH) == 0) { 00284 rpmlog(RPMLOG_ERR, _("Architecture is not included: %s\n"), arch); 00285 goto exit; 00286 } 00287 if (isMemberInEntry(spec->sourceHeader, os, RPMTAG_EXCLUDEOS) == 1) { 00288 rpmlog(RPMLOG_ERR, _("OS is excluded: %s\n"), os); 00289 goto exit; 00290 } 00291 if (isMemberInEntry(spec->sourceHeader, os, RPMTAG_EXCLUSIVEOS) == 0) { 00292 rpmlog(RPMLOG_ERR, _("OS is not included: %s\n"), os); 00293 goto exit; 00294 } 00295 rc = 0; 00296 exit: 00297 arch = _free(arch); 00298 os = _free(os); 00299 return rc; 00300 } 00301 00308 static rpmRC checkForRequired(Header h, const char * NVR) 00309 /*@*/ 00310 { 00311 rpmTag * p; 00312 rpmRC rc = RPMRC_OK; 00313 00314 for (p = requiredTags; *p != 0; p++) { 00315 if (!headerIsEntry(h, *p)) { 00316 rpmlog(RPMLOG_ERR, 00317 _("%s field must be present in package: %s\n"), 00318 tagName(*p), NVR); 00319 rc = RPMRC_FAIL; 00320 } 00321 } 00322 00323 return rc; 00324 } 00325 00332 static rpmRC checkForDuplicates(Header h, const char * NVR) 00333 /*@globals internalState @*/ 00334 /*@modifies h, internalState @*/ 00335 { 00336 HE_t he = memset(alloca(sizeof(*he)), 0, sizeof(*he)); 00337 HeaderIterator hi; 00338 rpmTag lastTag = 0; 00339 rpmRC rc = RPMRC_OK; 00340 00341 for (hi = headerInit(h); 00342 headerNext(hi, he, 0); 00343 he->p.ptr = _free(he->p.ptr)) 00344 { 00345 if (he->tag != lastTag) { 00346 lastTag = he->tag; 00347 continue; 00348 } 00349 rpmlog(RPMLOG_ERR, _("Duplicate %s entries in package: %s\n"), 00350 tagName(he->tag), NVR); 00351 rc = RPMRC_FAIL; 00352 } 00353 hi = headerFini(hi); 00354 00355 return rc; 00356 } 00357 00360 /*@observer@*/ /*@unchecked@*/ 00361 static struct optionalTag { 00362 rpmTag ot_tag; 00363 /*@observer@*/ /*@null@*/ 00364 const char * ot_mac; 00365 } optionalTags[] = { 00366 { RPMTAG_VENDOR, "%{vendor}" }, 00367 { RPMTAG_PACKAGER, "%{packager}" }, 00368 { RPMTAG_DISTEPOCH, "%{distepoch}" }, 00369 { RPMTAG_DISTRIBUTION, "%{distribution}" }, 00370 { RPMTAG_DISTTAG, "%{disttag}" }, 00371 { RPMTAG_DISTURL, "%{disturl}" }, 00372 { 0xffffffff, "%{class}" }, 00373 { -1, NULL } 00374 }; 00375 00378 static void fillOutMainPackage(Header h) 00379 /*@globals rpmGlobalMacroContext, h_errno, internalState @*/ 00380 /*@modifies h, rpmGlobalMacroContext, internalState @*/ 00381 { 00382 HE_t he = memset(alloca(sizeof(*he)), 0, sizeof(*he)); 00383 struct optionalTag *ot; 00384 int xx; 00385 00386 for (ot = optionalTags; ot->ot_mac != NULL; ot++) { 00387 const char * val; 00388 rpmTag tag; 00389 00390 tag = ot->ot_tag; 00391 00392 /* Generate arbitrary tag (if necessary). */ 00393 if (tag == 0xffffffff) { 00394 val = tagCanonicalize(ot->ot_mac + (sizeof("%{")-1)); 00395 tag = tagGenerate(val); 00396 val = _free(val); 00397 } 00398 00399 if (headerIsEntry(h, tag)) 00400 continue; 00401 val = rpmExpand(ot->ot_mac, NULL); 00402 if (val && *val != '%') { 00403 he->tag = tag; 00404 he->t = RPM_STRING_TYPE; 00405 he->p.str = val; 00406 he->c = 1; 00407 xx = headerPut(h, he, 0); 00408 } 00409 val = _free(val); 00410 } 00411 } 00412 00415 static int doIcon(Spec spec, Header h) 00416 /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/ 00417 /*@modifies h, rpmGlobalMacroContext, fileSystem, internalState @*/ 00418 { 00419 static size_t iconsize = 0; 00420 HE_t he = memset(alloca(sizeof(*he)), 0, sizeof(*he)); 00421 const char *fn, *Lurlfn = NULL; 00422 struct Source *sp; 00423 size_t nb; 00424 rpmuint8_t * icon; 00425 FD_t fd = NULL; 00426 int rc = RPMRC_FAIL; /* assume error */ 00427 int urltype; 00428 int xx; 00429 00430 if (iconsize == 0) { 00431 iconsize = rpmExpandNumeric("%{?_build_iconsize}"); 00432 if (iconsize < 2048) 00433 iconsize = 2048; 00434 } 00435 icon = alloca(iconsize+1); 00436 00437 for (sp = spec->sources; sp != NULL; sp = sp->next) { 00438 if (sp->flags & RPMFILE_ICON) 00439 break; 00440 } 00441 if (sp == NULL) { 00442 rpmlog(RPMLOG_ERR, _("No icon file in sources\n")); 00443 goto exit; 00444 } 00445 00446 #if defined(RPM_VENDOR_OPENPKG) /* splitted-source-directory */ 00447 /* support splitted source directories, i.e., source files which 00448 are alternatively placed into the .spec directory and picked 00449 up from there, too. */ 00450 Lurlfn = rpmGenPath(NULL, "%{_specdir}/", sp->source); 00451 if (access(Lurlfn, F_OK) == -1) { 00452 Lurlfn = _free(Lurlfn); 00453 Lurlfn = rpmGenPath(NULL, "%{_icondir}/", sp->source); 00454 } 00455 #else 00456 Lurlfn = rpmGenPath(NULL, "%{_icondir}/", sp->source); 00457 #endif 00458 00459 fn = NULL; 00460 urltype = urlPath(Lurlfn, &fn); 00461 switch (urltype) { 00462 case URL_IS_HTTPS: 00463 case URL_IS_HTTP: 00464 case URL_IS_FTP: 00465 case URL_IS_PATH: 00466 case URL_IS_UNKNOWN: 00467 break; 00468 case URL_IS_DASH: 00469 case URL_IS_HKP: 00470 rpmlog(RPMLOG_ERR, _("Invalid icon URL: %s\n"), Lurlfn); 00471 goto exit; 00472 /*@notreached@*/ break; 00473 } 00474 00475 fd = Fopen(fn, "r%{?_rpmgio}"); 00476 if (fd == NULL || Ferror(fd)) { 00477 rpmlog(RPMLOG_ERR, _("Unable to open icon %s: %s\n"), 00478 fn, Fstrerror(fd)); 00479 rc = RPMRC_FAIL; 00480 goto exit; 00481 } 00482 00483 *icon = '\0'; 00484 nb = Fread(icon, sizeof(icon[0]), iconsize, fd); 00485 if (Ferror(fd) || nb == 0) { 00486 rpmlog(RPMLOG_ERR, _("Unable to read icon %s: %s\n"), 00487 fn, Fstrerror(fd)); 00488 goto exit; 00489 } 00490 if (nb >= iconsize) { 00491 rpmlog(RPMLOG_ERR, _("Icon %s is too big (max. %d bytes)\n"), 00492 fn, (int)iconsize); 00493 goto exit; 00494 } 00495 00496 if (icon[0] == 'G' && icon[1] == 'I' && icon[2] == 'F') 00497 he->tag = RPMTAG_GIF; 00498 else 00499 if (icon[0] == '/' && icon[1] == '*' && icon[2] == ' ' 00500 && icon[3] == 'X' && icon[4] == 'P' && icon[5] == 'M') 00501 he->tag = RPMTAG_XPM; 00502 else 00503 he->tag = tagValue("Icon"); 00504 he->t = RPM_BIN_TYPE; 00505 he->p.ui8p = icon; 00506 he->c = (rpmTagCount)nb; 00507 xx = headerPut(h, he, 0); 00508 rc = 0; 00509 00510 exit: 00511 if (fd) { 00512 (void) Fclose(fd); 00513 fd = NULL; 00514 } 00515 Lurlfn = _free(Lurlfn); 00516 return rc; 00517 } 00518 00519 spectag stashSt(Spec spec, Header h, rpmTag tag, const char * lang) 00520 { 00521 HE_t he = memset(alloca(sizeof(*he)), 0, sizeof(*he)); 00522 spectag t = NULL; 00523 int xx; 00524 00525 if (spec->st) { 00526 spectags st = spec->st; 00527 if (st->st_ntags == st->st_nalloc) { 00528 st->st_nalloc += 10; 00529 st->st_t = xrealloc(st->st_t, st->st_nalloc * sizeof(*(st->st_t))); 00530 } 00531 t = st->st_t + st->st_ntags++; 00532 t->t_tag = tag; 00533 t->t_startx = spec->lineNum - 1; 00534 t->t_nlines = 1; 00535 t->t_lang = xstrdup(lang); 00536 t->t_msgid = NULL; 00537 if (!(t->t_lang && strcmp(t->t_lang, RPMBUILD_DEFAULT_LANG))) { 00538 he->tag = RPMTAG_NAME; 00539 xx = headerGet(h, he, 0); 00540 if (xx) { 00541 char buf[1024]; 00542 sprintf(buf, "%s(%s)", he->p.str, tagName(tag)); 00543 t->t_msgid = xstrdup(buf); 00544 } 00545 he->p.ptr = _free(he->p.ptr); 00546 } 00547 } 00548 /*@-usereleased -compdef@*/ 00549 return t; 00550 /*@=usereleased =compdef@*/ 00551 } 00552 00553 #define SINGLE_TOKEN_ONLY \ 00554 if (multiToken) { \ 00555 rpmlog(RPMLOG_ERR, _("line %d: Tag takes single token only: %s\n"), \ 00556 spec->lineNum, spec->line); \ 00557 return RPMRC_FAIL; \ 00558 } 00559 00560 /*@-redecl@*/ 00561 extern int noLang; 00562 /*@=redecl@*/ 00563 00564 static rpmRC tagValidate(Spec spec, rpmTag tag, const char * value) 00565 /*@*/ 00566 { 00567 const char * tagN = tagName(tag); 00568 const char * pattern = rpmExpand("%{?pattern_", tagN, "}", NULL); 00569 rpmRC ec = RPMRC_OK; 00570 00571 if (pattern && *pattern) { 00572 miRE mire; 00573 int xx; 00574 00575 mire = mireNew(RPMMIRE_REGEX, tag); 00576 xx = mireSetCOptions(mire, RPMMIRE_REGEX, 0, 0, NULL); 00577 if (!xx) 00578 xx = mireRegcomp(mire, pattern); 00579 if (!xx) 00580 xx = mireRegexec(mire, value, strlen(value)); 00581 if (!xx) 00582 ec = RPMRC_OK; 00583 else { 00584 rpmlog(RPMLOG_ERR, _("line %d: invalid tag value(\"%s\") %s: %s\n"), 00585 spec->lineNum, pattern, tagN, spec->line); 00586 ec = RPMRC_FAIL; 00587 } 00588 00589 mire = mireFree(mire); 00590 } 00591 00592 pattern = _free(pattern); 00593 00594 return ec; 00595 } 00596 00599 static rpmRC handlePreambleTag(Spec spec, Package pkg, rpmTag tag, 00600 const char *macro, const char *lang) 00601 /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/ 00602 /*@modifies spec->macros, spec->st, 00603 spec->sources, spec->numSources, spec->noSource, 00604 spec->sourceHeader, spec->BANames, spec->BACount, 00605 spec->line, 00606 pkg->header, pkg->autoProv, pkg->autoReq, pkg->noarch, 00607 rpmGlobalMacroContext, fileSystem, internalState @*/ 00608 { 00609 HE_t he = memset(alloca(sizeof(*he)), 0, sizeof(*he)); 00610 char * field = spec->line; 00611 char * end; 00612 int multiToken = 0; 00613 rpmsenseFlags tagflags; 00614 int len; 00615 rpmuint32_t num; 00616 int rc; 00617 int xx; 00618 #if defined(RPM_VENDOR_MANDRIVA) /* build-expand-field-for-single-token */ 00619 char * expand_field = NULL; 00620 #endif 00621 00622 if (field == NULL) return RPMRC_FAIL; /* XXX can't happen */ 00623 /* Find the start of the "field" and strip trailing space */ 00624 while ((*field) && (*field != ':')) 00625 field++; 00626 if (*field != ':') { 00627 rpmlog(RPMLOG_ERR, _("line %d: Malformed tag: %s\n"), 00628 spec->lineNum, spec->line); 00629 return RPMRC_FAIL; 00630 } 00631 field++; 00632 SKIPSPACE(field); 00633 if (!*field) { 00634 /* Empty field */ 00635 rpmlog(RPMLOG_ERR, _("line %d: Empty tag: %s\n"), 00636 spec->lineNum, spec->line); 00637 return RPMRC_FAIL; 00638 } 00639 #if defined(RPM_VENDOR_MANDRIVA) /* build-expand-field-for-single-token */ 00640 expand_field = rpmExpand(field, NULL); 00641 #else 00642 end = findLastChar(field); 00643 #endif 00644 00645 /* Validate tag data content. */ 00646 if (tagValidate(spec, tag, field) != RPMRC_OK) 00647 return RPMRC_FAIL; 00648 00649 /* See if this is multi-token */ 00650 #if defined(RPM_VENDOR_MANDRIVA) /* build-expand-field-for-single-token */ 00651 end = expand_field; 00652 #else 00653 end = field; 00654 #endif 00655 SKIPNONSPACE(end); 00656 if (*end != '\0') 00657 multiToken = 1; 00658 #if defined(RPM_VENDOR_MANDRIVA) /* build-expand-field-for-single-token */ 00659 _free(expand_field); 00660 #endif 00661 00662 switch (tag) { 00663 case RPMTAG_NAME: 00664 case RPMTAG_VERSION: 00665 case RPMTAG_RELEASE: 00666 case RPMTAG_DISTEPOCH: 00667 case RPMTAG_URL: 00668 case RPMTAG_DISTTAG: 00669 case RPMTAG_REPOTAG: 00670 case RPMTAG_CVSID: 00671 SINGLE_TOKEN_ONLY; 00672 /* These macros are for backward compatibility */ 00673 if (tag == RPMTAG_VERSION) { 00674 if (strchr(field, '-') != NULL) { 00675 rpmlog(RPMLOG_ERR, _("line %d: Illegal char '-' in %s: %s\n"), 00676 spec->lineNum, "version", spec->line); 00677 return RPMRC_FAIL; 00678 } 00679 addMacro(spec->macros, "PACKAGE_VERSION", NULL, field, RMIL_OLDSPEC); 00680 } else if (tag == RPMTAG_RELEASE) { 00681 if (strchr(field, '-') != NULL) { 00682 rpmlog(RPMLOG_ERR, _("line %d: Illegal char '-' in %s: %s\n"), 00683 spec->lineNum, "release", spec->line); 00684 return RPMRC_FAIL; 00685 } 00686 addMacro(spec->macros, "PACKAGE_RELEASE", NULL, field, RMIL_OLDSPEC-1); 00687 } 00688 he->tag = tag; 00689 he->t = RPM_STRING_TYPE; 00690 he->p.str = field; 00691 he->c = 1; 00692 xx = headerPut(pkg->header, he, 0); 00693 break; 00694 case RPMTAG_GROUP: 00695 case RPMTAG_SUMMARY: 00696 #if defined(RPM_VENDOR_OPENPKG) /* make-class-available-as-macro */ 00697 case RPMTAG_CLASS: 00698 #endif 00699 (void) stashSt(spec, pkg->header, tag, lang); 00700 /*@fallthrough@*/ 00701 case RPMTAG_DISTRIBUTION: 00702 case RPMTAG_VENDOR: 00703 case RPMTAG_LICENSE: 00704 case RPMTAG_PACKAGER: 00705 if (!*lang) { 00706 he->tag = tag; 00707 he->t = RPM_STRING_TYPE; 00708 he->p.str = field; 00709 he->c = 1; 00710 xx = headerPut(pkg->header, he, 0); 00711 } else if (!(noLang && strcmp(lang, RPMBUILD_DEFAULT_LANG))) { 00712 (void) headerAddI18NString(pkg->header, tag, field, lang); 00713 } 00714 break; 00715 /* XXX silently ignore BuildRoot: */ 00716 case RPMTAG_BUILDROOT: 00717 SINGLE_TOKEN_ONLY; 00718 macro = NULL; 00719 #ifdef DYING 00720 buildRootURL = rpmGenPath(spec->rootURL, "%{?buildroot}", NULL); 00721 (void) urlPath(buildRootURL, &buildRoot); 00722 if (*buildRoot == '\0') buildRoot = "/"; 00723 if (!strcmp(buildRoot, "/")) { 00724 rpmlog(RPMLOG_ERR, 00725 _("BuildRoot can not be \"/\": %s\n"), spec->buildRootURL); 00726 buildRootURL = _free(buildRootURL); 00727 return RPMRC_FAIL; 00728 } 00729 buildRootURL = _free(buildRootURL); 00730 #endif 00731 break; 00732 case RPMTAG_KEYWORDS: 00733 case RPMTAG_VARIANTS: 00734 case RPMTAG_PREFIXES: 00735 addOrAppendListEntry(pkg->header, tag, field); 00736 he->tag = tag; 00737 xx = headerGet(pkg->header, he, 0); 00738 if (tag == RPMTAG_PREFIXES) 00739 while (he->c--) { 00740 if (he->p.argv[he->c][0] != '/') { 00741 rpmlog(RPMLOG_ERR, 00742 _("line %d: Prefixes must begin with \"/\": %s\n"), 00743 spec->lineNum, spec->line); 00744 he->p.ptr = _free(he->p.ptr); 00745 return RPMRC_FAIL; 00746 } 00747 len = (int)strlen(he->p.argv[he->c]); 00748 if (he->p.argv[he->c][len - 1] == '/' && len > 1) { 00749 rpmlog(RPMLOG_ERR, 00750 _("line %d: Prefixes must not end with \"/\": %s\n"), 00751 spec->lineNum, spec->line); 00752 he->p.ptr = _free(he->p.ptr); 00753 return RPMRC_FAIL; 00754 } 00755 } 00756 he->p.ptr = _free(he->p.ptr); 00757 break; 00758 case RPMTAG_DOCDIR: 00759 SINGLE_TOKEN_ONLY; 00760 if (field[0] != '/') { 00761 rpmlog(RPMLOG_ERR, 00762 _("line %d: Docdir must begin with '/': %s\n"), 00763 spec->lineNum, spec->line); 00764 return RPMRC_FAIL; 00765 } 00766 macro = NULL; 00767 delMacro(NULL, "_docdir"); 00768 addMacro(NULL, "_docdir", NULL, field, RMIL_SPEC); 00769 break; 00770 case RPMTAG_XMAJOR: 00771 case RPMTAG_XMINOR: 00772 case RPMTAG_EPOCH: 00773 SINGLE_TOKEN_ONLY; 00774 if (parseNum(field, &num)) { 00775 rpmlog(RPMLOG_ERR, 00776 _("line %d: %s takes an integer value: %s\n"), 00777 spec->lineNum, tagName(tag), spec->line); 00778 return RPMRC_FAIL; 00779 } 00780 he->tag = tag; 00781 he->t = RPM_UINT32_TYPE; 00782 he->p.ui32p = # 00783 he->c = 1; 00784 xx = headerPut(pkg->header, he, 0); 00785 break; 00786 case RPMTAG_AUTOREQPROV: 00787 pkg->autoReq = parseYesNo(field); 00788 pkg->autoProv = pkg->autoReq; 00789 break; 00790 case RPMTAG_AUTOREQ: 00791 pkg->autoReq = parseYesNo(field); 00792 break; 00793 case RPMTAG_AUTOPROV: 00794 pkg->autoProv = parseYesNo(field); 00795 break; 00796 case RPMTAG_SOURCE: 00797 case RPMTAG_PATCH: 00798 SINGLE_TOKEN_ONLY; 00799 macro = NULL; 00800 if ((rc = addSource(spec, pkg, field, tag))) 00801 return rc; 00802 break; 00803 case RPMTAG_ICON: 00804 SINGLE_TOKEN_ONLY; 00805 macro = NULL; 00806 if ((rc = addSource(spec, pkg, field, tag))) 00807 return rc; 00808 /* XXX the fetch/load of icon needs to be elsewhere. */ 00809 if ((rc = doIcon(spec, pkg->header))) 00810 return rc; 00811 break; 00812 case RPMTAG_NOSOURCE: 00813 case RPMTAG_NOPATCH: 00814 spec->noSource = 1; 00815 if ((rc = parseNoSource(spec, field, tag))) 00816 return rc; 00817 break; 00818 case RPMTAG_BUILDPREREQ: 00819 case RPMTAG_BUILDREQUIRES: 00820 if ((rc = parseBits(lang, buildScriptBits, &tagflags))) { 00821 rpmlog(RPMLOG_ERR, 00822 _("line %d: Bad %s: qualifiers: %s\n"), 00823 spec->lineNum, tagName(tag), spec->line); 00824 return rc; 00825 } 00826 if ((rc = parseRCPOT(spec, pkg, field, tag, 0, tagflags))) 00827 return rc; 00828 break; 00829 case RPMTAG_PREREQ: 00830 case RPMTAG_REQUIREFLAGS: 00831 if ((rc = parseBits(lang, installScriptBits, &tagflags))) { 00832 rpmlog(RPMLOG_ERR, 00833 _("line %d: Bad %s: qualifiers: %s\n"), 00834 spec->lineNum, tagName(tag), spec->line); 00835 return rc; 00836 } 00837 if ((rc = parseRCPOT(spec, pkg, field, tag, 0, tagflags))) 00838 return rc; 00839 break; 00840 /* Aliases for BuildRequires(hint): */ 00841 case RPMTAG_BUILDSUGGESTS: 00842 case RPMTAG_BUILDENHANCES: 00843 tagflags = RPMSENSE_MISSINGOK; 00844 if ((rc = parseRCPOT(spec, pkg, field, tag, 0, tagflags))) 00845 return rc; 00846 break; 00847 /* Aliases for Requires(hint): */ 00848 case RPMTAG_SUGGESTSFLAGS: 00849 case RPMTAG_ENHANCESFLAGS: 00850 tag = RPMTAG_REQUIREFLAGS; 00851 tagflags = RPMSENSE_MISSINGOK; 00852 if ((rc = parseRCPOT(spec, pkg, field, tag, 0, tagflags))) 00853 return rc; 00854 break; 00855 case RPMTAG_BUILDOBSOLETES: 00856 case RPMTAG_BUILDPROVIDES: 00857 case RPMTAG_BUILDCONFLICTS: 00858 case RPMTAG_CONFLICTFLAGS: 00859 case RPMTAG_OBSOLETEFLAGS: 00860 case RPMTAG_PROVIDEFLAGS: 00861 tagflags = RPMSENSE_ANY; 00862 if ((rc = parseRCPOT(spec, pkg, field, tag, 0, tagflags))) 00863 return rc; 00864 break; 00865 case RPMTAG_BUILDPLATFORMS: /* XXX needs pattern parsing */ 00866 case RPMTAG_EXCLUDEARCH: 00867 case RPMTAG_EXCLUSIVEARCH: 00868 case RPMTAG_EXCLUDEOS: 00869 case RPMTAG_EXCLUSIVEOS: 00870 addOrAppendListEntry(spec->sourceHeader, tag, field); 00871 break; 00872 00873 case RPMTAG_BUILDARCHS: 00874 { const char ** BANames = NULL; 00875 int BACount = 0; 00876 if ((rc = poptParseArgvString(field, &BACount, &BANames))) { 00877 rpmlog(RPMLOG_ERR, 00878 _("line %d: Bad BuildArchitecture format: %s\n"), 00879 spec->lineNum, spec->line); 00880 return RPMRC_FAIL; 00881 } 00882 if (spec->toplevel) { 00883 if (BACount > 0 && BANames != NULL) { 00884 spec->BACount = BACount; 00885 spec->BANames = BANames; 00886 BANames = NULL; /* XXX don't free. */ 00887 } 00888 } else { 00889 if (BACount != 1 || strcmp(BANames[0], "noarch")) { 00890 rpmlog(RPMLOG_ERR, 00891 _("line %d: Only \"noarch\" sub-packages are supported: %s\n"), 00892 spec->lineNum, spec->line); 00893 BANames = _free(BANames); 00894 return RPMRC_FAIL; 00895 } 00896 pkg->noarch = 1; 00897 } 00898 BANames = _free(BANames); 00899 } break; 00900 00901 default: 00902 macro = NULL; 00903 he->tag = tag; 00904 he->t = RPM_STRING_ARRAY_TYPE; 00905 he->p.argv= (const char **) &field; /* XXX NOCAST */ 00906 he->c = 1; 00907 he->append = 1; 00908 xx = headerPut(pkg->header, he, 0); 00909 he->append = 0; 00910 break; 00911 } 00912 00913 /*@-usereleased@*/ 00914 if (macro) 00915 addMacro(spec->macros, macro, NULL, field, RMIL_SPEC); 00916 /*@=usereleased@*/ 00917 00918 return RPMRC_OK; 00919 } 00920 00921 /* This table has to be in a peculiar order. If one tag is the */ 00922 /* same as another, plus a few letters, it must come first. */ 00923 00926 typedef struct PreambleRec_s { 00927 rpmTag tag; 00928 int multiLang; 00929 int obsolete; 00930 /*@observer@*/ /*@null@*/ 00931 const char * token; 00932 } * PreambleRec; 00933 00934 /*@unchecked@*/ 00935 static struct PreambleRec_s preambleList[] = { 00936 {RPMTAG_NAME, 0, 0, "name"}, 00937 {RPMTAG_VERSION, 0, 0, "version"}, 00938 {RPMTAG_RELEASE, 0, 0, "release"}, 00939 {RPMTAG_DISTEPOCH, 0, 0, "distepoch"}, 00940 {RPMTAG_EPOCH, 0, 0, "epoch"}, 00941 {RPMTAG_EPOCH, 0, 1, "serial"}, 00942 {RPMTAG_SUMMARY, 1, 0, "summary"}, 00943 {RPMTAG_LICENSE, 0, 0, "copyright"}, 00944 {RPMTAG_LICENSE, 0, 0, "license"}, 00945 {RPMTAG_DISTRIBUTION, 0, 0, "distribution"}, 00946 {RPMTAG_DISTURL, 0, 0, "disturl"}, 00947 {RPMTAG_VENDOR, 0, 0, "vendor"}, 00948 {RPMTAG_GROUP, 1, 0, "group"}, 00949 {RPMTAG_PACKAGER, 0, 0, "packager"}, 00950 {RPMTAG_URL, 0, 0, "url"}, 00951 {RPMTAG_SOURCE, 0, 0, "source"}, 00952 {RPMTAG_PATCH, 0, 0, "patch"}, 00953 {RPMTAG_NOSOURCE, 0, 0, "nosource"}, 00954 {RPMTAG_NOPATCH, 0, 0, "nopatch"}, 00955 {RPMTAG_EXCLUDEARCH, 0, 0, "excludearch"}, 00956 {RPMTAG_EXCLUSIVEARCH, 0, 0, "exclusivearch"}, 00957 {RPMTAG_EXCLUDEOS, 0, 0, "excludeos"}, 00958 {RPMTAG_EXCLUSIVEOS, 0, 0, "exclusiveos"}, 00959 {RPMTAG_ICON, 0, 0, "icon"}, 00960 {RPMTAG_PROVIDEFLAGS, 0, 0, "provides"}, 00961 {RPMTAG_REQUIREFLAGS, 1, 0, "requires"}, 00962 {RPMTAG_PREREQ, 1, 0, "prereq"}, 00963 {RPMTAG_CONFLICTFLAGS, 0, 0, "conflicts"}, 00964 {RPMTAG_OBSOLETEFLAGS, 0, 0, "obsoletes"}, 00965 {RPMTAG_PREFIXES, 0, 0, "prefixes"}, 00966 {RPMTAG_PREFIXES, 0, 0, "prefix"}, 00967 {RPMTAG_BUILDROOT, 0, 0, "buildroot"}, 00968 {RPMTAG_BUILDARCHS, 0, 0, "buildarchitectures"}, 00969 {RPMTAG_BUILDARCHS, 0, 0, "buildarch"}, 00970 {RPMTAG_BUILDCONFLICTS, 0, 0, "buildconflicts"}, 00971 {RPMTAG_BUILDOBSOLETES, 0, 0, "buildobsoletes"}, 00972 {RPMTAG_BUILDPREREQ, 1, 0, "buildprereq"}, 00973 {RPMTAG_BUILDPROVIDES, 0, 0, "buildprovides"}, 00974 {RPMTAG_BUILDREQUIRES, 1, 0, "buildrequires"}, 00975 {RPMTAG_AUTOREQPROV, 0, 0, "autoreqprov"}, 00976 {RPMTAG_AUTOREQ, 0, 0, "autoreq"}, 00977 {RPMTAG_AUTOPROV, 0, 0, "autoprov"}, 00978 {RPMTAG_DOCDIR, 0, 0, "docdir"}, 00979 {RPMTAG_DISTTAG, 0, 0, "disttag"}, 00980 {RPMTAG_CVSID, 0, 0, "cvsid"}, 00981 {RPMTAG_SVNID, 0, 0, "svnid"}, 00982 {RPMTAG_SUGGESTSFLAGS, 0, 0, "suggests"}, 00983 {RPMTAG_ENHANCESFLAGS, 0, 0, "enhances"}, 00984 {RPMTAG_BUILDSUGGESTS, 0, 0, "buildsuggests"}, 00985 {RPMTAG_BUILDENHANCES, 0, 0, "buildenhances"}, 00986 {RPMTAG_VARIANTS, 0, 0, "variants"}, 00987 {RPMTAG_VARIANTS, 0, 0, "variant"}, 00988 {RPMTAG_XMAJOR, 0, 0, "xmajor"}, 00989 {RPMTAG_XMINOR, 0, 0, "xminor"}, 00990 {RPMTAG_REPOTAG, 0, 0, "repotag"}, 00991 {RPMTAG_KEYWORDS, 0, 0, "keywords"}, 00992 {RPMTAG_KEYWORDS, 0, 0, "keyword"}, 00993 {RPMTAG_BUILDPLATFORMS, 0, 0, "buildplatforms"}, 00994 #if defined(RPM_VENDOR_OPENPKG) /* make-class-available-as-macro */ 00995 {RPMTAG_CLASS, 0, 0, "class"}, 00996 #endif 00997 /*@-nullassign@*/ /* LCL: can't add null annotation */ 00998 {0, 0, 0, 0} 00999 /*@=nullassign@*/ 01000 }; 01001 01004 static int findPreambleTag(Spec spec, /*@out@*/rpmTag * tagp, 01005 /*@null@*/ /*@out@*/ const char ** macro, /*@out@*/ char * lang) 01006 /*@modifies *tagp, *macro, *lang @*/ 01007 { 01008 PreambleRec p; 01009 char *s; 01010 size_t len = 0; 01011 01012 /* Search for defined tags. */ 01013 for (p = preambleList; p->token != NULL; p++) { 01014 len = strlen(p->token); 01015 if (!(p->token && !xstrncasecmp(spec->line, p->token, len))) 01016 continue; 01017 if (p->obsolete) { 01018 rpmlog(RPMLOG_ERR, _("Legacy syntax is unsupported: %s\n"), 01019 p->token); 01020 p = NULL; 01021 } 01022 break; 01023 } 01024 if (p == NULL) 01025 return 1; 01026 01027 /* Search for arbitrary tags. */ 01028 if (tagp && p->token == NULL) { 01029 ARGV_t aTags = NULL; 01030 int rc = 1; /* assume failure */ 01031 01032 /*@-noeffect@*/ 01033 (void) tagName(0); /* XXX force arbitrary tags to be initialized. */ 01034 /*@=noeffect@*/ 01035 aTags = rpmTags->aTags; 01036 if (aTags != NULL && aTags[0] != NULL) { 01037 ARGV_t av; 01038 s = tagCanonicalize(spec->line); 01039 #if defined(RPM_VENDOR_OPENPKG) /* wildcard-matching-arbitrary-tagnames */ 01040 av = argvSearchLinear(aTags, s, argvFnmatchCasefold); 01041 #else 01042 av = argvSearch(aTags, s, argvStrcasecmp); 01043 #endif 01044 if (av != NULL) { 01045 *tagp = tagGenerate(s); 01046 rc = 0; 01047 } 01048 s = _free(s); 01049 } 01050 return rc; 01051 } 01052 01053 s = spec->line + len; 01054 SKIPSPACE(s); 01055 01056 switch (p->multiLang) { 01057 default: 01058 case 0: 01059 /* Unless this is a source or a patch, a ':' better be next */ 01060 if (p->tag != RPMTAG_SOURCE && p->tag != RPMTAG_PATCH) { 01061 if (*s != ':') return 1; 01062 } 01063 *lang = '\0'; 01064 break; 01065 case 1: /* Parse optional ( <token> ). */ 01066 if (*s == ':') { 01067 strcpy(lang, RPMBUILD_DEFAULT_LANG); 01068 break; 01069 } 01070 if (*s != '(') return 1; 01071 s++; 01072 SKIPSPACE(s); 01073 while (!xisspace(*s) && *s != ')') 01074 *lang++ = *s++; 01075 *lang = '\0'; 01076 SKIPSPACE(s); 01077 if (*s != ')') return 1; 01078 s++; 01079 SKIPSPACE(s); 01080 if (*s != ':') return 1; 01081 break; 01082 } 01083 01084 if (tagp) 01085 *tagp = p->tag; 01086 if (macro) 01087 /*@-onlytrans -observertrans -dependenttrans@*/ /* FIX: double indirection. */ 01088 *macro = p->token; 01089 /*@=onlytrans =observertrans =dependenttrans@*/ 01090 return 0; 01091 } 01092 01093 /* XXX should return rpmParseState, but RPMRC_FAIL forces int return. */ 01094 int parsePreamble(Spec spec, int initialPackage) 01095 { 01096 HE_t he = memset(alloca(sizeof(*he)), 0, sizeof(*he)); 01097 rpmParseState nextPart; 01098 int xx; 01099 char *linep; 01100 Package pkg; 01101 char NVR[BUFSIZ]; 01102 char lang[BUFSIZ]; 01103 rpmRC rc; 01104 01105 strcpy(NVR, "(main package)"); 01106 01107 pkg = newPackage(spec); 01108 if (spec->packages == NULL) { 01109 spec->packages = pkg; 01110 assert(initialPackage); 01111 } else if (! initialPackage) { 01112 char *name = NULL; 01113 rpmParseState flag; 01114 Package lastpkg; 01115 01116 /* There is one option to %package: <pkg> or -n <pkg> */ 01117 flag = PART_NONE; 01118 if (parseSimplePart(spec, &name, &flag)) { 01119 rpmlog(RPMLOG_ERR, _("Bad package specification: %s\n"), 01120 spec->line); 01121 return RPMRC_FAIL; 01122 } 01123 01124 lastpkg = NULL; 01125 if (lookupPackage(spec, name, flag, &lastpkg) == RPMRC_OK) { 01126 pkg->next = lastpkg->next; 01127 } else { 01128 /* Add package to end of list */ 01129 for (lastpkg = spec->packages; lastpkg->next != NULL; lastpkg = lastpkg->next) 01130 {}; 01131 } 01132 assert(lastpkg != NULL); 01133 lastpkg->next = pkg; 01134 01135 /* Construct the package */ 01136 if (flag == PART_SUBNAME) { 01137 he->tag = RPMTAG_NAME; 01138 xx = headerGet(spec->packages->header, he, 0); 01139 sprintf(NVR, "%s-%s", he->p.str, name); 01140 he->p.ptr = _free(he->p.ptr); 01141 } else 01142 strcpy(NVR, name); 01143 name = _free(name); 01144 he->tag = RPMTAG_NAME; 01145 he->t = RPM_STRING_TYPE; 01146 he->p.str = NVR; 01147 he->c = 1; 01148 xx = headerPut(pkg->header, he, 0); 01149 } 01150 01151 if ((rc = readLine(spec, STRIP_TRAILINGSPACE | STRIP_COMMENTS)) > 0) { 01152 nextPart = PART_NONE; 01153 } else { 01154 if (rc) 01155 return rc; 01156 while ((nextPart = isPart(spec)) == PART_NONE) { 01157 const char * macro = NULL; 01158 rpmTag tag; 01159 01160 /* Skip blank lines */ 01161 linep = spec->line; 01162 SKIPSPACE(linep); 01163 if (*linep != '\0') { 01164 if (findPreambleTag(spec, &tag, ¯o, lang)) { 01165 rpmlog(RPMLOG_ERR, _("line %d: Unknown tag: %s\n"), 01166 spec->lineNum, spec->line); 01167 return RPMRC_FAIL; 01168 } 01169 if (handlePreambleTag(spec, pkg, tag, macro, lang)) 01170 return RPMRC_FAIL; 01171 if (spec->BANames && !spec->recursing && spec->toplevel) 01172 return PART_BUILDARCHITECTURES; 01173 } 01174 if ((rc = 01175 readLine(spec, STRIP_TRAILINGSPACE | STRIP_COMMENTS)) > 0) { 01176 nextPart = PART_NONE; 01177 break; 01178 } 01179 if (rc) 01180 return rc; 01181 } 01182 } 01183 01184 /* Do some final processing on the header */ 01185 01186 /* 01187 * Expand buildroot one more time to get %{version} and the like 01188 * from the main package. 01189 */ 01190 if (initialPackage) { 01191 const char *s = rpmExpand("%{?buildroot}", NULL); 01192 if (s && *s) 01193 (void) addMacro(NULL, "buildroot", NULL, s, -1); 01194 s = _free(s); 01195 } 01196 01197 /* XXX Skip valid arch check if not building binary package */ 01198 if (!spec->anyarch && checkForValidArchitectures(spec)) 01199 return RPMRC_FAIL; 01200 01201 if (pkg == spec->packages) 01202 fillOutMainPackage(pkg->header); 01203 01204 if (checkForDuplicates(pkg->header, NVR) != RPMRC_OK) 01205 return RPMRC_FAIL; 01206 01207 if (pkg != spec->packages) 01208 headerCopyTags(spec->packages->header, pkg->header, 01209 (rpmuint32_t *)copyTagsDuringParse); 01210 01211 #ifdef RPM_VENDOR_PLD /* rpm-epoch0 */ 01212 /* Add Epoch: 0 to package header if it was not set by spec */ 01213 he->tag = RPMTAG_NAME; 01214 if (headerGet(spec->packages->header, he, 0) == 0) { 01215 rpmuint32_t num = 0; 01216 01217 he->tag = RPMTAG_EPOCH; 01218 he->t = RPM_UINT32_TYPE; 01219 he->p.ui32p = # 01220 he->c = 1; 01221 xx = headerPut(pkg->header, he, 0); 01222 01223 /* also declare %{epoch} to be same */ 01224 addMacro(spec->macros, "epoch", NULL, "0", RMIL_SPEC); 01225 } 01226 #endif /* RPM_VENDOR_PLD rpm-epoch0 */ 01227 01228 if (checkForRequired(pkg->header, NVR) != RPMRC_OK) 01229 return RPMRC_FAIL; 01230 01231 return nextPart; 01232 }