stdio.c revision 95154
1/*
2 * Copyright (c) 2000-2002 Sendmail, Inc. and its suppliers.
3 *      All rights reserved.
4 * Copyright (c) 1990, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Chris Torek.
9 *
10 * By using this file, you agree to the terms and conditions set
11 * forth in the LICENSE file which can be found at the top level of
12 * the sendmail distribution.
13 */
14
15#include <sm/gen.h>
16SM_RCSID("@(#)$Id: stdio.c,v 1.1.1.2 2002/04/10 03:04:56 gshapiro Exp $")
17#include <unistd.h>
18#include <errno.h>
19#include <fcntl.h>
20#include <string.h>	/* FreeBSD: FD_ZERO needs <string.h> */
21#include <sys/stat.h>
22#include <sys/time.h>
23#include <sm/heap.h>
24#include <sm/assert.h>
25#include <sm/varargs.h>
26#include <sm/io.h>
27#include <sm/fdset.h>
28#include <sm/setjmp.h>
29#include <sm/conf.h>
30#include "local.h"
31
32/*
33**  Overall:
34**  Small standard I/O/seek/close functions.
35**  These maintain the `known seek offset' for seek optimization.
36*/
37
38/*
39**  SM_STDOPEN -- open a file with stdio behavior
40**
41**  Not associated with the system's stdio in libc.
42**
43**	Parameters:
44**		fp -- file pointer to be associated with the open
45**		info -- pathname of the file to be opened
46**		flags -- indicates type of access methods
47**		rpool -- ignored
48**
49**	Returns:
50**		Failure: -1 and set errno
51**		Success: 0 or greater (fd of file from open(2)).
52**
53*/
54
55/* ARGSUSED3 */
56int
57sm_stdopen(fp, info, flags, rpool)
58	SM_FILE_T *fp;
59	const void *info;
60	int flags;
61	const void *rpool;
62{
63	char *path = (char *) info;
64	int oflags;
65
66	switch (flags)
67	{
68	  case SM_IO_RDWR:
69		oflags = O_RDWR;
70		break;
71	  case SM_IO_RDWRTR:
72		oflags = O_RDWR | O_CREAT | O_TRUNC;
73		break;
74	  case SM_IO_RDONLY:
75		oflags = O_RDONLY;
76		break;
77	  case SM_IO_WRONLY:
78		oflags = O_WRONLY | O_CREAT | O_TRUNC;
79		break;
80	  case SM_IO_APPEND:
81		oflags = O_APPEND | O_WRONLY | O_CREAT;
82		break;
83	  case SM_IO_APPENDRW:
84		oflags = O_APPEND | O_RDWR | O_CREAT;
85		break;
86	  default:
87		errno = EINVAL;
88		return -1;
89	}
90	fp->f_file = open(path, oflags,
91			  S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
92	if (fp->f_file < 0)
93		return -1; /* errno set by open() */
94
95	if (oflags & O_APPEND)
96		(void) (*fp->f_seek)((void *)fp, (off_t)0, SEEK_END);
97
98	return fp->f_file;
99}
100
101/*
102**  SM_STDREAD -- read from the file
103**
104**	Parameters:
105**		fp -- file pointer to read from
106**		buf -- location to place read data
107**		n -- number of bytes to read
108**
109**	Returns:
110**		Failure: -1 and sets errno
111**		Success: number of bytes read
112**
113**	Side Effects:
114**		Updates internal offset into file.
115*/
116
117ssize_t
118sm_stdread(fp, buf, n)
119	SM_FILE_T *fp;
120	char *buf;
121	size_t n;
122{
123	register int ret;
124
125	ret = read(fp->f_file, buf, n);
126
127	/* if the read succeeded, update the current offset */
128	if (ret > 0)
129		fp->f_lseekoff += ret;
130	return ret;
131}
132
133/*
134**  SM_STDWRITE -- write to the file
135**
136**	Parameters:
137**		fp -- file pointer ro write to
138**		buf -- location of data to be written
139**		n - number of bytes to write
140**
141**	Returns:
142**		Failure: -1 and sets errno
143**		Success: number of bytes written
144*/
145
146ssize_t
147sm_stdwrite(fp, buf, n)
148	SM_FILE_T *fp;
149	char const *buf;
150	size_t n;
151{
152	return write(fp->f_file, buf, n);
153}
154
155/*
156**  SM_STDSEEK -- set the file offset position
157**
158**	Parmeters:
159**		fp -- file pointer to position
160**		offset -- how far to position from "base" (set by 'whence')
161**		whence -- indicates where the "base" of the 'offset' to start
162**
163**	Results:
164**		Failure: -1 and sets errno
165**		Success: the current offset
166**
167**	Side Effects:
168**		Updates the internal value of the offset.
169*/
170
171off_t
172sm_stdseek(fp, offset, whence)
173	SM_FILE_T *fp;
174	off_t offset;
175	int whence;
176{
177	register off_t ret;
178
179	ret = lseek(fp->f_file, (off_t) offset, whence);
180	if (ret != (off_t) -1)
181		fp->f_lseekoff = ret;
182	return ret;
183}
184
185/*
186**  SM_STDCLOSE -- close the file
187**
188**	Parameters:
189**		fp -- the file pointer to close
190**
191**	Returns:
192**		Success: 0 (zero)
193**		Failure: -1 and sets errno
194*/
195
196int
197sm_stdclose(fp)
198	SM_FILE_T *fp;
199{
200	return close(fp->f_file);
201}
202
203/*
204**  SM_STDSETMODE -- set the access mode for the file
205**
206**  Called by sm_stdsetinfo().
207**
208**	Parameters:
209**		fp -- file pointer
210**		mode -- new mode to set the file access to
211**
212**	Results:
213**		Success: 0 (zero);
214**		Failure: -1 and sets errno
215*/
216
217int
218sm_stdsetmode(fp, mode)
219	SM_FILE_T *fp;
220	const int *mode;
221{
222	int flags = 0;
223
224	switch (*mode)
225	{
226	  case SM_IO_RDWR:
227		flags |= SMRW;
228		break;
229	  case SM_IO_RDONLY:
230		flags |= SMRD;
231		break;
232	  case SM_IO_WRONLY:
233		flags |= SMWR;
234		break;
235	  case SM_IO_APPEND:
236	  default:
237		errno = EINVAL;
238		return -1;
239	}
240	fp->f_flags = fp->f_flags & ~SMMODEMASK;
241	fp->f_flags |= flags;
242	return 0;
243}
244
245/*
246**  SM_STDGETMODE -- for getinfo determine open mode
247**
248**  Called by sm_stdgetinfo().
249**
250**	Parameters:
251**		fp -- the file mode being determined
252**		mode -- internal mode to map to external value
253**
254**	Results:
255**		Failure: -1 and sets errno
256**		Success: external mode value
257*/
258
259int
260sm_stdgetmode(fp, mode)
261	SM_FILE_T *fp;
262	int *mode;
263{
264	switch (fp->f_flags & SMMODEMASK)
265	{
266	  case SMRW:
267		*mode = SM_IO_RDWR;
268		break;
269	  case SMRD:
270		*mode = SM_IO_RDONLY;
271		break;
272	  case SMWR:
273		*mode = SM_IO_WRONLY;
274		break;
275	  default:
276		errno = EINVAL;
277		return -1;
278	}
279	return 0;
280}
281
282/*
283**  SM_STDSETINFO -- set/modify information for a file
284**
285**	Parameters:
286**		fp -- file to set info for
287**		what -- type of info to set
288**		valp -- location of data used for setting
289**
290**	Returns:
291**		Failure: -1 and sets errno
292**		Success: >=0
293*/
294
295int
296sm_stdsetinfo(fp, what, valp)
297	SM_FILE_T *fp;
298	int what;
299	void *valp;
300{
301	switch (what)
302	{
303	  case SM_IO_WHAT_MODE:
304		return sm_stdsetmode(fp, (const int *)valp);
305
306	  default:
307		errno = EINVAL;
308		return -1;
309	}
310}
311
312/*
313**  SM_GETINFO -- get information about the open file
314**
315**	Parameters:
316**		fp -- file to get info for
317**		what -- type of info to get
318**		valp -- location to place found info
319**
320**	Returns:
321**		Success: may or may not place info in 'valp' depending
322**			on 'what' value, and returns values >=0. Return
323**			value may be the obtained info
324**		Failure: -1 and sets errno
325*/
326
327int
328sm_stdgetinfo(fp, what, valp)
329	SM_FILE_T *fp;
330	int what;
331	void *valp;
332{
333	switch (what)
334	{
335	  case SM_IO_WHAT_MODE:
336		return sm_stdgetmode(fp, (int *)valp);
337
338	  case SM_IO_WHAT_FD:
339		return fp->f_file;
340
341	  case SM_IO_WHAT_SIZE:
342	  {
343		  struct stat st;
344
345		  if (fstat(fp->f_file, &st) == 0)
346			  return st.st_size;
347		  else
348			  return -1;
349	  }
350
351	  case SM_IO_IS_READABLE:
352	  {
353		  fd_set readfds;
354		  struct timeval timeout;
355
356		  FD_ZERO(&readfds);
357		  SM_FD_SET(fp->f_file, &readfds);
358		  timeout.tv_sec = 0;
359		  timeout.tv_usec = 0;
360		  if (select(fp->f_file + 1, FDSET_CAST &readfds,
361			     NULL, NULL, &timeout) > 0 &&
362		      SM_FD_ISSET(fp->f_file, &readfds))
363			  return 1;
364		  return 0;
365	  }
366
367	  default:
368		errno = EINVAL;
369		return -1;
370	}
371}
372
373/*
374**  SM_STDFDOPEN -- open file by primitive 'fd' rather than pathname
375**
376**	I/O function to handle fdopen() stdio equivalence. The rest of
377**	the functions are the same as the sm_stdopen() above.
378**
379**	Parameters:
380**		fp -- the file pointer to be associated with the open
381**		name -- the primitive file descriptor for association
382**		flags -- indicates type of access methods
383**		rpool -- ignored
384**
385**	Results:
386**		Success: primitive file descriptor value
387**		Failure: -1 and sets errno
388*/
389
390/* ARGSUSED3 */
391int
392sm_stdfdopen(fp, info, flags, rpool)
393	SM_FILE_T *fp;
394	const void *info;
395	int flags;
396	const void *rpool;
397{
398	int oflags, tmp, fdflags, fd = *((int *) info);
399
400	switch (flags)
401	{
402	  case SM_IO_RDWR:
403		oflags = O_RDWR | O_CREAT;
404		break;
405	  case SM_IO_RDONLY:
406		oflags = O_RDONLY;
407		break;
408	  case SM_IO_WRONLY:
409		oflags = O_WRONLY | O_CREAT | O_TRUNC;
410		break;
411	  case SM_IO_APPEND:
412		oflags = O_APPEND | O_WRONLY | O_CREAT;
413		break;
414	  case SM_IO_APPENDRW:
415		oflags = O_APPEND | O_RDWR | O_CREAT;
416		break;
417	  default:
418		errno = EINVAL;
419		return -1;
420	}
421
422	/* Make sure the mode the user wants is a subset of the actual mode. */
423	if ((fdflags = fcntl(fd, F_GETFL, 0)) < 0)
424		return -1;
425
426	tmp = fdflags & O_ACCMODE;
427	if (tmp != O_RDWR && (tmp != (oflags & O_ACCMODE)))
428	{
429		errno = EINVAL;
430		return -1;
431	}
432	fp->f_file = fd;
433	if (oflags & O_APPEND)
434		(void) (*fp->f_seek)(fp, (off_t)0, SEEK_END);
435	return fp->f_file;
436}
437
438/*
439**  SM_IO_FOPEN -- open a file
440**
441**	Same interface and semantics as the open() system call,
442**	except that it returns SM_FILE_T* instead of a file descriptor.
443**
444**	Parameters:
445**		pathname -- path of file to open
446**		flags -- flags controlling the open
447**		...  -- option "mode" for opening the file
448**
449**	Returns:
450**		Raises an exception on heap exhaustion.
451**		Returns NULL and sets errno if open() fails.
452**		Returns an SM_FILE_T pointer on success.
453*/
454
455SM_FILE_T *
456#if SM_VA_STD
457sm_io_fopen(char *pathname, int flags, ...)
458#else /* SM_VA_STD */
459sm_io_fopen(pathname, flags, va_alist)
460	char *pathname;
461	int flags;
462	va_dcl
463#endif /* SM_VA_STD */
464{
465	MODE_T mode;
466	SM_FILE_T *fp;
467	int ioflags;
468
469	if (flags & O_CREAT)
470	{
471		SM_VA_LOCAL_DECL
472
473		SM_VA_START(ap, flags);
474		mode = (MODE_T) SM_VA_ARG(ap, int);
475		SM_VA_END(ap);
476	}
477	else
478		mode = 0;
479
480	switch (flags & O_ACCMODE)
481	{
482	  case O_RDONLY:
483		ioflags = SMRD;
484		break;
485	  case O_WRONLY:
486		ioflags = SMWR;
487		break;
488	  case O_RDWR:
489		ioflags = SMRW;
490		break;
491	  default:
492		sm_abort("sm_io_fopen: bad flags 0%o", flags);
493	}
494
495	fp = sm_fp(SmFtStdio, ioflags, NULL);
496	fp->f_file = open(pathname, flags, mode);
497	if (fp->f_file == -1)
498	{
499		fp->f_flags = 0;
500		fp->sm_magic = NULL;
501		return NULL;
502	}
503	return fp;
504}
505