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>
  18 SM_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 
  30 static int      sm_strsetmode __P((SM_FILE_T*, const int *));
  31 static int      sm_strgetmode __P((SM_FILE_T*, int *));
  32 
  33 /*
  34 **  Cookie structure for the "strio" file type
  35 */
  36 
  37 struct 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 
  47 typedef 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 
  60 static bool sm_strgrow __P((SM_STR_OBJ_T *, size_t));
  61 
  62 static bool
  63 sm_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 
  93 ssize_t
  94 sm_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 
 126 ssize_t
 127 sm_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 
 165 off_t
 166 sm_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 
 174 reseek:
 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 
 218 int
 219 sm_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 
 293 int
 294 sm_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 
 321 static int
 322 sm_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 
 364 static int
 365 sm_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 
 404 int
 405 sm_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 
 435 int
 436 sm_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 
 475 void
 476 sm_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 }