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.
8 <<fmemopen>>---open a stream around a fixed-length string
15 FILE *fmemopen(void *restrict <[buf]>, size_t <[size]>,
16 const char *restrict <[mode]>);
19 <<fmemopen>> creates a seekable <<FILE>> stream that wraps a
20 fixed-length buffer of <[size]> bytes starting at <[buf]>. The stream
21 is opened with <[mode]> treated as in <<fopen>>, where append mode
22 starts writing at the first NUL byte. If <[buf]> is NULL, then
23 <[size]> bytes are automatically provided as if by <<malloc>>, with
24 the initial size of 0, and <[mode]> must contain <<+>> so that data
25 can be read after it is written.
27 The stream maintains a current position, which moves according to
28 bytes read or written, and which can be one past the end of the array.
29 The stream also maintains a current file size, which is never greater
30 than <[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>>,
33 and 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
35 location of the first NUL byte, or else <[size]> if <[buf]> was
38 When reading, NUL bytes have no significance, and reads cannot exceed
39 the 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
42 buffer). When the stream is flushed or closed after a write that
43 changed the file size, a NUL byte is written at the current position
44 if there is still room; if the stream is not also open for reading, a
45 NUL byte is additionally written at the last byte of <[buf]> when the
46 stream has exceeded <[size]>, so that a write-only <[buf]> is always
47 NUL-terminated when the stream is flushed or closed (and the initial
48 <[size]> should take this into account). It is not possible to seek
49 outside the bounds of <[size]>. A NUL byte written during a flush is
50 restored to its previous value when seeking elsewhere in the string.
53 The return value is an open FILE pointer on success. On error,
54 <<NULL>> is returned, and <<errno>> will be set to EINVAL if <[size]>
55 is zero or <[mode]> is invalid, ENOMEM if <[buf]> was NULL and memory
56 could not be allocated, or EMFILE if too many streams are already
60 This function is being added to POSIX 200x, but is not in POSIX 2001.
62 Supporting OS subroutines required: <<sbrk>>.
71 /* Describe details of an open memstream. */
72 typedef 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 */
83 /* Read up to non-zero N bytes into BUF from stream described by
84 COOKIE; return number of bytes read (0 on EOF). */
85 static _READ_WRITE_RETURN_TYPE
86 fmemreader (struct _reent
*ptr
,
89 _READ_WRITE_BUFSIZE_TYPE n
)
91 fmemcookie
*c
= (fmemcookie
*) cookie
;
92 /* Can't read beyond current size, but EOF condition is not an error. */
95 if (n
>= c
->eof
- c
->pos
)
97 memcpy (buf
, c
->buf
+ c
->pos
, n
);
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. */
104 static _READ_WRITE_RETURN_TYPE
105 fmemwriter (struct _reent
*ptr
,
108 _READ_WRITE_BUFSIZE_TYPE n
)
110 fmemcookie
*c
= (fmemcookie
*) cookie
;
111 int adjust
= 0; /* true if at EOF, but still need to write NUL. */
113 /* Append always seeks to eof; otherwise, if we have previously done
114 a seek beyond eof, ensure all intermediate bytes are NUL. */
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
)
122 adjust
= c
->writeonly
;
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
)
133 if (c
->eof
- adjust
< c
->max
)
134 c
->saved
= c
->buf
[c
->eof
- adjust
] = '\0';
136 else if (c
->writeonly
)
140 c
->saved
= c
->buf
[c
->pos
+ n
- adjust
];
141 c
->buf
[c
->pos
+ n
- adjust
] = '\0';
148 memcpy (c
->buf
+ c
->pos
- n
, buf
, n
- adjust
);
151 _REENT_ERRNO(ptr
) = ENOSPC
;
157 /* Seek to position POS relative to WHENCE within stream described by
158 COOKIE; return resulting position or fail with EOF. */
160 fmemseeker (struct _reent
*ptr
,
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 */
172 if (whence
== SEEK_CUR
)
174 else if (whence
== SEEK_END
)
178 _REENT_ERRNO(ptr
) = EINVAL
;
181 else if (offset
> c
->max
)
183 _REENT_ERRNO(ptr
) = ENOSPC
;
186 #ifdef __LARGE64_FILES
187 else if ((_fpos_t
) offset
!= offset
)
189 _REENT_ERRNO(ptr
) = EOVERFLOW
;
192 #endif /* __LARGE64_FILES */
195 if (c
->writeonly
&& c
->pos
< c
->eof
)
197 c
->buf
[c
->pos
] = c
->saved
;
201 if (c
->writeonly
&& c
->pos
< c
->eof
)
203 c
->saved
= c
->buf
[c
->pos
];
204 c
->buf
[c
->pos
] = '\0';
207 return (_fpos_t
) offset
;
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
214 fmemseeker64 (struct _reent
*ptr
,
219 _off64_t offset
= (_off64_t
) pos
;
220 fmemcookie
*c
= (fmemcookie
*) cookie
;
221 if (whence
== SEEK_CUR
)
223 else if (whence
== SEEK_END
)
227 _REENT_ERRNO(ptr
) = EINVAL
;
230 else if (offset
> c
->max
)
232 _REENT_ERRNO(ptr
) = ENOSPC
;
237 if (c
->writeonly
&& c
->pos
< c
->eof
)
239 c
->buf
[c
->pos
] = c
->saved
;
243 if (c
->writeonly
&& c
->pos
< c
->eof
)
245 c
->saved
= c
->buf
[c
->pos
];
246 c
->buf
[c
->pos
] = '\0';
249 return (_fpos64_t
) offset
;
251 #endif /* __LARGE64_FILES */
253 /* Reclaim resources used by stream described by COOKIE. */
255 fmemcloser (struct _reent
*ptr
,
258 fmemcookie
*c
= (fmemcookie
*) cookie
;
259 _free_r (ptr
, c
->storage
);
263 /* Open a memstream around buffer BUF of SIZE bytes, using MODE.
264 Return the new stream, or fail with NULL. */
266 _fmemopen_r (struct _reent
*ptr
,
267 void *__restrict buf
,
269 const char *__restrict mode
)
276 if ((flags
= __sflags (ptr
, mode
, &dummy
)) == 0)
278 if (!size
|| !(buf
|| flags
& __SRW
))
280 _REENT_ERRNO(ptr
) = EINVAL
;
283 if ((fp
= __sfp (ptr
)) == NULL
)
285 if ((c
= (fmemcookie
*) _malloc_r (ptr
, sizeof *c
+ (buf
? 0 : size
)))
288 _newlib_sfp_lock_start ();
289 fp
->_flags
= 0; /* release */
290 #ifndef __SINGLE_THREAD__
291 __lock_close_recursive (fp
->_lock
);
293 _newlib_sfp_lock_end ();
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;
305 /* r+/w+/a+, and no buf: file starts empty. */
306 c
->buf
= (char *) (c
+ 1);
309 c
->append
= (flags
& __SAPP
) != 0;
313 c
->buf
= (char *) buf
;
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';
326 /* r/r+ and buf: read at beginning, full size available. */
327 c
->pos
= c
->append
= 0;
331 /* w/w+ and buf: write at beginning, truncate to empty. */
332 c
->pos
= c
->append
= c
->eof
= 0;
340 _newlib_flockfile_start (fp
);
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
;
351 fp
->_close
= fmemcloser
;
352 _newlib_flockfile_end (fp
);
358 fmemopen (void *__restrict buf
,
360 const char *__restrict mode
)
362 return _fmemopen_r (_REENT
, buf
, size
, mode
);
364 #endif /* !_REENT_ONLY */