Subversion Repositories Kolibri OS

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
6536 serge 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
/*
7
FUNCTION
8
<>---open a stream around a fixed-length string
9
 
10
INDEX
11
	fmemopen
12
 
13
ANSI_SYNOPSIS
14
	#include 
15
	FILE *fmemopen(void *restrict <[buf]>, size_t <[size]>,
16
		       const char *restrict <[mode]>);
17
 
18
DESCRIPTION
19
<> creates a seekable <> stream that wraps a
20
fixed-length buffer of <[size]> bytes starting at <[buf]>.  The stream
21
is opened with <[mode]> treated as in <>, 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 <>, with
24
the initial size of 0, and <[mode]> must contain <<+>> so that data
25
can be read after it is written.
26
 
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 <>, the position starts at
31
<<0>>, and file size starts at <[size]> if <[buf]> was provided.  If
32
<[mode]> starts with <>, 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 <>, the position and file size start at the
35
location of the first NUL byte, or else <[size]> if <[buf]> was
36
provided.
37
 
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
<> 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.
51
 
52
RETURNS
53
The return value is an open FILE pointer on success.  On error,
54
<> is returned, and <> 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.
58
 
59
PORTABILITY
60
This function is being added to POSIX 200x, but is not in POSIX 2001.
61
 
62
Supporting OS subroutines required: <>.
63
*/
64
 
65
#include 
66
#include 
67
#include 
68
#include 
69
#include "local.h"
70
 
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;
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).  */
85
static _READ_WRITE_RETURN_TYPE
86
_DEFUN(fmemreader, (ptr, cookie, buf, n),
87
       struct _reent *ptr _AND
88
       void *cookie _AND
89
       char *buf _AND
90
       _READ_WRITE_BUFSIZE_TYPE n)
91
{
92
  fmemcookie *c = (fmemcookie *) cookie;
93
  /* Can't read beyond current size, but EOF condition is not an error.  */
94
  if (c->pos > c->eof)
95
    return 0;
96
  if (n >= c->eof - c->pos)
97
    n = c->eof - c->pos;
98
  memcpy (buf, c->buf + c->pos, n);
99
  c->pos += n;
100
  return n;
101
}
102
 
103
/* Write up to non-zero N bytes of BUF into the stream described by COOKIE,
104
   returning the number of bytes written or EOF on failure.  */
105
static _READ_WRITE_RETURN_TYPE
106
_DEFUN(fmemwriter, (ptr, cookie, buf, n),
107
       struct _reent *ptr _AND
108
       void *cookie _AND
109
       const char *buf _AND
110
       _READ_WRITE_BUFSIZE_TYPE n)
111
{
112
  fmemcookie *c = (fmemcookie *) cookie;
113
  int adjust = 0; /* true if at EOF, but still need to write NUL.  */
114
 
115
  /* Append always seeks to eof; otherwise, if we have previously done
116
     a seek beyond eof, ensure all intermediate bytes are NUL.  */
117
  if (c->append)
118
    c->pos = c->eof;
119
  else if (c->pos > c->eof)
120
    memset (c->buf + c->eof, '\0', c->pos - c->eof);
121
  /* Do not write beyond EOF; saving room for NUL on write-only stream.  */
122
  if (c->pos + n > c->max - c->writeonly)
123
    {
124
      adjust = c->writeonly;
125
      n = c->max - c->pos;
126
    }
127
  /* Now n is the number of bytes being modified, and adjust is 1 if
128
     the last byte is NUL instead of from buf.  Write a NUL if
129
     write-only; or if read-write, eof changed, and there is still
130
     room.  When we are within the file contents, remember what we
131
     overwrite so we can restore it if we seek elsewhere later.  */
132
  if (c->pos + n > c->eof)
133
    {
134
      c->eof = c->pos + n;
135
      if (c->eof - adjust < c->max)
136
	c->saved = c->buf[c->eof - adjust] = '\0';
137
    }
138
  else if (c->writeonly)
139
    {
140
      if (n)
141
	{
142
	  c->saved = c->buf[c->pos + n - adjust];
143
	  c->buf[c->pos + n - adjust] = '\0';
144
	}
145
      else
146
	adjust = 0;
147
    }
148
  c->pos += n;
149
  if (n - adjust)
150
    memcpy (c->buf + c->pos - n, buf, n - adjust);
151
  else
152
    {
153
      ptr->_errno = ENOSPC;
154
      return EOF;
155
    }
156
  return n;
157
}
158
 
