1/*
2 * Copyright (c) 2000-2001, 2004 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: ungetc.c,v 1.30 2005/06/14 23:07:20 ca Exp $")
19
20#include <stdlib.h>
21#include <string.h>
22#include <signal.h>
23#include <sm/time.h>
24#include <errno.h>
25#include <sm/io.h>
26#include <sm/heap.h>
27#include <sm/assert.h>
28#include <sm/conf.h>
29#include "local.h"
30
31static void	sm_submore_x __P((SM_FILE_T *));
32
33/*
34**  SM_SUBMORE_X -- expand ungetc buffer
35**
36**  Expand the ungetc buffer `in place'.  That is, adjust fp->f_p when
37**  the buffer moves, so that it points the same distance from the end,
38**  and move the bytes in the buffer around as necessary so that they
39**  are all at the end (stack-style).
40**
41**	Parameters:
42**		fp -- the file pointer
43**
44**	Results:
45**		none.
46**
47**	Exceptions:
48**		F:sm_heap -- out of memory
49*/
50
51static void
52sm_submore_x(fp)
53	SM_FILE_T *fp;
54{
55	register int i;
56	register unsigned char *p;
57
58	if (fp->f_ub.smb_base == fp->f_ubuf)
59	{
60		/* Get a buffer; f_ubuf is fixed size. */
61		p = sm_malloc_x((size_t) SM_IO_BUFSIZ);
62		fp->f_ub.smb_base = p;
63		fp->f_ub.smb_size = SM_IO_BUFSIZ;
64		p += SM_IO_BUFSIZ - sizeof(fp->f_ubuf);
65		for (i = sizeof(fp->f_ubuf); --i >= 0;)
66			p[i] = fp->f_ubuf[i];
67		fp->f_p = p;
68		return;
69	}
70	i = fp->f_ub.smb_size;
71	p = sm_realloc_x(fp->f_ub.smb_base, i << 1);
72
73	/* no overlap (hence can use memcpy) because we doubled the size */
74	(void) memcpy((void *) (p + i), (void *) p, (size_t) i);
75	fp->f_p = p + i;
76	fp->f_ub.smb_base = p;
77	fp->f_ub.smb_size = i << 1;
78}
79
80/*
81**  SM_IO_UNGETC -- place a character back into the buffer just read
82**
83**	Parameters:
84**		fp -- the file pointer affected
85**		timeout -- time to complete ungetc
86**		c -- the character to place back
87**
88**	Results:
89**		On success, returns value of character placed back, 0-255.
90**		Returns SM_IO_EOF if c == SM_IO_EOF or if last operation
91**		was a write and flush failed.
92**
93**	Exceptions:
94**		F:sm_heap -- out of memory
95*/
96
97int
98sm_io_ungetc(fp, timeout, c)
99	register SM_FILE_T *fp;
100	int timeout;
101	int c;
102{
103	SM_REQUIRE_ISA(fp, SmFileMagic);
104	if (c == SM_IO_EOF)
105		return SM_IO_EOF;
106	if (timeout == SM_TIME_IMMEDIATE)
107	{
108		/*
109		**  Ungetting the buffer will take time and we are wanted to
110		**  return immediately. So...
111		*/
112
113		errno = EAGAIN;
114		return SM_IO_EOF;
115	}
116
117	if (!Sm_IO_DidInit)
118		sm_init();
119	if ((fp->f_flags & SMRD) == 0)
120	{
121		/*
122		**  Not already reading: no good unless reading-and-writing.
123		**  Otherwise, flush any current write stuff.
124		*/
125
126		if ((fp->f_flags & SMRW) == 0)
127			return SM_IO_EOF;
128		if (fp->f_flags & SMWR)
129		{
130			if (sm_flush(fp, &timeout))
131				return SM_IO_EOF;
132			fp->f_flags &= ~SMWR;
133			fp->f_w = 0;
134			fp->f_lbfsize = 0;
135		}
136		fp->f_flags |= SMRD;
137	}
138	c = (unsigned char) c;
139
140	/*
141	**  If we are in the middle of ungetc'ing, just continue.
142	**  This may require expanding the current ungetc buffer.
143	*/
144
145	if (HASUB(fp))
146	{
147		if (fp->f_r >= fp->f_ub.smb_size)
148			sm_submore_x(fp);
149		*--fp->f_p = c;
150		fp->f_r++;
151		return c;
152	}
153	fp->f_flags &= ~SMFEOF;
154
155	/*
156	**  If we can handle this by simply backing up, do so,
157	**  but never replace the original character.
158	**  (This makes sscanf() work when scanning `const' data.)
159	*/
160
161	if (fp->f_bf.smb_base != NULL && fp->f_p > fp->f_bf.smb_base &&
162	    fp->f_p[-1] == c)
163	{
164		fp->f_p--;
165		fp->f_r++;
166		return c;
167	}
168
169	/*
170	**  Create an ungetc buffer.
171	**  Initially, we will use the `reserve' buffer.
172	*/
173
174	fp->f_ur = fp->f_r;
175	fp->f_up = fp->f_p;
176	fp->f_ub.smb_base = fp->f_ubuf;
177	fp->f_ub.smb_size = sizeof(fp->f_ubuf);
178	fp->f_ubuf[sizeof(fp->f_ubuf) - 1] = c;
179	fp->f_p = &fp->f_ubuf[sizeof(fp->f_ubuf) - 1];
180	fp->f_r = 1;
181
182	return c;
183}
184