1/*
2 * Copyright (c) 2000-2002, 2004, 2005 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#pragma ident	"%Z%%M%	%I%	%E% SMI"
16
17#include <sm/gen.h>
18SM_IDSTR(id, "@(#)$Id: strio.c,v 1.44 2005/06/09 21:40:19 ca Exp $")
19#include <stdlib.h>
20#include <unistd.h>
21#include <fcntl.h>
22#include <string.h>
23#include <errno.h>
24#include <sm/rpool.h>
25#include <sm/io.h>
26#include <sm/heap.h>
27#include <sm/conf.h>
28#include "local.h"
29
30static int	sm_strsetmode __P((SM_FILE_T*, const int *));
31static int	sm_strgetmode __P((SM_FILE_T*, int *));
32
33/*
34**  Cookie structure for the "strio" file type
35*/
36
37struct sm_str_obj
38{
39	char		*strio_base;
40	char		*strio_end;
41	size_t		strio_size;
42	size_t		strio_offset;
43	int		strio_flags;
44	const void	*strio_rpool;
45};
46
47typedef struct sm_str_obj SM_STR_OBJ_T;
48
49/*
50**  SM_STRGROW -- increase storage space for string
51**
52**	Parameters:
53**		s -- current cookie
54**		size -- new storage size request
55**
56**	Returns:
57**		true iff successful.
58*/
59
60static bool sm_strgrow __P((SM_STR_OBJ_T *, size_t));
61
62static bool
63sm_strgrow(s, size)
64	SM_STR_OBJ_T *s;
65	size_t size;
66{
67	register void *p;
68
69	if (s->strio_size >= size)
70		return true;
71	p = sm_realloc(s->strio_base, size);
72	if (p == NULL)
73		return false;
74	s->strio_base = p;
75	s->strio_end = s->strio_base + size;
76	s->strio_size = size;
77	return true;
78}
79
80/*
81**  SM_STRREAD -- read a portion of the string
82**
83**	Parameters:
84**		fp -- the file pointer
85**		buf -- location to place read data
86**		n -- number of bytes to read
87**
88**	Returns:
89**		Failure: -1 and sets errno
90**		Success: >=0, number of bytes read
91*/
92
93ssize_t
94sm_strread(fp, buf, n)
95	SM_FILE_T *fp;
96	char *buf;
97	size_t n;
98{
99	register SM_STR_OBJ_T *s = fp->f_cookie;
100	int len;
101
102	if (!(s->strio_flags & SMRD) && !(s->strio_flags & SMRW))
103	{
104		errno = EBADF;
105		return -1;
106	}
107	len = SM_MIN(s->strio_size - s->strio_offset, n);
108	(void) memmove(buf, s->strio_base + s->strio_offset, len);
109	s->strio_offset += len;
110	return len;
111}
112
113/*
114**  SM_STRWRITE -- write a portion of the string
115**
116**	Parameters:
117**		fp -- the file pointer
118**		buf -- location of data for writing
119**		n -- number of bytes to write
120**
121**	Returns:
122**		Failure: -1 and sets errno
123**		Success: >=0, number of bytes written
124*/
125
126ssize_t
127sm_strwrite(fp, buf, n)
128	SM_FILE_T *fp;
129	char const *buf;
130	size_t n;
131{
132	register SM_STR_OBJ_T *s = fp->f_cookie;
133
134	if (!(s->strio_flags & SMWR) && !(s->strio_flags & SMRW))
135	{
136		errno = EBADF;
137		return -1;
138	}
139	if (n + s->strio_offset > s->strio_size)
140	{
141		if (!sm_strgrow(s, n + s->strio_offset))
142			return 0;
143	}
144	(void) memmove(s->strio_base + s->strio_offset, buf, n);
145	s->strio_offset += n;
146	return n;
147}
148
149/*
150**  SM_STRSEEK -- position the offset pointer for the string
151**
152**	Only SM_IO_SEEK_SET, SM_IO_SEEK_CUR and SM_IO_SEEK_END are valid
153**	values for whence.
154**
155**	Parameters:
156**		fp -- the file pointer
157**		offset -- number of bytes offset from "base"
158**		whence -- determines "base" for 'offset'
159**
160**	Returns:
161**		Failure: -1 and sets errno
162**		Success: >=0, number of bytes read
163*/
164
165off_t
166sm_strseek(fp, offset, whence)
167	SM_FILE_T *fp;
168	off_t offset;
169	int whence;
170{
171	register off_t ret;
172	register SM_STR_OBJ_T *s = fp->f_cookie;
173
174reseek:
175	switch (whence)
176	{
177	  case SM_IO_SEEK_SET:
178		ret = offset;
179		break;
180	  case SM_IO_SEEK_CUR:
181		ret = s->strio_offset + offset;
182		break;
183	  case SM_IO_SEEK_END:
184		ret = s->strio_size;
185		break;
186	  default:
187		errno = EINVAL;
188		return -1;
189	}
190	if (ret < 0 || ret > (off_t)(size_t)(-1))	/* XXX ugly */
191		return -1;
192	if ((size_t) ret > s->strio_size)
193	{
194		if (sm_strgrow(s, (size_t)ret))
195			goto reseek;
196
197		/* errno set by sm_strgrow */
198		return -1;
199	}
200	s->strio_offset = (size_t) ret;
201	return ret;
202}
203
204/*
205**  SM_STROPEN -- open a string file type
206**
207**	Parameters:
208**		fp -- file pointer open to be associated with
209**		info -- initial contents (NULL for none)
210**		flags -- flags for methods of access (was mode)
211**		rpool -- resource pool to use memory from (if applicable)
212**
213**	Results:
214**		Success: 0 (zero)
215**		Failure: -1 and sets errno
216*/
217
218int
219sm_stropen(fp, info, flags, rpool)
220	SM_FILE_T *fp;
221	const void *info;
222	int flags;
223	const void *rpool;
224{
225	register SM_STR_OBJ_T *s;
226
227#if SM_RPOOL
228	s = sm_rpool_malloc_x(rpool, sizeof(SM_STR_OBJ_T));
229#else /* SM_RPOOL */
230	s = sm_malloc(sizeof(SM_STR_OBJ_T));
231	if (s == NULL)
232		return -1;
233#endif /* SM_RPOOL */
234
235	fp->f_cookie = s;
236	s->strio_rpool = rpool;
237	s->strio_offset = 0;
238	s->strio_size = 0;
239	s->strio_base = NULL;
240	s->strio_end = 0;
241
242	switch (flags)
243	{
244	  case SM_IO_RDWR:
245		s->strio_flags = SMRW;
246		break;
247	  case SM_IO_RDONLY:
248		s->strio_flags = SMRD;
249		break;
250	  case SM_IO_WRONLY:
251		s->strio_flags = SMWR;
252		break;
253	  case SM_IO_APPEND:
254		if (s->strio_rpool == NULL)
255			sm_free(s);
256		errno = EINVAL;
257		return -1;
258	  default:
259		if (s->strio_rpool == NULL)
260			sm_free(s);
261		errno = EINVAL;
262		return -1;
263	}
264
265	if (info != NULL)
266	{
267		s->strio_base = sm_strdup_x(info);
268		if (s->strio_base == NULL)
269		{
270			int save_errno = errno;
271
272			if (s->strio_rpool == NULL)
273				sm_free(s);
274			errno = save_errno;
275			return -1;
276		}
277		s->strio_size = strlen(info);
278		s->strio_end = s->strio_base + s->strio_size;
279	}
280	return 0;
281}
282
283/*
284**  SM_STRCLOSE -- close the string file type and free resources
285**
286**	Parameters:
287**		fp -- file pointer
288**
289**	Results:
290**		Success: 0 (zero)
291*/
292
293int
294sm_strclose(fp)
295	SM_FILE_T *fp;
296{
297	SM_STR_OBJ_T *s = fp->f_cookie;
298
299#if !SM_RPOOL
300	sm_free(s->strio_base);
301	s->strio_base = NULL;
302#endif /* !SM_RPOOL */
303	return 0;
304}
305
306/*
307**  SM_STRSETMODE -- set mode info for the file
308**
309**	 Note: changing the mode can be a safe way to have the "parent"
310**	 set up a string that the "child" is not to modify
311**
312**	Parameters:
313**		fp -- the file pointer
314**		mode -- location of new mode to set
315**
316**	Results:
317**		Success: 0 (zero)
318**		Failure: -1 and sets errno
319*/
320
321static int
322sm_strsetmode(fp, mode)
323	SM_FILE_T *fp;
324	const int *mode;
325{
326	register SM_STR_OBJ_T *s = fp->f_cookie;
327	int flags;
328
329	switch (*mode)
330	{
331	  case SM_IO_RDWR:
332		flags = SMRW;
333		break;
334	  case SM_IO_RDONLY:
335		flags = SMRD;
336		break;
337	  case SM_IO_WRONLY:
338		flags = SMWR;
339		break;
340	  case SM_IO_APPEND:
341		errno = EINVAL;
342		return -1;
343	  default:
344		errno = EINVAL;
345		return -1;
346	}
347	s->strio_flags &= ~SMMODEMASK;
348	s->strio_flags |= flags;
349	return 0;
350}
351
352/*
353**  SM_STRGETMODE -- get mode info for the file
354**
355**	Parameters:
356**		fp -- the file pointer
357**		mode -- location to store current mode
358**
359**	Results:
360**		Success: 0 (zero)
361**		Failure: -1 and sets errno
362*/
363
364static int
365sm_strgetmode(fp, mode)
366	SM_FILE_T *fp;
367	int *mode;
368{
369	register SM_STR_OBJ_T *s = fp->f_cookie;
370
371	switch (s->strio_flags & SMMODEMASK)
372	{
373	  case SMRW:
374		*mode = SM_IO_RDWR;
375		break;
376	  case SMRD:
377		*mode = SM_IO_RDONLY;
378		break;
379	  case SMWR:
380		*mode = SM_IO_WRONLY;
381		break;
382	  default:
383		errno = EINVAL;
384		return -1;
385	}
386	return 0;
387}
388
389/*
390**  SM_STRSETINFO -- set info for the file
391**
392**	Currently only SM_IO_WHAT_MODE is supported for 'what'.
393**
394**	Parameters:
395**		fp -- the file pointer
396**		what -- type of information to set
397**		valp -- location to data for doing set
398**
399**	Results:
400**		Failure: -1 and sets errno
401**		Success: sm_strsetmode() return [0 (zero)]
402*/
403
404int
405sm_strsetinfo(fp, what, valp)
406	SM_FILE_T *fp;
407	int what;
408	void *valp;
409{
410	switch(what)
411	{
412	  case SM_IO_WHAT_MODE:
413		return sm_strsetmode(fp, (int *) valp);
414	  default:
415		errno = EINVAL;
416		return -1;
417	}
418}
419
420/*
421**  SM_STRGETINFO -- get info for the file
422**
423**	Currently only SM_IO_WHAT_MODE is supported for 'what'.
424**
425**	Parameters:
426**		fp -- the file pointer
427**		what -- type of information requested
428**		valp -- location to return information in
429**
430**	Results:
431**		Failure: -1 and sets errno
432**		Success: sm_strgetmode() return [0 (zero)]
433*/
434
435int
436sm_strgetinfo(fp, what, valp)
437	SM_FILE_T *fp;
438	int what;
439	void *valp;
440{
441	switch(what)
442	{
443	  case SM_IO_WHAT_MODE:
444		return sm_strgetmode(fp, (int *) valp);
445	  default:
446		errno = EINVAL;
447		return -1;
448	}
449}
450
451/*
452**  SM_STRIO_INIT -- initializes a write-only string type
453**
454**  Original comments below. This function does not appear to be used anywhere.
455**  The same functionality can be done by changing the mode of the file.
456**  ------------
457** sm_strio_init initializes an SM_FILE_T structure as a write-only file
458** that writes into the specified buffer:
459** - Use sm_io_putc, sm_io_fprintf, etc, to write into the buffer.
460**   Attempts to write more than size-1 characters into the buffer will fail
461**   silently (no error is reported).
462** - Use sm_io_fflush to nul terminate the string in the buffer
463**   (the write pointer is not advanced).
464** No memory is allocated either by sm_strio_init or by sm_io_{putc,write} etc.
465**
466**	Parameters:
467**		fp -- file pointer
468**		buf -- memory location for stored data
469**		size -- size of 'buf'
470**
471**	Results:
472**		none.
473*/
474
475void
476sm_strio_init(fp, buf, size)
477	SM_FILE_T *fp;
478	char *buf;
479	size_t size;
480{
481	fp->sm_magic = SmFileMagic;
482	fp->f_flags = SMWR | SMSTR;
483	fp->f_file = -1;
484	fp->f_bf.smb_base = fp->f_p = (unsigned char *) buf;
485	fp->f_bf.smb_size = fp->f_w = (size ? size - 1 : 0);
486	fp->f_lbfsize = 0;
487	fp->f_r = 0;
488	fp->f_read = NULL;
489	fp->f_seek = NULL;
490	fp->f_getinfo = NULL;
491	fp->f_setinfo = NULL;
492}
493