159
/* Seek to position POS relative to WHENCE within stream described by
160
   COOKIE; return resulting position or fail with EOF.  */
161
static _fpos_t
162
_DEFUN(fmemseeker, (ptr, cookie, pos, whence),
163
       struct _reent *ptr _AND
164
       void *cookie _AND
165
       _fpos_t pos _AND
166
       int whence)
167
{
168
  fmemcookie *c = (fmemcookie *) cookie;
169
#ifndef __LARGE64_FILES
170
  off_t offset = (off_t) pos;
171
#else /* __LARGE64_FILES */
172
  _off64_t offset = (_off64_t) pos;
173
#endif /* __LARGE64_FILES */
174
 
175
  if (whence == SEEK_CUR)
176
    offset += c->pos;
177
  else if (whence == SEEK_END)
178
    offset += c->eof;
179
  if (offset < 0)
180
    {
181
      ptr->_errno = EINVAL;
182
      offset = -1;
183
    }
184
  else if (offset > c->max)
185
    {
186
      ptr->_errno = ENOSPC;
187
      offset = -1;
188
    }
189
#ifdef __LARGE64_FILES
190
  else if ((_fpos_t) offset != offset)
191
    {
192
      ptr->_errno = EOVERFLOW;
193
      offset = -1;
194
    }
195
#endif /* __LARGE64_FILES */
196
  else
197
    {
198
      if (c->writeonly && c->pos < c->eof)
199
	{
200
	  c->buf[c->pos] = c->saved;
201
	  c->saved = '\0';
202
	}
203
      c->pos = offset;
204
      if (c->writeonly && c->pos < c->eof)
205
	{
206
	  c->saved = c->buf[c->pos];
207
	  c->buf[c->pos] = '\0';
208
	}
209
    }
210
  return (_fpos_t) offset;
211
}
212
 
213
/* Seek to position POS relative to WHENCE within stream described by
214
   COOKIE; return resulting position or fail with EOF.  */
215
#ifdef __LARGE64_FILES
216
static _fpos64_t
217
_DEFUN(fmemseeker64, (ptr, cookie, pos, whence),
218
       struct _reent *ptr _AND
219
       void *cookie _AND
220
       _fpos64_t pos _AND
221
       int whence)
222
{
223
  _off64_t offset = (_off64_t) pos;
224
  fmemcookie *c = (fmemcookie *) cookie;
225
  if (whence == SEEK_CUR)
226
    offset += c->pos;
227
  else if (whence == SEEK_END)
228
    offset += c->eof;
229
  if (offset < 0)
230
    {
231
      ptr->_errno = EINVAL;
232
      offset = -1;
233
    }
234
  else if (offset > c->max)
235
    {
236
      ptr->_errno = ENOSPC;
237
      offset = -1;
238
    }
239
  else
240
    {
241
      if (c->writeonly && c->pos < c->eof)
242
	{
243
	  c->buf[c->pos] = c->saved;
244
	  c->saved = '\0';
245
	}
246
      c->pos = offset;
247
      if (c->writeonly && c->pos < c->eof)
248
	{
249
	  c->saved = c->buf[c->pos];
250
	  c->buf[c->pos] = '\0';
251
	}
252
    }
253
  return (_fpos64_t) offset;
254
}
255
#endif /* __LARGE64_FILES */
256
 
257
/* Reclaim resources used by stream described by COOKIE.  */
258
static int
259
_DEFUN(fmemcloser, (ptr, cookie),
260
       struct _reent *ptr _AND
261
       void *cookie)
