Coverage Report

Created: 2025-06-10 07:17

/src/ghostpdl/base/sfxstdio.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 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
5.23k
{
51
5.23k
    static const stream_procs p = {
52
5.23k
        s_file_available, s_file_read_seek, s_std_read_reset,
53
5.23k
        s_std_read_flush, s_file_read_close, s_file_read_process,
54
5.23k
        s_file_switch
55
5.23k
    };
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
5.23k
    int had_error = gp_ferror(file);
62
5.23k
    gs_offset_t curpos = gp_ftell(file);
63
5.23k
    bool seekable = (curpos != -1L && gp_fseek(file, curpos, SEEK_SET) == 0);
64
65
5.23k
    if (!had_error)
66
5.23k
        gp_clearerr(file);
67
5.23k
    s_std_init(s, buf, len, &p,
68
5.23k
               (seekable ? s_mode_read + s_mode_seek : s_mode_read));
69
5.23k
    if_debug1m('s', s->memory, "[s]read file="PRI_INTPTR"\n", (intptr_t)file);
70
5.23k
    s->file = file;
71
5.23k
    s->file_modes = s->modes;
72
5.23k
    s->file_offset = 0;
73
5.23k
    s->file_limit = (sizeof(gs_offset_t) > 4 ? max_int64_t : max_long);
74
5.23k
}
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
10.4k
{
96
10.4k
    gs_offset_t max_avail = s->file_limit - stell(s);
97
10.4k
    gs_offset_t buf_avail = sbufavailable(s);
98
99
10.4k
    *pl = min(max_avail, buf_avail);
100
10.4k
    if (sseekable(s)) {
101
10.4k
        gs_offset_t pos, end;
102
103
10.4k
        pos = gp_ftell(s->file);
104
10.4k
        if (gp_fseek(s->file, 0, SEEK_END))
105
0
            return ERRC;
106
10.4k
        end = gp_ftell(s->file);
107
10.4k
        if (gp_fseek(s->file, pos, SEEK_SET))
108
0
            return ERRC;
109
10.4k
        buf_avail += end - pos;
110
10.4k
        *pl = min(max_avail, buf_avail);
111
10.4k
        if (*pl == 0)
112
1.54k
            *pl = -1;   /* EOF */
113
10.4k
    } 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
10.4k
    return 0;
121
10.4k
}
122
static int
123
s_file_read_seek(register stream * s, gs_offset_t pos)
124
969k
{
125
969k
    gs_offset_t end = s->cursor.r.limit - s->cbuf + 1;
126
969k
    gs_offset_t offset = pos - s->position;
127
128
969k
    if (offset >= 0 && offset <= end) {  /* Staying within the same buffer */
129
408k
        s->cursor.r.ptr = s->cbuf + offset - 1;
130
408k
        return 0;
131
408k
    }
132
560k
    if (pos < 0 || pos > s->file_limit || s->file == NULL ||
133
560k
        gp_fseek(s->file, s->file_offset + pos, SEEK_SET) != 0
134
560k
        )
135
1.55k
        return ERRC;
136
559k
    s->cursor.r.ptr = s->cursor.r.limit = s->cbuf - 1;
137
559k
    s->end_status = 0;
138
559k
    s->position = pos;
139
559k
    return 0;
140
560k
}
141
static int
142
s_file_read_close(stream * s)
143
5.23k
{
144
5.23k
    gp_file *file = s->file;
145
146
5.23k
    if (file != 0) {
147
5.23k
        s->file = 0;
148
5.23k
        return (gp_fclose(file) ? ERRC : 0);
149
5.23k
    }
150
0
    return 0;
151
5.23k
}
152
153
/*
154
 * Process a buffer for a file reading stream.
155
 * This is the first stream in the pipeline, so pr is irrelevant.
156
 */
157
static int
158
s_file_read_process(stream_state * st, stream_cursor_read * ignore_pr,
159
                    stream_cursor_write * pw, bool last)
