Coverage Report

Created: 2025-01-28 06:32

/src/libvips/libvips/iofuncs/target.c
Line
Count
Source (jump to first uncovered line)
1
/* A byte source/sink .. it can be a pipe, file descriptor, memory area,
2
 * socket, node.js stream, etc.
3
 *
4
 * J.Cupitt, 19/6/14
5
 *
6
 * 26/11/20
7
 *  - use _setmode() on win to force binary write for previously opened
8
 *    descriptors
9
 */
10
11
/*
12
13
  This file is part of VIPS.
14
15
  VIPS is free software; you can redistribute it and/or modify
16
  it under the terms of the GNU Lesser General Public License as published by
17
  the Free Software Foundation; either version 2 of the License, or
18
  (at your option) any later version.
19
20
  This program is distributed in the hope that it will be useful,
21
  but WITHOUT ANY WARRANTY; without even the implied warranty of
22
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23
  GNU Lesser General Public License for more details.
24
25
  You should have received a copy of the GNU Lesser General Public License
26
  along with this program; if not, write to the Free Software
27
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
28
  02110-1301  USA
29
30
 */
31
32
/*
33
34
  These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
35
36
 */
37
38
/*
39
#define VIPS_DEBUG
40
 */
41
42
#ifdef HAVE_CONFIG_H
43
#include <config.h>
44
#endif /*HAVE_CONFIG_H*/
45
#include <glib/gi18n-lib.h>
46
47
#include <stdio.h>
48
#include <stdlib.h>
49
#ifdef HAVE_UNISTD_H
50
#include <unistd.h>
51
#endif /*HAVE_UNISTD_H*/
52
#include <string.h>
53
#include <errno.h>
54
#include <sys/types.h>
55
#include <sys/stat.h>
56
#include <fcntl.h>
57
58
#include <vips/vips.h>
59
60
#ifdef G_OS_WIN32
61
#include <io.h>
62
#endif /*G_OS_WIN32*/
63
64
#include <vips/debug.h>
65
#include <vips/internal.h>
66
67
/* libtiff needs to be able to seek and read back output files, unfortunately,
68
 * so we must open read-write.
69
 */
