Coverage Report

Created: 2025-06-24 07:01

/src/ghostpdl/base/gxclfile.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2001-2023 Artifex Software, Inc.
2
   All Rights Reserved.
3
4
   This software is provided AS-IS with no warranty, either express or
5
   implied.
6
7
   This software is distributed under license and may not be copied,
8
   modified or distributed except as expressly authorized under the terms
9
   of the license contained in the file LICENSE in this distribution.
10
11
   Refer to licensing information at http://www.artifex.com or contact
12
   Artifex Software, Inc.,  39 Mesa Street, Suite 108A, San Francisco,
13
   CA 94129, USA, for further information.
14
*/
15
16
17
/* File-based command list implementation */
18
#include "assert_.h"
19
#include "stdio_.h"
20
#include "string_.h"
21
#include "unistd_.h"
22
#include "gserrors.h"
23
#include "gsmemory.h"
24
#include "gp.h"
25
#include "gxclio.h"
26
27
#include "valgrind.h"
28
29
30
/* This is an implementation of the command list I/O interface */
31
/* that uses the file system for storage. */
32
33
/* clist cache code so that wrapped files don't incur a performance penalty */
34
24.7k
#define CL_CACHE_NSLOTS (3)
35
24.7k
#define CL_CACHE_SLOT_SIZE_LOG2 (15)
36
14.3M
#define CL_CACHE_SLOT_EMPTY (-1)
37
38
static clist_io_procs_t clist_io_procs_file;
39
40
typedef struct
41
{
42
    int64_t blocknum;
43
    byte *base;
44
} CL_CACHE_SLOT;
45
46
typedef struct
47
{
48
    int block_size;   /* full block size, MUST BE power of 2 */
49
    int nslots;
50
    int64_t filesize;
51
    gs_memory_t *memory;  /* save our allocator */
52
    CL_CACHE_SLOT *slots; /* array of slots */
53
    byte *base;                 /* save base of slot data area */
54
} CL_CACHE;
55
56
/* Forward references */
57
CL_CACHE *cl_cache_alloc(gs_memory_t *mem);
58
void cl_cache_destroy(CL_CACHE *cache);
59
CL_CACHE *cl_cache_read_init(CL_CACHE *cache, int nslots, int64_t block_size, int64_t filesize);
60
int cl_cache_read(byte *data, int len, int64_t pos, CL_CACHE *cache);
61
CL_CACHE_SLOT * cl_cache_get_empty_slot(CL_CACHE *cache, int64_t pos);
62
void cl_cache_load_slot(CL_CACHE *cache, CL_CACHE_SLOT *slot, int64_t pos, byte *data, int len);
63
64
1.70G
#define CL_CACHE_NEEDS_INIT(cache) (cache != NULL && cache->filesize == 0)
65
66
CL_CACHE *
67
cl_cache_alloc(gs_memory_t *mem)
68
24.7k
{
69
24.7k
    CL_CACHE *cache;
70
71
    /* allocate and initialilze the cache to filesize = 0 to signal read_init needed */
72
24.7k
    cache = (CL_CACHE *)gs_alloc_bytes(mem, sizeof(CL_CACHE), "alloc CL_CACHE");
73
24.7k
    if (cache != NULL) {
74
24.7k
        cache->filesize = 0;
75
24.7k
        cache->nslots = 0;
76
24.7k
        cache->block_size = 0;
77
24.7k
        cache->slots = NULL;
78
24.7k
        cache->base = NULL;
79
24.7k
        cache->memory = mem;
80
24.7k
    }
81
24.7k
    return cache;
82
24.7k
}
83
84
void
85
cl_cache_destroy(CL_CACHE *cache)
86
24.7k
{
87
24.7k
    if (cache == NULL)
88
0
        return;
89
90
24.7k
    if (cache->slots != NULL) {
91
24.7k
        gs_free_object(cache->memory, cache->base, "CL_CACHE SLOT data");
92
24.7k
        gs_free_object(cache->memory, cache->slots, "CL_CACHE slots array");
93
24.7k
    }
94
24.7k
    gs_free_object(cache->memory, cache, "CL_CACHE for IFILE");
95
24.7k
}
96
97
/* Set the cache up for reading. The filesize is used for EOF */
98
CL_CACHE *
99
cl_cache_read_init(CL_CACHE *cache, int nslots, int64_t block_size, int64_t filesize)
100
24.7k
{
101
    /* NB: if fail, and cache is still NULL, proceed without cache, reading will cope */
102
24.7k
    if (cache == NULL || cache->filesize != 0)
103
0
        return cache;   /* once we've done the init, filesize will be set */
104
105
24.7k
    if ((filesize+block_size)/block_size < nslots)
106
20.2k
        nslots = (filesize + block_size)/block_size; /* limit at blocks needed for entire file */
107
24.7k
    cache->slots = (CL_CACHE_SLOT *)gs_alloc_bytes(cache->memory, nslots * sizeof(CL_CACHE_SLOT),
108
24.7k
                                                       "CL_CACHE slots array");
109
24.7k
    if (cache->slots == NULL) {
110
0
        gs_free_object(cache->memory, cache, "Free CL_CACHE for IFILE");
111
0
        cache = NULL;     /* cache not possible */
112
24.7k
    } else {
113
24.7k
        cache->slots[0].base = (byte *)gs_alloc_bytes(cache->memory, nslots * block_size,
114
24.7k
                                                       "CL_CACHE_SLOT data");
115
24.7k
        if (cache->slots[0].base == NULL) {
116
0
            gs_free_object(cache->memory, cache->slots, "Free CL_CACHE for IFILE");
117
0
            gs_free_object(cache->memory, cache, "Free CL_CACHE for IFILE");
118
0
            cache = NULL;     /* cache not possible */
119
24.7k
        } else {
120
            /* success, initialize the slots */
121
24.7k
            int i;
122
123
59.0k
            for (i=0; i < nslots; i++) {
124
34.3k
                cache->slots[i].blocknum = CL_CACHE_SLOT_EMPTY;
125
34.3k
                cache->slots[i].base = cache->slots[0].base + (i * block_size);
126
34.3k
            }
127
24.7k
            cache->base = cache->slots[0].base;         /* save for the 'destroy' (slots array moves around) */
128
24.7k
            cache->nslots = nslots;
129
24.7k
            cache->block_size = block_size;
130
24.7k
            cache->filesize = filesize;
131
24.7k
        }
132
24.7k
    }
133
24.7k
    return cache; /* May be NULL. If so, no cache used */
134
24.7k
}
135
136
/* Find the cache for the slot containing the 'pos'. */
137
/* return the number of bytes read, up to 'len' bytes */
138
/* returns 0 if 'pos' not in cache, -1 if pos at or past EOF. */
139
int
140
cl_cache_read(byte *data, int len, int64_t pos, CL_CACHE *cache)
141
1.67G
{
142
1.67G
    int nread = 0;
143
1.67G
    int slot;
144
1.67G
    int offset;
145
1.67G
    int64_t blocknum = pos / cache->block_size;
146
147
1.67G
    if (pos >= cache->filesize)
148
0
        return -1;
149
150
    /* find the slot */
151
1.69G
    for (slot = 0; slot < cache->nslots; slot++) {
152
1.69G
        if (blocknum == cache->slots[slot].blocknum)
153
1.66G
            break;
154
1.69G
    }
155
1.67G
    if (slot >= cache->nslots)
156
7.19M
        return 0;               /* block not in cache */
157
158
1.66G
    if (slot != 0) {
159
        /* move the slot we found to the top, moving the rest down */
160
1.51M
        byte *base = cache->slots[slot].base;
161
1.51M
        int i;
162
163
3.61M
        for (i = slot; i > 0; i--) {
164
2.09M
            cache->slots[i].base = cache->slots[i-1].base;
165
2.09M
            cache->slots[i].blocknum = cache->slots[i-1].blocknum;
166
2.09M
        }
167
1.51M
        cache->slots[0].blocknum = blocknum;
168
1.51M
        cache->slots[0].base = base;
169
1.51M
    }
170
1.66G
    offset = pos - cache->slots[0].blocknum * cache->block_size;
171
1.66G
    nread = min(cache->block_size - offset, len);
172
1.66G
    if (nread + pos > cache->filesize)
173
0
        nread = cache->filesize - pos; /* limit for EOF */
174
1.66G
    memcpy(data, cache->slots[0].base + offset, nread);
175
1.66G
    return nread;
176
1.67G
}
177
178
/* 'pos' not used yet */
179
/* discard the LRU, move remaining slots down and return the first as new MRU */
180
CL_CACHE_SLOT *
181
cl_cache_get_empty_slot(CL_CACHE *cache, int64_t pos)
182
7.19M
{
183
    /* the LRU is in the last slot, so re-use it */
184
7.19M
    CL_CACHE_SLOT *pslot = &(cache->slots[0]);             /* slot used will always be first, possibly after moving */
185
7.19M
    int64_t slot0_blocknum = pslot->blocknum;
186
187
7.19M
    if (slot0_blocknum == CL_CACHE_SLOT_EMPTY)
188
24.7k
        return pslot;
189
190
    /* if more than on slot in the cache, handle moving slots to bump the LRU (last) */
191
    /* If the block at slot 0 hasn't been flushed at least once before, just use slot 0 */
192
7.16M
    if (cache->nslots > 1) {
193
        /* rotate the cache to re-use the last slot (LRU) and move it to the top, moving the rest down */
194
7.16M
        byte *last_slot_base = cache->slots[cache->nslots - 1].base;    /* save the base for the last slot */
195
7.16M
        int i;
196
197
        /* move the rest down */
198
21.4M
        for (i=cache->nslots - 1; i > 0; i--) {
199
14.3M
            cache->slots[i].blocknum = cache->slots[i-1].blocknum;
200
14.3M
            cache->slots[i].base = cache->slots[i-1].base;
201
14.3M
        }
202
7.16M
        pslot->base = last_slot_base;
203
7.16M
    }
204
7.16M
    pslot->blocknum = CL_CACHE_SLOT_EMPTY;
205
7.16M
    return pslot;
206
7.19M
}
207
208
void
209
cl_cache_load_slot(CL_CACHE *cache, CL_CACHE_SLOT *slot, int64_t pos, byte *data, int len)
210
7.19M
{
211
7.19M
    slot->blocknum = pos / cache->block_size;
212
7.19M
    memmove(slot->base, data, len);
213
7.19M
}
214
215
/* Use our own FILE structure so that, on some platforms, we write and read
216
 * tmp files via a single file descriptor. That allows cleaning of tmp files
217
 * to be addressed via DELETE_ON_CLOSE under Windows, and immediate unlink
218
 * after opening under Linux. When running in this mode, we keep our own
219
 * record of position within the file for the sake of thread safety
220
 */
