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