static int parseNoSource(rpmSpec spec, const char * field, rpmTagVal tag) { const char *f, *fe; const char *name; int flag; uint32_t num; if (tag == RPMTAG_NOSOURCE) { flag = RPMBUILD_ISSOURCE; name = "source"; } else { flag = RPMBUILD_ISPATCH; name = "patch"; } fe = field; for (f = fe; *f != '\0'; f = fe) { struct Source *p; SKIPWHITE(f); if (*f == '\0') break; fe = f; SKIPNONWHITE(fe); if (*fe != '\0') fe++; if (parseUnsignedNum(f, &num)) { rpmlog(RPMLOG_ERR, _("line %d: Bad number: %s\n"), spec->lineNum, f); return RPMRC_FAIL; } if (! (p = findSource(spec, num, flag))) { rpmlog(RPMLOG_ERR, _("line %d: Bad no%s number: %u\n"), spec->lineNum, name, num); return RPMRC_FAIL; } p->flags |= RPMBUILD_ISNO; } return 0; }
/** * Parse %patch line. * This supports too many crazy syntaxes: * - %patchN is equal to %patch -P<N> * - -P<N> -P<N+1>... can be used to apply several patch on a single line * - Any trailing arguments are treated as patch numbers * - Any combination of the above, except unless at least one -P is specified, * %patch is treated as %patch -P0 so that "%patch 1" is actually * equal to "%patch -P0 -P1". * * @param spec build info * @param line current line from spec file * @return RPMRC_OK on success */ static rpmRC doPatchMacro(rpmSpec spec, const char *line) { char *opt_b, *opt_P, *opt_d; char *buf = NULL; int opt_p, opt_R, opt_E, opt_F; int argc, c; const char **argv = NULL; ARGV_t patch, patchnums = NULL; rpmRC rc = RPMRC_FAIL; /* assume failure */ struct poptOption const patchOpts[] = { { NULL, 'P', POPT_ARG_STRING, &opt_P, 'P', NULL, NULL }, { NULL, 'p', POPT_ARG_INT, &opt_p, 'p', NULL, NULL }, { NULL, 'R', POPT_ARG_NONE, &opt_R, 'R', NULL, NULL }, { NULL, 'E', POPT_ARG_NONE, &opt_E, 'E', NULL, NULL }, { NULL, 'b', POPT_ARG_STRING, &opt_b, 'b', NULL, NULL }, { NULL, 'z', POPT_ARG_STRING, &opt_b, 'z', NULL, NULL }, { NULL, 'F', POPT_ARG_INT, &opt_F, 'F', NULL, NULL }, { NULL, 'd', POPT_ARG_STRING, &opt_d, 'd', NULL, NULL }, { NULL, 0, 0, NULL, 0, NULL, NULL } }; poptContext optCon = NULL; opt_p = opt_R = opt_E = 0; opt_F = rpmExpandNumeric("%{_default_patch_fuzz}"); /* get default fuzz factor for %patch */ opt_b = opt_d = NULL; /* Convert %patchN to %patch -PN to simplify further processing */ if (! strchr(" \t\n", line[6])) { rasprintf(&buf, "%%patch -P %s", line + 6); } else { if (strstr(line+6, " -P") == NULL) rasprintf(&buf, "%%patch -P %d %s", INT_MAX, line + 6); /* INT_MAX denotes not numbered %patch */ else buf = xstrdup(line); /* it is not numberless patch because -P is present */ } poptParseArgvString(buf, &argc, &argv); free(buf); /* * Grab all -P<N> numbers for later processing. Stored as strings * at this point so we only have to worry about conversion in one place. */ optCon = poptGetContext(NULL, argc, argv, patchOpts, 0); while ((c = poptGetNextOpt(optCon)) > 0) { switch (c) { case 'P': { char *arg = poptGetOptArg(optCon); if (arg) { argvAdd(&patchnums, arg); free(arg); } break; } default: break; } } if (c < -1) { rpmlog(RPMLOG_ERR, _("%s: %s: %s\n"), poptStrerror(c), poptBadOption(optCon, POPT_BADOPTION_NOALIAS), line); goto exit; } /* Any trailing arguments are treated as patch numbers */ argvAppend(&patchnums, (ARGV_const_t) poptGetArgs(optCon)); /* Convert to number, generate patch command and append to %prep script */ for (patch = patchnums; *patch; patch++) { uint32_t pnum; char *s; if (parseUnsignedNum(*patch, &pnum)) { rpmlog(RPMLOG_ERR, _("Invalid patch number %s: %s\n"), *patch, line); goto exit; } s = doPatch(spec, pnum, opt_p, opt_b, opt_R, opt_E, opt_F, opt_d); if (s == NULL) { goto exit; } appendLineStringBuf(spec->prep, s); free(s); } rc = RPMRC_OK; exit: argvFree(patchnums); free(argv); poptFreeContext(optCon); return rc; }
/** * Parse %setup macro. * @todo FIXME: Option -q broken when not immediately after %setup. * @param spec build info * @param line current line from spec file * @return RPMRC_OK on success */ static int doSetupMacro(rpmSpec spec, const char *line) { char *buf = NULL; StringBuf before = newStringBuf(); StringBuf after = newStringBuf(); poptContext optCon = NULL; int argc; const char ** argv = NULL; int arg; const char * optArg; int xx; rpmRC rc = RPMRC_FAIL; uint32_t num; int leaveDirs = 0, skipDefaultAction = 0; int createDir = 0, quietly = 0; const char * dirName = NULL; struct poptOption optionsTable[] = { { NULL, 'a', POPT_ARG_STRING, NULL, 'a', NULL, NULL}, { NULL, 'b', POPT_ARG_STRING, NULL, 'b', NULL, NULL}, { NULL, 'c', 0, &createDir, 0, NULL, NULL}, { NULL, 'D', 0, &leaveDirs, 0, NULL, NULL}, { NULL, 'n', POPT_ARG_STRING, &dirName, 0, NULL, NULL}, { NULL, 'T', 0, &skipDefaultAction, 0, NULL, NULL}, { NULL, 'q', 0, &quietly, 0, NULL, NULL}, { 0, 0, 0, 0, 0, NULL, NULL} }; if ((xx = poptParseArgvString(line, &argc, &argv))) { rpmlog(RPMLOG_ERR, _("Error parsing %%setup: %s\n"), poptStrerror(xx)); goto exit; } optCon = poptGetContext(NULL, argc, argv, optionsTable, 0); while ((arg = poptGetNextOpt(optCon)) > 0) { optArg = poptGetOptArg(optCon); /* We only parse -a and -b here */ if (parseUnsignedNum(optArg, &num)) { rpmlog(RPMLOG_ERR, _("line %d: Bad arg to %%setup: %s\n"), spec->lineNum, (optArg ? optArg : "???")); goto exit; } { char *chptr = doUntar(spec, num, quietly); if (chptr == NULL) goto exit; appendLineStringBuf((arg == 'a' ? after : before), chptr); free(chptr); } } if (arg < -1) { rpmlog(RPMLOG_ERR, _("line %d: Bad %%setup option %s: %s\n"), spec->lineNum, poptBadOption(optCon, POPT_BADOPTION_NOALIAS), poptStrerror(arg)); goto exit; } if (dirName) { spec->buildSubdir = xstrdup(dirName); } else { rasprintf(&spec->buildSubdir, "%s-%s", headerGetString(spec->packages->header, RPMTAG_NAME), headerGetString(spec->packages->header, RPMTAG_VERSION)); } addMacro(spec->macros, "buildsubdir", NULL, spec->buildSubdir, RMIL_SPEC); /* cd to the build dir */ { char * buildDir = rpmGenPath(spec->rootDir, "%{_builddir}", ""); rasprintf(&buf, "cd '%s'", buildDir); appendLineStringBuf(spec->prep, buf); free(buf); free(buildDir); } /* delete any old sources */ if (!leaveDirs) { rasprintf(&buf, "rm -rf '%s'", spec->buildSubdir); appendLineStringBuf(spec->prep, buf); free(buf); } /* if necessary, create and cd into the proper dir */ if (createDir) { rasprintf(&buf, RPM_MKDIR_P " %s\ncd '%s'", spec->buildSubdir, spec->buildSubdir); appendLineStringBuf(spec->prep, buf); free(buf); } /* do the default action */ if (!createDir && !skipDefaultAction) { char *chptr = doUntar(spec, 0, quietly); if (!chptr) goto exit; appendLineStringBuf(spec->prep, chptr); free(chptr); } appendStringBuf(spec->prep, getStringBuf(before)); if (!createDir) { rasprintf(&buf, "cd '%s'", spec->buildSubdir); appendLineStringBuf(spec->prep, buf); free(buf); } if (createDir && !skipDefaultAction) { char *chptr = doUntar(spec, 0, quietly); if (chptr == NULL) goto exit; appendLineStringBuf(spec->prep, chptr); free(chptr); } appendStringBuf(spec->prep, getStringBuf(after)); /* Fix the permissions of the setup build tree */ { char *fix = rpmExpand("%{_fixperms} .", NULL); if (fix && *fix != '%') { appendLineStringBuf(spec->prep, fix); } free(fix); } rc = RPMRC_OK; exit: freeStringBuf(before); freeStringBuf(after); poptFreeContext(optCon); free(argv); return rc; }
static rpmRC handlePreambleTag(rpmSpec spec, Package pkg, rpmTagVal tag, const char *macro, const char *lang) { char * field = spec->line; char * end; int multiToken = 0; rpmsenseFlags tagflags = RPMSENSE_ANY; rpmRC rc = RPMRC_FAIL; if (field == NULL) /* XXX can't happen */ goto exit; /* Find the start of the "field" and strip trailing space */ while ((*field) && (*field != ':')) field++; if (*field != ':') { rpmlog(RPMLOG_ERR, _("line %d: Malformed tag: %s\n"), spec->lineNum, spec->line); goto exit; } field++; SKIPSPACE(field); if (!*field) { /* Empty field */ rpmlog(RPMLOG_ERR, _("line %d: Empty tag: %s\n"), spec->lineNum, spec->line); goto exit; } end = findLastChar(field); *(end+1) = '\0'; /* See if this is multi-token */ end = field; SKIPNONSPACE(end); if (*end != '\0') multiToken = 1; switch (tag) { case RPMTAG_NAME: SINGLE_TOKEN_ONLY; if (rpmCharCheck(spec, field, WHITELIST_NAME)) goto exit; headerPutString(pkg->header, tag, field); /* Main pkg name is unknown at the start, populate as soon as we can */ if (pkg == spec->packages) pkg->name = rpmstrPoolId(spec->pool, field, 1); break; case RPMTAG_VERSION: case RPMTAG_RELEASE: SINGLE_TOKEN_ONLY; if (rpmCharCheck(spec, field, "._+%{}~")) goto exit; headerPutString(pkg->header, tag, field); break; case RPMTAG_URL: case RPMTAG_DISTTAG: case RPMTAG_BUGURL: /* XXX TODO: validate format somehow */ case RPMTAG_VCS: SINGLE_TOKEN_ONLY; headerPutString(pkg->header, tag, field); break; case RPMTAG_GROUP: case RPMTAG_SUMMARY: case RPMTAG_DISTRIBUTION: case RPMTAG_VENDOR: case RPMTAG_LICENSE: case RPMTAG_PACKAGER: if (addLangTag(spec, pkg->header, tag, field, lang)) goto exit; break; case RPMTAG_BUILDROOT: /* just silently ignore BuildRoot */ macro = NULL; break; case RPMTAG_PREFIXES: { struct rpmtd_s td; const char *str; if (addOrAppendListEntry(pkg->header, tag, field)) goto exit; headerGet(pkg->header, tag, &td, HEADERGET_MINMEM); while ((str = rpmtdNextString(&td))) { size_t len = strlen(str); if (len > 1 && str[len-1] == '/') { rpmlog(RPMLOG_ERR, _("line %d: Prefixes must not end with \"/\": %s\n"), spec->lineNum, spec->line); rpmtdFreeData(&td); goto exit; } } rpmtdFreeData(&td); break; } case RPMTAG_DOCDIR: SINGLE_TOKEN_ONLY; if (field[0] != '/') { rpmlog(RPMLOG_ERR, _("line %d: Docdir must begin with '/': %s\n"), spec->lineNum, spec->line); goto exit; } macro = NULL; rpmPopMacro(NULL, "_docdir"); rpmPushMacro(NULL, "_docdir", NULL, field, RMIL_SPEC); break; case RPMTAG_EPOCH: { SINGLE_TOKEN_ONLY; uint32_t epoch; if (parseUnsignedNum(field, &epoch)) { rpmlog(RPMLOG_ERR, _("line %d: Epoch field must be an unsigned number: %s\n"), spec->lineNum, spec->line); goto exit; } headerPutUint32(pkg->header, tag, &epoch, 1); break; } case RPMTAG_AUTOREQPROV: pkg->autoReq = parseYesNo(field); pkg->autoProv = pkg->autoReq; break; case RPMTAG_AUTOREQ: pkg->autoReq = parseYesNo(field); break; case RPMTAG_AUTOPROV: pkg->autoProv = parseYesNo(field); break; case RPMTAG_SOURCE: case RPMTAG_PATCH: macro = NULL; if (addSource(spec, pkg, field, tag)) goto exit; break; case RPMTAG_ICON: SINGLE_TOKEN_ONLY; if (addSource(spec, pkg, field, tag) || readIcon(pkg->header, field)) goto exit; break; case RPMTAG_NOSOURCE: case RPMTAG_NOPATCH: spec->noSource = 1; if (parseNoSource(spec, field, tag)) goto exit; break; case RPMTAG_ORDERFLAGS: case RPMTAG_REQUIREFLAGS: if (parseBits(lang, installScriptBits, &tagflags)) { rpmlog(RPMLOG_ERR, _("line %d: Bad %s: qualifiers: %s\n"), spec->lineNum, rpmTagGetName(tag), spec->line); goto exit; } /* fallthrough */ case RPMTAG_PREREQ: case RPMTAG_RECOMMENDFLAGS: case RPMTAG_SUGGESTFLAGS: case RPMTAG_SUPPLEMENTFLAGS: case RPMTAG_ENHANCEFLAGS: case RPMTAG_CONFLICTFLAGS: case RPMTAG_OBSOLETEFLAGS: case RPMTAG_PROVIDEFLAGS: if (parseRCPOT(spec, pkg, field, tag, 0, tagflags)) goto exit; break; case RPMTAG_BUILDPREREQ: case RPMTAG_BUILDREQUIRES: case RPMTAG_BUILDCONFLICTS: if (parseRCPOT(spec, spec->sourcePackage, field, tag, 0, tagflags)) goto exit; break; case RPMTAG_EXCLUDEARCH: case RPMTAG_EXCLUSIVEARCH: case RPMTAG_EXCLUDEOS: case RPMTAG_EXCLUSIVEOS: if (addOrAppendListEntry(spec->buildRestrictions, tag, field)) goto exit; break; case RPMTAG_BUILDARCHS: { int BACount; const char **BANames = NULL; if (poptParseArgvString(field, &BACount, &BANames)) { rpmlog(RPMLOG_ERR, _("line %d: Bad BuildArchitecture format: %s\n"), spec->lineNum, spec->line); goto exit; } if (spec->packages == pkg) { if (spec->BANames) { rpmlog(RPMLOG_ERR, _("line %d: Duplicate BuildArch entry: %s\n"), spec->lineNum, spec->line); BANames = _free(BANames); goto exit; } spec->BACount = BACount; spec->BANames = BANames; } else { if (BACount != 1 || !rstreq(BANames[0], "noarch")) { rpmlog(RPMLOG_ERR, _("line %d: Only noarch subpackages are supported: %s\n"), spec->lineNum, spec->line); BANames = _free(BANames); goto exit; } headerPutString(pkg->header, RPMTAG_ARCH, "noarch"); } if (!BACount) spec->BANames = _free(spec->BANames); break; } case RPMTAG_REMOVEPATHPOSTFIXES: argvSplit(&pkg->removePostfixes, field, ":"); break; default: rpmlog(RPMLOG_ERR, _("Internal error: Bogus tag %d\n"), tag); goto exit; } if (macro) { rpmPushMacro(spec->macros, macro, NULL, field, RMIL_SPEC); /* Add a separate uppercase macro for tags from the main package */ if (pkg == spec->packages) { char *m = xstrdup(macro); for (char *p = m; *p; ++p) *p = rtoupper(*p); rpmPushMacro(spec->macros, m, NULL, field, RMIL_SPEC); free(m); } } rc = RPMRC_OK; exit: return rc; }
static int addSource(rpmSpec spec, Package pkg, const char *field, rpmTagVal tag) { struct Source *p; int flag = 0; const char *name = NULL; char *nump; char *fieldp = NULL; char *buf = NULL; uint32_t num = 0; switch (tag) { case RPMTAG_SOURCE: flag = RPMBUILD_ISSOURCE; name = "source"; fieldp = spec->line + 6; break; case RPMTAG_PATCH: flag = RPMBUILD_ISPATCH; name = "patch"; fieldp = spec->line + 5; break; case RPMTAG_ICON: flag = RPMBUILD_ISICON; fieldp = NULL; break; default: return -1; break; } /* Get the number */ if (tag != RPMTAG_ICON) { /* We already know that a ':' exists, and that there */ /* are no spaces before it. */ /* This also now allows for spaces and tabs between */ /* the number and the ':' */ char ch; char *fieldp_backup = fieldp; while ((*fieldp != ':') && (*fieldp != ' ') && (*fieldp != '\t')) { fieldp++; } ch = *fieldp; *fieldp = '\0'; nump = fieldp_backup; SKIPSPACE(nump); if (nump == NULL || *nump == '\0') { num = flag == RPMBUILD_ISSOURCE ? 0 : INT_MAX; } else { if (parseUnsignedNum(fieldp_backup, &num)) { rpmlog(RPMLOG_ERR, _("line %d: Bad %s number: %s\n"), spec->lineNum, name, spec->line); *fieldp = ch; return RPMRC_FAIL; } } *fieldp = ch; } /* Check whether tags of the same number haven't already been defined */ for (p = spec->sources; p != NULL; p = p->next) { if ( p->num != num ) continue; if ((tag == RPMTAG_SOURCE && p->flags == RPMBUILD_ISSOURCE) || (tag == RPMTAG_PATCH && p->flags == RPMBUILD_ISPATCH)) { rpmlog(RPMLOG_ERR, _("%s %d defined multiple times\n"), name, num); return RPMRC_FAIL; } } /* Create the entry and link it in */ p = xmalloc(sizeof(*p)); p->num = num; p->fullSource = xstrdup(field); p->flags = flag; p->source = strrchr(p->fullSource, '/'); if (p->source) { if ((buf = strrchr(p->source,'='))) p->source = buf; p->source++; } else { p->source = p->fullSource; } if (tag != RPMTAG_ICON) { p->next = spec->sources; spec->sources = p; } else { p->next = pkg->icon; pkg->icon = p; } spec->numSources++; if (tag != RPMTAG_ICON) { char *body = rpmGetPath("%{_sourcedir}/", p->source, NULL); struct stat st; int nofetch = (spec->flags & RPMSPEC_FORCE) || rpmExpandNumeric("%{_disable_source_fetch}"); /* try to download source/patch if it's missing */ if (lstat(body, &st) != 0 && errno == ENOENT && !nofetch) { char *url = NULL; if (urlIsURL(p->fullSource) != URL_IS_UNKNOWN) { url = rstrdup(p->fullSource); } else { url = rpmExpand("%{_default_source_url}", NULL); rstrcat(&url, p->source); if (*url == '%') url = _free(url); } if (url) { rpmlog(RPMLOG_WARNING, _("Downloading %s to %s\n"), url, body); if (urlGetFile(url, body) != 0) { free(url); rpmlog(RPMLOG_ERR, _("Couldn't download %s\n"), p->fullSource); return RPMRC_FAIL; } free(url); } } rasprintf(&buf, "%s%d", (flag & RPMBUILD_ISPATCH) ? "PATCH" : "SOURCE", num); rpmPushMacro(spec->macros, buf, NULL, body, RMIL_SPEC); free(buf); rasprintf(&buf, "%sURL%d", (flag & RPMBUILD_ISPATCH) ? "PATCH" : "SOURCE", num); rpmPushMacro(spec->macros, buf, NULL, p->fullSource, RMIL_SPEC); free(buf); #ifdef WITH_LUA { rpmlua lua = NULL; /* global state */ const char * what = (flag & RPMBUILD_ISPATCH) ? "patches" : "sources"; rpmluaPushTable(lua, what); rpmluav var = rpmluavNew(); rpmluavSetListMode(var, 1); rpmluavSetValue(var, RPMLUAV_STRING, body); rpmluaSetVar(lua, var); rpmluavFree(var); rpmluaPop(lua); } #endif free(body); } return 0; }