221
222
1.29M
#define ENC_FILE_STR ("encoded_file_ptr_%p")
223
1.27M
#define ENC_FILE_STRX ("encoded_file_ptr_0x%p")
224
225
typedef struct
226
{
227
    gs_memory_t *mem;
228
    gp_file *f;
229
    int64_t pos;
230
    int64_t filesize;   /* filesize maintained by clist_fwrite */
231
    CL_CACHE *cache;
232
} IFILE;
233
234
static void
235
file_to_fake_path(clist_file_ptr file, char fname[gp_file_name_sizeof])
236
12.3k
{
237
12.3k
    gs_snprintf(fname, gp_file_name_sizeof, ENC_FILE_STR, file);
238
12.3k
}
239
240
static clist_file_ptr
241
fake_path_to_file(const char *fname)
242
1.27M
{
243
1.27M
    clist_file_ptr i1, i2;
244
245
1.27M
    int r1 = sscanf(fname, ENC_FILE_STR, &i1);
246
1.27M
    int r2 = sscanf(fname, ENC_FILE_STRX, &i2);
247
1.27M
    return r2 == 1 ? i2 : (r1 == 1 ? i1 : NULL);
248
1.27M
}
249
250
static IFILE *wrap_file(gs_memory_t *mem, gp_file *f, const char *fmode)
251
12.3k
{
252
12.3k
    IFILE *ifile;
253
254
12.3k
    if (!f) return NULL;
255
12.3k
    ifile = (IFILE *)gs_alloc_bytes(mem->non_gc_memory, sizeof(*ifile), "Allocate wrapped IFILE");
256
12.3k
    if (!ifile) {
257
0
        gp_fclose(f);
258
0
        return NULL;
259
0
    }
260
12.3k
    ifile->mem = mem->non_gc_memory;
261
12.3k
    ifile->f = f;
262
12.3k
    ifile->pos = 0;
263
12.3k
    ifile->filesize = 0;
264
12.3k
    ifile->cache = cl_cache_alloc(ifile->mem);
265
12.3k
    return ifile;
266
12.3k
}
267
268
static int clist_close_file(IFILE *ifile)
269
12.3k
{
270
12.3k
    int res = 0;
271
12.3k
    if (ifile) {
272
12.3k
        if (ifile->f != NULL)
273
12.3k
            res = gp_fclose(ifile->f);
274
12.3k
        if (ifile->cache != NULL)
275
12.3k
            cl_cache_destroy(ifile->cache);
276
12.3k
        gs_free_object(ifile->mem, ifile, "Free wrapped IFILE");
277
12.3k
    }
278
12.3k
    return res;
279
12.3k
}
280
281
/* ------ Open/close/unlink ------ */
282
283
static int
284
clist_fopen(char fname[gp_file_name_sizeof], const char *fmode,
285
            clist_file_ptr * pcf, gs_memory_t * mem, gs_memory_t *data_mem,
286
            bool ok_to_compress)
