Cygwin: mmap: allow remapping part of an existing anonymous mapping
[newlib-cygwin.git] / newlib / libc / stdio / fmemopen.c
blob886ba7efe0399e34678266efadb6844702945687
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 */
6 /*
7 FUNCTION
8 <<fmemopen>>---open a stream around a fixed-length string
10 INDEX
11 fmemopen
13 SYNOPSIS
14 #include <stdio.h>
15 FILE *fmemopen(void *restrict <[buf]>, size_t <[size]>,
16 const char *restrict <[mode]>);
18 DESCRIPTION
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
36 provided.
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.
52 RETURNS
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
57 open.
59 PORTABILITY
60 This function is being added to POSIX 200x, but is not in POSIX 2001.
62 Supporting OS subroutines required: <<sbrk>>.
65 #include <stdio.h>
66 #include <errno.h>
67 #include <string.h>
68 #include <sys/lock.h>
69 #include "local.h"
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 */
81 } fmemcookie;
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,
87 void *cookie,
88 char *buf,
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. */
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;
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,
106 void *cookie,
107 const char *buf,
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. */
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)
122 adjust = c->writeonly;
123 n = c->max - c->pos;
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)
132 c->eof = c->pos + n;
133 if (c->eof - adjust < c->max)
134 c->saved = c->buf[c->eof - adjust] = '\0';
136 else if (c->writeonly)
138 if (n)
140 c->saved = c->buf[c->pos + n - adjust];
141 c->buf[c->pos + n - adjust] = '\0';
143 else
144 adjust = 0;
146 c->pos += n;
147 if (n - adjust)
148 memcpy (c->buf + c->pos - n, buf, n - adjust);
149 else
151 _REENT_ERRNO(ptr) = ENOSPC;
152 return EOF;
154 return n;
157 /* Seek to position POS relative to WHENCE within stream described by
158 COOKIE; return resulting position or fail with EOF. */
159 static _fpos_t
160 fmemseeker (struct _reent *ptr,
161 void *cookie,
162 _fpos_t pos,
163 int whence)
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)
173 offset += c->pos;
174 else if (whence == SEEK_END)
175 offset += c->eof;
176 if (offset < 0)
178 _REENT_ERRNO(ptr) = EINVAL;
179 offset = -1;
181 else if (offset > c->max)
183 _REENT_ERRNO(ptr) = ENOSPC;
184 offset = -1;
186 #ifdef __LARGE64_FILES
187 else if ((_fpos_t) offset != offset)
189 _REENT_ERRNO(ptr) = EOVERFLOW;
190 offset = -1;
192 #endif /* __LARGE64_FILES */
193 else
195 if (c->writeonly && c->pos < c->eof)
197 c->buf[c->pos] = c->saved;
198 c->saved = '\0';
200 c->pos = offset;
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
213 static _fpos64_t
214 fmemseeker64 (struct _reent *ptr,
215 void *cookie,
216 _fpos64_t pos,
217 int whence)
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)
227 _REENT_ERRNO(ptr) = EINVAL;
228 offset = -1;
230 else if (offset > c->max)
232 _REENT_ERRNO(ptr) = ENOSPC;
233 offset = -1;
235 else
237 if (c->writeonly && c->pos < c->eof)
239 c->buf[c->pos] = c->saved;
240 c->saved = '\0';
242 c->pos = offset;
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. */
254 static int
255 fmemcloser (struct _reent *ptr,
256 void *cookie)
258 fmemcookie *c = (fmemcookie *) cookie;
259 _free_r (ptr, c->storage);
260 return 0;
263 /* Open a memstream around buffer BUF of SIZE bytes, using MODE.
264 Return the new stream, or fail with NULL. */
265 FILE *
266 _fmemopen_r (struct _reent *ptr,
267 void *__restrict buf,
268 size_t size,
269 const char *__restrict mode)
271 FILE *fp;
272 fmemcookie *c;
273 int flags;
274 int dummy;
276 if ((flags = __sflags (ptr, mode, &dummy)) == 0)
277 return NULL;
278 if (!size || !(buf || flags & __SRW))
280 _REENT_ERRNO(ptr) = EINVAL;
281 return NULL;
283 if ((fp = __sfp (ptr)) == NULL)
284 return NULL;
285 if ((c = (fmemcookie *) _malloc_r (ptr, sizeof *c + (buf ? 0 : size)))
286 == NULL)
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;
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)
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;
311 else
313 c->buf = (char *) buf;
314 switch (*mode)
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 ();
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;
356 #ifndef _REENT_ONLY
357 FILE *
358 fmemopen (void *__restrict buf,
359 size_t size,
360 const char *__restrict mode)
362 return _fmemopen_r (_REENT, buf, size, mode);
364 #endif /* !_REENT_ONLY */