Coverage Report

Created: 2025-06-10 07:27

/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
92.4k
{
51
92.4k
    static const stream_procs p = {
52
92.4k
        s_file_available, s_file_read_seek, s_std_read_reset,
53
92.4k
        s_std_read_flush, s_file_read_close, s_file_read_process,
54
92.4k
        s_file_switch
55
92.4k
    };
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
92.4k
    int had_error = gp_ferror(file);
62
92.4k
    gs_offset_t curpos = gp_ftell(file);
63
92.4k
    bool seekable = (curpos != -1L && gp_fseek(file, curpos, SEEK_SET) == 0);
64
65
92.4k
    if (!had_error)
66
92.4k
        gp_clearerr(file);
67
92.4k
    s_std_init(s, buf, len, &p,
68
92.4k
               (seekable ? s_mode_read + s_mode_seek : s_mode_read));
69
92.4k
    if_debug1m('s', s->memory, "[s]read file="PRI_INTPTR"\n", (intptr_t)file);
70
92.4k
    s->file = file;
71
92.4k
    s->file_modes = s->modes;
72
92.4k
    s->file_offset = 0;
73
92.4k
    s->file_limit = (sizeof(gs_offset_t) > 4 ? max_int64_t : max_long);
74
92.4k
}
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
184k
{
96
184k
    gs_offset_t max_avail = s->file_limit - stell(s);
97
184k
    gs_offset_t buf_avail = sbufavailable(s);
98
99
184k
    *pl = min(max_avail, buf_avail);
100
184k
    if (sseekable(s)) {
101
184k
        gs_offset_t pos, end;
102
103
184k
        pos = gp_ftell(s->file);
104
184k
        if (gp_fseek(s->file, 0, SEEK_END))
105
0
            return ERRC;
106
184k
        end = gp_ftell(s->file);
107
184k
        if (gp_fseek(s->file, pos, SEEK_SET))
108
0
            return ERRC;
109
184k
        buf_avail += end - pos;
110
184k
        *pl = min(max_avail, buf_avail);
111
184k
        if (*pl == 0)
112
26.1k
            *pl = -1;   /* EOF */
113
184k
    } 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
184k
    return 0;
121
184k
}
122
static int
123
s_file_read_seek(register stream * s, gs_offset_t pos)
124
15.7M
{
125
15.7M
    gs_offset_t end = s->cursor.r.limit - s->cbuf + 1;
126
15.7M
    gs_offset_t offset = pos - s->position;
127
128
15.7M
    if (offset >= 0 && offset <= end) {  /* Staying within the same buffer */
129
6.65M
        s->cursor.r.ptr = s->cbuf + offset - 1;
130
6.65M
        return 0;
131
6.65M
    }
132
9.11M
    if (pos < 0 || pos > s->file_limit || s->file == NULL ||
133
9.11M
        gp_fseek(s->file, s->file_offset + pos, SEEK_SET) != 0
134
9.11M
        )
135
26.2k
        return ERRC;
136
9.09M
    s->cursor.r.ptr = s->cursor.r.limit = s->cbuf - 1;
137
9.09M
    s->end_status = 0;
138
9.09M
    s->position = pos;
139
9.09M
    return 0;
140
9.11M
}
141
static int
142
s_file_read_close(stream * s)
143
92.4k
{
144
92.4k
    gp_file *file = s->file;
145
146
92.4k
    if (file != 0) {
147
92.4k
        s->file = 0;
148
92.4k
        return (gp_fclose(file) ? ERRC : 0);
149
92.4k
    }
150
0
    return 0;
151
92.4k
}
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
12.0M
{
161
12.0M
    stream *s = (stream *)st; /* no separate state */
162
12.0M
    gp_file *file = s->file;
163
12.0M
    gs_offset_t max_count = pw->limit - pw->ptr;
164
12.0M
    int status = 1;
165
12.0M
    int count;
166
167
12.0M
    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
12.0M
    count = gp_fread(pw->ptr + 1, 1, max_count, file);
174
12.0M
    if (count < 0)
175
0
        count = 0;
176
12.0M
    pw->ptr += count;
177
12.0M
    process_interrupts(s->memory);
178
12.0M
    return (gp_ferror(file) ? ERRC : gp_feof(file) ? EOFC : status);
179
12.0M
}
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
288k
{
187
288k
    static const stream_procs p = {
188
288k
        s_std_noavailable, s_file_write_seek, s_std_write_reset,
189
288k
        s_file_write_flush, s_file_write_close, s_file_write_process,
190
288k
        s_file_switch
191
288k
    };
192
193
288k
    s_std_init(s, buf, len, &p,
194
288k
               (gp_get_file(file) == stdout ? s_mode_write : s_mode_write + s_mode_seek));
195
288k
    if_debug1m('s', s->memory, "[s]write file="PRI_INTPTR"\n", (intptr_t) file);
196
288k
    s->file = file;
197
288k
    s->file_modes = s->modes;
198
288k
    s->file_offset = 0;   /* in case we switch to reading later */
199
288k
    s->file_limit = S_FILE_LIMIT_MAX;
200
288k
}
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
452k
{
217
    /* We must flush the buffer to reposition. */
218
452k
    int code = sflush(s);
219
220
452k
    if (code < 0)
221
0
        return code;
222
452k
    if (gp_fseek(s->file, pos, SEEK_SET) != 0)
223
0
        return ERRC;
224
452k
    s->position = pos;
225
452k
    return 0;
226
452k
}
227
static int
228
s_file_write_flush(register stream * s)
229
11.1M
{
230
11.1M
    int result = s_process_write_buf(s, false);
231
232
11.1M
    gp_fflush(s->file);
233
11.1M
    return result;
234
11.1M
}
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
47.1M
{
250
47.1M
    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
47.1M
    if (count != 0) {
257
32.4M
        gp_file *file = ((stream *) st)->file;
258
32.4M
        int written = gp_fwrite(pr->ptr + 1, 1, count, file);
259
260
32.4M
        if (written < 0)
261
0
            written = 0;
262
32.4M
        pr->ptr += written;
263
32.4M
        process_interrupts(st->memory);
264
32.4M
        return (gp_ferror(file) ? ERRC : 0);
265
32.4M
    } else {
266
14.6M
        process_interrupts(st->memory);
267
14.6M
        return 0;
268
14.6M
    }
269
47.1M
}
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
92.4k
{
277
92.4k
    uint modes = s->file_modes;
278
92.4k
    gp_file *file = s->file;
279
92.4k
    gs_offset_t pos;
280
281
92.4k
    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
92.4k
    } else {
298
92.4k
        if (!(s->file_modes & s_mode_read))
299
0
            return ERRC;
300
92.4k
        pos = stell(s);
301
92.4k
        if_debug2m('s', s->memory, "[s]switch 0x%"PRIu64" to read at %"PRId64"\n",
302
92.4k
                   (uint64_t) s, (int64_t)pos);
303
92.4k
        if (sflush(s) < 0)
304
0
            return ERRC;
305
92.4k
        if (gp_fseek(file, 0L, SEEK_CUR) != 0)
306
0
            return ERRC;
307
92.4k
        sread_file(s, file, s->cbuf, s->cbsize);
308
92.4k
        s->modes |= modes & s_mode_append; /* don't lose append info */
309
92.4k
        s->position = pos;
310
92.4k
    }
311
92.4k
    s->file_modes = modes;
312
92.4k
    return 0;
313
92.4k
}