287
12.3k
{
288
12.3k
    if (*fname == 0) {
289
12.3k
        if (fmode[0] == 'r')
290
0
            return_error(gs_error_invalidfileaccess);
291
12.3k
        if (gp_can_share_fdesc()) {
292
12.3k
            *pcf = (clist_file_ptr)wrap_file(mem, gp_open_scratch_file_rm(mem,
293
12.3k
                                                       gp_scratch_file_name_prefix,
294
12.3k
                                                       fname, fmode), fmode);
295
            /* If the platform supports FILE duplication then we overwrite the
296
             * file name with an encoded form of the FILE pointer */
297
12.3k
            if (*pcf != NULL)
298
12.3k
                file_to_fake_path(*pcf, fname);
299
12.3k
        } else {
300
0
            *pcf = (clist_file_ptr)wrap_file(mem, gp_open_scratch_file(mem,
301
0
                                                       gp_scratch_file_name_prefix,
302
0
                                                       fname, fmode), fmode);
303
0
        }
304
12.3k
    } else {
305
0
        clist_file_ptr ocf = fake_path_to_file(fname);
306
0
        if (ocf) {
307
            /*  A special (fake) fname is passed in. If so, clone the FILE handle */
308
0
            *pcf = wrap_file(mem, gp_fdup(((IFILE *)ocf)->f, fmode), fmode);
309
            /* when cloning, copy other parts not done by wrap_file */
310
0
            if (*pcf)
311
0
                ((IFILE *)(*pcf))->filesize = ((IFILE *)ocf)->filesize;
312
0
        } else {
313
0
            *pcf = wrap_file(mem, gp_fopen(mem, fname, fmode), fmode);
314
0
        }
315
0
    }
316
317
12.3k
    if (*pcf == NULL) {
318
0
        emprintf1(mem, "Could not open the scratch file %s.\n", fname);
319
0
        return_error(gs_error_invalidfileaccess);
320
0
    }
321
322
12.3k
    return 0;
323
12.3k
}
324
325
static int
326
clist_unlink(const char *fname)
327
0
{
328
0
    clist_file_ptr ocf = fake_path_to_file(fname);
329
0
    if (ocf) {
330
        /* fname is an encoded file pointer. The file will either have been
331
         * created with the delete-on-close option, or already have been
332
         * unlinked. We need only close the FILE */
333
0
        return clist_close_file((IFILE *)ocf) != 0 ? gs_note_error(gs_error_ioerror) : 0;
334
0
    } else {
335
0
        return (unlink(fname) != 0 ? gs_note_error(gs_error_ioerror) : 0);
336
0
    }
337
0
}
338
339
static int
340
clist_fclose(clist_file_ptr cf, const char *fname, bool delete)
341
12.3k
{
342
12.3k
    clist_file_ptr ocf = fake_path_to_file(fname);
343
12.3k
    if (ocf == cf) {
344
        /* fname is an encoded file pointer, and cf is the FILE used to create it.
345
         * We shouldn't close it unless we have been asked to delete it, in which
346
         * case closing it will delete it */
347
12.3k
        return delete ? (clist_close_file((IFILE *)ocf) ? gs_note_error(gs_error_ioerror) : 0) : 0;
348
12.3k
    } else {
349
0
        return (clist_close_file((IFILE *) cf) != 0 ? gs_note_error(gs_error_ioerror) :
350
0
                delete ? clist_unlink(fname) :
351
0
                0);
352
0
    }
353
12.3k
}
354
355
/* ------ Writing ------ */
356
357
static int
358
clist_fwrite_chars(const void *data, uint len, clist_file_ptr cf)
359
42.3M
{
360
42.3M
    int res = 0;
361
42.3M
    IFILE *icf = (IFILE *)cf;
362
363
42.3M
    if (gp_can_share_fdesc()) {
364
42.3M
        res = gp_fpwrite((char *)data, len, ((IFILE *)cf)->pos, ((IFILE *)cf)->f);
365
42.3M
    } else {
366
0
        res = gp_fwrite(data, 1, len, ((IFILE *)cf)->f);
367
0
    }
368
42.3M
    if (res >= 0)
369
42.3M
        icf->pos += len;
370
42.3M
    icf->filesize = icf->pos; /* write truncates file */
371
42.3M
    if (!CL_CACHE_NEEDS_INIT(icf->cache)) {
372
        /* writing invalidates the read cache */
373
0
        cl_cache_destroy(icf->cache);
374
0
        icf->cache = NULL;
375
0
    }
376
42.3M
    return res;
377
42.3M
}
378
379
/* ------ Reading ------ */
380
381
static int
382
clist_fread_chars(void *data, uint len, clist_file_ptr cf)
383
1.66G
{
384
1.66G
    int nread = 0;
385
386
1.66G
    if (gp_can_share_fdesc()) {
387
1.66G
        IFILE *icf = (IFILE *)cf;
388
1.66G
        byte *dp = data;
389
390
        /* if we have a cache, check if it needs init, and do it */
391
1.66G
        if (CL_CACHE_NEEDS_INIT(icf->cache)) {
392
24.7k
            icf->cache = cl_cache_read_init(icf->cache, CL_CACHE_NSLOTS, 1<<CL_CACHE_SLOT_SIZE_LOG2, icf->filesize);
393
24.7k
        }
394
        /* cl_cache_read_init may have failed, and set cache to NULL, check before using it */
395
1.66G
        if (icf->cache != NULL) {
396
1.67G
            do {
397
1.67G
                int n;
398
399
1.67G
                if ((n = cl_cache_read(dp, len-nread, icf->pos+nread, icf->cache)) < 0)
400
0
                    break;
401
1.67G
                if (n == 0) {
402
                    /* pos was not in cache, get a slot and load it, then loop */
403
7.19M
                    CL_CACHE_SLOT *slot = cl_cache_get_empty_slot(icf->cache, icf->pos+nread);  /* cannot fail */
404
7.19M
                    int64_t block_pos = (icf->pos+nread) & ~(icf->cache->block_size - 1);
405
7.19M
                    int fill_len = gp_fpread((char *)(slot->base), icf->cache->block_size, block_pos, icf->f);
406
407
7.19M
                    cl_cache_load_slot(icf->cache, slot, block_pos, slot->base, fill_len);
408
7.19M
                }
409
1.67G
                nread += n;
410
1.67G
                dp += n;
411
1.67G
            } while (nread < len);
412
1.66G
        } else {
413
             /* no cache -- just do the read */
414
0
            nread = gp_fpread(data, len, icf->pos, icf->f);
415
0
        }
416
1.66G
        if (nread >= 0)
417
1.66G
            icf->pos += nread;
418
1.66G
    } else {
419
0
        gp_file *f = ((IFILE *)cf)->f;
420
0
        byte *str = data;
421
422
        /* The typical implementation of fread */
423
        /* is extremely inefficient for small counts, */
424
        /* so we just use straight-line code instead. */
425
0
        switch (len) {
426
0
            default:
427
0
                return gp_fread(str, 1, len, f);
428
0
            case 8:
429
0
                *str++ = (byte) gp_fgetc(f);
430
0
            case 7:
431
0
                *str++ = (byte) gp_fgetc(f);
432
0
            case 6:
433
0
                *str++ = (byte) gp_fgetc(f);
434
0
            case 5:
435
0
                *str++ = (byte) gp_fgetc(f);
436
0
            case 4:
437
0
                *str++ = (byte) gp_fgetc(f);
438
0
            case 3:
439
0
                *str++ = (byte) gp_fgetc(f);
440
0
            case 2:
441
0
                *str++ = (byte) gp_fgetc(f);
442
0
            case 1:
443
0
                *str = (byte) gp_fgetc(f);
444
0
        }
445
0
        nread = len;
446
0
    }
447
1.66G
    return nread;
448
1.66G
}
449
450
/* ------ Position/status ------ */
451
452
static int
453
clist_set_memory_warning(clist_file_ptr cf, int bytes_left)
454
24.7k
{
455
24.7k
    return 0;     /* no-op */
456
24.7k
}
457
458
static int
459
clist_ferror_code(clist_file_ptr cf)
460
115M
{
461
115M
    return (gp_ferror(((IFILE *)cf)->f) ? gs_error_ioerror : 0);
462
115M
}
463
464
static int64_t
465
clist_ftell(clist_file_ptr cf)
466
10.0M
{
467
10.0M
    IFILE *ifile = (IFILE *)cf;
468
469
10.0M
    return gp_can_share_fdesc() ? ifile->pos : gp_ftell(ifile->f);
470
10.0M
}
471
472
static int
473
clist_rewind(clist_file_ptr cf, bool discard_data, const char *fname)
474
1.26M
{
475
1.26M
    gp_file *f = ((IFILE *)cf)->f;
476
1.26M
    IFILE *ocf = fake_path_to_file(fname);
477
1.26M
    char fmode[4];
478
479
1.26M
    snprintf(fmode, sizeof(fmode), "w+%s", gp_fmode_binary_suffix);
480
481
1.26M
    if (ocf) {
482
1.26M
        if (discard_data) {
483
            /* fname is an encoded ifile pointer. We can use an entirely
484
             * new scratch file. */
485
12.3k
            char tfname[gp_file_name_sizeof] = {0};
486
12.3k
            const gs_memory_t *mem = ocf->f->memory;
487
12.3k
            gp_fclose(ocf->f);
488
12.3k
            ocf->f = gp_open_scratch_file_rm(mem, gp_scratch_file_name_prefix, tfname, fmode);
489
12.3k
            if (ocf->f == NULL)
490
0
                return_error(gs_error_ioerror);
491
            /* if there was a cache, get rid of it an get a new (empty) one */
492
            /* When we start reading, we will allocate a cache based on the filesize */
493
12.3k
            if (ocf->cache != NULL) {
494
12.3k
                cl_cache_destroy(ocf->cache);
495
12.3k
                ocf->cache = cl_cache_alloc(ocf->mem);
496
12.3k
                if (ocf->cache == NULL)
497
0
                    return_error(gs_error_ioerror);
498
12.3k
            }
499
12.3k
            ((IFILE *)cf)->filesize = 0;
500
12.3k
        }
501
1.26M
        ((IFILE *)cf)->pos = 0;
502
1.26M
    } else {
503
0
        if (discard_data) {
504
            /*
505
             * The ANSI C stdio specification provides no operation for
506
             * truncating a file at a given position, or even just for
507
             * deleting its contents; we have to use a bizarre workaround to
508
             * get the same effect.
509
             */
510
511
            /* Opening with "w" mode deletes the contents when closing. */
512
0
            f = gp_freopen(fname, gp_fmode_wb, f);
513
0
            if (f == NULL) return_error(gs_error_ioerror);
514
0
            ((IFILE *)cf)->f = gp_freopen(fname, fmode, f);
515
0
            if (((IFILE *)cf)->f == NULL) return_error(gs_error_ioerror);
516
0
            ((IFILE *)cf)->pos = 0;
517
0
            ((IFILE *)cf)->filesize = 0;
518
0
        } else {
519
0
            gp_rewind(f);
520
0
        }
521
0
    }
522
1.26M
    return 0;
523
1.26M
}
524
525
static int
526
clist_fseek(clist_file_ptr cf, int64_t offset, int mode, const char *ignore_fname)
527
90.3M
{
528
90.3M
    IFILE *ifile = (IFILE *)cf;
529
90.3M
    int res = 0;
530
531
90.3M
    if (!gp_can_share_fdesc()) {
532
0
        res = gp_fseek(ifile->f, offset, mode);
533
0
    }
534
    /* NB: if gp_can_share_fdesc, we don't actually seek */
535
    /* The following lgtm tag is required because on some platforms
536
     * !gp_can_share_fdesc() is always true, so the value of res is
537
     * known. On other platforms though, this is NOT true. */
538
90.3M
    if (res >= 0) { /* lgtm [cpp/constant-comparison] */
539
        /* Update the ifile->pos */
540
90.3M
        switch (mode) {
541
90.3M
            case SEEK_SET:
542
90.3M
                ifile->pos = offset;
543
90.3M
                break;
544
0
            case SEEK_CUR:
545
0
                ifile->pos += offset;
546
0
                break;
547
0
            case SEEK_END:
548
0
                ifile->pos = ifile->filesize; /* filesize maintained in clist_fwrite */
549
0
                break;
550
90.3M
        }
551
90.3M
    }
552
90.3M
    return res;
553
90.3M
}
554
555
static clist_io_procs_t clist_io_procs_file = {
556
    clist_fopen,
557
    clist_fclose,
558
    clist_unlink,
559
    clist_fwrite_chars,
560
    clist_fread_chars,
561
    clist_set_memory_warning,
562
    clist_ferror_code,
563
    clist_ftell,
564
    clist_rewind,
565
    clist_fseek,
566
};
567
568
init_proc(gs_gxclfile_init);
569
int
570
gs_gxclfile_init(gs_memory_t *mem)
571
162k
{
572
162k
    gs_lib_ctx_core_t *core = mem->gs_lib_ctx->core;
573
162k
#ifdef PACIFY_VALGRIND
574
162k
    VALGRIND_HG_DISABLE_CHECKING(&core->clist_io_procs_file, sizeof(core->clist_io_procs_file));
575
162k
#endif
576
162k
    core->clist_io_procs_file = &clist_io_procs_file;
577
162k
    return 0;
578
162k
}