/src/ghostpdl/base/sfxcommon.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 | | /* $Id: sfxcommon.c 8250 2007-09-25 13:31:24Z giles $ */ |
18 | | /* Common routines for stdio and fd file stream implementations. */ |
19 | | #include "stdio_.h" /* includes std.h */ |
20 | | #include "memory_.h" |
21 | | #include "unistd_.h" |
22 | | #include "gsmemory.h" |
23 | | #include "gp.h" |
24 | | #include "gserrors.h" |
25 | | #include "stream.h" |
26 | | #include "assert_.h" |
27 | | |
28 | | #define DEFAULT_BUFFER_SIZE 2048 |
29 | | const uint file_default_buffer_size = DEFAULT_BUFFER_SIZE; |
30 | | |
31 | | /* Allocate and return a file stream. */ |
32 | | /* Return 0 if the allocation failed. */ |
33 | | /* The stream is initialized to an invalid state, so the caller need not */ |
34 | | /* worry about cleaning up if a later step in opening the stream fails. */ |
35 | | stream * |
36 | | file_alloc_stream(gs_memory_t * mem, client_name_t cname) |
37 | 98.4M | { |
38 | 98.4M | stream *s; |
39 | 98.4M | s = s_alloc(mem, cname); |
40 | 98.4M | if (s == 0) |
41 | 0 | return 0; |
42 | 98.4M | s_init_ids(s); |
43 | 98.4M | s->is_temp = 0; /* not a temp stream */ |
44 | 98.4M | s->foreign = 0; |
45 | | /* |
46 | | * Disable the stream now (in case we can't open the file, |
47 | | * or a filter init procedure fails) so that `restore' won't |
48 | | * crash when it tries to close open files. |
49 | | */ |
50 | 98.4M | s_disable(s); |
51 | 98.4M | s->prev = 0; |
52 | 98.4M | s->next = 0; |
53 | 98.4M | return s; |
54 | 98.4M | } |
55 | | |
56 | | /* Open a file stream, optionally on an OS file. */ |
57 | | /* Return 0 if successful, error code if not. */ |
58 | | /* On a successful return, the C file name is in the stream buffer. */ |
59 | | /* If fname==0, set up the file entry, stream, and buffer, */ |
60 | | /* but don't open an OS file or initialize the stream. */ |
61 | | int |
62 | | file_open_stream(const char *fname, uint len, const char *file_access, |
63 | | uint buffer_size, stream ** ps, gx_io_device *iodev, |
64 | | iodev_proc_fopen_t fopen_proc, gs_memory_t *mem) |
65 | 80.9M | { |
66 | 80.9M | int code; |
67 | 80.9M | gp_file *file; |
68 | 80.9M | char fmode[4]; /* r/w/a, [+], [b], null */ |
69 | | |
70 | | #ifdef DEBUG |
71 | | if (strlen(gp_fmode_binary_suffix) > 0) { |
72 | | if (strchr(file_access, gp_fmode_binary_suffix[0]) != NULL) |
73 | | dmprintf(mem, "\nWarning: spurious 'b' character in file access mode\n"); |
74 | | assert(strchr(file_access, gp_fmode_binary_suffix[0]) == NULL); |
75 | | } |
76 | | #endif |
77 | | |
78 | 80.9M | if (!iodev) |
79 | 6.75M | iodev = iodev_default(mem); |
80 | 80.9M | code = file_prepare_stream(fname, len, file_access, buffer_size, ps, fmode, mem); |
81 | 80.9M | if (code < 0) |
82 | 0 | return code; |
83 | 80.9M | if (fname == 0) |
84 | 6.75M | return 0; |
85 | 74.2M | if (fname[0] == 0) { /* fopen_proc gets NUL terminated string, not len */ |
86 | | /* so this is the same as len == 0, so return NULL */ |
87 | | /* discard the stuff we allocated to keep from accumulating stuff needing GC */ |
88 | 43 | gs_free_object(mem, (*ps)->cbuf, "file_close(buffer)"); |
89 | 43 | gs_free_object(mem, *ps, "file_prepare_stream(stream)"); |
90 | 43 | *ps = NULL; |
91 | | |
92 | 43 | return 0; |
93 | 43 | } |
94 | 74.2M | code = (*fopen_proc)(iodev, (char *)(*ps)->cbuf, fmode, &file, |
95 | 74.2M | (char *)(*ps)->cbuf, (*ps)->bsize, mem); |
96 | 74.2M | if (code < 0) { |
97 | | /* discard the stuff we allocated to keep from accumulating stuff needing GC */ |
98 | 74.2M | gs_free_object(mem, (*ps)->cbuf, "file_close(buffer)"); |
99 | 74.2M | gs_free_object(mem, *ps, "file_prepare_stream(stream)"); |
100 | 74.2M | *ps = NULL; |
101 | 74.2M | return code; |
102 | 74.2M | } |
103 | 0 | if (file_init_stream(*ps, file, fmode, (*ps)->cbuf, (*ps)->bsize) != 0) |
104 | 0 | return_error(gs_error_ioerror); |
105 | 0 | return 0; |
106 | 0 | } |
107 | | |
108 | | /* Close a file stream. This replaces the close procedure in the stream */ |
109 | | /* for normal (OS) files and for filters. */ |
110 | | int |
111 | | file_close_file(stream * s) |
112 | 6.74M | { |
113 | 6.74M | stream *stemp = s->strm; |
114 | 6.74M | gs_memory_t *mem; |
115 | 6.74M | int code = file_close_disable(s); |
116 | | |
117 | 6.74M | if (code) |
118 | 184k | return code; |
119 | | /* |
120 | | * Check for temporary streams created for filters. |
121 | | * There may be more than one in the case of a procedure-based filter, |
122 | | * or if we created an intermediate stream to ensure |
123 | | * a large enough buffer. Note that these streams may have been |
124 | | * allocated by file_alloc_stream, so we mustn't free them. |
125 | | */ |
126 | 7.44M | while (stemp != 0 && stemp->is_temp != 0) { |
127 | 879k | stream *snext = stemp->strm; |
128 | | |
129 | 879k | mem = stemp->memory; |
130 | 879k | if (stemp->is_temp > 1) |
131 | 879k | gs_free_object(mem, stemp->cbuf, |
132 | 879k | "file_close(temp stream buffer)"); |
133 | 879k | s_disable(stemp); |
134 | 879k | stemp = snext; |
135 | 879k | } |
136 | 6.56M | mem = s->memory; |
137 | 6.56M | gs_free_object(mem, s->cbuf, "file_close(buffer)"); |
138 | 6.56M | if (s->close_strm && stemp != 0) |
139 | 0 | return sclose(stemp); |
140 | 6.56M | return 0; |
141 | 6.56M | } |
142 | | |
143 | | /* |
144 | | * Set up a file stream on an OS file. The caller has allocated the |
145 | | * stream and buffer. |
146 | | */ |
147 | | int |
148 | | file_init_stream(stream *s, gp_file *file, const char *fmode, byte *buffer, |
149 | | uint buffer_size) |
150 | 94.5k | { |
151 | 94.5k | switch (fmode[0]) { |
152 | 0 | case 'a': |
153 | 0 | if (sappend_file(s, file, buffer, buffer_size) != 0) |
154 | 0 | return ERRC; |
155 | 0 | break; |
156 | 0 | case 'r': |
157 | | /* Defeat buffering for terminals. */ |
158 | 0 | { |
159 | 0 | int char_buffered = gp_file_is_char_buffered(file); |
160 | 0 | if (char_buffered < 0) |
161 | 0 | return char_buffered; |
162 | 0 | sread_file(s, file, buffer, char_buffered ? 1 : buffer_size); |
163 | 0 | } |
164 | 0 | break; |
165 | 94.5k | case 'w': |
166 | 94.5k | swrite_file(s, file, buffer, buffer_size); |
167 | 94.5k | } |
168 | 94.5k | if (fmode[1] == '+') |
169 | 94.5k | s->file_modes |= s_mode_read | s_mode_write; |
170 | 94.5k | s->save_close = s->procs.close; |
171 | 94.5k | s->procs.close = file_close_file; |
172 | 94.5k | return 0; |
173 | 94.5k | } |
174 | | |
175 | | /* Prepare a stream with a file name. */ |
176 | | /* Return 0 if successful, error code if not. */ |
177 | | /* On a successful return, the C file name is in the stream buffer. */ |
178 | | /* If fname==0, set up stream, and buffer. */ |
179 | | int |
180 | | file_prepare_stream(const char *fname, uint len, const char *file_access, |
181 | | uint buffer_size, stream ** ps, char fmode[4], gs_memory_t *mem) |
182 | 96.4M | { |
183 | 96.4M | byte *buffer; |
184 | 96.4M | register stream *s; |
185 | | |
186 | 96.4M | if (strlen(file_access) > 2) |
187 | 0 | return_error(gs_error_invalidfileaccess); |
188 | | |
189 | | /* Open the file, always in binary mode. */ |
190 | 96.4M | strcpy(fmode, file_access); |
191 | 96.4M | strcat(fmode, gp_fmode_binary_suffix); |
192 | 96.4M | if (buffer_size == 0) |
193 | 0 | buffer_size = file_default_buffer_size; |
194 | 96.4M | if (len >= buffer_size) /* we copy the file name into the buffer */ |
195 | 0 | return_error(gs_error_limitcheck); |
196 | | /* Allocate the stream first, since it persists */ |
197 | | /* even after the file has been closed. */ |
198 | 96.4M | s = file_alloc_stream(mem, "file_prepare_stream"); |
199 | 96.4M | if (s == 0) |
200 | 0 | return_error(gs_error_VMerror); |
201 | | /* Allocate the buffer. */ |
202 | 96.4M | buffer = gs_alloc_bytes(mem, buffer_size, "file_prepare_stream(buffer)"); |
203 | 96.4M | if (buffer == 0) { |
204 | 0 | gs_free_object(mem, s, "file_prepare_stream"); |
205 | 0 | return_error(gs_error_VMerror); |
206 | 0 | } |
207 | 96.4M | if (fname != 0) { |
208 | 89.7M | memcpy(buffer, fname, len); |
209 | 89.7M | buffer[len] = 0; /* terminate string */ |
210 | 89.7M | } else |
211 | 6.75M | buffer[0] = 0; /* safety */ |
212 | 96.4M | s->cbuf = buffer; |
213 | 96.4M | s->bsize = s->cbsize = buffer_size; |
214 | 96.4M | s->save_close = 0; /* in case this stream gets disabled before init finishes */ |
215 | 96.4M | *ps = s; |
216 | 96.4M | return 0; |
217 | 96.4M | } |