/src/libvips/libvips/iofuncs/system.c
Line  | Count  | Source (jump to first uncovered line)  | 
1  |  | /* vips_system(): run a command on an image  | 
2  |  |  *  | 
3  |  |  * 7/3/00 JC  | 
4  |  |  *  - hacked it in  | 
5  |  |  * 21/10/02 JC  | 
6  |  |  *  - use mktemp() if mkstemp() is not available  | 
7  |  |  * 10/3/03 JC  | 
8  |  |  *  - out can be NULL  | 
9  |  |  * 23/12/04  | 
10  |  |  *  - use g_mkstemp()  | 
11  |  |  * 8/9/09  | 
12  |  |  *  - add .v suffix (thanks Roland)  | 
13  |  |  *  - use vipsbuf  | 
14  |  |  *  - rewrite to make it simpler  | 
15  |  |  * 2/2/10  | 
16  |  |  *  - gtkdoc  | 
17  |  |  * 4/6/13  | 
18  |  |  *  - redo as a class  | 
19  |  |  *  - input and output images are now optional  | 
20  |  |  * 3/5/14  | 
21  |  |  *  - switch to g_spawn_command_line_sync() from popen() ... helps stop  | 
22  |  |  *    stray command-windows on Windows  | 
23  |  |  * 27/3/16  | 
24  |  |  *  - allow [options] in out_format  | 
25  |  |  */  | 
26  |  |  | 
27  |  | /*  | 
28  |  |  | 
29  |  |   This file is part of VIPS.  | 
30  |  |  | 
31  |  |   VIPS is free software; you can redistribute it and/or modify  | 
32  |  |   it under the terms of the GNU Lesser General Public License as published by  | 
33  |  |   the Free Software Foundation; either version 2 of the License, or  | 
34  |  |   (at your option) any later version.  | 
35  |  |  | 
36  |  |   This program is distributed in the hope that it will be useful,  | 
37  |  |   but WITHOUT ANY WARRANTY; without even the implied warranty of  | 
38  |  |   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the  | 
39  |  |   GNU Lesser General Public License for more details.  | 
40  |  |  | 
41  |  |   You should have received a copy of the GNU Lesser General Public License  | 
42  |  |   along with this program; if not, write to the Free Software  | 
43  |  |   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  | 
44  |  |   02110-1301  USA  | 
45  |  |  | 
46  |  |  */  | 
47  |  |  | 
48  |  | /*  | 
49  |  |  | 
50  |  |   These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk  | 
51  |  |  | 
52  |  |  */  | 
53  |  |  | 
54  |  | #ifdef HAVE_CONFIG_H  | 
55  |  | #include <config.h>  | 
56  |  | #endif /*HAVE_CONFIG_H*/  | 
57  |  | #include <glib/gi18n-lib.h>  | 
58  |  |  | 
59  |  | #include <stdio.h>  | 
60  |  | #include <stdlib.h>  | 
61  |  |  | 
62  |  | #ifdef HAVE_UNISTD_H  | 
63  |  | #include <unistd.h>  | 
64  |  | #endif /*HAVE_UNISTD_H*/  | 
65  |  | #include <stdarg.h>  | 
66  |  | #include <string.h>  | 
67  |  | #include <errno.h>  | 
68  |  |  | 
69  |  | #include <vips/vips.h>  | 
70  |  | #include <vips/internal.h>  | 
71  |  |  | 
72  |  | typedef struct _VipsSystem { | 
73  |  |   VipsOperation parent_instance;  | 
74  |  |  | 
75  |  |   VipsArrayImage *in;  | 
76  |  |   VipsImage *out;  | 
77  |  |   char *cmd_format;  | 
78  |  |   char *in_format;  | 
79  |  |   char *out_format;  | 
80  |  |   char *log;  | 
81  |  |  | 
82  |  |   /* Array of names we wrote the input images to.  | 
83  |  |    */  | 
84  |  |   char **in_name;  | 
85  |  |  | 
86  |  |   /* Output name without any options, so /tmp/vips-weifh.svg, for  | 
87  |  |    * example.  | 
88  |  |    */  | 
89  |  |   char *out_name;  | 
90  |  |  | 
91  |  |   /* Output name with any options, so /tmp/vips-weifh.svg[scale=2], for  | 
92  |  |    * example.  | 
93  |  |    */  | 
94  |  |   char *out_name_options;  | 
95  |  |  | 
96  |  | } VipsSystem;  | 
97  |  |  | 
98  |  | typedef VipsOperationClass VipsSystemClass;  | 
99  |  |  | 
100  |  | G_DEFINE_TYPE(VipsSystem, vips_system, VIPS_TYPE_OPERATION);  | 
101  |  |  | 
102  |  | static void  | 
103  |  | vips_system_dispose(GObject *gobject)  | 
104  | 0  | { | 
105  | 0  |   VipsSystem *system = (VipsSystem *) gobject;  | 
106  |  | 
  | 
107  | 0  |   if (system->in_name) { | 
108  | 0  |     int i;  | 
109  |  | 
  | 
110  | 0  |     for (i = 0; i < VIPS_AREA(system->in)->n; i++) { | 
111  | 0  |       g_unlink(system->in_name[i]);  | 
112  | 0  |       VIPS_FREE(system->in_name[i]);  | 
113  | 0  |     }  | 
114  | 0  |   }  | 
115  |  | 
  | 
116  | 0  |   VIPS_FREE(system->out_name);  | 
117  | 0  |   VIPS_FREE(system->out_name_options);  | 
118  |  | 
  | 
119  | 0  |   G_OBJECT_CLASS(vips_system_parent_class)->dispose(gobject);  | 
120  | 0  | }  | 
121  |  |  | 
122  |  | static int  | 
123  |  | vips_system_build(VipsObject *object)  | 
124  | 0  | { | 
125  | 0  |   VipsObjectClass *class = VIPS_OBJECT_GET_CLASS(object);  | 
126  | 0  |   VipsSystem *system = (VipsSystem *) object;  | 
127  |  | 
  | 
128  | 0  |   int i;  | 
129  |  | 
  | 
130  | 0  |   char cmd[VIPS_PATH_MAX];  | 
131  |  | 
  | 
132  | 0  |   char *p;  | 
133  | 0  |   char *std_output;  | 
134  | 0  |   char *std_error;  | 
135  | 0  |   int result;  | 
136  | 0  |   GError *error = NULL;  | 
137  |  | 
  | 
138  | 0  |   if (VIPS_OBJECT_CLASS(vips_system_parent_class)->build(object))  | 
139  | 0  |     return -1;  | 
140  |  |  | 
141  |  |   /* Write the input images to files. We must always make copies of the  | 
142  |  |    * files, even if this image is a disc file already, in case the  | 
143  |  |    * command needs a different format.  | 
144  |  |    */  | 
145  | 0  |   if (system->in) { | 
146  | 0  |     char *in_format = system->in_format  | 
147  | 0  |       ? system->in_format  | 
148  | 0  |       : "%s.tif";  | 
149  | 0  |     int n;  | 
150  | 0  |     VipsImage **in = vips_array_image_get(system->in, &n);  | 
151  |  | 
  | 
152  | 0  |     if (!(system->in_name = VIPS_ARRAY(object, n, char *)))  | 
153  | 0  |       return -1;  | 
154  | 0  |     memset(system->in_name, 0, n * sizeof(char *));  | 
155  | 0  |     for (i = 0; i < n; i++) { | 
156  | 0  |       if (!(system->in_name[i] =  | 
157  | 0  |             vips__temp_name(in_format)))  | 
158  | 0  |         return -1;  | 
159  | 0  |       if (vips_image_write_to_file(in[i],  | 
160  | 0  |           system->in_name[i], NULL))  | 
161  | 0  |         return -1;  | 
162  | 0  |     }  | 
163  | 0  |   }  | 
164  |  |  | 
165  |  |   /* Make the output filename.  | 
166  |  |    */  | 
167  | 0  |   if (system->out_format) { | 
168  | 0  |     char filename[VIPS_PATH_MAX];  | 
169  | 0  |     char option_string[VIPS_PATH_MAX];  | 
170  |  | 
  | 
171  | 0  |     vips__filename_split8(system->out_format,  | 
172  | 0  |       filename, option_string);  | 
173  | 0  |     if (!(system->out_name = vips__temp_name(filename)))  | 
174  | 0  |       return -1;  | 
175  | 0  |     system->out_name_options =  | 
176  | 0  |       g_strconcat(system->out_name, option_string, NULL);  | 
177  | 0  |   }  | 
178  |  |  | 
179  | 0  |   g_strlcpy(cmd, system->cmd_format, VIPS_PATH_MAX);  | 
180  | 0  |   if (system->in)  | 
181  | 0  |     for (i = 0; i < VIPS_AREA(system->in)->n; i++)  | 
182  | 0  |       if (vips__substitute(cmd, VIPS_PATH_MAX,  | 
183  | 0  |           system->in_name[i])) { | 
184  | 0  |         vips_error(class->nickname, "%s",  | 
185  | 0  |           _("unable to substitute input filename")); | 
186  | 0  |         return -1;  | 
187  | 0  |       }  | 
188  | 0  |   if (system->out_name &&  | 
189  | 0  |     vips__substitute(cmd, VIPS_PATH_MAX, system->out_name)) { | 
190  | 0  |     vips_error(class->nickname, "%s",  | 
191  | 0  |       _("unable to substitute output filename")); | 
192  | 0  |     return -1;  | 
193  | 0  |   }  | 
194  |  |  | 
195  |  |   /* Swap all "%%" in the string for a single "%". We need this for  | 
196  |  |    * compatibility with older printf-based vips_system()s which  | 
197  |  |    * needed a double %%.  | 
198  |  |    */  | 
199  | 0  |   for (p = cmd; *p; p++)  | 
200  | 0  |     if (p[0] == '%' &&  | 
201  | 0  |       p[1] == '%')  | 
202  | 0  |       memmove(p, p + 1, strlen(p));  | 
203  |  | 
  | 
204  | 0  |   if (!g_spawn_command_line_sync(cmd,  | 
205  | 0  |       &std_output, &std_error, &result, &error) ||  | 
206  | 0  |     result) { | 
207  | 0  |     if (error) { | 
208  | 0  |       vips_error(class->nickname, "%s", error->message);  | 
209  | 0  |       g_error_free(error);  | 
210  | 0  |     }  | 
211  | 0  |     if (std_error) { | 
212  | 0  |       g_strchomp(std_error);  | 
213  | 0  |       if (strcmp(std_error, "") != 0)  | 
214  | 0  |         vips_error(class->nickname,  | 
215  | 0  |           "error output: %s", std_error);  | 
216  | 0  |       VIPS_FREE(std_error);  | 
217  | 0  |     }  | 
218  | 0  |     if (std_output) { | 
219  | 0  |       g_strchomp(std_output);  | 
220  | 0  |       if (strcmp(std_output, "") != 0)  | 
221  | 0  |         vips_error(class->nickname,  | 
222  | 0  |           "output: %s", std_output);  | 
223  | 0  |       VIPS_FREE(std_output);  | 
224  | 0  |     }  | 
225  | 0  |     vips_error_system(result, class->nickname,  | 
226  | 0  |       _("command \"%s\" failed"), cmd); | 
227  |  | 
  | 
228  | 0  |     return -1;  | 
229  | 0  |   }  | 
230  |  |  | 
231  | 0  |   if (std_error) { | 
232  | 0  |     g_strchomp(std_error);  | 
233  | 0  |     if (strcmp(std_error, "") != 0)  | 
234  | 0  |       g_warning(_("stderr output: %s"), std_error); | 
235  | 0  |   }  | 
236  | 0  |   if (std_output) { | 
237  | 0  |     g_strchomp(std_output);  | 
238  | 0  |     g_object_set(system, "log", std_output, NULL);  | 
239  | 0  |   }  | 
240  |  | 
  | 
241  | 0  |   VIPS_FREE(std_output);  | 
242  | 0  |   VIPS_FREE(std_error);  | 
243  |  | 
  | 
244  | 0  |   if (system->out_name_options) { | 
245  | 0  |     VipsImage *out;  | 
246  |  | 
  | 
247  | 0  |     if (!(out = vips_image_new_from_file(system->out_name_options,  | 
248  | 0  |           NULL)))  | 
249  | 0  |       return -1;  | 
250  | 0  |     vips_image_set_delete_on_close(out, TRUE);  | 
251  | 0  |     g_object_set(system, "out", out, NULL);  | 
252  | 0  |   }  | 
253  |  |  | 
254  | 0  |   return 0;  | 
255  | 0  | }  | 
256  |  |  | 
257  |  | static void  | 
258  |  | vips_system_class_init(VipsSystemClass *class)  | 
259  | 1  | { | 
260  | 1  |   GObjectClass *gobject_class = G_OBJECT_CLASS(class);  | 
261  | 1  |   VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS(class);  | 
262  | 1  |   VipsOperationClass *operation_class = VIPS_OPERATION_CLASS(class);  | 
263  |  |  | 
264  | 1  |   gobject_class->dispose = vips_system_dispose;  | 
265  | 1  |   gobject_class->set_property = vips_object_set_property;  | 
266  | 1  |   gobject_class->get_property = vips_object_get_property;  | 
267  |  |  | 
268  | 1  |   vobject_class->nickname = "system";  | 
269  | 1  |   vobject_class->description = _("run an external command"); | 
270  | 1  |   vobject_class->build = vips_system_build;  | 
271  |  |  | 
272  |  |   /* Commands can have side-effects, so don't cache them.  | 
273  |  |    */  | 
274  | 1  |   operation_class->flags |= VIPS_OPERATION_NOCACHE;  | 
275  |  |  | 
276  | 1  |   VIPS_ARG_BOXED(class, "in", 0,  | 
277  | 1  |     _("Input"), | 
278  | 1  |     _("Array of input images"), | 
279  | 1  |     VIPS_ARGUMENT_OPTIONAL_INPUT,  | 
280  | 1  |     G_STRUCT_OFFSET(VipsSystem, in),  | 
281  | 1  |     VIPS_TYPE_ARRAY_IMAGE);  | 
282  |  |  | 
283  | 1  |   VIPS_ARG_IMAGE(class, "out", 1,  | 
284  | 1  |     _("Output"), | 
285  | 1  |     _("Output image"), | 
286  | 1  |     VIPS_ARGUMENT_OPTIONAL_OUTPUT,  | 
287  | 1  |     G_STRUCT_OFFSET(VipsSystem, out));  | 
288  |  |  | 
289  | 1  |   VIPS_ARG_STRING(class, "cmd_format", 2,  | 
290  | 1  |     _("Command"), | 
291  | 1  |     _("Command to run"), | 
292  | 1  |     VIPS_ARGUMENT_REQUIRED_INPUT,  | 
293  | 1  |     G_STRUCT_OFFSET(VipsSystem, cmd_format),  | 
294  | 1  |     NULL);  | 
295  |  |  | 
296  | 1  |   VIPS_ARG_STRING(class, "in_format", 2,  | 
297  | 1  |     _("Input format"), | 
298  | 1  |     _("Format for input filename"), | 
299  | 1  |     VIPS_ARGUMENT_OPTIONAL_INPUT,  | 
300  | 1  |     G_STRUCT_OFFSET(VipsSystem, in_format),  | 
301  | 1  |     NULL);  | 
302  |  |  | 
303  | 1  |   VIPS_ARG_STRING(class, "out_format", 2,  | 
304  | 1  |     _("Output format"), | 
305  | 1  |     _("Format for output filename"), | 
306  | 1  |     VIPS_ARGUMENT_OPTIONAL_INPUT,  | 
307  | 1  |     G_STRUCT_OFFSET(VipsSystem, out_format),  | 
308  | 1  |     NULL);  | 
309  |  |  | 
310  | 1  |   VIPS_ARG_STRING(class, "log", 2,  | 
311  | 1  |     _("Log"), | 
312  | 1  |     _("Command log"), | 
313  | 1  |     VIPS_ARGUMENT_OPTIONAL_OUTPUT,  | 
314  | 1  |     G_STRUCT_OFFSET(VipsSystem, log),  | 
315  | 1  |     NULL);  | 
316  | 1  | }  | 
317  |  |  | 
318  |  | static void  | 
319  |  | vips_system_init(VipsSystem *system)  | 
320  | 0  | { | 
321  | 0  | }  | 
322  |  |  | 
323  |  | /**  | 
324  |  |  * vips_system:  | 
325  |  |  * @cmd_format: command to run  | 
326  |  |  * @...: %NULL-terminated list of optional named arguments  | 
327  |  |  *  | 
328  |  |  * Optional arguments:  | 
329  |  |  *  | 
330  |  |  * * @in: array of input images  | 
331  |  |  * * @out: output image  | 
332  |  |  * * @in_format: write input files like this  | 
333  |  |  * * @out_format: write output filename like this  | 
334  |  |  * * @log: stdout of command is returned here  | 
335  |  |  *  | 
336  |  |  * vips_system() runs a command, optionally passing a set of images in and  | 
337  |  |  * optionally getting an image back. The command's stdout is returned in @log.  | 
338  |  |  *  | 
339  |  |  * First, if @in is set, the array of images are written to files. See  | 
340  |  |  * vips_image_new_temp_file() to see how temporary files are created.  | 
341  |  |  * If @in_format is  | 
342  |  |  * something like %s.png, the file will be written in PNG format. By  | 
343  |  |  * default, @in_format is %s.tif.  | 
344  |  |  *  | 
345  |  |  * If @out_format is set, an output filename is formed in the same way. Any  | 
346  |  |  * trailing [options] are stripped from @out_format.  | 
347  |  |  *  | 
348  |  |  * The command string to run is made by substituting the first set of %s  | 
349  |  |  * in @cmd_format for the names of the input files, if @in is set, and then  | 
350  |  |  * the next %s for the output filename, if @out_format is set.  | 
351  |  |  * You can put a number between the % and the s to change the order  | 
352  |  |  * in which the substitution occurs.  | 
353  |  |  *  | 
354  |  |  * The command is executed with popen() and the output captured in @log.  | 
355  |  |  *  | 
356  |  |  * After the command finishes, if @out_format is set, the output image is  | 
357  |  |  * opened and returned in @out. You can append [options] to @out_format to  | 
358  |  |  * control how this open happens.  | 
359  |  |  * Closing @out image will automatically delete the output file.  | 
360  |  |  *  | 
361  |  |  * Finally the input images are deleted.  | 
362  |  |  *  | 
363  |  |  * For example, this call will run the ImageMagick convert program on an  | 
364  |  |  * image, using JPEG files to pass images into and out of the convert command.  | 
365  |  |  *  | 
366  |  |  * |[  | 
367  |  |  * VipsArrayImage *in;  | 
368  |  |  * VipsImage *out;  | 
369  |  |  * char *log;  | 
370  |  |  *  | 
371  |  |  * if (vips_system("convert %s -swirl 45 %s", | 
372  |  |  *         "in", in,  | 
373  |  |  *         "out", &out,  | 
374  |  |  *         "in_format", "%s.jpg",  | 
375  |  |  *         "out_format", "%s.jpg",  | 
376  |  |  *         "log", &log,  | 
377  |  |  *         NULL))  | 
378  |  |  *     error ...  | 
379  |  |  * ]|  | 
380  |  |  *  | 
381  |  |  * Returns: 0 on success, -1 on failure.  | 
382  |  |  */  | 
383  |  | int  | 
384  |  | vips_system(const char *cmd_format, ...)  | 
385  | 0  | { | 
386  | 0  |   va_list ap;  | 
387  | 0  |   int result;  | 
388  |  | 
  | 
389  | 0  |   va_start(ap, cmd_format);  | 
390  | 0  |   result = vips_call_split("system", ap, cmd_format); | 
391  | 0  |   va_end(ap);  | 
392  |  | 
  | 
393  | 0  |   return result;  | 
394  | 0  | }  |