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>
  18 SM_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 
  31 static 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 
  51 static void
  52 sm_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 
  97 int
  98 sm_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 }