Coverage Report

Created: 2026-04-09 07:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/base/sfxstdio.c
Line
Count
Source
1
/* Copyright (C) 2001-2026 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 stream implementation using stdio */
18
#include "stdio_.h"   /* includes std.h */
19
#include "memory_.h"
20
#include "unistd_.h"
21
#include "gsmemory.h"
22
#include "gdebug.h"
23
#include "gpcheck.h"
24
#include "gp.h"
25
#include "gserrors.h"
26
#include "stream.h"
27
#include "strimpl.h"
28
29
/* Forward references for file stream procedures */
30
static int
31
    s_file_available(stream *, gs_offset_t *),
32
    s_file_read_seek(stream *, gs_offset_t),
33
    s_file_read_close(stream *),
34
    s_file_read_process(stream_state *, stream_cursor_read *,
35
                        stream_cursor_write *, bool);
36
static int
37
    s_file_write_seek(stream *, gs_offset_t),
38
    s_file_write_flush(stream *),
39
    s_file_write_close(stream *),
40
    s_file_write_process(stream_state *, stream_cursor_read *,
41
                         stream_cursor_write *, bool);
42
static int
43
    s_file_switch(stream *, bool);
44
45
/* ------ File reading ------ */
46
47
/* Initialize a stream for reading an OS file. */
48
void
49
sread_file(register stream * s, gp_file * file, byte * buf, uint len)
50
96.0k
{
51
96.0k
    static const stream_procs p = {
52
96.0k
        s_file_available, s_file_read_seek, s_std_read_reset,
53
96.0k
        s_std_read_flush, s_file_read_close, s_file_read_process,
54
96.0k
        s_file_switch
55
96.0k
    };
56
    /*
57
     * There is no really portable way to test seekability, but this should
58
     * work on most systems.  Note that if our probe sets the ferror bit for
59
     * the stream, we have to clear it again to avoid trouble later.
60
     */
61
96.0k
    int had_error = gp_ferror(file);
62
96.0k
    gs_offset_t curpos = gp_ftell(file);
63
96.0k
    bool seekable = (curpos != -1L && gp_fseek(file, curpos, SEEK_SET) == 0);
64
65
96.0k
    if (!had_error)
66
96.0k
        gp_clearerr(file);
67
96.0k
    s_std_init(s, buf, len, &p,
68
96.0k
               (seekable ? s_mode_read + s_mode_seek : s_mode_read));
69
96.0k
    if_debug1m('s', s->memory, "[s]read file="PRI_INTPTR"\n", (intptr_t)file);
70
96.0k
    s->file = file;
71
96.0k
    s->file_modes = s->modes;
72
96.0k
    s->file_offset = 0;
73
96.0k
    s->file_limit = (sizeof(gs_offset_t) > 4 ? max_int64_t : max_long);
74
96.0k
}
75
76
/* Confine reading to a subfile.  This is primarily for reusable streams. */
77
int
78
sread_subfile(stream *s, gs_offset_t start, gs_offset_t length)
79
0
{
80
0
    if (s->file == 0 || s->modes != s_mode_read + s_mode_seek ||
81
0
        s->file_offset != 0 ||
82
0
        s->file_limit != S_FILE_LIMIT_MAX ||
83
0
        ((s->position < start || s->position > start + length) && sseek(s, start) < 0)
84
0
        )
85
0
        return ERRC;
86
0
    s->position -= start;
87
0
    s->file_offset = start;
88
0
    s->file_limit = length;
89
0
    return 0;
90
0
}
91
92
/* Procedures for reading from a file */
93
static int
94
s_file_available(register stream * s, gs_offset_t *pl)
95
172k
{
96
172k
    gs_offset_t max_avail = s->file_limit - stell(s);
97
172k
    gs_offset_t buf_avail = sbufavailable(s);
98
99
172k
    *pl = min(max_avail, buf_avail);
100
172k
    if (sseekable(s)) {
101
172k
        gs_offset_t pos, end;
102
103
172k
        pos = gp_ftell(s->file);
104
172k
        if (gp_fseek(s->file, 0, SEEK_END))
105
0
            return ERRC;
106
172k
        end = gp_ftell(s->file);
107
172k
        if (gp_fseek(s->file, pos, SEEK_SET))
108
0
            return ERRC;
109
172k
        buf_avail += end - pos;
110
172k
        *pl = min(max_avail, buf_avail);
111
172k
        if (*pl == 0)
112
23.2k
            *pl = -1;   /* EOF */
113
172k
    } else {
114
        /* s->end_status == EOFC may indicate the stream is disabled
115
         * or that the underlying gp_file * has reached EOF.
116
         */
117
0
        if (*pl == 0 && (s->end_status == EOFC || gp_feof(s->file)))
118
0
            *pl = -1;   /* EOF */
119
0
    }
120
172k
    return 0;
121
172k
}
122
static int
123
s_file_read_seek(register stream * s, gs_offset_t pos)
124
13.7M
{
125
13.7M
    gs_offset_t end = s->cursor.r.limit - s->cbuf + 1;
126
13.7M
    gs_offset_t offset = pos - s->position;
127
128
13.7M
    if (s->cbuf == NULL)
129
0
        return ERRC;
130
131
13.7M
    if (offset >= 0 && offset <= end) {  /* Staying within the same buffer */
132
5.81M
        s->cursor.r.ptr = s->cbuf + offset - 1;
133
5.81M
        return 0;
134
5.81M
    }
135
7.95M
    if (pos < 0 || pos > s->file_limit || s->file == NULL ||
136
7.92M
        gp_fseek(s->file, s->file_offset + pos, SEEK_SET) != 0
137
7.95M
        )
138
23.3k
        return ERRC;
139
7.92M
    s->cursor.r.ptr = s->cursor.r.limit = s->cbuf - 1;
140
7.92M
    s->end_status = 0;
141
7.92M
    s->position = pos;
142
7.92M
    return 0;
143
7.95M
}
144
static int
145
s_file_read_close(stream * s)
146
96.0k
{
147
96.0k
    gp_file *file = s->file;
148
149
96.0k
    if (file != 0) {
150
96.0k
        s->file = 0;
151
96.0k
        return (gp_fclose(file) ? ERRC : 0);
152
96.0k
    }
153
0
    return 0;
154
96.0k
}
155
156
/*
157
 * Process a buffer for a file reading stream.
158
 * This is the first stream in the pipeline, so pr is irrelevant.
159
 */