262
{
263
  fmemcookie *c = (fmemcookie *) cookie;
264
  _free_r (ptr, c->storage);
265
  return 0;
266
}
267
 
268
/* Open a memstream around buffer BUF of SIZE bytes, using MODE.
269
   Return the new stream, or fail with NULL.  */
270
FILE *
271
_DEFUN(_fmemopen_r, (ptr, buf, size, mode),
272
       struct _reent *ptr _AND
273
       void *__restrict buf _AND
274
       size_t size _AND
275
       const char *__restrict mode)
276
{
277
  FILE *fp;
278
  fmemcookie *c;
279
  int flags;
280
  int dummy;
281
 
282
  if ((flags = __sflags (ptr, mode, &dummy)) == 0)
283
    return NULL;
284
  if (!size || !(buf || flags & __SRW))
285
    {
286
      ptr->_errno = EINVAL;
287
      return NULL;
288
    }
289
  if ((fp = __sfp (ptr)) == NULL)
290
    return NULL;
291
  if ((c = (fmemcookie *) _malloc_r (ptr, sizeof *c + (buf ? 0 : size)))
292
      == NULL)
293
    {
294
      _newlib_sfp_lock_start ();
295
      fp->_flags = 0;		/* release */
296
#ifndef __SINGLE_THREAD__
297
      __lock_close_recursive (fp->_lock);
298
#endif
299
      _newlib_sfp_lock_end ();
300
      return NULL;
301
    }
302
 
303
  c->storage = c;
304
  c->max = size;
305
  /* 9 modes to worry about.  */
306
  /* w/a, buf or no buf: Guarantee a NUL after any file writes.  */
307
  c->writeonly = (flags & __SWR) != 0;
308
  c->saved = '\0';
309
  if (!buf)
310
    {
311
      /* r+/w+/a+, and no buf: file starts empty.  */
312
      c->buf = (char *) (c + 1);
313
      c->buf[0] = '\0';
314
      c->pos = c->eof = 0;
315
      c->append = (flags & __SAPP) != 0;
316
    }
317
  else
318
    {
319
      c->buf = (char *) buf;
320
      switch (*mode)
321
	{
322
	case 'a':
323
	  /* a/a+ and buf: position and size at first NUL.  */
324
	  buf = memchr (c->buf, '\0', size);
325
	  c->eof = c->pos = buf ? (char *) buf - c->buf : size;
326
	  if (!buf && c->writeonly)
327
	    /* a: guarantee a NUL within size even if no writes.  */
328
	    c->buf[size - 1] = '\0';
329
	  c->append = 1;
330
	  break;
331
	case 'r':
332
	  /* r/r+ and buf: read at beginning, full size available.  */
333
	  c->pos = c->append = 0;
334
	  c->eof = size;
335
	  break;
336
	case 'w':
337
	  /* w/w+ and buf: write at beginning, truncate to empty.  */
338
	  c->pos = c->append = c->eof = 0;
339
	  *c->buf = '\0';
340
	  break;
341
	default:
342
	  abort ();
343
	}
344
    }
345
 
346
  _newlib_flockfile_start (fp);
347
  fp->_file = -1;
348
  fp->_flags = flags;
349
  fp->_cookie = c;
350
  fp->_read = flags & (__SRD | __SRW) ? fmemreader : NULL;
351
  fp->_write = flags & (__SWR | __SRW) ? fmemwriter : NULL;
352
  fp->_seek = fmemseeker;
353
#ifdef __LARGE64_FILES
354
  fp->_seek64 = fmemseeker64;
355
  fp->_flags |= __SL64;
356
#endif
357
  fp->_close = fmemcloser;
358
  _newlib_flockfile_end (fp);
359
  return fp;
360
}
361
 
362
#ifndef _REENT_ONLY
363
FILE *
364
_DEFUN(fmemopen, (buf, size, mode),
365
       void *__restrict buf _AND
366
       size_t size _AND
367
       const char *__restrict mode)
368
{
369
  return _fmemopen_r (_REENT, buf, size, mode);
370
}
371
#endif /* !_REENT_ONLY */