70
0
#define MODE_READWRITE CLOEXEC(BINARYIZE(O_RDWR | O_CREAT | O_TRUNC))
71
72
G_DEFINE_TYPE(VipsTarget, vips_target, VIPS_TYPE_CONNECTION);
73
74
static void
75
vips_target_finalize(GObject *gobject)
76
15.5k
{
77
15.5k
  VipsTarget *target = VIPS_TARGET(gobject);
78
79
15.5k
  VIPS_DEBUG_MSG("vips_target_finalize:\n");
80
81
15.5k
  if (target->memory_buffer) {
82
8.25k
    g_string_free(target->memory_buffer, TRUE);
83
8.25k
    target->memory_buffer = NULL;
84
8.25k
  }
85
86
15.5k
  if (target->blob) {
87
0
    vips_area_unref(VIPS_AREA(target->blob));
88
0
    target->blob = NULL;
89
0
  }
90
91
15.5k
  if (target->delete_on_close &&
92
15.5k
    target->delete_on_close_filename)
93
0
    g_unlink(target->delete_on_close_filename);
94
95
15.5k
  VIPS_FREE(target->delete_on_close_filename);
96
97
15.5k
  G_OBJECT_CLASS(vips_target_parent_class)->finalize(gobject);
98
15.5k
}
99
100
static int
101
vips_target_build(VipsObject *object)
102
15.5k
{
103
15.5k
  VipsConnection *connection = VIPS_CONNECTION(object);
104
15.5k
  VipsTarget *target = VIPS_TARGET(object);
105
106
15.5k
  VIPS_DEBUG_MSG("vips_target_build: %p\n", connection);
107
108
15.5k
  if (VIPS_OBJECT_CLASS(vips_target_parent_class)->build(object))
109
0
    return -1;
110
111
15.5k
  if (vips_object_argument_isset(object, "filename") &&
112
15.5k
    vips_object_argument_isset(object, "descriptor")) {
113
0
    vips_error(vips_connection_nick(connection),
114
0
      "%s", _("don't set 'filename' and 'descriptor'"));
115
0
    return -1;
116
0
  }
117
118
15.5k
  if (connection->filename) {
119
0
    const char *filename = connection->filename;
120
121
0
    int fd;
122
123
    /* 0644 is rw user, r group and other.
124
     */
125
0
    if ((fd = vips_tracked_open(filename,
126
0
         MODE_READWRITE, 0644)) == -1) {
127
0
      vips_error_system(errno,
128
0
        vips_connection_nick(connection),
129
0
        "%s", _("unable to open for write"));
130
0
      return -1;
131
0
    }
132
133
0
    connection->tracked_descriptor = fd;
134
0
    connection->descriptor = fd;
135
0
  }
136
15.5k
  else if (vips_object_argument_isset(object, "descriptor")) {
137
0
    connection->descriptor = dup(connection->descriptor);
138
0
    connection->close_descriptor = connection->descriptor;
139
140
#ifdef G_OS_WIN32
141
    /* Windows will create eg. stdin and stdout in text mode.
142
     * We always write in binary mode.
143
     */
144
    _setmode(connection->descriptor, _O_BINARY);
145
#endif /*G_OS_WIN32*/
146
0
  }
147
15.5k
  else if (target->memory)
148
15.5k
    target->memory_buffer =
149
15.5k
      g_string_sized_new(VIPS_TARGET_BUFFER_SIZE);
150
151
15.5k
  return 0;
152
15.5k
}
153
154
static gint64
155
vips_target_write_real(VipsTarget *target, const void *data, size_t length)
156
11.3k
{
157
11.3k
  VipsConnection *connection = VIPS_CONNECTION(target);
158
159
11.3k
  gint64 result;
160
161
11.3k
  VIPS_DEBUG_MSG("vips_target_write_real: %zd bytes\n", length);
162
163
11.3k
  if (target->memory_buffer) {
164
11.3k
    VIPS_DEBUG_MSG("vips_target_write_real: to position %zd\n",
165
11.3k
      target->position);
166
167
11.3k
    g_string_overwrite_len(target->memory_buffer, target->position,
168
11.3k
      data, length);
169
11.3k
    target->position += length;
170
11.3k
    result = length;
171
11.3k
  }
172
0
  else
173
0
    result = write(connection->descriptor, data, length);
174
175
11.3k
  return result;
176
11.3k
}
177
178
static gint64
179
vips_target_seek_real(VipsTarget *target, gint64 offset, int whence)
180
0
{
181
0
  VipsConnection *connection = VIPS_CONNECTION(target);
182
0
  const char *nick = vips_connection_nick(connection);
183
184
0
  gint64 new_position;
185
186
0
  VIPS_DEBUG_MSG(
187
0
    "vips_target_seek_real: offset = %" G_GINT64_FORMAT
188
0
    ", whence = %d\n",
189
0
    offset, whence);
190
191
0
  if (target->memory_buffer) {
192
0
    switch (whence) {
193
0
    case SEEK_SET:
194
0
      new_position = offset;
195
0
      break;
196
197
0
    case SEEK_CUR:
198
0
      new_position = target->position + offset;
199
0
      break;
200
201
0
    case SEEK_END:
202
0
      new_position = target->memory_buffer->len + offset;
203
0
      break;
204
205
0
    default:
206
0
      vips_error(nick, "%s", _("bad 'whence'"));
207
0
      return -1;
208
0
    }
209
210
0
    if (new_position > target->memory_buffer->len)
211
0
      g_string_set_size(target->memory_buffer,
212
0
        new_position);
213
214
0
    target->position = new_position;
215
0
  }
216
0
  else
217
    /* We need to use the vips__seek() wrapper so we can seek long
218
     * files on Windows.
219
     */
220
0
    new_position = vips__seek_no_error(connection->descriptor,
221
0
      offset, whence);
222
223
0
  return new_position;
224
0
}
225
226
static gint64
227
vips_target_read_real(VipsTarget *target, void *data, size_t length)
228
0
{
229
0
  gint64 bytes_read;
230
231
0
  VIPS_DEBUG_MSG("vips_target_read_real: %zd bytes\n", length);
232
233
0
  if (target->memory_buffer) {
234
0
    bytes_read = VIPS_MIN(length,
235
0
      target->memory_buffer->len - target->position);
236
237
0
    VIPS_DEBUG_MSG("    %zd bytes from memory\n", bytes_read);
238
0
    memcpy(data,
239
0
      target->memory_buffer->str +
240
0
        target->position,
241
0
      bytes_read);
242
0
    target->position += bytes_read;
243
0
  }
244
0
  else {
245
0
    VipsConnection *connection = VIPS_CONNECTION(target);
246
0
    int fd = connection->descriptor;
247
248
0
    do {
249
0
      bytes_read = read(fd, data, length);
250
0
    } while (bytes_read < 0 && errno == EINTR);
251
0
  }
252
253
0
  VIPS_DEBUG_MSG("  read %zd bytes\n", bytes_read);
254
255
0
  return bytes_read;
256
0
}
257
258
static int
259
vips_target_end_real(VipsTarget *target)
260
0
{
261
0
  VIPS_DEBUG_MSG("vips_target_finish_real:\n");
262
263
0
  return 0;
264
0
}
265
266
static void
267
vips_target_finish_real(VipsTarget *target)
268
0
{
269
0
  VIPS_DEBUG_MSG("vips_target_finish_real:\n");
270
0
}
271
272
static void
273
vips_target_class_init(VipsTargetClass *class)
274
1
{
275
1
  GObjectClass *gobject_class = G_OBJECT_CLASS(class);
276
1
  VipsObjectClass *object_class = VIPS_OBJECT_CLASS(class);
277
278
1
  gobject_class->finalize = vips_target_finalize;
279
1
  gobject_class->set_property = vips_object_set_property;
280
1
  gobject_class->get_property = vips_object_get_property;
281
282
1
  object_class->nickname = "target";
283
1
  object_class->description = _("Target");
284
285
1
  object_class->build = vips_target_build;
286
287
1
  class->write = vips_target_write_real;
288
1
  class->read = vips_target_read_real;
289
1
  class->seek = vips_target_seek_real;
290
1
  class->end = vips_target_end_real;
291
1
  class->finish = vips_target_finish_real;
292
293
1
  VIPS_ARG_BOOL(class, "memory", 3,
294
1
    _("Memory"),
295
1
    _("File descriptor should output to memory"),
296
1
    VIPS_ARGUMENT_OPTIONAL_INPUT,
297
1
    G_STRUCT_OFFSET(VipsTarget, memory),
298
1
    FALSE);
299
300
  /* SET_ALWAYS means that blob is set by C and the obj system is not
301
   * involved in creation or destruction. It can be read at any time.
302
   */
303
1
  VIPS_ARG_BOXED(class, "blob", 4,
304
1
    _("Blob"),
305
1
    _("Blob to save to"),
306
1
    VIPS_ARGUMENT_SET_ALWAYS,
307
1
    G_STRUCT_OFFSET(VipsTarget, blob),
308
1
    VIPS_TYPE_BLOB);
309
1
}
310
311
static void
312
vips_target_init(VipsTarget *target)
313
15.5k
{
314
15.5k
  target->blob = vips_blob_new(NULL, NULL, 0);
315
15.5k
  target->write_point = 0;
316
15.5k
}
317
318
/**
319
 * vips_target_new_to_descriptor:
320
 * @descriptor: write to this file descriptor
321
 *
322
 * Create a target attached to a file descriptor.
323
 * @descriptor is kept open until the target is finalized.
324
 *
325
 * See also: vips_target_new_to_file().
326
 *
327
 * Returns: a new target.
328
 */
