1 /* Copyright (C) 2008 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, modified
8    or distributed except as expressly authorized under the terms of that
9    license.  Refer to licensing information at http://www.artifex.com/
10    or contact Artifex Software, Inc.,  7 Mt. Lassen Drive - Suite A-134,
11    San Rafael, CA  94903, U.S.A., +1(415)492-9861, for further information.
12 */
13 
14 /* $Id: sfxcommon.c 8250 2007-09-25 13:31:24Z giles $ */
15 /* Common routines for stdio and fd file stream implementations. */
16 #include "stdio_.h"		/* includes std.h */
17 #include "memory_.h"
18 #include "unistd_.h"
19 #include "gsmemory.h"
20 #include "gp.h"
21 #include "gserror.h"
22 #include "gserrors.h"
23 #include "stream.h"
24 
25 #define DEFAULT_BUFFER_SIZE 2048
26 const uint file_default_buffer_size = DEFAULT_BUFFER_SIZE;
27 
28 /* Allocate and return a file stream. */
29 /* Return 0 if the allocation failed. */
30 /* The stream is initialized to an invalid state, so the caller need not */
31 /* worry about cleaning up if a later step in opening the stream fails. */
32 stream *
file_alloc_stream(gs_memory_t * mem,client_name_t cname)33 file_alloc_stream(gs_memory_t * mem, client_name_t cname)
34 {
35     stream *s;
36     s = s_alloc(mem, cname);
37     if (s == 0)
38 	return 0;
39     s_init_ids(s);
40     s->is_temp = 0;		/* not a temp stream */
41     s->foreign = 0;
42     /*
43      * Disable the stream now (in case we can't open the file,
44      * or a filter init procedure fails) so that `restore' won't
45      * crash when it tries to close open files.
46      */
47     s_disable(s);
48     s->prev = 0;
49     s->next = 0;
50     return s;
51 }
52 
53 /* Open a file stream, optionally on an OS file. */
54 /* Return 0 if successful, error code if not. */
55 /* On a successful return, the C file name is in the stream buffer. */
56 /* If fname==0, set up the file entry, stream, and buffer, */
57 /* but don't open an OS file or initialize the stream. */
58 int
file_open_stream(const char * fname,uint len,const char * file_access,uint buffer_size,stream ** ps,gx_io_device * iodev,iodev_proc_fopen_t fopen_proc,gs_memory_t * mem)59 file_open_stream(const char *fname, uint len, const char *file_access,
60 		 uint buffer_size, stream ** ps, gx_io_device *iodev,
61 		 iodev_proc_fopen_t fopen_proc, gs_memory_t *mem)
62 {
63     int code;
64     FILE *file;
65     char fmode[4];  /* r/w/a, [+], [b], null */
66 
67     if (!iodev)
68 	iodev = iodev_default;
69     code = file_prepare_stream(fname, len, file_access, buffer_size, ps, fmode, mem);
70     if (code < 0)
71 	return code;
72     if (fname == 0)
73 	return 0;
74     if (fname[0] == 0)		/* fopen_proc gets NUL terminated string, not len */
75 	return 0;		/* so this is the same as len == 0, so return NULL */
76     code = (*fopen_proc)(iodev, (char *)(*ps)->cbuf, fmode, &file,
77 			 (char *)(*ps)->cbuf, (*ps)->bsize);
78     if (code < 0) {
79 	/* discard the stuff we allocated to keep from accumulating stuff needing GC */
80 	gs_free_object(mem, (*ps)->cbuf, "file_close(buffer)");
81 	gs_free_object(mem, *ps, "file_prepare_stream(stream)");
82 	*ps = NULL;
83 	return code;
84     }
85     file_init_stream(*ps, file, fmode, (*ps)->cbuf, (*ps)->bsize);
86     return 0;
87 }
88 
89 /* Close a file stream.  This replaces the close procedure in the stream */
90 /* for normal (OS) files and for filters. */
91 int
file_close_file(stream * s)92 file_close_file(stream * s)
93 {
94     stream *stemp = s->strm;
95     gs_memory_t *mem;
96     int code = file_close_disable(s);
97 
98     if (code)
99 	return code;
100     /*
101      * Check for temporary streams created for filters.
102      * There may be more than one in the case of a procedure-based filter,
103      * or if we created an intermediate stream to ensure
104      * a large enough buffer.  Note that these streams may have been
105      * allocated by file_alloc_stream, so we mustn't free them.
106      */
107     while (stemp != 0 && stemp->is_temp != 0) {
108 	stream *snext = stemp->strm;
109 
110 	mem = stemp->memory;
111 	if (stemp->is_temp > 1)
112 	    gs_free_object(mem, stemp->cbuf,
113 			   "file_close(temp stream buffer)");
114 	s_disable(stemp);
115 	stemp = snext;
116     }
117     mem = s->memory;
118     gs_free_object(mem, s->cbuf, "file_close(buffer)");
119     if (s->close_strm && stemp != 0)
120 	return sclose(stemp);
121     return 0;
122 }
123 
124 /*
125  * Set up a file stream on an OS file.  The caller has allocated the
126  * stream and buffer.
127  */
128 void
file_init_stream(stream * s,FILE * file,const char * fmode,byte * buffer,uint buffer_size)129 file_init_stream(stream *s, FILE *file, const char *fmode, byte *buffer,
130 		 uint buffer_size)
131 {
132     switch (fmode[0]) {
133     case 'a':
134 	sappend_file(s, file, buffer, buffer_size);
135 	break;
136     case 'r':
137 	/* Defeat buffering for terminals. */
138 	{
139 	    struct stat rstat;
140 
141 	    fstat(fileno(file), &rstat);
142 	    sread_file(s, file, buffer,
143 		       (S_ISCHR(rstat.st_mode) ? 1 : buffer_size));
144 	}
145 	break;
146     case 'w':
147 	swrite_file(s, file, buffer, buffer_size);
148     }
149     if (fmode[1] == '+')
150 	s->file_modes |= s_mode_read | s_mode_write;
151     s->save_close = s->procs.close;
152     s->procs.close = file_close_file;
153 }
154 
155 /* Prepare a stream with a file name. */
156 /* Return 0 if successful, error code if not. */
157 /* On a successful return, the C file name is in the stream buffer. */
158 /* If fname==0, set up stream, and buffer. */
159 int
file_prepare_stream(const char * fname,uint len,const char * file_access,uint buffer_size,stream ** ps,char fmode[4],gs_memory_t * mem)160 file_prepare_stream(const char *fname, uint len, const char *file_access,
161 		 uint buffer_size, stream ** ps, char fmode[4], gs_memory_t *mem)
162 {
163     byte *buffer;
164     register stream *s;
165 
166     /* Open the file, always in binary mode. */
167     strcpy(fmode, file_access);
168     strcat(fmode, gp_fmode_binary_suffix);
169     if (buffer_size == 0)
170 	buffer_size = file_default_buffer_size;
171     if (len >= buffer_size)    /* we copy the file name into the buffer */
172 	return_error(gs_error_limitcheck);
173     /* Allocate the stream first, since it persists */
174     /* even after the file has been closed. */
175     s = file_alloc_stream(mem, "file_prepare_stream");
176     if (s == 0)
177 	return_error(gs_error_VMerror);
178     /* Allocate the buffer. */
179     buffer = gs_alloc_bytes(mem, buffer_size, "file_prepare_stream(buffer)");
180     if (buffer == 0)
181 	return_error(gs_error_VMerror);
182     if (fname != 0) {
183 	memcpy(buffer, fname, len);
184 	buffer[len] = 0;	/* terminate string */
185     } else
186 	buffer[0] = 0;	/* safety */
187     s->cbuf = buffer;
188     s->bsize = s->cbsize = buffer_size;
189     s->save_close = 0;	    /* in case this stream gets disabled before init finishes */
190     *ps = s;
191     return 0;
192 }
193