Back to index

glibc  2.9
fmemopen.c
Go to the documentation of this file.
00001 /* Fmemopen implementation.
00002    Copyright (C) 2000, 2002, 2005, 2006, 2008 Free Software Foundation, Inc.
00003    This file is part of the GNU C Library.
00004    Contributed by Hanno Mueller, kontakt@hanno.de, 2000.
00005 
00006    The GNU C Library is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU Lesser General Public
00008    License as published by the Free Software Foundation; either
00009    version 2.1 of the License, or (at your option) any later version.
00010 
00011    The GNU C Library is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014    Lesser General Public License for more details.
00015 
00016    You should have received a copy of the GNU Lesser General Public
00017    License along with the GNU C Library; if not, write to the Free
00018    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00019    02111-1307 USA.  */
00020 
00021 /*
00022  * fmemopen() - "my" version of a string stream
00023  * Hanno Mueller, kontakt@hanno.de
00024  *
00025  *
00026  * I needed fmemopen() for an application that I currently work on,
00027  * but couldn't find it in libio. The following snippet of code is an
00028  * attempt to implement what glibc's documentation describes.
00029  *
00030  *
00031  *
00032  * I already see some potential problems:
00033  *
00034  * - I never used the "original" fmemopen(). I am sure that "my"
00035  *   fmemopen() behaves differently than the original version.
00036  *
00037  * - The documentation doesn't say wether a string stream allows
00038  *   seeks. I checked the old fmemopen implementation in glibc's stdio
00039  *   directory, wasn't quite able to see what is going on in that
00040  *   source, but as far as I understand there was no seek there. For
00041  *   my application, I needed fseek() and ftell(), so it's here.
00042  *
00043  * - "append" mode and fseek(p, SEEK_END) have two different ideas
00044  *   about the "end" of the stream.
00045  *
00046  *   As described in the documentation, when opening the file in
00047  *   "append" mode, the position pointer will be set to the first null
00048  *   character of the string buffer (yet the buffer may already
00049  *   contain more data). For fseek(), the last byte of the buffer is
00050  *   used as the end of the stream.
00051  *
00052  * - It is unclear to me what the documentation tries to say when it
00053  *   explains what happens when you use fmemopen with a NULL
00054  *   buffer.
00055  *
00056  *   Quote: "fmemopen [then] allocates an array SIZE bytes long. This
00057  *   is really only useful if you are going to write things to the
00058  *   buffer and then read them back in again."
00059  *
00060  *   What does that mean if the original fmemopen() did not allow
00061  *   seeking? How do you read what you just wrote without seeking back
00062  *   to the beginning of the stream?
00063  *
00064  * - I think there should be a second version of fmemopen() that does
00065  *   not add null characters for each write. (At least in my
00066  *   application, I am not actually using strings but binary data and
00067  *   so I don't need the stream to add null characters on its own.)
00068  */
00069 
00070 #include <errno.h>
00071 #include <libio.h>
00072 #include <stdio.h>
00073 #include <stdlib.h>
00074 #include <stdint.h>
00075 #include <string.h>
00076 #include <sys/types.h>
00077 #include "libioP.h"
00078 
00079 
00080 typedef struct fmemopen_cookie_struct fmemopen_cookie_t;
00081 struct fmemopen_cookie_struct
00082 {
00083   char *buffer;
00084   int mybuffer;
00085   int binmode;
00086   size_t size;
00087   _IO_off64_t pos;
00088   size_t maxpos;
00089 };
00090 
00091 
00092 static ssize_t
00093 fmemopen_read (void *cookie, char *b, size_t s)
00094 {
00095   fmemopen_cookie_t *c;
00096 
00097   c = (fmemopen_cookie_t *) cookie;
00098 
00099   if (c->pos + s > c->size)
00100     {
00101       if ((size_t) c->pos == c->size)
00102        return 0;
00103       s = c->size - c->pos;
00104     }
00105 
00106   memcpy (b, &(c->buffer[c->pos]), s);
00107 
00108   c->pos += s;
00109   if ((size_t) c->pos > c->maxpos)
00110     c->maxpos = c->pos;
00111 
00112   return s;
00113 }
00114 
00115 
00116 static ssize_t
00117 fmemopen_write (void *cookie, const char *b, size_t s)
00118 {
00119   fmemopen_cookie_t *c;
00120   int addnullc;
00121 
00122   c = (fmemopen_cookie_t *) cookie;
00123 
00124   addnullc = c->binmode == 0 && (s == 0 || b[s - 1] != '\0');
00125 
00126   if (c->pos + s + addnullc > c->size)
00127     {
00128       if ((size_t) (c->pos + addnullc) == c->size)
00129        {
00130          __set_errno (ENOSPC);
00131          return 0;
00132        }
00133       s = c->size - c->pos - addnullc;
00134     }
00135 
00136   memcpy (&(c->buffer[c->pos]), b, s);
00137 
00138   c->pos += s;
00139   if ((size_t) c->pos > c->maxpos)
00140     {
00141       c->maxpos = c->pos;
00142       if (addnullc)
00143        c->buffer[c->maxpos] = '\0';
00144     }
00145 
00146   return s;
00147 }
00148 
00149 
00150 static int
00151 fmemopen_seek (void *cookie, _IO_off64_t *p, int w)
00152 {
00153   _IO_off64_t np;
00154   fmemopen_cookie_t *c;
00155 
00156   c = (fmemopen_cookie_t *) cookie;
00157 
00158   switch (w)
00159     {
00160     case SEEK_SET:
00161       np = *p;
00162       break;
00163 
00164     case SEEK_CUR:
00165       np = c->pos + *p;
00166       break;
00167 
00168     case SEEK_END:
00169       np = (c->binmode ? c->size : c->maxpos) - *p;
00170       break;
00171 
00172     default:
00173       return -1;
00174     }
00175 
00176   if (np < 0 || (size_t) np > c->size)
00177     return -1;
00178 
00179   *p = c->pos = np;
00180 
00181   return 0;
00182 }
00183 
00184 
00185 static int
00186 fmemopen_close (void *cookie)
00187 {
00188   fmemopen_cookie_t *c;
00189 
00190   c = (fmemopen_cookie_t *) cookie;
00191 
00192   if (c->mybuffer)
00193     free (c->buffer);
00194   free (c);
00195 
00196   return 0;
00197 }
00198 
00199 
00200 FILE *
00201 fmemopen (void *buf, size_t len, const char *mode)
00202 {
00203   cookie_io_functions_t iof;
00204   fmemopen_cookie_t *c;
00205 
00206   if (__builtin_expect (len == 0, 0))
00207     {
00208     einval:
00209       __set_errno (EINVAL);
00210       return NULL;
00211     }
00212 
00213   c = (fmemopen_cookie_t *) malloc (sizeof (fmemopen_cookie_t));
00214   if (c == NULL)
00215     return NULL;
00216 
00217   c->mybuffer = (buf == NULL);
00218 
00219   if (c->mybuffer)
00220     {
00221       c->buffer = (char *) malloc (len);
00222       if (c->buffer == NULL)
00223        {
00224          free (c);
00225          return NULL;
00226        }
00227       c->buffer[0] = '\0';
00228     }
00229   else
00230     {
00231       if (__builtin_expect ((uintptr_t) len > -(uintptr_t) buf, 0))
00232        {
00233          free (c);
00234          goto einval;
00235        }
00236 
00237       c->buffer = buf;
00238     }
00239 
00240   c->size = len;
00241 
00242   if (mode[0] == 'w')
00243     c->buffer[0] = '\0';
00244 
00245   c->maxpos = strlen (c->buffer);
00246 
00247   if (mode[0] == 'a')
00248     c->pos = c->maxpos;
00249   else
00250     c->pos = 0;
00251 
00252   c->binmode = mode[0] != '\0' && mode[1] == 'b';
00253 
00254   iof.read = fmemopen_read;
00255   iof.write = fmemopen_write;
00256   iof.seek = fmemopen_seek;
00257   iof.close = fmemopen_close;
00258 
00259   return _IO_fopencookie (c, mode, iof);
00260 }