/** \ingroup rpmbuild * \file build/parseScript.c * Parse install-time script section from spec file. */ #include "system.h" #include "rpmbuild.h" #include "debug.h" /*@access StringBuf@*/ /* XXX compared with NULL */ /*@access poptContext @*/ /* compared with NULL */ /** */ static int addTriggerIndex(Package pkg, const char *file, const char *script, const char *prog) /*@modifies pkg->triggerFiles @*/ { struct TriggerFileEntry *tfe; struct TriggerFileEntry *list = pkg->triggerFiles; struct TriggerFileEntry *last = NULL; int index = 0; while (list) { last = list; list = list->next; } if (last) index = last->index + 1; tfe = xcalloc(1, sizeof(*tfe)); tfe->fileName = (file) ? xstrdup(file) : NULL; tfe->script = (script && *script != '\0') ? xstrdup(script) : NULL; tfe->prog = xstrdup(prog); tfe->index = index; tfe->next = NULL; if (last) last->next = tfe; else pkg->triggerFiles = tfe; return index; } /* these have to be global because of stupid compilers */ /*@unchecked@*/ /*@observer@*/ /*@null@*/ static const char *name = NULL; /*@unchecked@*/ /*@observer@*/ /*@null@*/ static const char *prog = NULL; /*@unchecked@*/ /*@observer@*/ /*@null@*/ static const char *file = NULL; /*@unchecked@*/ static struct poptOption optionsTable[] = { { NULL, 'p', POPT_ARG_STRING, &prog, 'p', NULL, NULL}, { NULL, 'n', POPT_ARG_STRING, &name, 'n', NULL, NULL}, { NULL, 'f', POPT_ARG_STRING, &file, 'f', NULL, NULL}, { 0, 0, 0, 0, 0, NULL, NULL} }; /* %trigger is a strange combination of %pre and Requires: behavior */ /* We can handle it by parsing the args before "--" in parseScript. */ /* We then pass the remaining arguments to parseRCPOT, along with */ /* an index we just determined. */ /*@-boundswrite@*/ int parseScript(Spec spec, int parsePart) { /* There are a few options to scripts: */ /* */ /* -n */ /* -p */ /* -p " ..." */ /* -f */ char *p; const char **progArgv = NULL; int progArgc; char *partname = NULL; int reqtag = 0; int tag = 0; int tagflags = 0; int progtag = 0; int flag = PART_SUBNAME; Package pkg; StringBuf sb = NULL; int nextPart; int index; char reqargs[BUFSIZ]; int rc, argc; int arg; const char **argv = NULL; poptContext optCon = NULL; reqargs[0] = '\0'; /*@-mods@*/ name = NULL; prog = "/bin/sh"; file = NULL; /*@=mods@*/ /*@-branchstate@*/ switch (parsePart) { case PART_PRE: tag = RPMTAG_PREIN; tagflags = RPMSENSE_SCRIPT_PRE; progtag = RPMTAG_PREINPROG; partname = "%pre"; break; case PART_POST: tag = RPMTAG_POSTIN; tagflags = RPMSENSE_SCRIPT_POST; progtag = RPMTAG_POSTINPROG; partname = "%post"; break; case PART_PREUN: tag = RPMTAG_PREUN; tagflags = RPMSENSE_SCRIPT_PREUN; progtag = RPMTAG_PREUNPROG; partname = "%preun"; break; case PART_POSTUN: tag = RPMTAG_POSTUN; tagflags = RPMSENSE_SCRIPT_POSTUN; progtag = RPMTAG_POSTUNPROG; partname = "%postun"; break; case PART_VERIFYSCRIPT: tag = RPMTAG_VERIFYSCRIPT; tagflags = RPMSENSE_SCRIPT_VERIFY; progtag = RPMTAG_VERIFYSCRIPTPROG; partname = "%verifyscript"; break; case PART_TRIGGERIN: tag = RPMTAG_TRIGGERSCRIPTS; tagflags = 0; reqtag = RPMTAG_TRIGGERIN; progtag = RPMTAG_TRIGGERSCRIPTPROG; partname = "%triggerin"; break; case PART_TRIGGERUN: tag = RPMTAG_TRIGGERSCRIPTS; tagflags = 0; reqtag = RPMTAG_TRIGGERUN; progtag = RPMTAG_TRIGGERSCRIPTPROG; partname = "%triggerun"; break; case PART_TRIGGERPOSTUN: tag = RPMTAG_TRIGGERSCRIPTS; tagflags = 0; reqtag = RPMTAG_TRIGGERPOSTUN; progtag = RPMTAG_TRIGGERSCRIPTPROG; partname = "%triggerpostun"; break; } /*@=branchstate@*/ if (tag == RPMTAG_TRIGGERSCRIPTS) { /* break line into two */ p = strstr(spec->line, "--"); if (!p) { rpmError(RPMERR_BADSPEC, _("line %d: triggers must have --: %s\n"), spec->lineNum, spec->line); return RPMERR_BADSPEC; } *p = '\0'; strcpy(reqargs, p + 2); } if ((rc = poptParseArgvString(spec->line, &argc, &argv))) { rpmError(RPMERR_BADSPEC, _("line %d: Error parsing %s: %s\n"), spec->lineNum, partname, poptStrerror(rc)); return RPMERR_BADSPEC; } optCon = poptGetContext(NULL, argc, argv, optionsTable, 0); while ((arg = poptGetNextOpt(optCon)) > 0) { switch (arg) { case 'p': if (prog[0] != '/') { rpmError(RPMERR_BADSPEC, _("line %d: script program must begin " "with \'/\': %s\n"), spec->lineNum, prog); rc = RPMERR_BADSPEC; goto exit; } /*@switchbreak@*/ break; case 'n': flag = PART_NAME; /*@switchbreak@*/ break; } } if (arg < -1) { rpmError(RPMERR_BADSPEC, _("line %d: Bad option %s: %s\n"), spec->lineNum, poptBadOption(optCon, POPT_BADOPTION_NOALIAS), spec->line); rc = RPMERR_BADSPEC; goto exit; } if (poptPeekArg(optCon)) { /*@-mods@*/ if (name == NULL) name = poptGetArg(optCon); /*@=mods@*/ if (poptPeekArg(optCon)) { rpmError(RPMERR_BADSPEC, _("line %d: Too many names: %s\n"), spec->lineNum, spec->line); rc = RPMERR_BADSPEC; goto exit; } } if (lookupPackage(spec, name, flag, &pkg)) { rpmError(RPMERR_BADSPEC, _("line %d: Package does not exist: %s\n"), spec->lineNum, spec->line); rc = RPMERR_BADSPEC; goto exit; } if (tag != RPMTAG_TRIGGERSCRIPTS) { if (headerIsEntry(pkg->header, progtag)) { rpmError(RPMERR_BADSPEC, _("line %d: Second %s\n"), spec->lineNum, partname); rc = RPMERR_BADSPEC; goto exit; } } if ((rc = poptParseArgvString(prog, &progArgc, &progArgv))) { rpmError(RPMERR_BADSPEC, _("line %d: Error parsing %s: %s\n"), spec->lineNum, partname, poptStrerror(rc)); rc = RPMERR_BADSPEC; goto exit; } sb = newStringBuf(); if ((rc = readLine(spec, STRIP_NOTHING)) > 0) { nextPart = PART_NONE; } else { if (rc) goto exit; while (! (nextPart = isPart(spec->line))) { appendStringBuf(sb, spec->line); if ((rc = readLine(spec, STRIP_NOTHING)) > 0) { nextPart = PART_NONE; break; } if (rc) goto exit; } } stripTrailingBlanksStringBuf(sb); p = getStringBuf(sb); (void) addReqProv(spec, pkg->header, (tagflags | RPMSENSE_INTERP), progArgv[0], NULL, 0); /* Trigger script insertion is always delayed in order to */ /* get the index right. */ if (tag == RPMTAG_TRIGGERSCRIPTS) { /* Add file/index/prog triple to the trigger file list */ index = addTriggerIndex(pkg, file, p, progArgv[0]); /* Generate the trigger tags */ if ((rc = parseRCPOT(spec, pkg, reqargs, reqtag, index, tagflags))) goto exit; } else { if (progArgc == 1) (void) headerAddEntry(pkg->header, progtag, RPM_STRING_TYPE, *progArgv, progArgc); else { (void) rpmlibNeedsFeature(pkg->header, "ScriptletInterpreterArgs", "4.0.3-1"); (void) headerAddEntry(pkg->header, progtag, RPM_STRING_ARRAY_TYPE, progArgv, progArgc); } if (*p != '\0') (void) headerAddEntry(pkg->header, tag, RPM_STRING_TYPE, p, 1); if (file) { switch (parsePart) { case PART_PRE: pkg->preInFile = xstrdup(file); break; case PART_POST: pkg->postInFile = xstrdup(file); break; case PART_PREUN: pkg->preUnFile = xstrdup(file); break; case PART_POSTUN: pkg->postUnFile = xstrdup(file); break; case PART_VERIFYSCRIPT: pkg->verifyFile = xstrdup(file); break; } } } rc = nextPart; exit: sb = freeStringBuf(sb); progArgv = _free(progArgv); argv = _free(argv); optCon = poptFreeContext(optCon); return rc; } /*@=boundswrite@*/