Coverage Report

Created: 2025-06-10 07:15

/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
6.04k
#define CL_CACHE_NSLOTS (3)
35
6.04k
#define CL_CACHE_SLOT_SIZE_LOG2 (15)
36
3.81M
#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
437M
#define CL_CACHE_NEEDS_INIT(cache) (cache != NULL && cache->filesize == 0)
65
66
CL_CACHE *
67
cl_cache_alloc(gs_memory_t *mem)
68
6.04k
{
69
6.04k
    CL_CACHE *cache;
70
71
    /* allocate and initialilze the cache to filesize = 0 to signal read_init needed */
72
6.04k
    cache = (CL_CACHE *)gs_alloc_bytes(mem, sizeof(CL_CACHE), "alloc CL_CACHE");
73
6.04k
    if (cache != NULL) {
74
6.04k
        cache->filesize = 0;
75
6.04k
        cache->nslots = 0;
76
6.04k
        cache->block_size = 0;
77
6.04k
        cache->slots = NULL;
78
6.04k
        cache->base = NULL;
79
6.04k
        cache->memory = mem;
80
6.04k
    }
81
6.04k
    return cache;
82
6.04k
}
83
84
void
85
cl_cache_destroy(CL_CACHE *cache)
86
6.04k
{
87
6.04k
    if (cache == NULL)
88
0
        return;
89
90
6.04k
    if (cache->slots != NULL) {
91
6.04k
        gs_free_object(cache->memory, cache->base, "CL_CACHE SLOT data");
92
6.04k
        gs_free_object(cache->memory, cache->slots, "CL_CACHE slots array");
93
6.04k
    }
94
6.04k
    gs_free_object(cache->memory, cache, "CL_CACHE for IFILE");
95
6.04k
}
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
6.04k
{
101
    /* NB: if fail, and cache is still NULL, proceed without cache, reading will cope */
102
6.04k
    if (cache == NULL || cache->filesize != 0)
103
0
        return cache;   /* once we've done the init, filesize will be set */
104
105
6.04k
    if ((filesize+block_size)/block_size < nslots)
106
4.92k
        nslots = (filesize + block_size)/block_size; /* limit at blocks needed for entire file */
107
6.04k
    cache->slots = (CL_CACHE_SLOT *)gs_alloc_bytes(cache->memory, nslots * sizeof(CL_CACHE_SLOT),
108
6.04k
                                                       "CL_CACHE slots array");
109
6.04k
    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
6.04k
    } else {
113
6.04k
        cache->slots[0].base = (byte *)gs_alloc_bytes(cache->memory, nslots * block_size,
114
6.04k
                                                       "CL_CACHE_SLOT data");
115
6.04k
        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
6.04k
        } else {
120
            /* success, initialize the slots */
121
6.04k
            int i;
122
123
14.5k
            for (i=0; i < nslots; i++) {
124
8.47k
                cache->slots[i].blocknum = CL_CACHE_SLOT_EMPTY;
125
8.47k
                cache->slots[i].base = cache->slots[0].base + (i * block_size);
126
8.47k
            }
127
6.04k
            cache->base = cache->slots[0].base;         /* save for the 'destroy' (slots array moves around) */
128
6.04k
            cache->nslots = nslots;
129
6.04k
            cache->block_size = block_size;
130
6.04k
            cache->filesize = filesize;
131
6.04k
        }
132
6.04k
    }
133
6.04k
    return cache; /* May be NULL. If so, no cache used */
