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