1 /*
2  * Copyright (c) 2000-2002, 2004 Sendmail, Inc. and its suppliers.
3  *      All rights reserved.
4  *
5  * By using this file, you agree to the terms and conditions set
6  * forth in the LICENSE file which can be found at the top level of
7  * the sendmail distribution.
8  */
9 
10 #include <sm/gen.h>
11 SM_IDSTR(id, "@(#)$Id: smstdio.c,v 1.34 2004/08/03 20:46:34 ca Exp $")
12 #include <unistd.h>
13 #include <stdio.h>
14 #include <fcntl.h>
15 #include <errno.h>
16 #include <sys/stat.h>
17 #include <sm/assert.h>
18 #include <sm/io.h>
19 #include <sm/string.h>
20 #include "local.h"
21 
22 static void	setup __P((SM_FILE_T *));
23 
24 /*
25 ** Overall:
26 **	This is a file type which implements a layer on top of the system
27 **	stdio. fp->f_cookie is the FILE* of stdio. The cookie may be
28 **	"bound late" because of the manner which Linux implements stdio.
29 **	When binding late  (when fp->f_cookie==NULL) then the value of
30 **	fp->f_ival is used (0, 1 or 2) to map to stdio's stdin, stdout or
31 **	stderr.
32 */
33 
34 /*
35 **  SM_STDIOOPEN -- open a file to system stdio implementation
36 **
37 **	Parameters:
38 **		fp -- file pointer assign for this open
39 **		info -- info about file to open
40 **		flags -- indicating method of opening
41 **		rpool -- ignored
42 **
43 **	Returns:
44 **		Failure: -1
45 **		Success: 0 (zero)
46 */
47 
48 /* ARGSUSED3 */
49 int
sm_stdioopen(fp,info,flags,rpool)50 sm_stdioopen(fp, info, flags, rpool)
51 	SM_FILE_T *fp;
52 	const void *info;
53 	int flags;
54 	const void *rpool;
55 {
56 	register FILE *s;
57 	char *stdiomode;
58 
59 	switch (flags)
60 	{
61 	  case SM_IO_RDONLY:
62 		stdiomode = "r";
63 		break;
64 	  case SM_IO_WRONLY:
65 		stdiomode = "w";
66 		break;
67 	  case SM_IO_APPEND:
68 		stdiomode = "a";
69 		break;
70 	  case SM_IO_APPENDRW:
71 		stdiomode = "a+";
72 		break;
73 #if SM_IO_BINARY != 0
74 	  case SM_IO_RDONLY_B:
75 		stdiomode = "rb";
76 		break;
77 	  case SM_IO_WRONLY_B:
78 		stdiomode = "wb";
79 		break;
80 	  case SM_IO_APPEND_B:
81 		stdiomode = "ab";
82 		break;
83 	  case SM_IO_APPENDRW_B:
84 		stdiomode = "a+b";
85 		break;
86 	  case SM_IO_RDWR_B:
87 		stdiomode = "r+b";
88 		break;
89 #endif /* SM_IO_BINARY != 0 */
90 	  case SM_IO_RDWR:
91 	  default:
92 		stdiomode = "r+";
93 		break;
94 	}
95 
96 	if ((s = fopen((char *)info, stdiomode)) == NULL)
97 		return -1;
98 	fp->f_cookie = s;
99 	return 0;
100 }
101 
102 /*
103 **  SETUP -- assign file type cookie when not already assigned
104 **
105 **	Parameters:
106 **		fp - the file pointer to get the cookie assigned
107 **
108 **	Return:
109 **		none.
110 */
111 
112 static void
setup(fp)113 setup(fp)
114 	SM_FILE_T *fp;
115 {
116 	if (fp->f_cookie == NULL)
117 	{
118 		switch (fp->f_ival)
119 		{
120 		  case 0:
121 			fp->f_cookie = stdin;
122 			break;
123 		  case 1:
124 			fp->f_cookie = stdout;
125 			break;
126 		  case 2:
127 			fp->f_cookie = stderr;
128 			break;
129 		  default:
130 			sm_abort("fp->f_ival=%d: out of range (0...2)", fp->f_ival);
131 			break;
132 		}
133 	}
134 }
135 
136 /*
137 **  SM_STDIOREAD -- read from the file
138 **
139 **	Parameters:
140 **		fp -- the file pointer
141 **		buf -- location to place the read data
142 **		n - number of bytes to read
143 **
144 **	Returns:
145 **		result from fread().
146 */
147 
148 ssize_t
sm_stdioread(fp,buf,n)149 sm_stdioread(fp, buf, n)
150 	SM_FILE_T *fp;
151 	char *buf;
152 	size_t n;
153 {
154 	register FILE *s;
155 
156 	if (fp->f_cookie == NULL)
157 		setup(fp);
158 	s = fp->f_cookie;
159 	return fread(buf, 1, n, s);
160 }
161 
162 /*
163 **  SM_STDIOWRITE -- write to the file
164 **
165 **	Parameters:
166 **		fp -- the file pointer
167 **		buf -- location of data to write
168 **		n - number of bytes to write
169 **
170 **	Returns:
171 **		result from fwrite().
172 */
173 
174 ssize_t
sm_stdiowrite(fp,buf,n)175 sm_stdiowrite(fp, buf, n)
176 	SM_FILE_T *fp;
177 	char const *buf;
178 	size_t n;
179 {
180 	register FILE *s;
181 
182 	if (fp->f_cookie == NULL)
183 		setup(fp);
184 	s = fp->f_cookie;
185 	return fwrite(buf, 1, n, s);
186 }
187 
188 /*
189 **  SM_STDIOSEEK -- set position within file
190 **
191 **	Parameters:
192 **		fp -- the file pointer
193 **		offset -- new location based on 'whence'
194 **		whence -- indicates "base" for 'offset'
195 **
196 **	Returns:
197 **		result from fseek().
198 */
199 
200 off_t
sm_stdioseek(fp,offset,whence)201 sm_stdioseek(fp, offset, whence)
202 	SM_FILE_T *fp;
203 	off_t offset;
204 	int whence;
205 {
206 	register FILE *s;
207 
208 	if (fp->f_cookie == NULL)
209 		setup(fp);
210 	s = fp->f_cookie;
211 	return fseek(s, offset, whence);
212 }
213 
214 /*
215 **  SM_STDIOCLOSE -- close the file
216 **
217 **	Parameters:
218 **		fp -- close file pointer
219 **
220 **	Return:
221 **		status from fclose()
222 */
223 
224 int
sm_stdioclose(fp)225 sm_stdioclose(fp)
226 	SM_FILE_T *fp;
227 {
228 	register FILE *s;
229 
230 	if (fp->f_cookie == NULL)
231 		setup(fp);
232 	s = fp->f_cookie;
233 	return fclose(s);
234 }
235 
236 /*
237 **  SM_STDIOSETINFO -- set info for this open file
238 **
239 **	Parameters:
240 **		fp -- the file pointer
241 **		what -- type of information setting
242 **		valp -- memory location of info to set
243 **
244 **	Return:
245 **		Failure: -1 and sets errno
246 **		Success: none (currently).
247 */
248 
249 /* ARGSUSED2 */
250 int
sm_stdiosetinfo(fp,what,valp)251 sm_stdiosetinfo(fp, what, valp)
252 	SM_FILE_T *fp;
253 	int what;
254 	void *valp;
255 {
256 	switch (what)
257 	{
258 	  case SM_IO_WHAT_MODE:
259 	  default:
260 		errno = EINVAL;
261 		return -1;
262 	}
263 }
264 
265 /*
266 **  SM_STDIOGETINFO -- get info for this open file
267 **
268 **	Parameters:
269 **		fp -- the file pointer
270 **		what -- type of information request
271 **		valp -- memory location to place info
272 **
273 **	Return:
274 **		Failure: -1 and sets errno
275 **		Success: none (currently).
276 */
277 
278 /* ARGSUSED2 */
279 int
sm_stdiogetinfo(fp,what,valp)280 sm_stdiogetinfo(fp, what, valp)
281 	SM_FILE_T *fp;
282 	int what;
283 	void *valp;
284 {
285 	switch (what)
286 	{
287 	  case SM_IO_WHAT_SIZE:
288 	  {
289 		  int fd;
290 		  struct stat st;
291 
292 		  if (fp->f_cookie == NULL)
293 			  setup(fp);
294 		  fd = fileno((FILE *) fp->f_cookie);
295 		  if (fd < 0)
296 			  return -1;
297 		  if (fstat(fd, &st) == 0)
298 			  return st.st_size;
299 		  else
300 			  return -1;
301 	  }
302 
303 	  case SM_IO_WHAT_MODE:
304 	  default:
305 		errno = EINVAL;
306 		return -1;
307 	}
308 }
309 
310 /*
311 **  SM_IO_STDIOOPEN -- create an SM_FILE which interfaces to a stdio FILE
312 **
313 **	Parameters:
314 **		stream -- an open stdio stream, as returned by fopen()
315 **		mode -- the mode argument to fopen() which describes stream
316 **
317 **	Return:
318 **		On success, return a pointer to an SM_FILE object which
319 **		can be used for reading and writing 'stream'.
320 **		Abort if mode is gibberish or stream is bad.
321 **		Raise an exception if we can't allocate memory.
322 */
323 
324 SM_FILE_T *
sm_io_stdioopen(stream,mode)325 sm_io_stdioopen(stream, mode)
326 	FILE *stream;
327 	char *mode;
328 {
329 	int fd;
330 	bool r, w;
331 	int ioflags;
332 	SM_FILE_T *fp;
333 
334 	fd = fileno(stream);
335 	SM_REQUIRE(fd >= 0);
336 
337 	r = w = false;
338 	switch (mode[0])
339 	{
340 	  case 'r':
341 		r = true;
342 		break;
343 	  case 'w':
344 	  case 'a':
345 		w = true;
346 		break;
347 	  default:
348 		sm_abort("sm_io_stdioopen: mode '%s' is bad", mode);
349 	}
350 	if (strchr(&mode[1], '+') != NULL)
351 		r = w = true;
352 	if (r && w)
353 		ioflags = SMRW;
354 	else if (r)
355 		ioflags = SMRD;
356 	else
357 		ioflags = SMWR;
358 
359 	fp = sm_fp(SmFtRealStdio, ioflags, NULL);
360 	fp->f_file = fd;
361 	fp->f_cookie = stream;
362 	return fp;
363 }
364