(root)/
glibc-2.38/
libio/
fmemopen.c
       1  /* fmemopen implementation.
       2     Copyright (C) 2015-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  /* fmemopen() from 2.22 and forward works as defined by POSIX.  It also
      20     provides an older symbol, version 2.2.5, that behaves different regarding
      21     SEEK_END (libio/oldfmemopen.c).  */
      22  
      23  
      24  #include <errno.h>
      25  #include <stdio.h>
      26  #include <stdlib.h>
      27  #include <stdint.h>
      28  #include <string.h>
      29  #include <sys/types.h>
      30  #include "libioP.h"
      31  
      32  
      33  typedef struct fmemopen_cookie_struct fmemopen_cookie_t;
      34  struct fmemopen_cookie_struct
      35  {
      36    char        *buffer;   /* memory buffer.  */
      37    int         mybuffer;  /* allocated my buffer?  */
      38    int         append;    /* buffer open for append?  */
      39    size_t      size;      /* buffer length in bytes.  */
      40    off64_t     pos;       /* current position at the buffer.  */
      41    size_t      maxpos;    /* max position in buffer.  */
      42  };
      43  
      44  
      45  static ssize_t
      46  fmemopen_read (void *cookie, char *b, size_t s)
      47  {
      48    fmemopen_cookie_t *c = (fmemopen_cookie_t *) cookie;
      49  
      50    if (c->pos + s > c->maxpos)
      51      {
      52        s = c->maxpos - c->pos;
      53        if ((size_t) c->pos > c->maxpos)
      54  	s = 0;
      55      }
      56  
      57    memcpy (b, &(c->buffer[c->pos]), s);
      58  
      59    c->pos += s;
      60  
      61    return s;
      62  }
      63  
      64  
      65  static ssize_t
      66  fmemopen_write (void *cookie, const char *b, size_t s)
      67  {
      68    fmemopen_cookie_t *c = (fmemopen_cookie_t *) cookie;;
      69    off64_t pos = c->append ? c->maxpos : c->pos;
      70    int addnullc = (s == 0 || b[s - 1] != '\0');
      71  
      72    if (pos + s > c->size)
      73      {
      74        if ((size_t) (c->pos + addnullc) >= c->size)
      75  	{
      76  	  __set_errno (ENOSPC);
      77  	  return 0;
      78  	}
      79        s = c->size - pos;
      80      }
      81  
      82    memcpy (&(c->buffer[pos]), b, s);
      83  
      84    c->pos = pos + s;
      85    if ((size_t) c->pos > c->maxpos)
      86      {
      87        c->maxpos = c->pos;
      88        if (c->maxpos < c->size && addnullc)
      89  	c->buffer[c->maxpos] = '\0';
      90        /* A null byte is written in a stream open for update iff it fits.  */
      91        else if (c->append == 0 && addnullc != 0)
      92  	c->buffer[c->size-1] = '\0';
      93      }
      94  
      95    return s;
      96  }
      97  
      98  
      99  static int
     100  fmemopen_seek (void *cookie, off64_t *p, int w)
     101  {
     102    off64_t np;
     103    fmemopen_cookie_t *c = (fmemopen_cookie_t *) cookie;
     104  
     105    switch (w)
     106      {
     107      case SEEK_SET:
     108        np = *p;
     109        break;
     110  
     111      case SEEK_CUR:
     112        np = c->pos + *p;
     113        break;
     114  
     115      case SEEK_END:
     116        np = c->maxpos + *p;
     117        break;
     118  
     119      default:
     120        return -1;
     121      }
     122  
     123    if (np < 0 || (size_t) np > c->size)
     124      {
     125        __set_errno (EINVAL);
     126        return -1;
     127      }
     128  
     129    *p = c->pos = np;
     130  
     131    return 0;
     132  }
     133  
     134  
     135  static int
     136  fmemopen_close (void *cookie)
     137  {
     138    fmemopen_cookie_t *c = (fmemopen_cookie_t *) cookie;
     139  
     140    if (c->mybuffer)
     141      free (c->buffer);
     142    free (c);
     143  
     144    return 0;
     145  }
     146  
     147  
     148  FILE *
     149  __fmemopen (void *buf, size_t len, const char *mode)
     150  {
     151    cookie_io_functions_t iof;
     152    fmemopen_cookie_t *c;
     153    FILE *result;
     154  
     155    c = (fmemopen_cookie_t *) calloc (sizeof (fmemopen_cookie_t), 1);
     156    if (c == NULL)
     157      return NULL;
     158  
     159    c->mybuffer = (buf == NULL);
     160  
     161    if (c->mybuffer)
     162      {
     163        c->buffer = (char *) malloc (len);
     164        if (c->buffer == NULL)
     165  	{
     166  	  free (c);
     167  	  return NULL;
     168  	}
     169        c->buffer[0] = '\0';
     170      }
     171    else
     172      {
     173        if (__glibc_unlikely ((uintptr_t) len > -(uintptr_t) buf))
     174  	{
     175  	  free (c);
     176  	  __set_errno (EINVAL);
     177  	  return NULL;
     178  	}
     179  
     180        c->buffer = buf;
     181  
     182        /* POSIX states that w+ mode should truncate the buffer.  */
     183        if (mode[0] == 'w' && mode[1] == '+')
     184  	c->buffer[0] = '\0';
     185  
     186        if (mode[0] == 'a')
     187          c->maxpos = strnlen (c->buffer, len);
     188      }
     189  
     190  
     191    /* Mode   |  starting position (cookie::pos) |          size (cookie::size)
     192       ------ |----------------------------------|-----------------------------
     193       read   |          beginning of the buffer |                size argument
     194       write  |          beginning of the buffer |                         zero
     195       append |    first null or size buffer + 1 |  first null or size argument
     196     */
     197  
     198    c->size = len;
     199  
     200    if (mode[0] == 'r')
     201      c->maxpos = len;
     202  
     203    c->append = mode[0] == 'a';
     204    if (c->append)
     205      c->pos = c->maxpos;
     206    else
     207      c->pos = 0;
     208  
     209    iof.read = fmemopen_read;
     210    iof.write = fmemopen_write;
     211    iof.seek = fmemopen_seek;
     212    iof.close = fmemopen_close;
     213  
     214    result = _IO_fopencookie (c, mode, iof);
     215    if (__glibc_unlikely (result == NULL))
     216      {
     217        if (c->mybuffer)
     218  	free (c->buffer);
     219  
     220        free (c);
     221      }
     222  
     223    return result;
     224  }
     225  libc_hidden_def (__fmemopen)
     226  versioned_symbol (libc, __fmemopen, fmemopen, GLIBC_2_22);