329
VipsTarget *
330
vips_target_new_to_descriptor(int descriptor)
331
0
{
332
0
  VipsTarget *target;
333
334
0
  VIPS_DEBUG_MSG("vips_target_new_to_descriptor: %d\n",
335
0
    descriptor);
336
337
0
  target = VIPS_TARGET(g_object_new(VIPS_TYPE_TARGET,
338
0
    "descriptor", descriptor,
339
0
    NULL));
340
341
0
  if (vips_object_build(VIPS_OBJECT(target))) {
342
0
    VIPS_UNREF(target);
343
0
    return NULL;
344
0
  }
345
346
0
  return target;
347
0
}
348
349
/**
350
 * vips_target_new_to_file:
351
 * @filename: write to this file
352
 *
353
 * Create a target attached to a file.
354
 *
355
 * Returns: a new target.
356
 */
357
VipsTarget *
358
vips_target_new_to_file(const char *filename)
359
0
{
360
0
  VipsTarget *target;
361
362
0
  VIPS_DEBUG_MSG("vips_target_new_to_file: %s\n",
363
0
    filename);
364
365
0
  target = VIPS_TARGET(g_object_new(VIPS_TYPE_TARGET,
366
0
    "filename", filename,
367
0
    NULL));
368
369
0
  if (vips_object_build(VIPS_OBJECT(target))) {
370
0
    VIPS_UNREF(target);
371
0
    return NULL;
372
0
  }
373
374
0
  return target;
375
0
}
376
377
/**
378
 * vips_target_new_to_memory:
379
 *
380
 * Create a target which will write to a memory area. Read from @blob to get
381
 * memory.
382
 *
383
 * See also: vips_target_new_to_file().
384
 *
385
 * Returns: a new #VipsConnection
386
 */
