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