Realize the remaining bits of direct rpmdb interface are dead too
[platform/upstream/rpm.git] / build / parsePrep.c
1 /** \ingroup rpmbuild
2  * \file build/parsePrep.c
3  *  Parse %prep 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 #include <rpm/rpmfileutil.h>
12 #include "debug.h"
13
14 /**
15  * Check that file owner and group are known.
16  * @param urlfn         file url
17  * @return              RPMRC_OK on success
18  */
19 static rpmRC checkOwners(const char * urlfn)
20 {
21     struct stat sb;
22
23     if (lstat(urlfn, &sb)) {
24         rpmlog(RPMLOG_ERR, _("Bad source: %s: %s\n"),
25                 urlfn, strerror(errno));
26         return RPMRC_FAIL;
27     }
28     if (!getUname(sb.st_uid) || !getGname(sb.st_gid)) {
29         rpmlog(RPMLOG_ERR, _("Bad owner/group: %s\n"), urlfn);
30         return RPMRC_FAIL;
31     }
32
33     return RPMRC_OK;
34 }
35
36 /**
37  * Expand %patchN macro into %prep scriptlet.
38  * @param spec          build info
39  * @param c             patch index
40  * @param strip         patch level (i.e. patch -p argument)
41  * @param db            saved file suffix (i.e. patch --suffix argument)
42  * @param reverse       include -R?
43  * @param removeEmpties include -E?
44  * @param fuzz          fuzz factor, fuzz<0 means no fuzz set
45  * @param dir           dir to change to (i.e. patch -d argument)
46  * @return              expanded %patch macro (NULL on error)
47  */
48
49 static char *doPatch(rpmSpec spec, uint32_t c, int strip, const char *db,
50                      int reverse, int removeEmpties, int fuzz, const char *dir)
51 {
52     char *fn = NULL;
53     char *buf = NULL;
54     char *arg_backup = NULL;
55     char *arg_fuzz = NULL;
56     char *arg_dir = NULL;
57     char *args = NULL;
58     char *arg_patch_flags = rpmExpand("%{?_default_patch_flags}", NULL);
59     struct Source *sp;
60     char *patchcmd;
61
62     for (sp = spec->sources; sp != NULL; sp = sp->next) {
63         if ((sp->flags & RPMBUILD_ISPATCH) && (sp->num == c)) {
64             break;
65         }
66     }
67     if (sp == NULL) {
68         if (c != INT_MAX) {
69             rpmlog(RPMLOG_ERR, _("No patch number %u\n"), c);
70         } else {
71             rpmlog(RPMLOG_ERR, _("%%patch without corresponding \"Patch:\" tag\n"));
72         }
73         goto exit;
74     }
75
76     fn = rpmGetPath("%{_sourcedir}/", sp->source, NULL);
77
78     /* On non-build parse's, file cannot be stat'd or read. */
79     if (spec->force || checkOwners(fn)) goto exit;
80
81     if (db) {
82         rasprintf(&arg_backup,
83 #if HAVE_OLDPATCH_21 == 0
84                   "-b "
85 #endif
86                   "--suffix %s", db);
87     } else arg_backup = xstrdup("");
88
89     if (dir) {
90         rasprintf(&arg_dir, " -d %s", dir);
91     } else arg_dir = xstrdup("");
92
93     if (fuzz >= 0) {
94         rasprintf(&arg_fuzz, " --fuzz=%d", fuzz);
95     } else arg_fuzz = xstrdup("");
96
97     rasprintf(&args, "%s -p%d %s%s%s%s%s", arg_patch_flags, strip, arg_backup, arg_fuzz, arg_dir,
98                 reverse ? " -R" : "", 
99                 removeEmpties ? " -E" : "");
100
101     patchcmd = rpmExpand("%{uncompress: ", fn, "} | %{__patch} ", args, NULL);
102
103     free(arg_patch_flags);
104     free(arg_fuzz);
105     free(arg_dir);
106     free(arg_backup);
107     free(args);
108
109     if (c != INT_MAX) {
110         rasprintf(&buf, "echo \"Patch #%u (%s):\"\n"
111                         "%s\n", 
112                         c, basename(fn), patchcmd);
113     } else {
114         rasprintf(&buf, "echo \"Patch (%s):\"\n"
115                         "%s\n", 
116                         basename(fn), patchcmd);
117     }
118     free(patchcmd);
119
120 exit:
121     free(fn);
122     return buf;
123 }
124
125 /**
126  * Expand %setup macro into %prep scriptlet.
127  * @param spec          build info
128  * @param c             source index
129  * @param quietly       should -vv be omitted from tar?
130  * @return              expanded %setup macro (NULL on error)
131  */
132 static char *doUntar(rpmSpec spec, uint32_t c, int quietly)
133 {
134     char *fn;
135     char *buf = NULL;
136     char *tar, *taropts;
137     struct Source *sp;
138     rpmCompressedMagic compressed = COMPRESSED_NOT;
139
140     for (sp = spec->sources; sp != NULL; sp = sp->next) {
141         if ((sp->flags & RPMBUILD_ISSOURCE) && (sp->num == c)) {
142             break;
143         }
144     }
145     if (sp == NULL) {
146         if (c) {
147             rpmlog(RPMLOG_ERR, _("No source number %u\n"), c);
148         } else {
149             rpmlog(RPMLOG_ERR, _("No \"Source:\" tag in the spec file\n"));
150         }
151         return NULL;
152     }
153
154     fn = rpmGetPath("%{_sourcedir}/", sp->source, NULL);
155
156     /* FIX: shrug */
157     taropts = ((rpmIsVerbose() && !quietly) ? "-xvvf" : "-xf");
158
159 #ifdef AUTOFETCH_NOT    /* XXX don't expect this code to be enabled */
160     /* XXX
161      * XXX If nosource file doesn't exist, try to fetch from url.
162      * XXX TODO: add a "--fetch" enabler.
163      */
164     if (sp->flags & RPMTAG_NOSOURCE && autofetchnosource) {
165         struct stat st;
166         int rc;
167         if (lstat(fn, &st) != 0 && errno == ENOENT &&
168             urlIsUrl(sp->fullSource) != URL_IS_UNKNOWN) {
169             if ((rc = urlGetFile(sp->fullSource, fn)) != 0) {
170                 rpmlog(RPMLOG_ERR,
171                         _("Couldn't download nosource %s: %s\n"),
172                         sp->fullSource);
173                 return NULL;
174             }
175         }
176     }
177 #endif
178
179     /* XXX On non-build parse's, file cannot be stat'd or read */
180     if (!spec->force && (rpmFileIsCompressed(fn, &compressed) || checkOwners(fn))) {
181         fn = _free(fn);
182         return NULL;
183     }
184
185     tar = rpmGetPath("%{__tar}", NULL);
186     if (compressed != COMPRESSED_NOT) {
187         char *zipper, *t = NULL;
188         int needtar = 1;
189
190         switch (compressed) {
191         case COMPRESSED_NOT:    /* XXX can't happen */
192         case COMPRESSED_OTHER:
193             t = "%{__gzip} -dc";
194             break;
195         case COMPRESSED_BZIP2:
196             t = "%{__bzip2} -dc";
197             break;
198         case COMPRESSED_ZIP:
199             if (rpmIsVerbose() && !quietly)
200                 t = "%{__unzip}";
201             else
202                 t = "%{__unzip} -qq";
203             needtar = 0;
204             break;
205         case COMPRESSED_LZMA:
206             t = "%{__lzma} -dc";
207             break;
208         case COMPRESSED_XZ:
209             t = "%{__xz} -dc";
210             break;
211         }
212         zipper = rpmGetPath(t, NULL);
213         if (needtar) {
214             rasprintf(&buf, "%s '%s' | %s %s - \n"
215                 "STATUS=$?\n"
216                 "if [ $STATUS -ne 0 ]; then\n"
217                 "  exit $STATUS\n"
218                 "fi", zipper, fn, tar, taropts);
219         } else {
220             rasprintf(&buf, "%s '%s'\n"
221                 "STATUS=$?\n"
222                 "if [ $STATUS -ne 0 ]; then\n"
223                 "  exit $STATUS\n"
224                 "fi", zipper, fn);
225         }
226         zipper = _free(zipper);
227     } else {
228         rasprintf(&buf, "%s %s %s", tar, taropts, fn);
229     }
230
231     fn = _free(fn);
232     tar = _free(tar);
233     return buf;
234 }
235
236 /**
237  * Parse %setup macro.
238  * @todo FIXME: Option -q broken when not immediately after %setup.
239  * @param spec          build info
240  * @param line          current line from spec file
241  * @return              RPMRC_OK on success
242  */
243 static int doSetupMacro(rpmSpec spec, const char *line)
244 {
245     char *buf = NULL;
246     StringBuf before = newStringBuf();
247     StringBuf after = newStringBuf();
248     poptContext optCon = NULL;
249     int argc;
250     const char ** argv = NULL;
251     int arg;
252     const char * optArg;
253     int xx;
254     rpmRC rc = RPMRC_FAIL;
255     uint32_t num;
256     int leaveDirs = 0, skipDefaultAction = 0;
257     int createDir = 0, quietly = 0;
258     const char * dirName = NULL;
259     struct poptOption optionsTable[] = {
260             { NULL, 'a', POPT_ARG_STRING, NULL, 'a',    NULL, NULL},
261             { NULL, 'b', POPT_ARG_STRING, NULL, 'b',    NULL, NULL},
262             { NULL, 'c', 0, &createDir, 0,              NULL, NULL},
263             { NULL, 'D', 0, &leaveDirs, 0,              NULL, NULL},
264             { NULL, 'n', POPT_ARG_STRING, &dirName, 0,  NULL, NULL},
265             { NULL, 'T', 0, &skipDefaultAction, 0,      NULL, NULL},
266             { NULL, 'q', 0, &quietly, 0,                NULL, NULL},
267             { 0, 0, 0, 0, 0,    NULL, NULL}
268     };
269
270     if ((xx = poptParseArgvString(line, &argc, &argv))) {
271         rpmlog(RPMLOG_ERR, _("Error parsing %%setup: %s\n"), poptStrerror(xx));
272         goto exit;
273     }
274
275     optCon = poptGetContext(NULL, argc, argv, optionsTable, 0);
276     while ((arg = poptGetNextOpt(optCon)) > 0) {
277         optArg = poptGetOptArg(optCon);
278
279         /* We only parse -a and -b here */
280
281         if (parseUnsignedNum(optArg, &num)) {
282             rpmlog(RPMLOG_ERR, _("line %d: Bad arg to %%setup: %s\n"),
283                      spec->lineNum, (optArg ? optArg : "???"));
284             goto exit;
285         }
286
287         {   char *chptr = doUntar(spec, num, quietly);
288             if (chptr == NULL)
289                 goto exit;
290
291             appendLineStringBuf((arg == 'a' ? after : before), chptr);
292             free(chptr);
293         }
294     }
295
296     if (arg < -1) {
297         rpmlog(RPMLOG_ERR, _("line %d: Bad %%setup option %s: %s\n"),
298                  spec->lineNum,
299                  poptBadOption(optCon, POPT_BADOPTION_NOALIAS), 
300                  poptStrerror(arg));
301         goto exit;
302     }
303
304     if (dirName) {
305         spec->buildSubdir = xstrdup(dirName);
306     } else {
307         rasprintf(&spec->buildSubdir, "%s-%s", 
308                   headerGetString(spec->packages->header, RPMTAG_NAME),
309                   headerGetString(spec->packages->header, RPMTAG_VERSION));
310     }
311     addMacro(spec->macros, "buildsubdir", NULL, spec->buildSubdir, RMIL_SPEC);
312     
313     /* cd to the build dir */
314     {   char * buildDir = rpmGenPath(spec->rootDir, "%{_builddir}", "");
315
316         rasprintf(&buf, "cd '%s'", buildDir);
317         appendLineStringBuf(spec->prep, buf);
318         free(buf);
319         free(buildDir);
320     }
321     
322     /* delete any old sources */
323     if (!leaveDirs) {
324         rasprintf(&buf, "rm -rf '%s'", spec->buildSubdir);
325         appendLineStringBuf(spec->prep, buf);
326         free(buf);
327     }
328
329     /* if necessary, create and cd into the proper dir */
330     if (createDir) {
331         rasprintf(&buf, RPM_MKDIR_P " %s\ncd '%s'",
332                 spec->buildSubdir, spec->buildSubdir);
333         appendLineStringBuf(spec->prep, buf);
334         free(buf);
335     }
336
337     /* do the default action */
338    if (!createDir && !skipDefaultAction) {
339         char *chptr = doUntar(spec, 0, quietly);
340         if (!chptr)
341             goto exit;
342         appendLineStringBuf(spec->prep, chptr);
343         free(chptr);
344     }
345
346     appendStringBuf(spec->prep, getStringBuf(before));
347
348     if (!createDir) {
349         rasprintf(&buf, "cd '%s'", spec->buildSubdir);
350         appendLineStringBuf(spec->prep, buf);
351         free(buf);
352     }
353
354     if (createDir && !skipDefaultAction) {
355         char *chptr = doUntar(spec, 0, quietly);
356         if (chptr == NULL)
357             goto exit;
358         appendLineStringBuf(spec->prep, chptr);
359         free(chptr);
360     }
361     
362     appendStringBuf(spec->prep, getStringBuf(after));
363
364     /* Fix the permissions of the setup build tree */
365     {   char *fix = rpmExpand("%{_fixperms} .", NULL);
366         if (fix && *fix != '%') {
367             appendLineStringBuf(spec->prep, fix);
368         }
369         free(fix);
370     }
371     rc = RPMRC_OK;
372
373 exit:
374     freeStringBuf(before);
375     freeStringBuf(after);
376     poptFreeContext(optCon);
377     free(argv);
378
379     return rc;
380 }
381
382 /**
383  * Parse %patch line.
384  * This supports too many crazy syntaxes:
385  * - %patchN is equal to %patch -P<N>
386  * - -P<N> -P<N+1>... can be used to apply several patch on a single line
387  * - Any trailing arguments are treated as patch numbers
388  * - Any combination of the above, except unless at least one -P is specified,
389  *   %patch is treated as %patch -P0 so that "%patch 1" is actually
390  *   equal to "%patch -P0 -P1".
391  *
392  * @param spec          build info
393  * @param line          current line from spec file
394  * @return              RPMRC_OK on success
395  */
396 static rpmRC doPatchMacro(rpmSpec spec, const char *line)
397 {
398     char *opt_b, *opt_P, *opt_d;
399     char *buf = NULL;
400     int opt_p, opt_R, opt_E, opt_F;
401     int argc, c;
402     const char **argv = NULL;
403     ARGV_t patch, patchnums = NULL;
404     rpmRC rc = RPMRC_FAIL; /* assume failure */
405     
406     struct poptOption const patchOpts[] = {
407         { NULL, 'P', POPT_ARG_STRING, &opt_P, 'P', NULL, NULL },
408         { NULL, 'p', POPT_ARG_INT, &opt_p, 'p', NULL, NULL },
409         { NULL, 'R', POPT_ARG_NONE, &opt_R, 'R', NULL, NULL },
410         { NULL, 'E', POPT_ARG_NONE, &opt_E, 'E', NULL, NULL },
411         { NULL, 'b', POPT_ARG_STRING, &opt_b, 'b', NULL, NULL },
412         { NULL, 'z', POPT_ARG_STRING, &opt_b, 'z', NULL, NULL },
413         { NULL, 'F', POPT_ARG_INT, &opt_F, 'F', NULL, NULL },
414         { NULL, 'd', POPT_ARG_STRING, &opt_d, 'd', NULL, NULL },
415         { NULL, 0, 0, NULL, 0, NULL, NULL }
416     };
417     poptContext optCon = NULL;
418
419     opt_p = opt_R = opt_E = 0;
420     opt_F = rpmExpandNumeric("%{_default_patch_fuzz}");         /* get default fuzz factor for %patch */
421     opt_b = opt_d = NULL;
422
423     /* Convert %patchN to %patch -PN to simplify further processing */
424     if (! strchr(" \t\n", line[6])) {
425         rasprintf(&buf, "%%patch -P %s", line + 6);
426     } else {
427         if (strstr(line+6, " -P") == NULL)
428             rasprintf(&buf, "%%patch -P %d %s", INT_MAX, line + 6); /* INT_MAX denotes not numbered %patch */
429         else
430             buf = xstrdup(line); /* it is not numberless patch because -P is present */
431     }
432     poptParseArgvString(buf, &argc, &argv);
433     free(buf);
434
435     /* 
436      * Grab all -P<N> numbers for later processing. Stored as strings
437      * at this point so we only have to worry about conversion in one place.
438      */
439     optCon = poptGetContext(NULL, argc, argv, patchOpts, 0);
440     while ((c = poptGetNextOpt(optCon)) > 0) {
441         switch (c) {
442         case 'P': {
443             char *arg = poptGetOptArg(optCon);
444             if (arg) {
445                 argvAdd(&patchnums, arg);
446                 free(arg);
447             }
448             break;
449         }
450         default:
451             break;
452         }
453     }
454
455     if (c < -1) {
456         rpmlog(RPMLOG_ERR, _("%s: %s: %s\n"), poptStrerror(c), 
457                 poptBadOption(optCon, POPT_BADOPTION_NOALIAS), line);
458         goto exit;
459     }
460
461     /* Any trailing arguments are treated as patch numbers */
462     argvAppend(&patchnums, (ARGV_const_t) poptGetArgs(optCon));
463
464     /* Convert to number, generate patch command and append to %prep script */
465     for (patch = patchnums; *patch; patch++) {
466         uint32_t pnum;
467         char *s;
468         if (parseUnsignedNum(*patch, &pnum)) {
469             rpmlog(RPMLOG_ERR, _("Invalid patch number %s: %s\n"),
470                      *patch, line);
471             goto exit;
472         }
473         s = doPatch(spec, pnum, opt_p, opt_b, opt_R, opt_E, opt_F, opt_d);
474         if (s == NULL) {
475             goto exit;
476         }
477         appendLineStringBuf(spec->prep, s);
478         free(s);
479     }
480         
481     rc = RPMRC_OK;
482
483 exit:
484     argvFree(patchnums);
485     free(argv);
486     poptFreeContext(optCon);
487     return rc;
488 }
489
490 int parsePrep(rpmSpec spec)
491 {
492     int nextPart, res, rc;
493     StringBuf sb;
494     char **lines;
495     ARGV_t saveLines = NULL;
496
497     if (spec->prep != NULL) {
498         rpmlog(RPMLOG_ERR, _("line %d: second %%prep\n"), spec->lineNum);
499         return PART_ERROR;
500     }
501
502     spec->prep = newStringBuf();
503
504     /* There are no options to %prep */
505     if ((rc = readLine(spec, STRIP_NOTHING)) > 0) {
506         return PART_NONE;
507     } else if (rc < 0) {
508         return PART_ERROR;
509     }
510     
511     sb = newStringBuf();
512     
513     while (! (nextPart = isPart(spec->line))) {
514         /* Need to expand the macros inline.  That way we  */
515         /* can give good line number information on error. */
516         appendStringBuf(sb, spec->line);
517         if ((rc = readLine(spec, STRIP_NOTHING)) > 0) {
518             nextPart = PART_NONE;
519             break;
520         } else if (rc < 0) {
521             goto exit;
522         }
523     }
524
525     argvSplit(&saveLines, getStringBuf(sb), "\n");
526     for (lines = saveLines; *lines; lines++) {
527         res = 0;
528         if (rstreqn(*lines, "%setup", sizeof("%setup")-1)) {
529             res = doSetupMacro(spec, *lines);
530         } else if (rstreqn(*lines, "%patch", sizeof("%patch")-1)) {
531             res = doPatchMacro(spec, *lines);
532         } else {
533             appendLineStringBuf(spec->prep, *lines);
534         }
535         if (res && !spec->force) {
536             /* fixup from RPMRC_FAIL do*Macro() codes for now */
537             nextPart = PART_ERROR; 
538             goto exit;
539         }
540     }
541     res = nextPart;
542
543 exit:
544     argvFree(saveLines);
545     sb = freeStringBuf(sb);
546
547     return nextPart;
548 }