387
VipsTarget *
388
vips_target_new_to_memory(void)
389
15.5k
{
390
15.5k
  VipsTarget *target;
391
392
15.5k
  VIPS_DEBUG_MSG("vips_target_new_to_memory:\n");
393
394
15.5k
  target = VIPS_TARGET(g_object_new(VIPS_TYPE_TARGET,
395
15.5k
    "memory", TRUE,
396
15.5k
    NULL));
397
398
15.5k
  if (vips_object_build(VIPS_OBJECT(target))) {
399
0
    VIPS_UNREF(target);
400
0
    return NULL;
401
0
  }
402
403
15.5k
  return target;
404
15.5k
}
405
406
/**
407
 * vips_target_new_temp:
408
 * @based_on: base the temporary target on this target
409
 *
410
 * Create a temporary target -- either a temporary file on disc, or an area in
411
 * memory, depending on what sort of target @based_on is.
412
 *
413
 * See also: vips_target_new_to_file().
414
 *
415
 * Returns: a new target.
416
 */
417
VipsTarget *
418
vips_target_new_temp(VipsTarget *based_on)
419
0
{
420
0
  VipsTarget *target;
421
422
0
  VIPS_DEBUG_MSG("vips_target_new_temp: %p\n", based_on);
423
424
0
  if (vips_connection_filename(VIPS_CONNECTION(based_on))) {
425
0
    int descriptor;
426
0
    char *filename;
427
428
0
    if (!(filename = vips__temp_name("%s.target")))
429
0
      return NULL;
430
0
    if ((descriptor =
431
0
          vips__open_image_write(filename, TRUE)) < 0) {
432
0
      g_free(filename);
433
0
      return NULL;
434
0
    }
435
0
    if (!(target = vips_target_new_to_descriptor(descriptor))) {
436
0
      g_free(filename);
437
0
      vips_tracked_close(descriptor);
438
0
      return NULL;
439
0
    }
440
0
    vips_tracked_close(descriptor);
441
0
    target->delete_on_close = TRUE;
442
0
    target->delete_on_close_filename = filename;
443
0
  }
444
0
  else
445
0
    target = vips_target_new_to_memory();
446
447
0
  return target;
448
0
}
449
450
static int
451
vips_target_write_unbuffered(VipsTarget *target,
452
  const void *data, size_t length)
