Yet more rpmbuild fixups from rpmerr.h removal (partly from rpm5.org)
[platform/upstream/rpm.git] / build / parseScript.c
1 /** \ingroup rpmbuild
2  * \file build/parseScript.c
3  *  Parse install-time script section from spec file.
4  */
5
6 #include "system.h"
7
8 #include <rpmbuild.h>
9 #include <rpmlog.h>
10 #include "debug.h"
11
12 #include "rpmio/rpmlua.h"
13
14 /**
15  */
16 static int addTriggerIndex(Package pkg, const char *file,
17         const char *script, const char *prog)
18 {
19     struct TriggerFileEntry *tfe;
20     struct TriggerFileEntry *list = pkg->triggerFiles;
21     struct TriggerFileEntry *last = NULL;
22     int index = 0;
23
24     while (list) {
25         last = list;
26         list = list->next;
27     }
28
29     if (last)
30         index = last->index + 1;
31
32     tfe = xcalloc(1, sizeof(*tfe));
33
34     tfe->fileName = (file) ? xstrdup(file) : NULL;
35     tfe->script = (script && *script != '\0') ? xstrdup(script) : NULL;
36     tfe->prog = xstrdup(prog);
37     tfe->index = index;
38     tfe->next = NULL;
39
40     if (last)
41         last->next = tfe;
42     else
43         pkg->triggerFiles = tfe;
44
45     return index;
46 }
47
48 /* these have to be global because of stupid compilers */
49     static const char *name = NULL;
50     static const char *prog = NULL;
51     static const char *file = NULL;
52     static struct poptOption optionsTable[] = {
53         { NULL, 'p', POPT_ARG_STRING, &prog, 'p',       NULL, NULL},
54         { NULL, 'n', POPT_ARG_STRING, &name, 'n',       NULL, NULL},
55         { NULL, 'f', POPT_ARG_STRING, &file, 'f',       NULL, NULL},
56         { 0, 0, 0, 0, 0,        NULL, NULL}
57     };
58
59 /* %trigger is a strange combination of %pre and Requires: behavior */
60 /* We can handle it by parsing the args before "--" in parseScript. */
61 /* We then pass the remaining arguments to parseRCPOT, along with   */
62 /* an index we just determined.                                     */
63
64 int parseScript(rpmSpec spec, int parsePart)
65 {
66     /* There are a few options to scripts: */
67     /*  <pkg>                              */
68     /*  -n <pkg>                           */
69     /*  -p <sh>                            */
70     /*  -p "<sh> <args>..."                */
71     /*  -f <file>                          */
72
73     char *p;
74     const char **progArgv = NULL;
75     int progArgc;
76     char *partname = NULL;
77     rpmTag reqtag = 0;
78     rpmTag tag = 0;
79     int tagflags = 0;
80     rpmTag progtag = 0;
81     int flag = PART_SUBNAME;
82     Package pkg;
83     StringBuf sb = NULL;
84     int nextPart;
85     int index;
86     char reqargs[BUFSIZ];
87
88     int rc, argc;
89     int arg;
90     const char **argv = NULL;
91     poptContext optCon = NULL;
92     
93     reqargs[0] = '\0';
94     name = NULL;
95     prog = "/bin/sh";
96     file = NULL;
97     
98     switch (parsePart) {
99       case PART_PRE:
100         tag = RPMTAG_PREIN;
101         tagflags = RPMSENSE_SCRIPT_PRE;
102         progtag = RPMTAG_PREINPROG;
103         partname = "%pre";
104         break;
105       case PART_POST:
106         tag = RPMTAG_POSTIN;
107         tagflags = RPMSENSE_SCRIPT_POST;
108         progtag = RPMTAG_POSTINPROG;
109         partname = "%post";
110         break;
111       case PART_PREUN:
112         tag = RPMTAG_PREUN;
113         tagflags = RPMSENSE_SCRIPT_PREUN;
114         progtag = RPMTAG_PREUNPROG;
115         partname = "%preun";
116         break;
117       case PART_POSTUN:
118         tag = RPMTAG_POSTUN;
119         tagflags = RPMSENSE_SCRIPT_POSTUN;
120         progtag = RPMTAG_POSTUNPROG;
121         partname = "%postun";
122         break;
123       case PART_PRETRANS:
124         tag = RPMTAG_PRETRANS;
125         tagflags = 0;
126         progtag = RPMTAG_PRETRANSPROG;
127         partname = "%pretrans";
128         break;
129       case PART_POSTTRANS:
130         tag = RPMTAG_POSTTRANS;
131         tagflags = 0;
132         progtag = RPMTAG_POSTTRANSPROG;
133         partname = "%posttrans";
134         break;
135       case PART_VERIFYSCRIPT:
136         tag = RPMTAG_VERIFYSCRIPT;
137         tagflags = RPMSENSE_SCRIPT_VERIFY;
138         progtag = RPMTAG_VERIFYSCRIPTPROG;
139         partname = "%verifyscript";
140         break;
141       case PART_TRIGGERIN:
142         tag = RPMTAG_TRIGGERSCRIPTS;
143         tagflags = 0;
144         reqtag = RPMTAG_TRIGGERIN;
145         progtag = RPMTAG_TRIGGERSCRIPTPROG;
146         partname = "%triggerin";
147         break;
148       case PART_TRIGGERUN:
149         tag = RPMTAG_TRIGGERSCRIPTS;
150         tagflags = 0;
151         reqtag = RPMTAG_TRIGGERUN;
152         progtag = RPMTAG_TRIGGERSCRIPTPROG;
153         partname = "%triggerun";
154         break;
155       case PART_TRIGGERPOSTUN:
156         tag = RPMTAG_TRIGGERSCRIPTS;
157         tagflags = 0;
158         reqtag = RPMTAG_TRIGGERPOSTUN;
159         progtag = RPMTAG_TRIGGERSCRIPTPROG;
160         partname = "%triggerpostun";
161         break;
162     }
163
164     if (tag == RPMTAG_TRIGGERSCRIPTS) {
165         /* break line into two */
166         p = strstr(spec->line, "--");
167         if (!p) {
168             rpmlog(RPMLOG_ERR, _("line %d: triggers must have --: %s\n"),
169                      spec->lineNum, spec->line);
170             return RPMRC_FAIL;
171         }
172
173         *p = '\0';
174         strcpy(reqargs, p + 2);
175     }
176     
177     if ((rc = poptParseArgvString(spec->line, &argc, &argv))) {
178         rpmlog(RPMLOG_ERR, _("line %d: Error parsing %s: %s\n"),
179                  spec->lineNum, partname, poptStrerror(rc));
180         return RPMRC_FAIL;
181     }
182     
183     optCon = poptGetContext(NULL, argc, argv, optionsTable, 0);
184     while ((arg = poptGetNextOpt(optCon)) > 0) {
185         switch (arg) {
186         case 'p':
187             if (prog[0] == '<') {
188                 if (prog[strlen(prog)-1] != '>') {
189                     rpmlog(RPMLOG_ERR,
190                              _("line %d: internal script must end "
191                              "with \'>\': %s\n"), spec->lineNum, prog);
192                     rc = RPMRC_FAIL;
193                     goto exit;
194                 }
195             } else if (prog[0] != '/') {
196                 rpmlog(RPMLOG_ERR,
197                          _("line %d: script program must begin "
198                          "with \'/\': %s\n"), spec->lineNum, prog);
199                 rc = RPMRC_FAIL;
200                 goto exit;
201             }
202             break;
203         case 'n':
204             flag = PART_NAME;
205             break;
206         }
207     }
208     
209     if (arg < -1) {
210         rpmlog(RPMLOG_ERR, _("line %d: Bad option %s: %s\n"),
211                  spec->lineNum,
212                  poptBadOption(optCon, POPT_BADOPTION_NOALIAS), 
213                  spec->line);
214         rc = RPMRC_FAIL;
215         goto exit;
216     }
217
218     if (poptPeekArg(optCon)) {
219         if (name == NULL)
220             name = poptGetArg(optCon);
221         if (poptPeekArg(optCon)) {
222             rpmlog(RPMLOG_ERR, _("line %d: Too many names: %s\n"),
223                      spec->lineNum,
224                      spec->line);
225             rc = RPMRC_FAIL;
226             goto exit;
227         }
228     }
229     
230     if (lookupPackage(spec, name, flag, &pkg)) {
231         rpmlog(RPMLOG_ERR, _("line %d: Package does not exist: %s\n"),
232                  spec->lineNum, spec->line);
233         rc = RPMRC_FAIL;
234         goto exit;
235     }
236
237     if (tag != RPMTAG_TRIGGERSCRIPTS) {
238         if (headerIsEntry(pkg->header, progtag)) {
239             rpmlog(RPMLOG_ERR, _("line %d: Second %s\n"),
240                      spec->lineNum, partname);
241             rc = RPMRC_FAIL;
242             goto exit;
243         }
244     }
245
246     if ((rc = poptParseArgvString(prog, &progArgc, &progArgv))) {
247         rpmlog(RPMLOG_ERR, _("line %d: Error parsing %s: %s\n"),
248                  spec->lineNum, partname, poptStrerror(rc));
249         rc = RPMRC_FAIL;
250         goto exit;
251     }
252
253     sb = newStringBuf();
254     if ((rc = readLine(spec, STRIP_NOTHING)) > 0) {
255         nextPart = PART_NONE;
256     } else {
257         if (rc)
258             goto exit;
259         while (! (nextPart = isPart(spec->line))) {
260             appendStringBuf(sb, spec->line);
261             if ((rc = readLine(spec, STRIP_NOTHING)) > 0) {
262                 nextPart = PART_NONE;
263                 break;
264             }
265             if (rc)
266                 goto exit;
267         }
268     }
269     stripTrailingBlanksStringBuf(sb);
270     p = getStringBuf(sb);
271
272 #ifdef WITH_LUA
273     if (!strcmp(progArgv[0], "<lua>")) {
274         rpmlua lua = NULL; /* Global state. */
275         if (rpmluaCheckScript(lua, p, partname) != RPMRC_OK) {
276             rc = RPMRC_FAIL;
277             goto exit;
278         }
279         (void) rpmlibNeedsFeature(pkg->header,
280                                   "BuiltinLuaScripts", "4.2.2-1");
281     } else
282 #endif
283     if (progArgv[0][0] == '<') {
284         rpmlog(RPMLOG_ERR,
285                  _("line %d: unsupported internal script: %s\n"),
286                  spec->lineNum, progArgv[0]);
287         rc = RPMRC_FAIL;
288         goto exit;
289     } else {
290         (void) addReqProv(spec, pkg->header, RPMTAG_REQUIRENAME,
291                 progArgv[0], NULL, (tagflags | RPMSENSE_INTERP), 0);
292     }
293
294     /* Trigger script insertion is always delayed in order to */
295     /* get the index right.                                   */
296     if (tag == RPMTAG_TRIGGERSCRIPTS) {
297         /* Add file/index/prog triple to the trigger file list */
298         index = addTriggerIndex(pkg, file, p, progArgv[0]);
299
300         /* Generate the trigger tags */
301         if ((rc = parseRCPOT(spec, pkg, reqargs, reqtag, index, tagflags)))
302             goto exit;
303     } else {
304         if (progArgc == 1)
305             (void) headerAddEntry(pkg->header, progtag, RPM_STRING_TYPE,
306                         *progArgv, progArgc);
307         else {
308             (void) rpmlibNeedsFeature(pkg->header,
309                         "ScriptletInterpreterArgs", "4.0.3-1");
310             (void) headerAddEntry(pkg->header, progtag, RPM_STRING_ARRAY_TYPE,
311                         progArgv, progArgc);
312         }
313
314         if (*p != '\0')
315             (void) headerAddEntry(pkg->header, tag, RPM_STRING_TYPE, p, 1);
316
317         if (file) {
318             switch (parsePart) {
319               case PART_PRE:
320                 pkg->preInFile = xstrdup(file);
321                 break;
322               case PART_POST:
323                 pkg->postInFile = xstrdup(file);
324                 break;
325               case PART_PREUN:
326                 pkg->preUnFile = xstrdup(file);
327                 break;
328               case PART_POSTUN:
329                 pkg->postUnFile = xstrdup(file);
330                 break;
331               case PART_PRETRANS:
332                 pkg->preTransFile = xstrdup(file);
333                 break;
334               case PART_POSTTRANS:
335                 pkg->postTransFile = xstrdup(file);
336                 break;
337               case PART_VERIFYSCRIPT:
338                 pkg->verifyFile = xstrdup(file);
339                 break;
340             }
341         }
342     }
343     rc = nextPart;
344     
345 exit:
346     sb = freeStringBuf(sb);
347     progArgv = _free(progArgv);
348     argv = _free(argv);
349     optCon = poptFreeContext(optCon);
350     
351     return rc;
352 }