/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 | } |