453
11.3k
{
454
11.3k
  VipsTargetClass *class = VIPS_TARGET_GET_CLASS(target);
455
456
11.3k
  VIPS_DEBUG_MSG("vips_target_write_unbuffered:\n");
457
458
11.3k
  if (target->ended)
459
0
    return 0;
460
461
22.6k
  while (length > 0) {
462
    // write() uses int not size_t on windows, so we need to chunk
463
    // ... max 1gb, why not
464
11.3k
    int chunk_size = VIPS_MIN(1024 * 1024 * 1024, length);
465
11.3k
    gint64 bytes_written = class->write(target, data, chunk_size);
466
467
    /* n == 0 isn't strictly an error, but we treat it as
468
     * one to make sure we don't get stuck in this loop.
469
     */
470
11.3k
    if (bytes_written <= 0) {
471
0
      vips_error_system(errno,
472
0
        vips_connection_nick(
473
0
          VIPS_CONNECTION(target)),
474
0
        "%s", _("write error"));
475
0
      return -1;
476
0
    }
477
478
11.3k
    length -= bytes_written;
479
11.3k
    data = (char *) data + bytes_written;
480
11.3k
  }
481
482
11.3k
  return 0;
483
11.3k
}
484
485
static int
486
vips_target_flush(VipsTarget *target)
487
11.3k
{
488
11.3k
  g_assert(target->write_point >= 0);
489
11.3k
  g_assert(target->write_point <= VIPS_TARGET_BUFFER_SIZE);
490
491
11.3k
  VIPS_DEBUG_MSG("vips_target_flush:\n");
492
493
11.3k
  if (target->write_point > 0) {
494
11.3k
    if (vips_target_write_unbuffered(target,
495
11.3k
        target->output_buffer, target->write_point))
496
0
      return -1;
497
11.3k
    target->write_point = 0;
498
11.3k
  }
499
500
11.3k
  return 0;
501
11.3k
}
502
503
/**
504
 * vips_target_write:
505
 * @target: target to operate on
506
 * @buffer: bytes to write
507
 * @length: length of @buffer in bytes
508
 *
509
 * Write @length bytes from @buffer to the output.
510
 *
511
 * Returns: 0 on success, -1 on error.
512
 */
513
int
514
vips_target_write(VipsTarget *target, const void *buffer, size_t length)
515
29.1M
{
516
29.1M
  VIPS_DEBUG_MSG("vips_target_write: %zd bytes\n", length);
517
518
29.1M
  if (length > VIPS_TARGET_BUFFER_SIZE - target->write_point &&
519
29.1M
    vips_target_flush(target))
520
0
    return -1;
521
522
29.1M
  if (length > VIPS_TARGET_BUFFER_SIZE - target->write_point) {
523
    /* Still too large? Do an unbuffered write.
524
     */
525
0
    if (vips_target_write_unbuffered(target, buffer, length))
526
0
      return -1;
527
0
  }
528
29.1M
  else {
529
29.1M
    memcpy(target->output_buffer + target->write_point,
530
29.1M
      buffer, length);
531
29.1M
    target->write_point += length;
532
29.1M
  }
533
534
29.1M
  return 0;
535
29.1M
}
536
537
/**
538
 * vips_target_read:
539
 * @target: target to operate on
540
 * @buffer: store bytes here
541
 * @length: length of @buffer in bytes
542
 *
543
 * Read up to @length bytes from @target and store the bytes in @buffer.
544
 * Return the number of bytes actually read. If all bytes have been read from
545
 * the file, return 0.
546
 *
547
 * Arguments exactly as read(2).
548
 *
549
 * Reading from a target sounds weird, but libtiff needs this for
550
 * multi-page writes. This method will fail for targets like pipes.
551
 *
552
 * Returns: the number of bytes read, 0 on end of file, -1 on error.
553
 */
554
gint64
555
vips_target_read(VipsTarget *target, void *buffer, size_t length)
556
0
{
557
0
  VipsTargetClass *class = VIPS_TARGET_GET_CLASS(target);
558
559
0
  VIPS_DEBUG_MSG("vips_target_read: %zd bytes\n", length);
560
561
0
  if (vips_target_flush(target))
562
0
    return -1;
563
564
0
  return class->read(target, buffer, length);
565
0
}
566
567
/**
568
 * vips_target_seek:
569
 * @target: target to operate on
570
 * @position: position to seek to
571
 * @whence: seek relative to beginning, offset, or end
572
 *
573
 * Seek the target. This behaves exactly as lseek(2).
574
 *
575
 * Seeking a target sounds weird, but libtiff needs this. This method will
576
 * fail for targets like pipes.
577
 *
578
 * Returns: the new seek position, -1 on error.
579
 */
