(linenum→info "unix/slp.c:2238")

glibc/2.7/libio/fmemopen.c

    1: /* Fmemopen implementation.
    2:    Copyright (C) 2000, 2002, 2005, 2006 Free Software Foundation, Inc.
    3:    This file is part of the GNU C Library.
    4:    Contributed by  Hanno Mueller, kontakt@hanno.de, 2000.
    5: 
    6:    The GNU C Library is free software; you can redistribute it and/or
    7:    modify it under the terms of the GNU Lesser General Public
    8:    License as published by the Free Software Foundation; either
    9:    version 2.1 of the License, or (at your option) any later version.
   10: 
   11:    The GNU C Library is distributed in the hope that it will be useful,
   12:    but WITHOUT ANY WARRANTY; without even the implied warranty of
   13:    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   14:    Lesser General Public License for more details.
   15: 
   16:    You should have received a copy of the GNU Lesser General Public
   17:    License along with the GNU C Library; if not, write to the Free
   18:    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   19:    02111-1307 USA.  */
   20: 
   21: /*
   22:  * fmemopen() - "my" version of a string stream
   23:  * Hanno Mueller, kontakt@hanno.de
   24:  *
   25:  *
   26:  * I needed fmemopen() for an application that I currently work on,
   27:  * but couldn't find it in libio. The following snippet of code is an
   28:  * attempt to implement what glibc's documentation describes.
   29:  *
   30:  *
   31:  *
   32:  * I already see some potential problems:
   33:  *
   34:  * - I never used the "original" fmemopen(). I am sure that "my"
   35:  *   fmemopen() behaves differently than the original version.
   36:  *
   37:  * - The documentation doesn't say wether a string stream allows
   38:  *   seeks. I checked the old fmemopen implementation in glibc's stdio
   39:  *   directory, wasn't quite able to see what is going on in that
   40:  *   source, but as far as I understand there was no seek there. For
   41:  *   my application, I needed fseek() and ftell(), so it's here.
   42:  *
   43:  * - "append" mode and fseek(p, SEEK_END) have two different ideas
   44:  *   about the "end" of the stream.
   45:  *
   46:  *   As described in the documentation, when opening the file in
   47:  *   "append" mode, the position pointer will be set to the first null
   48:  *   character of the string buffer (yet the buffer may already
   49:  *   contain more data). For fseek(), the last byte of the buffer is
   50:  *   used as the end of the stream.
   51:  *
   52:  * - It is unclear to me what the documentation tries to say when it
   53:  *   explains what happens when you use fmemopen with a NULL
   54:  *   buffer.
   55:  *
   56:  *   Quote: "fmemopen [then] allocates an array SIZE bytes long. This
   57:  *   is really only useful if you are going to write things to the
   58:  *   buffer and then read them back in again."
   59:  *
   60:  *   What does that mean if the original fmemopen() did not allow
   61:  *   seeking? How do you read what you just wrote without seeking back
   62:  *   to the beginning of the stream?
   63:  *
   64:  * - I think there should be a second version of fmemopen() that does
   65:  *   not add null characters for each write. (At least in my
   66:  *   application, I am not actually using strings but binary data and
   67:  *   so I don't need the stream to add null characters on its own.)
   68:  */
   69: 
   70: #include <errno.h>
   71: #include <libio.h>
   72: #include <stdio.h>
   73: #include <stdlib.h>
   74: #include <stdint.h>
   75: #include <string.h>
   76: #include <sys/types.h>
   77: #include "libioP.h"
   78: 
   79: 
   80: typedef struct fmemopen_cookie_struct fmemopen_cookie_t;
   81: struct fmemopen_cookie_struct
   82: {
   83:   char *buffer;
   84:   int mybuffer;
   85:   size_t size;
   86:   _IO_off64_t pos;
   87:   size_t maxpos;
   88: };
   89: 
   90: 
   91: static ssize_t
   92: fmemopen_read (void *cookie, char *b, size_t s)
   93: {
   94:   fmemopen_cookie_t *c;
   95: 
   96:   c = (fmemopen_cookie_t *) cookie;
   97: 
   98:   if (c->pos + s > c->size)
   99:     {
  100:       if ((size_t) c->pos == c->size)
  101:         return 0;
  102:       s = c->size - c->pos;
  103:     }
  104: 
  105:   memcpy (b, &(c->buffer[c->pos]), s);
  106: 
  107:   c->pos += s;
  108:   if ((size_t) c->pos > c->maxpos)
  109:     c->maxpos = c->pos;
  110: 
  111:   return s;
  112: }
  113: 
  114: 
  115: static ssize_t
  116: fmemopen_write (void *cookie, const char *b, size_t s)
  117: {
  118:   fmemopen_cookie_t *c;
  119:   int addnullc;
  120: 
  121:   c = (fmemopen_cookie_t *) cookie;
  122: 
  123:   addnullc = s == 0 || b[s - 1] != '\0';
  124: 
  125:   if (c->pos + s + addnullc > c->size)
  126:     {
  127:       if ((size_t) (c->pos + addnullc) == c->size)
  128:         {
  129:           __set_errno (ENOSPC);
  130:           return 0;
  131:         }
  132:       s = c->size - c->pos - addnullc;
  133:     }
  134: 
  135:   memcpy (&(c->buffer[c->pos]), b, s);
  136: 
  137:   c->pos += s;
  138:   if ((size_t) c->pos > c->maxpos)
  139:     {
  140:       c->maxpos = c->pos;
  141:       if (addnullc)
  142:         c->buffer[c->maxpos] = '\0';
  143:     }
  144: 
  145:   return s;
  146: }
  147: 
  148: 
  149: static int
  150: fmemopen_seek (void *cookie, _IO_off64_t *p, int w)
  151: {
  152:   _IO_off64_t np;
  153:   fmemopen_cookie_t *c;
  154: 
  155:   c = (fmemopen_cookie_t *) cookie;
  156: 
  157:   switch (w)
  158:     {
  159:     case SEEK_SET:
  160:       np = *p;
  161:       break;
  162: 
  163:     case SEEK_CUR:
  164:       np = c->pos + *p;
  165:       break;
  166: 
  167:     case SEEK_END:
  168:       np = c->maxpos - *p;
  169:       break;
  170: 
  171:     default:
  172:       return -1;
  173:     }
  174: 
  175:   if (np < 0 || (size_t) np > c->size)
  176:     return -1;
  177: 
  178:   *p = c->pos = np;
  179: 
  180:   return 0;
  181: }
  182: 
  183: 
  184: static int
  185: fmemopen_close (void *cookie)
  186: {
  187:   fmemopen_cookie_t *c;
  188: 
  189:   c = (fmemopen_cookie_t *) cookie;
  190: 
  191:   if (c->mybuffer)
  192:     free (c->buffer);
  193:   free (c);
  194: 
  195:   return 0;
  196: }
  197: 
  198: 
  199: FILE *
  200: fmemopen (void *buf, size_t len, const char *mode)
  201: {
  202:   cookie_io_functions_t iof;
  203:   fmemopen_cookie_t *c;
  204: 
  205:   if (__builtin_expect (len == 0, 0))
  206:     {
  207:     einval:
  208:       __set_errno (EINVAL);
  209:       return NULL;
  210:     }
  211: 
  212:   c = (fmemopen_cookie_t *) malloc (sizeof (fmemopen_cookie_t));
  213:   if (c == NULL)
  214:     return NULL;
  215: 
  216:   c->mybuffer = (buf == NULL);
  217: 
  218:   if (c->mybuffer)
  219:     {
  220:       c->buffer = (char *) malloc (len);
  221:       if (c->buffer == NULL)
  222:         {
  223:           free (c);
  224:           return NULL;
  225:         }
  226:       c->buffer[0] = '\0';
  227:     }
  228:   else
  229:     {
  230:       if (__builtin_expect ((uintptr_t) len > -(uintptr_t) buf, 0))
  231:         {
  232:           free (c);
  233:           goto einval;
  234:         }
  235: 
  236:       c->buffer = buf;
  237:     }
  238: 
  239:   c->size = len;
  240: 
  241:   if (mode[0] == 'w')
  242:     c->buffer[0] = '\0';
  243: 
  244:   c->maxpos = strlen (c->buffer);
  245: 
  246:   if (mode[0] == 'a')
  247:     c->pos = c->maxpos;
  248:   else
  249:     c->pos = 0;
  250: 
  251:   iof.read = fmemopen_read;
  252:   iof.write = fmemopen_write;
  253:   iof.seek = fmemopen_seek;
  254:   iof.close = fmemopen_close;
  255: 
  256:   return _IO_fopencookie (c, mode, iof);
  257: }
Syntax (Markdown)