160
static int
161
s_file_read_process(stream_state * st, stream_cursor_read * ignore_pr,
162
                    stream_cursor_write * pw, bool last)
163
10.4M
{
164
10.4M
    stream *s = (stream *)st; /* no separate state */
165
10.4M
    gp_file *file = s->file;
166
10.4M
    gs_offset_t max_count = pw->limit - pw->ptr;
167
10.4M
    int status = 1;
168
10.4M
    int count;
169
170
10.4M
    if (s->file_limit < S_FILE_LIMIT_MAX) {
171
0
        gs_offset_t limit_count = s->file_offset + s->file_limit - gp_ftell(file);
172
173
0
        if (max_count > limit_count)
174
0
            max_count = limit_count, status = EOFC;
175
0
    }
176
10.4M
    count = gp_fread(pw->ptr + 1, 1, max_count, file);
177
10.4M
    if (count < 0)
178
0
        count = 0;
179
10.4M
    pw->ptr += count;
180
10.4M
    process_interrupts(s->memory);
181
10.4M
    return (gp_ferror(file) ? ERRC : gp_feof(file) ? EOFC : status);
182
10.4M
}
183
184
/* ------ File writing ------ */
185
186
/* Initialize a stream for writing an OS file. */
187
void
188
swrite_file(register stream * s, gp_file * file, byte * buf, uint len)
189
236k
{
190
236k
    static const stream_procs p = {
191
236k
        s_std_noavailable, s_file_write_seek, s_std_write_reset,
192
236k
        s_file_write_flush, s_file_write_close, s_file_write_process,
193
236k
        s_file_switch
194
236k
    };
195
196
236k
    s_std_init(s, buf, len, &p,
197
236k
               (gp_get_file(file) == stdout ? s_mode_write : s_mode_write + s_mode_seek));
198
236k
    if_debug1m('s', s->memory, "[s]write file="PRI_INTPTR"\n", (intptr_t) file);
199
236k
    s->file = file;
200
236k
    s->file_modes = s->modes;
201
236k
    s->file_offset = 0;   /* in case we switch to reading later */
202
236k
    s->file_limit = S_FILE_LIMIT_MAX;
203
236k
}
204
/* Initialize for appending to an OS file. */
205
int
206
sappend_file(register stream * s, gp_file * file, byte * buf, uint len)
207
0
{
208
0
    swrite_file(s, file, buf, len);
209
0
    s->modes = s_mode_write + s_mode_append; /* no seek */
210
0
    s->file_modes = s->modes;
211
0
    if (gp_fseek(file, 0L, SEEK_END) != 0)
212
0
        return ERRC;
213
0
    s->position = gp_ftell(file);
214
0
    return 0;
215
0
}
216
/* Procedures for writing on a file */
217
static int
218
s_file_write_seek(stream * s, gs_offset_t pos)
219
311k
{
220
    /* We must flush the buffer to reposition. */
221
311k
    int code = sflush(s);
222
223
311k
    if (code < 0)
224
0
        return code;
225
311k
    if (gp_fseek(s->file, pos, SEEK_SET) != 0)
226
0
        return ERRC;
227
311k
    s->position = pos;
228
311k
    return 0;
229
311k
}
230
static int
231
s_file_write_flush(register stream * s)
232
4.36M
{
233
4.36M
    int result = s_process_write_buf(s, false);
234
235
4.36M
    gp_fflush(s->file);
236
4.36M
    return result;
237
4.36M
}
238
static int
239
s_file_write_close(register stream * s)
240
0
{
241
0
    s_process_write_buf(s, true);
242
0
    return s_file_read_close(s);
243
0
}
244
245
/*
246
 * Process a buffer for a file writing stream.
247
 * This is the last stream in the pipeline, so pw is irrelevant.
248
 */
