190792Sgshapiro/*
2141858Sgshapiro * Copyright (c) 2000-2001, 2004 Sendmail, Inc. and its suppliers.
390792Sgshapiro *      All rights reserved.
490792Sgshapiro * Copyright (c) 1990, 1993
590792Sgshapiro *	The Regents of the University of California.  All rights reserved.
690792Sgshapiro *
790792Sgshapiro * This code is derived from software contributed to Berkeley by
890792Sgshapiro * Chris Torek.
990792Sgshapiro *
1090792Sgshapiro * By using this file, you agree to the terms and conditions set
1190792Sgshapiro * forth in the LICENSE file which can be found at the top level of
1290792Sgshapiro * the sendmail distribution.
1390792Sgshapiro */
1490792Sgshapiro
1590792Sgshapiro#include <sm/gen.h>
16157001SgshapiroSM_IDSTR(id, "@(#)$Id: ungetc.c,v 1.30 2005/06/14 23:07:20 ca Exp $")
1790792Sgshapiro
1890792Sgshapiro#include <stdlib.h>
1990792Sgshapiro#include <string.h>
2090792Sgshapiro#include <signal.h>
21157001Sgshapiro#include <sm/time.h>
2290792Sgshapiro#include <errno.h>
2390792Sgshapiro#include <sm/io.h>
2490792Sgshapiro#include <sm/heap.h>
2590792Sgshapiro#include <sm/assert.h>
2690792Sgshapiro#include <sm/conf.h>
2790792Sgshapiro#include "local.h"
2890792Sgshapiro
29141858Sgshapirostatic void	sm_submore_x __P((SM_FILE_T *));
30141858Sgshapiro
3190792Sgshapiro/*
3290792Sgshapiro**  SM_SUBMORE_X -- expand ungetc buffer
3390792Sgshapiro**
3490792Sgshapiro**  Expand the ungetc buffer `in place'.  That is, adjust fp->f_p when
3590792Sgshapiro**  the buffer moves, so that it points the same distance from the end,
3690792Sgshapiro**  and move the bytes in the buffer around as necessary so that they
3790792Sgshapiro**  are all at the end (stack-style).
3890792Sgshapiro**
3990792Sgshapiro**	Parameters:
4090792Sgshapiro**		fp -- the file pointer
4190792Sgshapiro**
4290792Sgshapiro**	Results:
4390792Sgshapiro**		none.
4490792Sgshapiro**
4590792Sgshapiro**	Exceptions:
4690792Sgshapiro**		F:sm_heap -- out of memory
4790792Sgshapiro*/
4890792Sgshapiro
4990792Sgshapirostatic void
5090792Sgshapirosm_submore_x(fp)
51141858Sgshapiro	SM_FILE_T *fp;
5290792Sgshapiro{
5390792Sgshapiro	register int i;
5490792Sgshapiro	register unsigned char *p;
5590792Sgshapiro
5690792Sgshapiro	if (fp->f_ub.smb_base == fp->f_ubuf)
5790792Sgshapiro	{
5890792Sgshapiro		/* Get a buffer; f_ubuf is fixed size. */
5990792Sgshapiro		p = sm_malloc_x((size_t) SM_IO_BUFSIZ);
6090792Sgshapiro		fp->f_ub.smb_base = p;
6190792Sgshapiro		fp->f_ub.smb_size = SM_IO_BUFSIZ;
6290792Sgshapiro		p += SM_IO_BUFSIZ - sizeof(fp->f_ubuf);
6390792Sgshapiro		for (i = sizeof(fp->f_ubuf); --i >= 0;)
6490792Sgshapiro			p[i] = fp->f_ubuf[i];
6590792Sgshapiro		fp->f_p = p;
6690792Sgshapiro		return;
6790792Sgshapiro	}
6890792Sgshapiro	i = fp->f_ub.smb_size;
6990792Sgshapiro	p = sm_realloc_x(fp->f_ub.smb_base, i << 1);
7090792Sgshapiro
7190792Sgshapiro	/* no overlap (hence can use memcpy) because we doubled the size */
7290792Sgshapiro	(void) memcpy((void *) (p + i), (void *) p, (size_t) i);
7390792Sgshapiro	fp->f_p = p + i;
7490792Sgshapiro	fp->f_ub.smb_base = p;
7590792Sgshapiro	fp->f_ub.smb_size = i << 1;
7690792Sgshapiro}
7790792Sgshapiro
7890792Sgshapiro/*
7990792Sgshapiro**  SM_IO_UNGETC -- place a character back into the buffer just read
8090792Sgshapiro**
8190792Sgshapiro**	Parameters:
8290792Sgshapiro**		fp -- the file pointer affected
8390792Sgshapiro**		timeout -- time to complete ungetc
8490792Sgshapiro**		c -- the character to place back
8590792Sgshapiro**
8690792Sgshapiro**	Results:
8790792Sgshapiro**		On success, returns value of character placed back, 0-255.
8890792Sgshapiro**		Returns SM_IO_EOF if c == SM_IO_EOF or if last operation
8990792Sgshapiro**		was a write and flush failed.
9090792Sgshapiro**
9190792Sgshapiro**	Exceptions:
9290792Sgshapiro**		F:sm_heap -- out of memory
9390792Sgshapiro*/
9490792Sgshapiro
9590792Sgshapiroint
9690792Sgshapirosm_io_ungetc(fp, timeout, c)
9790792Sgshapiro	register SM_FILE_T *fp;
9890792Sgshapiro	int timeout;
9990792Sgshapiro	int c;
10090792Sgshapiro{
10190792Sgshapiro	SM_REQUIRE_ISA(fp, SmFileMagic);
10290792Sgshapiro	if (c == SM_IO_EOF)
10390792Sgshapiro		return SM_IO_EOF;
10490792Sgshapiro	if (timeout == SM_TIME_IMMEDIATE)
10590792Sgshapiro	{
10690792Sgshapiro		/*
10790792Sgshapiro		**  Ungetting the buffer will take time and we are wanted to
10890792Sgshapiro		**  return immediately. So...
10990792Sgshapiro		*/
11090792Sgshapiro
11190792Sgshapiro		errno = EAGAIN;
11290792Sgshapiro		return SM_IO_EOF;
11390792Sgshapiro	}
11490792Sgshapiro
11590792Sgshapiro	if (!Sm_IO_DidInit)
11690792Sgshapiro		sm_init();
11790792Sgshapiro	if ((fp->f_flags & SMRD) == 0)
11890792Sgshapiro	{
11990792Sgshapiro		/*
12090792Sgshapiro		**  Not already reading: no good unless reading-and-writing.
12190792Sgshapiro		**  Otherwise, flush any current write stuff.
12290792Sgshapiro		*/
12390792Sgshapiro
12490792Sgshapiro		if ((fp->f_flags & SMRW) == 0)
12590792Sgshapiro			return SM_IO_EOF;
12690792Sgshapiro		if (fp->f_flags & SMWR)
12790792Sgshapiro		{
12890792Sgshapiro			if (sm_flush(fp, &timeout))
12990792Sgshapiro				return SM_IO_EOF;
13090792Sgshapiro			fp->f_flags &= ~SMWR;
13190792Sgshapiro			fp->f_w = 0;
13290792Sgshapiro			fp->f_lbfsize = 0;
13390792Sgshapiro		}
13490792Sgshapiro		fp->f_flags |= SMRD;
13590792Sgshapiro	}
13690792Sgshapiro	c = (unsigned char) c;
13790792Sgshapiro
13890792Sgshapiro	/*
13990792Sgshapiro	**  If we are in the middle of ungetc'ing, just continue.
14090792Sgshapiro	**  This may require expanding the current ungetc buffer.
14190792Sgshapiro	*/
14290792Sgshapiro
14390792Sgshapiro	if (HASUB(fp))
14490792Sgshapiro	{
14590792Sgshapiro		if (fp->f_r >= fp->f_ub.smb_size)
14690792Sgshapiro			sm_submore_x(fp);
14790792Sgshapiro		*--fp->f_p = c;
14890792Sgshapiro		fp->f_r++;
14990792Sgshapiro		return c;
15090792Sgshapiro	}
15190792Sgshapiro	fp->f_flags &= ~SMFEOF;
15290792Sgshapiro
15390792Sgshapiro	/*
15490792Sgshapiro	**  If we can handle this by simply backing up, do so,
15590792Sgshapiro	**  but never replace the original character.
15690792Sgshapiro	**  (This makes sscanf() work when scanning `const' data.)
15790792Sgshapiro	*/
15890792Sgshapiro
15990792Sgshapiro	if (fp->f_bf.smb_base != NULL && fp->f_p > fp->f_bf.smb_base &&
16090792Sgshapiro	    fp->f_p[-1] == c)
16190792Sgshapiro	{
16290792Sgshapiro		fp->f_p--;
16390792Sgshapiro		fp->f_r++;
16490792Sgshapiro		return c;
16590792Sgshapiro	}
16690792Sgshapiro
16790792Sgshapiro	/*
16890792Sgshapiro	**  Create an ungetc buffer.
16990792Sgshapiro	**  Initially, we will use the `reserve' buffer.
17090792Sgshapiro	*/
17190792Sgshapiro
17290792Sgshapiro	fp->f_ur = fp->f_r;
17390792Sgshapiro	fp->f_up = fp->f_p;
17490792Sgshapiro	fp->f_ub.smb_base = fp->f_ubuf;
17590792Sgshapiro	fp->f_ub.smb_size = sizeof(fp->f_ubuf);
17690792Sgshapiro	fp->f_ubuf[sizeof(fp->f_ubuf) - 1] = c;
17790792Sgshapiro	fp->f_p = &fp->f_ubuf[sizeof(fp->f_ubuf) - 1];
17890792Sgshapiro	fp->f_r = 1;
17990792Sgshapiro
18090792Sgshapiro	return c;
18190792Sgshapiro}
182