source: trunk/libs/newlib/src/newlib/libc/stdio/fmemopen.c @ 444

Last change on this file since 444 was 444, checked in by satin@…, 6 years ago

add newlib,libalmos-mkh, restructure shared_syscalls.h and mini-libc

File size: 10.0 KB
Line 
1/* Copyright (C) 2007 Eric Blake
2 * Permission to use, copy, modify, and distribute this software
3 * is freely granted, provided that this notice is preserved.
4 */
5
6/*
7FUNCTION
8<<fmemopen>>---open a stream around a fixed-length string
9
10INDEX
11        fmemopen
12
13SYNOPSIS
14        #include <stdio.h>
15        FILE *fmemopen(void *restrict <[buf]>, size_t <[size]>,
16                       const char *restrict <[mode]>);
17
18DESCRIPTION
19<<fmemopen>> creates a seekable <<FILE>> stream that wraps a
20fixed-length buffer of <[size]> bytes starting at <[buf]>.  The stream
21is opened with <[mode]> treated as in <<fopen>>, where append mode
22starts writing at the first NUL byte.  If <[buf]> is NULL, then
23<[size]> bytes are automatically provided as if by <<malloc>>, with
24the initial size of 0, and <[mode]> must contain <<+>> so that data
25can be read after it is written.
26
27The stream maintains a current position, which moves according to
28bytes read or written, and which can be one past the end of the array.
29The stream also maintains a current file size, which is never greater
30than <[size]>.  If <[mode]> starts with <<r>>, the position starts at
31<<0>>, and file size starts at <[size]> if <[buf]> was provided.  If
32<[mode]> starts with <<w>>, the position and file size start at <<0>>,
33and if <[buf]> was provided, the first byte is set to NUL.  If
34<[mode]> starts with <<a>>, the position and file size start at the
35location of the first NUL byte, or else <[size]> if <[buf]> was
36provided.
37
38When reading, NUL bytes have no significance, and reads cannot exceed
39the current file size.  When writing, the file size can increase up to
40<[size]> as needed, and NUL bytes may be embedded in the stream (see
41<<open_memstream>> for an alternative that automatically enlarges the
42buffer).  When the stream is flushed or closed after a write that
43changed the file size, a NUL byte is written at the current position
44if there is still room; if the stream is not also open for reading, a
45NUL byte is additionally written at the last byte of <[buf]> when the
46stream has exceeded <[size]>, so that a write-only <[buf]> is always
47NUL-terminated when the stream is flushed or closed (and the initial
48<[size]> should take this into account).  It is not possible to seek
49outside the bounds of <[size]>.  A NUL byte written during a flush is
50restored to its previous value when seeking elsewhere in the string.
51
52RETURNS
53The return value is an open FILE pointer on success.  On error,
54<<NULL>> is returned, and <<errno>> will be set to EINVAL if <[size]>
55is zero or <[mode]> is invalid, ENOMEM if <[buf]> was NULL and memory
56could not be allocated, or EMFILE if too many streams are already
57open.
58
59PORTABILITY
60This function is being added to POSIX 200x, but is not in POSIX 2001.
61
62Supporting OS subroutines required: <<sbrk>>.
63*/
64
65#include <stdio.h>
66#include <errno.h>
67#include <string.h>
68#include <sys/lock.h>
69#include "local.h"
70
71/* Describe details of an open memstream.  */
72typedef struct fmemcookie {
73  void *storage; /* storage to free on close */
74  char *buf; /* buffer start */
75  size_t pos; /* current position */
76  size_t eof; /* current file size */
77  size_t max; /* maximum file size */
78  char append; /* nonzero if appending */
79  char writeonly; /* 1 if write-only */
80  char saved; /* saved character that lived at pos before write-only NUL */
81} fmemcookie;
82
83/* Read up to non-zero N bytes into BUF from stream described by
84   COOKIE; return number of bytes read (0 on EOF).  */
85static _READ_WRITE_RETURN_TYPE
86fmemreader (struct _reent *ptr,
87       void *cookie,
88       char *buf,
89       _READ_WRITE_BUFSIZE_TYPE n)
90{
91  fmemcookie *c = (fmemcookie *) cookie;
92  /* Can't read beyond current size, but EOF condition is not an error.  */
93  if (c->pos > c->eof)
94    return 0;
95  if (n >= c->eof - c->pos)
96    n = c->eof - c->pos;
97  memcpy (buf, c->buf + c->pos, n);
98  c->pos += n;
99  return n;
100}
101
102/* Write up to non-zero N bytes of BUF into the stream described by COOKIE,
103   returning the number of bytes written or EOF on failure.  */
104static _READ_WRITE_RETURN_TYPE
105fmemwriter (struct _reent *ptr,
106       void *cookie,
107       const char *buf,
108       _READ_WRITE_BUFSIZE_TYPE n)
109{
110  fmemcookie *c = (fmemcookie *) cookie;
111  int adjust = 0; /* true if at EOF, but still need to write NUL.  */
112
113  /* Append always seeks to eof; otherwise, if we have previously done
114     a seek beyond eof, ensure all intermediate bytes are NUL.  */
115  if (c->append)
116    c->pos = c->eof;
117  else if (c->pos > c->eof)
118    memset (c->buf + c->eof, '\0', c->pos - c->eof);
119  /* Do not write beyond EOF; saving room for NUL on write-only stream.  */
120  if (c->pos + n > c->max - c->writeonly)
121    {
122      adjust = c->writeonly;
123      n = c->max - c->pos;
124    }
125  /* Now n is the number of bytes being modified, and adjust is 1 if
126     the last byte is NUL instead of from buf.  Write a NUL if
127     write-only; or if read-write, eof changed, and there is still
128     room.  When we are within the file contents, remember what we
129     overwrite so we can restore it if we seek elsewhere later.  */
130  if (c->pos + n > c->eof)
131    {
132      c->eof = c->pos + n;
133      if (c->eof - adjust < c->max)
134        c->saved = c->buf[c->eof - adjust] = '\0';
135    }
136  else if (c->writeonly)
137    {
138      if (n)
139        {
140          c->saved = c->buf[c->pos + n - adjust];
141          c->buf[c->pos + n - adjust] = '\0';
142        }
143      else
144        adjust = 0;
145    }
146  c->pos += n;
147  if (n - adjust)
148    memcpy (c->buf + c->pos - n, buf, n - adjust);
149  else
150    {
151      ptr->_errno = ENOSPC;
152      return EOF;
153    }
154  return n;
155}
156
157/* Seek to position POS relative to WHENCE within stream described by
158   COOKIE; return resulting position or fail with EOF.  */
159static _fpos_t
160fmemseeker (struct _reent *ptr,
161       void *cookie,
162       _fpos_t pos,
163       int whence)
164{
165  fmemcookie *c = (fmemcookie *) cookie;
166#ifndef __LARGE64_FILES
167  off_t offset = (off_t) pos;
168#else /* __LARGE64_FILES */
169  _off64_t offset = (_off64_t) pos;
170#endif /* __LARGE64_FILES */
171
172  if (whence == SEEK_CUR)
173    offset += c->pos;
174  else if (whence == SEEK_END)
175    offset += c->eof;
176  if (offset < 0)
177    {
178      ptr->_errno = EINVAL;
179      offset = -1;
180    }
181  else if (offset > c->max)
182    {
183      ptr->_errno = ENOSPC;
184      offset = -1;
185    }
186#ifdef __LARGE64_FILES
187  else if ((_fpos_t) offset != offset)
188    {
189      ptr->_errno = EOVERFLOW;
190      offset = -1;
191    }
192#endif /* __LARGE64_FILES */
193  else
194    {
195      if (c->writeonly && c->pos < c->eof)
196        {
197          c->buf[c->pos] = c->saved;
198          c->saved = '\0';
199        }
200      c->pos = offset;
201      if (c->writeonly && c->pos < c->eof)
202        {
203          c->saved = c->buf[c->pos];
204          c->buf[c->pos] = '\0';
205        }
206    }
207  return (_fpos_t) offset;
208}
209
210/* Seek to position POS relative to WHENCE within stream described by
211   COOKIE; return resulting position or fail with EOF.  */
212#ifdef __LARGE64_FILES
213static _fpos64_t
214fmemseeker64 (struct _reent *ptr,
215       void *cookie,
216       _fpos64_t pos,
217       int whence)
218{
219  _off64_t offset = (_off64_t) pos;
220  fmemcookie *c = (fmemcookie *) cookie;
221  if (whence == SEEK_CUR)
222    offset += c->pos;
223  else if (whence == SEEK_END)
224    offset += c->eof;
225  if (offset < 0)
226    {
227      ptr->_errno = EINVAL;
228      offset = -1;
229    }
230  else if (offset > c->max)
231    {
232      ptr->_errno = ENOSPC;
233      offset = -1;
234    }
235  else
236    {
237      if (c->writeonly && c->pos < c->eof)
238        {
239          c->buf[c->pos] = c->saved;
240          c->saved = '\0';
241        }
242      c->pos = offset;
243      if (c->writeonly && c->pos < c->eof)
244        {
245          c->saved = c->buf[c->pos];
246          c->buf[c->pos] = '\0';
247        }
248    }
249  return (_fpos64_t) offset;
250}
251#endif /* __LARGE64_FILES */
252
253/* Reclaim resources used by stream described by COOKIE.  */
254static int
255fmemcloser (struct _reent *ptr,
256       void *cookie)
257{
258  fmemcookie *c = (fmemcookie *) cookie;
259  _free_r (ptr, c->storage);
260  return 0;
261}
262
263/* Open a memstream around buffer BUF of SIZE bytes, using MODE.
264   Return the new stream, or fail with NULL.  */
265FILE *
266_fmemopen_r (struct _reent *ptr,
267       void *__restrict buf,
268       size_t size,
269       const char *__restrict mode)
270{
271  FILE *fp;
272  fmemcookie *c;
273  int flags;
274  int dummy;
275
276  if ((flags = __sflags (ptr, mode, &dummy)) == 0)
277    return NULL;
278  if (!size || !(buf || flags & __SRW))
279    {
280      ptr->_errno = EINVAL;
281      return NULL;
282    }
283  if ((fp = __sfp (ptr)) == NULL)
284    return NULL;
285  if ((c = (fmemcookie *) _malloc_r (ptr, sizeof *c + (buf ? 0 : size)))
286      == NULL)
287    {
288      _newlib_sfp_lock_start ();
289      fp->_flags = 0;           /* release */
290#ifndef __SINGLE_THREAD__
291      __lock_close_recursive (fp->_lock);
292#endif
293      _newlib_sfp_lock_end ();
294      return NULL;
295    }
296
297  c->storage = c;
298  c->max = size;
299  /* 9 modes to worry about.  */
300  /* w/a, buf or no buf: Guarantee a NUL after any file writes.  */
301  c->writeonly = (flags & __SWR) != 0;
302  c->saved = '\0';
303  if (!buf)
304    {
305      /* r+/w+/a+, and no buf: file starts empty.  */
306      c->buf = (char *) (c + 1);
307      c->buf[0] = '\0';
308      c->pos = c->eof = 0;
309      c->append = (flags & __SAPP) != 0;
310    }
311  else
312    {
313      c->buf = (char *) buf;
314      switch (*mode)
315        {
316        case 'a':
317          /* a/a+ and buf: position and size at first NUL.  */
318          buf = memchr (c->buf, '\0', size);
319          c->eof = c->pos = buf ? (char *) buf - c->buf : size;
320          if (!buf && c->writeonly)
321            /* a: guarantee a NUL within size even if no writes.  */
322            c->buf[size - 1] = '\0';
323          c->append = 1;
324          break;
325        case 'r':
326          /* r/r+ and buf: read at beginning, full size available.  */
327          c->pos = c->append = 0;
328          c->eof = size;
329          break;
330        case 'w':
331          /* w/w+ and buf: write at beginning, truncate to empty.  */
332          c->pos = c->append = c->eof = 0;
333          *c->buf = '\0';
334          break;
335        default:
336          abort ();
337        }
338    }
339
340  _newlib_flockfile_start (fp);
341  fp->_file = -1;
342  fp->_flags = flags;
343  fp->_cookie = c;
344  fp->_read = flags & (__SRD | __SRW) ? fmemreader : NULL;
345  fp->_write = flags & (__SWR | __SRW) ? fmemwriter : NULL;
346  fp->_seek = fmemseeker;
347#ifdef __LARGE64_FILES
348  fp->_seek64 = fmemseeker64;
349  fp->_flags |= __SL64;
350#endif
351  fp->_close = fmemcloser;
352  _newlib_flockfile_end (fp);
353  return fp;
354}
355
356#ifndef _REENT_ONLY
357FILE *
358fmemopen (void *__restrict buf,
359       size_t size,
360       const char *__restrict mode)
361{
362  return _fmemopen_r (_REENT, buf, size, mode);
363}
364#endif /* !_REENT_ONLY */
Note: See TracBrowser for help on using the repository browser.