249
static int
250
s_file_write_process(stream_state * st, stream_cursor_read * pr,
251
                     stream_cursor_write * ignore_pw, bool last)
252
34.1M
{
253
34.1M
    uint count = pr->limit - pr->ptr;
254
255
    /*
256
     * The DEC C library on AXP architectures gives an error on
257
     * fwrite if the count is zero!
258
     */
259
34.1M
    if (count != 0) {
260
23.5M
        gp_file *file = ((stream *) st)->file;
261
23.5M
        int written = gp_fwrite(pr->ptr + 1, 1, count, file);
262
263
23.5M
        if (written < 0)
264
0
            written = 0;
265
23.5M
        pr->ptr += written;
266
23.5M
        process_interrupts(st->memory);
267
23.5M
        return (gp_ferror(file) ? ERRC : 0);
268
23.5M
    } else {
269
10.5M
        process_interrupts(st->memory);
270
10.5M
        return 0;
271
10.5M
    }
272
34.1M
}
273
274
/* ------ File switching ------ */
275
276
/* Switch a file stream to reading or writing. */
277
static int
278
s_file_switch(stream * s, bool writing)
279
85.5k
{
280
85.5k
    uint modes = s->file_modes;
281
85.5k
    gp_file *file = s->file;
282
85.5k
    gs_offset_t pos;
283
284
85.5k
    if (writing) {
285
0
        if (!(s->file_modes & s_mode_write))
286
0
            return ERRC;
287
0
        pos = stell(s);
288
0
        if_debug2m('s', s->memory, "[s]switch 0x%"PRIx64" to write at %"PRId64"\n",
289
0
                   (uint64_t) s, (int64_t)pos);
290
0
        if (gp_fseek(file, pos, SEEK_SET) != 0)
291
0
            return ERRC;
292
0
        if (modes & s_mode_append) {
293
0
            if (sappend_file(s, file, s->cbuf, s->cbsize)!= 0) /* sets position */
294
0
                return ERRC;
295
0
        } else {
296
0
            swrite_file(s, file, s->cbuf, s->cbsize);
297
0
            s->position = pos;
298
0
        }
299
0
        s->modes = modes;
300
85.5k
    } else {
301
85.5k
        if (!(s->file_modes & s_mode_read))
302
0
            return ERRC;
303
85.5k
        pos = stell(s);
304
85.5k
        if_debug2m('s', s->memory, "[s]switch 0x%"PRIu64" to read at %"PRId64"\n",
305
85.5k
                   (uint64_t) s, (int64_t)pos);
306
85.5k
        if (sflush(s) < 0)
307
0
            return ERRC;
308
85.5k
        if (gp_fseek(file, 0L, SEEK_CUR) != 0)
309
0
            return ERRC;
310
85.5k
        sread_file(s, file, s->cbuf, s->cbsize);
311
85.5k
        s->modes |= modes & s_mode_append; /* don't lose append info */
312
85.5k
        s->position = pos;
313
85.5k
    }
314
85.5k
    s->file_modes = modes;
315
85.5k
    return 0;
316
85.5k
}