580
gint64
581
vips_target_seek(VipsTarget *target, gint64 position, int whence)
582
0
{
583
0
  VipsTargetClass *class = VIPS_TARGET_GET_CLASS(target);
584
585
0
  gint64 new_position;
586
587
0
  VIPS_DEBUG_MSG("vips_target_seek: pos = %" G_GINT64_FORMAT
588
0
    ", whence = %d\n",
589
0
    position, whence);
590
591
0
  if (vips_target_flush(target))
592
0
    return -1;
593
594
0
  new_position = class->seek(target, position, whence);
595
596
0
  VIPS_DEBUG_MSG("vips_target_seek: new_position = %" G_GINT64_FORMAT "\n",
597
0
    new_position);
598
599
0
  return new_position;
600
0
}
601
602
/**
603
 * vips_target_end:
604
 * @target: target to operate on
605
 * @buffer: bytes to write
606
 * @length: length of @buffer in bytes
607
 *
608
 * Call this at the end of write to make the target do any cleaning up. You
609
 * can call it many times.
610
 *
611
 * After a target has been ended, further writes will do nothing.
612
 *
613
 * Returns: 0 on success, -1 on error.
614
 */
615
int
616
vips_target_end(VipsTarget *target)
617
7.30k
{
618
7.30k
  VipsTargetClass *class = VIPS_TARGET_GET_CLASS(target);
619
620
7.30k
  VIPS_DEBUG_MSG("vips_target_end:\n");
621
622
7.30k
  if (target->ended)
623
0
    return 0;
624
625
7.30k
  if (vips_target_flush(target))
626
0
    return -1;
627
628
  /* Move the target buffer into the blob so it can be read out.
629
   */
630
7.30k
  if (target->memory_buffer) {
631
7.30k
    const char *data;
632
7.30k
    size_t length;
633
634
7.30k
    length = target->memory_buffer->len;
635
7.30k
    data = g_string_free(target->memory_buffer, FALSE);
636
7.30k
    target->memory_buffer = NULL;
637
7.30k
    vips_blob_set(target->blob,
638
7.30k
      (VipsCallbackFn) vips_area_free_cb, data, length);
639
7.30k
  }
640
0
  else {
641
0
    if (class->end(target))
642
0
      return -1;
643
0
  }
644
645
7.30k
  target->ended = TRUE;
646
647
7.30k
  return 0;
648
7.30k
}
649
650
/**
651
 * vips_target_steal:
652
 * @target: target to operate on
653
 * @length: return number of bytes of data
654
 *
655
 * Memory targets only (see vips_target_new_to_memory()). Steal all data
656
 * written to the target so far, and call vips_target_end().
657
 *
658
 * You must free the returned pointer with g_free().
659
 *
660
 * The data is NOT automatically null-terminated. vips_target_putc() a '\0'
661
 * before calling this to get a null-terminated string.
662
 *
663
 * You can't call this after vips_target_end(), since that moves the data to a
664
 * blob, and we can't steal from that in case the pointer has been be shared.
665
 *
666
 * You can't call this function more than once.
667
 *
668
 * Returns: (array length=length) (element-type guint8) (transfer full): the
669
 * data
670
 */
671
unsigned char *
672
vips_target_steal(VipsTarget *target, size_t *length)
673
0
{
674
0
  const char *data;
675
676
0
  (void) vips_target_flush(target);
677
678
0
  data = NULL;
679
680
0
  if (target->memory_buffer) {
681
0
    if (length)
682
0
      *length = target->memory_buffer->len;
683
0
    data = g_string_free(target->memory_buffer, FALSE);
684
0
    target->memory_buffer = NULL;
685
686
    /* We must have a valid byte array, or end will fail.
687
     */
688
0
    target->memory_buffer = g_string_sized_new(0);
689
0
  }
690
691
0
  if (vips_target_end(target))
692
0
    return NULL;
693
694
0
  return (unsigned char *) data;
695
0
}
696
697
/**
698
 * vips_target_steal_text:
699
 * @target: target to operate on
700
 *
701
 * As vips_target_steal_text(), but return a null-terminated string.
702
 *
703
 * Returns: (transfer full): target contents as a null-terminated string.
704
 */
