Coverage Report

Created: 2025-01-28 06:34

/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 &percnt;s.png, the file will be written in PNG format. By
343
 * default, @in_format is &percnt;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 &percnt;s
349
 * in @cmd_format for the names of the input files, if @in is set, and then
350
 * the next &percnt;s for the output filename, if @out_format is set.
351
 * You can put a number between the &percnt; 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
}