160
750k
{
161
750k
    stream *s = (stream *)st; /* no separate state */
162
750k
    gp_file *file = s->file;
163
750k
    gs_offset_t max_count = pw->limit - pw->ptr;
164
750k
    int status = 1;
165
750k
    int count;
166
167
750k
    if (s->file_limit < S_FILE_LIMIT_MAX) {
168
0
        gs_offset_t limit_count = s->file_offset + s->file_limit - gp_ftell(file);
169
170
0
        if (max_count > limit_count)
171
0
            max_count = limit_count, status = EOFC;
172
0
    }
173
750k
    count = gp_fread(pw->ptr + 1, 1, max_count, file);
174
750k
    if (count < 0)
175
0
        count = 0;
176
750k
    pw->ptr += count;
177
750k
    process_interrupts(s->memory);
178
750k
    return (gp_ferror(file) ? ERRC : gp_feof(file) ? EOFC : status);
179
750k
}
180
181
/* ------ File writing ------ */
182
183
/* Initialize a stream for writing an OS file. */
184
void
185
swrite_file(register stream * s, gp_file * file, byte * buf, uint len)
186
14.8k
{
187
14.8k
    static const stream_procs p = {
188
14.8k
        s_std_noavailable, s_file_write_seek, s_std_write_reset,
189
14.8k
        s_file_write_flush, s_file_write_close, s_file_write_process,
190
14.8k
        s_file_switch
191
14.8k
    };
192
193
14.8k
    s_std_init(s, buf, len, &p,
194
14.8k
               (gp_get_file(file) == stdout ? s_mode_write : s_mode_write + s_mode_seek));
195
14.8k
    if_debug1m('s', s->memory, "[s]write file="PRI_INTPTR"\n", (intptr_t) file);
196
14.8k
    s->file = file;
197
14.8k
    s->file_modes = s->modes;
198
14.8k
    s->file_offset = 0;   /* in case we switch to reading later */
199
14.8k
    s->file_limit = S_FILE_LIMIT_MAX;
200
14.8k
}
201
/* Initialize for appending to an OS file. */
202
int
203
sappend_file(register stream * s, gp_file * file, byte * buf, uint len)
204
0
{
205
0
    swrite_file(s, file, buf, len);
206
0
    s->modes = s_mode_write + s_mode_append; /* no seek */
207
0
    s->file_modes = s->modes;
208
0
    if (gp_fseek(file, 0L, SEEK_END) != 0)
209
0
        return ERRC;
210
0
    s->position = gp_ftell(file);
211
0
    return 0;
212
0
}
213
/* Procedures for writing on a file */
214
static int
215
s_file_write_seek(stream * s, gs_offset_t pos)
216
5.23k
{
217
    /* We must flush the buffer to reposition. */
218
5.23k
    int code = sflush(s);
219
220
5.23k
    if (code < 0)
221
0
        return code;
222
5.23k
    if (gp_fseek(s->file, pos, SEEK_SET) != 0)
223
0
        return ERRC;
224
5.23k
    s->position = pos;
225
5.23k
    return 0;
226
5.23k
}
227
static int
228
s_file_write_flush(register stream * s)
229
38.1k
{
230
38.1k
    int result = s_process_write_buf(s, false);
231
232
38.1k
    gp_fflush(s->file);
233
38.1k
    return result;
234
38.1k
}
235
static int
236
s_file_write_close(register stream * s)
237
0
{
238
0
    s_process_write_buf(s, true);
239
0
    return s_file_read_close(s);
240
0
}
241
242
/*
243
 * Process a buffer for a file writing stream.
244
 * This is the last stream in the pipeline, so pw is irrelevant.
245
 */
246
static int
247
s_file_write_process(stream_state * st, stream_cursor_read * pr,
248
                     stream_cursor_write * ignore_pw, bool last)
249
1.90M
{
250
1.90M
    uint count = pr->limit - pr->ptr;
251
252
    /*
253
     * The DEC C library on AXP architectures gives an error on
254
     * fwrite if the count is zero!
255
     */
256
1.90M
    if (count != 0) {
257
1.88M
        gp_file *file = ((stream *) st)->file;
258
1.88M
        int written = gp_fwrite(pr->ptr + 1, 1, count, file);
259
260
1.88M
        if (written < 0)
261
0
            written = 0;
262
1.88M
        pr->ptr += written;
263
1.88M
        process_interrupts(st->memory);
264
1.88M
        return (gp_ferror(file) ? ERRC : 0);
265
1.88M
    } else {
266
19.0k
        process_interrupts(st->memory);
267
19.0k
        return 0;
268
19.0k
    }
269
1.90M
}
270
271
/* ------ File switching ------ */
272
273
/* Switch a file stream to reading or writing. */
274
static int
275
s_file_switch(stream * s, bool writing)
276
5.23k
{
277
5.23k
    uint modes = s->file_modes;
278
5.23k
    gp_file *file = s->file;
279
5.23k
    gs_offset_t pos;
280
281
5.23k
    if (writing) {
282
0
        if (!(s->file_modes & s_mode_write))
283
0
            return ERRC;
284
0
        pos = stell(s);
285
0
        if_debug2m('s', s->memory, "[s]switch 0x%"PRIx64" to write at %"PRId64"\n",
286
0
                   (uint64_t) s, (int64_t)pos);
287
0
        if (gp_fseek(file, pos, SEEK_SET) != 0)
288
0
            return ERRC;
289
0
        if (modes & s_mode_append) {
290
0
            if (sappend_file(s, file, s->cbuf, s->cbsize)!= 0) /* sets position */
291
0
                return ERRC;
292
0
        } else {
293
0
            swrite_file(s, file, s->cbuf, s->cbsize);
294
0
            s->position = pos;
295
0
        }
296
0
        s->modes = modes;
297
5.23k
    } else {
298
5.23k
        if (!(s->file_modes & s_mode_read))
299
0
            return ERRC;
300
5.23k
        pos = stell(s);
301
5.23k
        if_debug2m('s', s->memory, "[s]switch 0x%"PRIu64" to read at %"PRId64"\n",
302
5.23k
                   (uint64_t) s, (int64_t)pos);
303
5.23k
        if (sflush(s) < 0)
304
0
            return ERRC;
305
5.23k
        if (gp_fseek(file, 0L, SEEK_CUR) != 0)
306
0
            return ERRC;
307
5.23k
        sread_file(s, file, s->cbuf, s->cbsize);
308
5.23k
        s->modes |= modes & s_mode_append; /* don't lose append info */
309
5.23k
        s->position = pos;
310
5.23k
    }
311
5.23k
    s->file_modes = modes;
312
5.23k
    return 0;
313
5.23k
}