Coverage Report

Created: 2026-04-01 07:17

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
107k
{
51
107k
    static const stream_procs p = {
52
107k
        s_file_available, s_file_read_seek, s_std_read_reset,
53
107k
        s_std_read_flush, s_file_read_close, s_file_read_process,
54
107k
        s_file_switch
55
107k
    };
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
107k
    int had_error = gp_ferror(file);
62
107k
    gs_offset_t curpos = gp_ftell(file);
63
107k
    bool seekable = (curpos != -1L && gp_fseek(file, curpos, SEEK_SET) == 0);
64
65
107k
    if (!had_error)
66
107k
        gp_clearerr(file);
67
107k
    s_std_init(s, buf, len, &p,
68
107k
               (seekable ? s_mode_read + s_mode_seek : s_mode_read));
69
107k
    if_debug1m('s', s->memory, "[s]read file="PRI_INTPTR"\n", (intptr_t)file);
70
107k
    s->file = file;
71
107k
    s->file_modes = s->modes;
72
107k
    s->file_offset = 0;
73
107k
    s->file_limit = (sizeof(gs_offset_t) > 4 ? max_int64_t : max_long);
74
107k
}
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
197k
{
96
197k
    gs_offset_t max_avail = s->file_limit - stell(s);
97
197k
    gs_offset_t buf_avail = sbufavailable(s);
98
99
197k
    *pl = min(max_avail, buf_avail);
100
197k
    if (sseekable(s)) {
101
197k
        gs_offset_t pos, end;
102
103
197k
        pos = gp_ftell(s->file);
104
197k
        if (gp_fseek(s->file, 0, SEEK_END))
105
0
            return ERRC;
106
197k
        end = gp_ftell(s->file);
107
197k
        if (gp_fseek(s->file, pos, SEEK_SET))
108
0
            return ERRC;
109
197k
        buf_avail += end - pos;
110
197k
        *pl = min(max_avail, buf_avail);
111
197k
        if (*pl == 0)
112
30.2k
            *pl = -1;   /* EOF */
113
197k
    } 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
197k
    return 0;
121
197k
}
122
static int
123
s_file_read_seek(register stream * s, gs_offset_t pos)
124
13.9M
{
125
13.9M
    gs_offset_t end = s->cursor.r.limit - s->cbuf + 1;
126
13.9M
    gs_offset_t offset = pos - s->position;
127
128
13.9M
    if (s->cbuf == NULL)
129
0
        return ERRC;
130
131
13.9M
    if (offset >= 0 && offset <= end) {  /* Staying within the same buffer */
132
5.96M
        s->cursor.r.ptr = s->cbuf + offset - 1;
133
5.96M
        return 0;
134
5.96M
    }
135
8.03M
    if (pos < 0 || pos > s->file_limit || s->file == NULL ||
136
8.00M
        gp_fseek(s->file, s->file_offset + pos, SEEK_SET) != 0
137
8.03M
        )
138
30.3k
        return ERRC;
139
8.00M
    s->cursor.r.ptr = s->cursor.r.limit = s->cbuf - 1;
140
8.00M
    s->end_status = 0;
141
8.00M
    s->position = pos;
142
8.00M
    return 0;
143
8.03M
}
144
static int
145
s_file_read_close(stream * s)
146
107k
{
147
107k
    gp_file *file = s->file;
148
149
107k
    if (file != 0) {
150
107k
        s->file = 0;
151
107k
        return (gp_fclose(file) ? ERRC : 0);
152
107k
    }
153
0
    return 0;
154
107k
}
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.6M
{
164
10.6M
    stream *s = (stream *)st; /* no separate state */
165
10.6M
    gp_file *file = s->file;
166
10.6M
    gs_offset_t max_count = pw->limit - pw->ptr;
167
10.6M
    int status = 1;
168
10.6M
    int count;
169
170
10.6M
    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.6M
    count = gp_fread(pw->ptr + 1, 1, max_count, file);
177
10.6M
    if (count < 0)
178
0
        count = 0;
179
10.6M
    pw->ptr += count;
180
10.6M
    process_interrupts(s->memory);
181
10.6M
    return (gp_ferror(file) ? ERRC : gp_feof(file) ? EOFC : status);
182
10.6M
}
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
303k
{
190
303k
    static const stream_procs p = {
191
303k
        s_std_noavailable, s_file_write_seek, s_std_write_reset,
192
303k
        s_file_write_flush, s_file_write_close, s_file_write_process,
193
303k
        s_file_switch
194
303k
    };
195
196
303k
    s_std_init(s, buf, len, &p,
197
303k
               (gp_get_file(file) == stdout ? s_mode_write : s_mode_write + s_mode_seek));
198
303k
    if_debug1m('s', s->memory, "[s]write file="PRI_INTPTR"\n", (intptr_t) file);
199
303k
    s->file = file;
200
303k
    s->file_modes = s->modes;
201
303k
    s->file_offset = 0;   /* in case we switch to reading later */
202
303k
    s->file_limit = S_FILE_LIMIT_MAX;
203
303k
}
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
353k
{
220
    /* We must flush the buffer to reposition. */
221
353k
    int code = sflush(s);
222
223
353k
    if (code < 0)
224
0
        return code;
225
353k
    if (gp_fseek(s->file, pos, SEEK_SET) != 0)
226
0
        return ERRC;
227
353k
    s->position = pos;
228
353k
    return 0;
229
353k
}
230
static int
231
s_file_write_flush(register stream * s)
232
5.24M
{
233
5.24M
    int result = s_process_write_buf(s, false);
234
235
5.24M
    gp_fflush(s->file);
236
5.24M
    return result;
237
5.24M
}
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
35.0M
{
253
35.0M
    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
35.0M
    if (count != 0) {
260
23.6M
        gp_file *file = ((stream *) st)->file;
261
23.6M
        int written = gp_fwrite(pr->ptr + 1, 1, count, file);
262
263
23.6M
        if (written < 0)
264
0
            written = 0;
265
23.6M
        pr->ptr += written;
266
23.6M
        process_interrupts(st->memory);
267
23.6M
        return (gp_ferror(file) ? ERRC : 0);
268
23.6M
    } else {
269
11.3M
        process_interrupts(st->memory);
270
11.3M
        return 0;
271
11.3M
    }
272
35.0M
}
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
98.9k
{
280
98.9k
    uint modes = s->file_modes;
281
98.9k
    gp_file *file = s->file;
282
98.9k
    gs_offset_t pos;
283
284
98.9k
    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
98.9k
    } else {
301
98.9k
        if (!(s->file_modes & s_mode_read))
302
0
            return ERRC;
303
98.9k
        pos = stell(s);
304
98.9k
        if_debug2m('s', s->memory, "[s]switch 0x%"PRIu64" to read at %"PRId64"\n",
305
98.9k
                   (uint64_t) s, (int64_t)pos);
306
98.9k
        if (sflush(s) < 0)
307
0
            return ERRC;
308
98.9k
        if (gp_fseek(file, 0L, SEEK_CUR) != 0)
309
0
            return ERRC;
310
98.9k
        sread_file(s, file, s->cbuf, s->cbsize);
311
98.9k
        s->modes |= modes & s_mode_append; /* don't lose append info */
312
98.9k
        s->position = pos;
313
98.9k
    }
314
98.9k
    s->file_modes = modes;
315
98.9k
    return 0;
316
98.9k
}