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