705
char *
706
vips_target_steal_text(VipsTarget *target)
707
0
{
708
0
  vips_target_putc(target, '\0');
709
710
0
  return (char *) vips_target_steal(target, NULL);
711
0
}
712
713
/**
714
 * vips_target_putc:
715
 * @target: target to operate on
716
 * @ch: character to write
717
 *
718
 * Write a single character @ch to @target. See the macro VIPS_TARGET_PUTC()
719
 * for a faster way to do this.
720
 *
721
 * Returns: 0 on success, -1 on error.
722
 */
723
int
724
vips_target_putc(VipsTarget *target, int ch)
725
0
{
726
0
  VIPS_DEBUG_MSG("vips_target_putc: %d\n", ch);
727
728
0
  if (target->write_point >= VIPS_TARGET_BUFFER_SIZE &&
729
0
    vips_target_flush(target))
730
0
    return -1;
731
732
0
  target->output_buffer[target->write_point++] = ch;
733
734
0
  return 0;
735
0
}
736
737
/**
738
 * vips_target_writes:
739
 * @target: target to operate on
740
 * @str: string to write
741
 *
742
 * Write a null-terminated string to @target.
743
 *
744
 * Returns: 0 on success, and -1 on error.
745
 */
746
int
747
vips_target_writes(VipsTarget *target, const char *str)
748
29.1M
{
749
29.1M
  return vips_target_write(target,
750
29.1M
    (unsigned char *) str, strlen(str));
751
29.1M
}
752
753
/**
754
 * vips_target_writef:
755
 * @target: target to operate on
756
 * @fmt: <function>printf()</function>-style format string
757
 * @...: arguments to format string
758
 *
759
 * Format the string and write to @target.
760
 *
761
 * Returns: 0 on success, and -1 on error.
762
 */
763
int
764
vips_target_writef(VipsTarget *target, const char *fmt, ...)
765
14.8k
{
766
14.8k
  va_list ap;
767
14.8k
  char *line;
768
14.8k
  int result;
769
770
14.8k
  va_start(ap, fmt);
771
14.8k
  line = g_strdup_vprintf(fmt, ap);
772
14.8k
  va_end(ap);
773
774
14.8k
  result = vips_target_writes(target, line);
775
776
14.8k
  g_free(line);
777
778
14.8k
  return result;
779
14.8k
}
780
781
/**
782
 * vips_target_write_amp:
783
 * @target: target to operate on
784
 * @str: string to write
785
 *
786
 * Write @str to @target, but escape stuff that xml hates in text. Our
787
 * argument string is utf-8.
788
 *
789
 * XML rules:
790
 *
791
 * - We must escape &<>
792
 * - Don't escape \n, \t, \r
793
 * - Do escape the other ASCII codes.
794
 *
795
 * Returns: 0 on success, -1 on error.
796
 */
797
int
798
vips_target_write_amp(VipsTarget *target, const char *str)
799
0
{
800
0
  const char *p;
801
802
0
  for (p = str; *p; p++)
803
0
    if (*p < 32 &&
804
0
      *p != '\n' &&
805
0
      *p != '\t' &&
806
0
      *p != '\r') {
807
      /* You'd think we could output "&#x02%x;", but xml
808
       * 1.0 parsers barf on that. xml 1.1 allows this, but
809
       * there are almost no parsers.
810
       *
811
       * U+2400 onwards are unicode glyphs for the ASCII
812
       * control characters, so we can use them -- thanks
813
       * electroly.
814
       */
815
0
      if (vips_target_writef(target,
816
0
          "&#x%04x;", 0x2400 + *p))
817
0
        return -1;
818
0
    }
819
0
    else if (*p == '<') {
820
0
      if (vips_target_writes(target, "&lt;"))
821
0
        return -1;
822
0
    }
823
0
    else if (*p == '>') {
824
0
      if (vips_target_writes(target, "&gt;"))
825
0
        return -1;
826
0
    }
827
0
    else if (*p == '&') {
828
0
      if (vips_target_writes(target, "&amp;"))
829
0
        return -1;
830
0
    }
831
0
    else {
832
0
      if (VIPS_TARGET_PUTC(target, *p))
833
0
        return -1;
834
0
    }
835
836
0
  return 0;
837
0
}