134
6.04k
}
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
428M
{
142
428M
    int nread = 0;
143
428M
    int slot;
144
428M
    int offset;
145
428M
    int64_t blocknum = pos / cache->block_size;
146
147
428M
    if (pos >= cache->filesize)
148
0
        return -1;
149
150
    /* find the slot */
151
435M
    for (slot = 0; slot < cache->nslots; slot++) {
152
433M
        if (blocknum == cache->slots[slot].blocknum)
153
427M
            break;
154
433M
    }
155
428M
    if (slot >= cache->nslots)
156
1.90M
        return 0;               /* block not in cache */
157
158
427M
    if (slot != 0) {
159
        /* move the slot we found to the top, moving the rest down */
160
359k
        byte *base = cache->slots[slot].base;
161
359k
        int i;
162
163
853k
        for (i = slot; i > 0; i--) {
164
494k
            cache->slots[i].base = cache->slots[i-1].base;
165
494k
            cache->slots[i].blocknum = cache->slots[i-1].blocknum;
166
494k
        }
167
359k
        cache->slots[0].blocknum = blocknum;
168
359k
        cache->slots[0].base = base;
169
359k
    }
170
427M
    offset = pos - cache->slots[0].blocknum * cache->block_size;
171
427M
    nread = min(cache->block_size - offset, len);
172
427M
    if (nread + pos > cache->filesize)
173
0
        nread = cache->filesize - pos; /* limit for EOF */
174
427M
    memcpy(data, cache->slots[0].base + offset, nread);
175
427M
    return nread;
176
428M
}
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
1.90M
{
183
    /* the LRU is in the last slot, so re-use it */
184
1.90M
    CL_CACHE_SLOT *pslot = &(cache->slots[0]);             /* slot used will always be first, possibly after moving */
185
1.90M
    int64_t slot0_blocknum = pslot->blocknum;
186
187
1.90M
    if (slot0_blocknum == CL_CACHE_SLOT_EMPTY)
188
6.04k
        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
1.90M
    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
1.90M
        byte *last_slot_base = cache->slots[cache->nslots - 1].base;    /* save the base for the last slot */
195
1.90M
        int i;
196
197
        /* move the rest down */
198
5.70M
        for (i=cache->nslots - 1; i > 0; i--) {
199
3.80M
            cache->slots[i].blocknum = cache->slots[i-1].blocknum;
200
3.80M
            cache->slots[i].base = cache->slots[i-1].base;
201
3.80M
        }
202
1.90M
        pslot->base = last_slot_base;
203
1.90M
    }
204
1.90M
    pslot->blocknum = CL_CACHE_SLOT_EMPTY;
205
1.90M
    return pslot;
206
1.90M
}
207
208
void
209
cl_cache_load_slot(CL_CACHE *cache, CL_CACHE_SLOT *slot, int64_t pos, byte *data, int len)
210
1.90M
{
211
1.90M
    slot->blocknum = pos / cache->block_size;
212
1.90M
    memmove(slot->base, data, len);
213
1.90M
}
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
322k
#define ENC_FILE_STR ("encoded_file_ptr_%p")
223
319k
#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
3.02k
{
237
3.02k
    gs_snprintf(fname, gp_file_name_sizeof, ENC_FILE_STR, file);
238
3.02k
}
239
240
static clist_file_ptr
241
fake_path_to_file(const char *fname)
242
319k
{
243
319k
    clist_file_ptr i1, i2;
244
245
319k
    int r1 = sscanf(fname, ENC_FILE_STR, &i1);
246
319k
    int r2 = sscanf(fname, ENC_FILE_STRX, &i2);
247
319k
    return r2 == 1 ? i2 : (r1 == 1 ? i1 : NULL);
248
319k
}
249
250
static IFILE *wrap_file(gs_memory_t *mem, gp_file *f, const char *fmode)
251
3.02k
{
252
3.02k
    IFILE *ifile;
253
254
3.02k
    if (!f) return NULL;
255
3.02k
    ifile = (IFILE *)gs_alloc_bytes(mem->non_gc_memory, sizeof(*ifile), "Allocate wrapped IFILE");
256
3.02k
    if (!ifile) {
257
0
        gp_fclose(f);
258
0
        return NULL;
259
0
    }
260
3.02k
    ifile->mem = mem->non_gc_memory;
261
3.02k
    ifile->f = f;
262
3.02k
    ifile->pos = 0;
263
3.02k
    ifile->filesize = 0;
264
3.02k
    ifile->cache = cl_cache_alloc(ifile->mem);
265
3.02k
    return ifile;
266
3.02k
}
267
268
static int clist_close_file(IFILE *ifile)
269
3.02k
{
270
3.02k
    int res = 0;
271
3.02k
    if (ifile) {
272
3.02k
        if (ifile->f != NULL)
273
3.02k
            res = gp_fclose(ifile->f);
274
3.02k
        if (ifile->cache != NULL)
275
3.02k
            cl_cache_destroy(ifile->cache);
276
3.02k
        gs_free_object(ifile->mem, ifile, "Free wrapped IFILE");
277
3.02k
    }
278
3.02k
    return res;
279
3.02k
}
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
3.02k
{
288
3.02k
    if (*fname == 0) {
289
3.02k
        if (fmode[0] == 'r')
290
0
            return_error(gs_error_invalidfileaccess);
291
3.02k
        if (gp_can_share_fdesc()) {
292
3.02k
            *pcf = (clist_file_ptr)wrap_file(mem, gp_open_scratch_file_rm(mem,
293
3.02k
                                                       gp_scratch_file_name_prefix,
294
3.02k
                                                       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
3.02k
            if (*pcf != NULL)
298
3.02k
                file_to_fake_path(*pcf, fname);
299
3.02k
        } 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
3.02k
    } 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
3.02k
    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
3.02k
    return 0;
323
3.02k
}
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
3.02k
{
342
3.02k
    clist_file_ptr ocf = fake_path_to_file(fname);
343
3.02k
    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
3.02k
        return delete ? (clist_close_file((IFILE *)ocf) ? gs_note_error(gs_error_ioerror) : 0) : 0;
348
3.02k
    } 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
3.02k
}
354
355
/* ------ Writing ------ */
356
357
static int
358
clist_fwrite_chars(const void *data, uint len, clist_file_ptr cf)
359
11.2M
{
360
11.2M
    int res = 0;
361
11.2M
    IFILE *icf = (IFILE *)cf;
362
363
11.2M
    if (gp_can_share_fdesc()) {
364
11.2M
        res = gp_fpwrite((char *)data, len, ((IFILE *)cf)->pos, ((IFILE *)cf)->f);
365
11.2M
    } else {
366
0
        res = gp_fwrite(data, 1, len, ((IFILE *)cf)->f);
367
0
    }
368
11.2M
    if (res >= 0)
369
11.2M
        icf->pos += len;
370
11.2M
    icf->filesize = icf->pos; /* write truncates file */
371
11.2M
    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
11.2M
    return res;
377
11.2M
}
378
379
/* ------ Reading ------ */
380
381
static int
382
clist_fread_chars(void *data, uint len, clist_file_ptr cf)
383
426M
{
384
426M
    int nread = 0;
385
386
426M
    if (gp_can_share_fdesc()) {
387
426M
        IFILE *icf = (IFILE *)cf;
388
426M
        byte *dp = data;
389
390
        /* if we have a cache, check if it needs init, and do it */
391
426M
        if (CL_CACHE_NEEDS_INIT(icf->cache)) {
392
6.04k
            icf->cache = cl_cache_read_init(icf->cache, CL_CACHE_NSLOTS, 1<<CL_CACHE_SLOT_SIZE_LOG2, icf->filesize);
393
6.04k
        }
394
        /* cl_cache_read_init may have failed, and set cache to NULL, check before using it */
395
426M
        if (icf->cache != NULL) {
396
428M
            do {
397
428M
                int n;
398
399
428M
                if ((n = cl_cache_read(dp, len-nread, icf->pos+nread, icf->cache)) < 0)
400
0
                    break;
401
428M
                if (n == 0) {
402
                    /* pos was not in cache, get a slot and load it, then loop */
403
1.90M
                    CL_CACHE_SLOT *slot = cl_cache_get_empty_slot(icf->cache, icf->pos+nread);  /* cannot fail */
404
1.90M
                    int64_t block_pos = (icf->pos+nread) & ~(icf->cache->block_size - 1);
405
1.90M
                    int fill_len = gp_fpread((char *)(slot->base), icf->cache->block_size, block_pos, icf->f);
406
407
1.90M
                    cl_cache_load_slot(icf->cache, slot, block_pos, slot->base, fill_len);
408
1.90M
                }
409
428M
                nread += n;
410
428M
                dp += n;
411
428M
            } while (nread < len);
412
426M
        } else {
413
             /* no cache -- just do the read */
414
0
            nread = gp_fpread(data, len, icf->pos, icf->f);
415
0
        }
416
426M
        if (nread >= 0)
417
426M
            icf->pos += nread;
418
426M
    } 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
426M
    return nread;
448
426M
}
449
450
/* ------ Position/status ------ */
451
452
static int
453
clist_set_memory_warning(clist_file_ptr cf, int bytes_left)
454
6.04k
{
455
6.04k
    return 0;     /* no-op */
456
6.04k
}
457
458
static int
459
clist_ferror_code(clist_file_ptr cf)
460
29.5M
{
461
29.5M
    return (gp_ferror(((IFILE *)cf)->f) ? gs_error_ioerror : 0);
462
29.5M
}
463
464
static int64_t
465
clist_ftell(clist_file_ptr cf)
466
2.56M
{
467
2.56M
    IFILE *ifile = (IFILE *)cf;
468
469
2.56M
    return gp_can_share_fdesc() ? ifile->pos : gp_ftell(ifile->f);
470
2.56M
}
471
472
static int
473
clist_rewind(clist_file_ptr cf, bool discard_data, const char *fname)
474
316k
{
475
316k
    gp_file *f = ((IFILE *)cf)->f;
476
316k
    IFILE *ocf = fake_path_to_file(fname);
477
316k
    char fmode[4];
478
479
316k
    snprintf(fmode, sizeof(fmode), "w+%s", gp_fmode_binary_suffix);
480
481
316k
    if (ocf) {
482
316k
        if (discard_data) {
483
            /* fname is an encoded ifile pointer. We can use an entirely
484
             * new scratch file. */
485
3.02k
            char tfname[gp_file_name_sizeof] = {0};
486
3.02k
            const gs_memory_t *mem = ocf->f->memory;
487
3.02k
            gp_fclose(ocf->f);
488
3.02k
            ocf->f = gp_open_scratch_file_rm(mem, gp_scratch_file_name_prefix, tfname, fmode);
489
3.02k
            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
3.02k
            if (ocf->cache != NULL) {
494
3.02k
                cl_cache_destroy(ocf->cache);
495
3.02k
                ocf->cache = cl_cache_alloc(ocf->mem);
496
3.02k
                if (ocf->cache == NULL)
497
0
                    return_error(gs_error_ioerror);
498
3.02k
            }
499
3.02k
            ((IFILE *)cf)->filesize = 0;
500
3.02k
        }
501
316k
        ((IFILE *)cf)->pos = 0;
502
316k
    } 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
316k
    return 0;
523
316k
}
524
525
static int
526
clist_fseek(clist_file_ptr cf, int64_t offset, int mode, const char *ignore_fname)
527
23.7M
{
528
23.7M
    IFILE *ifile = (IFILE *)cf;
529
23.7M
    int res = 0;
530
531
23.7M
    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
23.7M
    if (res >= 0) { /* lgtm [cpp/constant-comparison] */
539
        /* Update the ifile->pos */
540
23.7M
        switch (mode) {
541
23.7M
            case SEEK_SET:
542
23.7M
                ifile->pos = offset;
543
23.7M
                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
23.7M
        }
551
23.7M
    }
552
23.7M
    return res;
553
23.7M
}
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
12.7k
{
572
12.7k
    gs_lib_ctx_core_t *core = mem->gs_lib_ctx->core;
573
12.7k
#ifdef PACIFY_VALGRIND
574
12.7k
    VALGRIND_HG_DISABLE_CHECKING(&core->clist_io_procs_file, sizeof(core->clist_io_procs_file));
575
12.7k
#endif
576
12.7k
    core->clist_io_procs_file = &clist_io_procs_file;
577
12.7k
    return 0;
578
12.7k
}