├── .gitignore ├── Android.mk ├── README.md ├── include └── stdioext.h └── src ├── fmemopen.c ├── fopencookie.c └── open_memstream.c /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | obj/ 3 | -------------------------------------------------------------------------------- /Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | 3 | include $(CLEAR_VARS) 4 | 5 | LOCAL_MODULE := stdioext 6 | 7 | LOCAL_C_INCLUDES := $(LOCAL_PATH)/include 8 | 9 | # missing stdio functions 10 | 11 | LOCAL_SRC_FILES := \ 12 | $(LOCAL_PATH)/src/open_memstream.c \ 13 | $(LOCAL_PATH)/src/fopencookie.c \ 14 | $(LOCAL_PATH)/src/fmemopen.c 15 | 16 | LOCAL_CFLAGS += -O3 17 | 18 | include $(BUILD_STATIC_LIBRARY) 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Android stdioext library 2 | ======================== 3 | 4 | This repository contains C functions to access a memory buffer as a FILE, such as `fmemopen()` and `open_memstream()`. 5 | 6 | The code has been taken from [Tesseract Tools for Android](https://code.google.com/p/tesseract-android-tools/) in order to provide a project independent version of the functions. 7 | 8 | An Android makefile is provided to help building the library. 9 | -------------------------------------------------------------------------------- /include/stdioext.h: -------------------------------------------------------------------------------- 1 | #ifndef _STDIOEXT_H 2 | #define _STDIOEXT_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | #include 10 | 11 | typedef struct cookie_io_functions_t { 12 | ssize_t (*read)(void *cookie, char *buf, size_t n); 13 | ssize_t (*write)(void *cookie, const char *buf, size_t n); 14 | int (*seek)(void *cookie, off_t *pos, int whence); 15 | int (*close)(void *cookie); 16 | } cookie_io_functions_t; 17 | 18 | FILE *fopencookie(void *cookie, const char *mode, cookie_io_functions_t functions); 19 | 20 | FILE *fmemopen(void *buf, size_t size, const char *mode); 21 | 22 | FILE *open_memstream(char **buf, size_t *size); 23 | 24 | #ifdef __cplusplus 25 | } // extern "C" 26 | #endif 27 | 28 | #endif /* _STDIOEXT_H */ 29 | -------------------------------------------------------------------------------- /src/fmemopen.c: -------------------------------------------------------------------------------- 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 | * Modifications for Android written Jul 2009 by Alan Viverette 6 | */ 7 | 8 | /* 9 | FUNCTION 10 | <>---open a stream around a fixed-length string 11 | 12 | INDEX 13 | fmemopen 14 | 15 | ANSI_SYNOPSIS 16 | #include 17 | FILE *fmemopen(void *restrict <[buf]>, size_t <[size]>, 18 | const char *restrict <[mode]>); 19 | 20 | DESCRIPTION 21 | <> creates a seekable <> stream that wraps a 22 | fixed-length buffer of <[size]> bytes starting at <[buf]>. The stream 23 | is opened with <[mode]> treated as in <>, where append mode 24 | starts writing at the first NUL byte. If <[buf]> is NULL, then 25 | <[size]> bytes are automatically provided as if by <>, with 26 | the initial size of 0, and <[mode]> must contain <<+>> so that data 27 | can be read after it is written. 28 | 29 | The stream maintains a current position, which moves according to 30 | bytes read or written, and which can be one past the end of the array. 31 | The stream also maintains a current file size, which is never greater 32 | than <[size]>. If <[mode]> starts with <>, the position starts at 33 | <<0>>, and file size starts at <[size]> if <[buf]> was provided. If 34 | <[mode]> starts with <>, the position and file size start at <<0>>, 35 | and if <[buf]> was provided, the first byte is set to NUL. If 36 | <[mode]> starts with <>, the position and file size start at the 37 | location of the first NUL byte, or else <[size]> if <[buf]> was 38 | provided. 39 | 40 | When reading, NUL bytes have no significance, and reads cannot exceed 41 | the current file size. When writing, the file size can increase up to 42 | <[size]> as needed, and NUL bytes may be embedded in the stream (see 43 | <> for an alternative that automatically enlarges the 44 | buffer). When the stream is flushed or closed after a write that 45 | changed the file size, a NUL byte is written at the current position 46 | if there is still room; if the stream is not also open for reading, a 47 | NUL byte is additionally written at the last byte of <[buf]> when the 48 | stream has exceeded <[size]>, so that a write-only <[buf]> is always 49 | NUL-terminated when the stream is flushed or closed (and the initial 50 | <[size]> should take this into account). It is not possible to seek 51 | outside the bounds of <[size]>. A NUL byte written during a flush is 52 | restored to its previous value when seeking elsewhere in the string. 53 | 54 | RETURNS 55 | The return value is an open FILE pointer on success. On error, 56 | <> is returned, and <> will be set to EINVAL if <[size]> 57 | is zero or <[mode]> is invalid, ENOMEM if <[buf]> was NULL and memory 58 | could not be allocated, or EMFILE if too many streams are already 59 | open. 60 | 61 | PORTABILITY 62 | This function is being added to POSIX 200x, but is not in POSIX 2001. 63 | 64 | Supporting OS subroutines required: <>. 65 | */ 66 | 67 | #include 68 | #include 69 | #include 70 | #include 71 | #include 72 | #include "stdioext.h" 73 | 74 | /* Describe details of an open memstream. */ 75 | typedef struct fmemcookie { 76 | void *storage; /* storage to free on close */ 77 | char *buf; /* buffer start */ 78 | size_t pos; /* current position */ 79 | size_t eof; /* current file size */ 80 | size_t max; /* maximum file size */ 81 | char append; /* nonzero if appending */ 82 | char writeonly; /* 1 if write-only */ 83 | char saved; /* saved character that lived at pos before write-only NUL */ 84 | } fmemcookie; 85 | 86 | /* Read up to non-zero N bytes into BUF from stream described by 87 | COOKIE; return number of bytes read (0 on EOF). */ 88 | static int 89 | fmemread(void *cookie, char *buf, int 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. */ 104 | static int 105 | fmemwrite(void *cookie, const char *buf, int n) 106 | { 107 | fmemcookie *c = (fmemcookie *) cookie; 108 | int adjust = 0; /* true if at EOF, but still need to write NUL. */ 109 | 110 | /* Append always seeks to eof; otherwise, if we have previously done 111 | a seek beyond eof, ensure all intermediate bytes are NUL. */ 112 | if (c->append) 113 | c->pos = c->eof; 114 | else if (c->pos > c->eof) 115 | memset (c->buf + c->eof, '\0', c->pos - c->eof); 116 | /* Do not write beyond EOF; saving room for NUL on write-only stream. */ 117 | if (c->pos + n > c->max - c->writeonly) 118 | { 119 | adjust = c->writeonly; 120 | n = c->max - c->pos; 121 | } 122 | /* Now n is the number of bytes being modified, and adjust is 1 if 123 | the last byte is NUL instead of from buf. Write a NUL if 124 | write-only; or if read-write, eof changed, and there is still 125 | room. When we are within the file contents, remember what we 126 | overwrite so we can restore it if we seek elsewhere later. */ 127 | if (c->pos + n > c->eof) 128 | { 129 | c->eof = c->pos + n; 130 | if (c->eof - adjust < c->max) 131 | c->saved = c->buf[c->eof - adjust] = '\0'; 132 | } 133 | else if (c->writeonly) 134 | { 135 | if (n) 136 | { 137 | c->saved = c->buf[c->pos + n - adjust]; 138 | c->buf[c->pos + n - adjust] = '\0'; 139 | } 140 | else 141 | adjust = 0; 142 | } 143 | c->pos += n; 144 | if (n - adjust) 145 | memcpy (c->buf + c->pos - n, buf, n - adjust); 146 | else 147 | { 148 | return EOF; 149 | } 150 | return n; 151 | } 152 | 153 | /* Seek to position POS relative to WHENCE within stream described by 154 | COOKIE; return resulting position or fail with EOF. */ 155 | static fpos_t 156 | fmemseek(void *cookie, fpos_t pos, int whence) 157 | { 158 | fmemcookie *c = (fmemcookie *) cookie; 159 | off_t offset = (off_t) pos; 160 | 161 | if (whence == SEEK_CUR) 162 | offset += c->pos; 163 | else if (whence == SEEK_END) 164 | offset += c->eof; 165 | if (offset < 0) 166 | { 167 | offset = -1; 168 | } 169 | else if (offset > c->max) 170 | { 171 | offset = -1; 172 | } 173 | else 174 | { 175 | if (c->writeonly && c->pos < c->eof) 176 | { 177 | c->buf[c->pos] = c->saved; 178 | c->saved = '\0'; 179 | } 180 | c->pos = offset; 181 | if (c->writeonly && c->pos < c->eof) 182 | { 183 | c->saved = c->buf[c->pos]; 184 | c->buf[c->pos] = '\0'; 185 | } 186 | } 187 | return (fpos_t) offset; 188 | } 189 | 190 | /* Reclaim resources used by stream described by COOKIE. */ 191 | static int 192 | fmemclose(void *cookie) 193 | { 194 | fmemcookie *c = (fmemcookie *) cookie; 195 | free (c->storage); 196 | return 0; 197 | } 198 | 199 | /* Open a memstream around buffer BUF of SIZE bytes, using MODE. 200 | Return the new stream, or fail with NULL. */ 201 | FILE * 202 | fmemopen(void *buf, size_t size, const char *mode) 203 | { 204 | FILE *fp; 205 | fmemcookie *c; 206 | int flags; 207 | int dummy; 208 | 209 | if ((flags = __sflags (mode, &dummy)) == 0) 210 | return NULL; 211 | if (!size || !(buf || flags & __SAPP)) 212 | { 213 | return NULL; 214 | } 215 | if ((fp = (FILE *) __sfp ()) == NULL) 216 | return NULL; 217 | if ((c = (fmemcookie *) malloc (sizeof *c + (buf ? 0 : size))) == NULL) 218 | { 219 | fp->_flags = 0; /* release */ 220 | 221 | return NULL; 222 | } 223 | 224 | c->storage = c; 225 | c->max = size; 226 | /* 9 modes to worry about. */ 227 | /* w/a, buf or no buf: Guarantee a NUL after any file writes. */ 228 | c->writeonly = (flags & __SWR) != 0; 229 | c->saved = '\0'; 230 | if (!buf) 231 | { 232 | /* r+/w+/a+, and no buf: file starts empty. */ 233 | c->buf = (char *) (c + 1); 234 | *(char *) buf = '\0'; 235 | c->pos = c->eof = 0; 236 | c->append = (flags & __SAPP) != 0; 237 | } 238 | else 239 | { 240 | c->buf = (char *) buf; 241 | switch (*mode) 242 | { 243 | case 'a': 244 | /* a/a+ and buf: position and size at first NUL. */ 245 | buf = memchr (c->buf, '\0', size); 246 | c->eof = c->pos = buf ? (char *) buf - c->buf : size; 247 | if (!buf && c->writeonly) 248 | /* a: guarantee a NUL within size even if no writes. */ 249 | c->buf[size - 1] = '\0'; 250 | c->append = 1; 251 | break; 252 | case 'r': 253 | /* r/r+ and buf: read at beginning, full size available. */ 254 | c->pos = c->append = 0; 255 | c->eof = size; 256 | break; 257 | case 'w': 258 | /* w/w+ and buf: write at beginning, truncate to empty. */ 259 | c->pos = c->append = c->eof = 0; 260 | *c->buf = '\0'; 261 | break; 262 | default: 263 | abort(); 264 | } 265 | } 266 | 267 | fp->_file = -1; 268 | fp->_flags = flags; 269 | fp->_cookie = c; 270 | fp->_read = flags & (__SRD | __SRW) ? fmemread : NULL; 271 | fp->_write = flags & (__SWR | __SRW) ? fmemwrite : NULL; 272 | fp->_seek = fmemseek; 273 | fp->_close = fmemclose; 274 | 275 | return fp; 276 | } 277 | 278 | -------------------------------------------------------------------------------- /src/fopencookie.c: -------------------------------------------------------------------------------- 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 | * Modifications for Android written Jul 2009 by Alan Viverette 6 | */ 7 | 8 | /* 9 | FUNCTION 10 | <>---open a stream with custom callbacks 11 | 12 | INDEX 13 | fopencookie 14 | 15 | ANSI_SYNOPSIS 16 | #include 17 | typedef ssize_t (*cookie_read_function_t)(void *_cookie, char *_buf, 18 | size_t _n); 19 | typedef ssize_t (*cookie_write_function_t)(void *_cookie, 20 | const char *_buf, size_t _n); 21 | typedef int (*cookie_seek_function_t)(void *_cookie, off_t *_off, 22 | int _whence); 23 | typedef int (*cookie_close_function_t)(void *_cookie); 24 | FILE *fopencookie(const void *<[cookie]>, const char *<[mode]>, 25 | cookie_io_functions_t <[functions]>); 26 | 27 | DESCRIPTION 28 | <> creates a <> stream where I/O is performed using 29 | custom callbacks. The callbacks are registered via the structure: 30 | 31 | . typedef struct 32 | . { 33 | . cookie_read_function_t *read; 34 | . cookie_write_function_t *write; 35 | . cookie_seek_function_t *seek; 36 | . cookie_close_function_t *close; 37 | . } cookie_io_functions_t; 38 | 39 | The stream is opened with <[mode]> treated as in <>. The 40 | callbacks <[functions.read]> and <[functions.write]> may only be NULL 41 | when <[mode]> does not require them. 42 | 43 | <[functions.read]> should return -1 on failure, or else the number of 44 | bytes read (0 on EOF). It is similar to <>, except that 45 | <[cookie]> will be passed as the first argument. 46 | 47 | <[functions.write]> should return -1 on failure, or else the number of 48 | bytes written. It is similar to <>, except that <[cookie]> 49 | will be passed as the first argument. 50 | 51 | <[functions.seek]> should return -1 on failure, and 0 on success, with 52 | *<[_off]> set to the current file position. It is a cross between 53 | <> and <>, with the <[_whence]> argument interpreted in 54 | the same manner. A NULL <[functions.seek]> makes the stream behave 55 | similarly to a pipe in relation to stdio functions that require 56 | positioning. 57 | 58 | <[functions.close]> should return -1 on failure, or 0 on success. It 59 | is similar to <>, except that <[cookie]> will be passed as the 60 | first argument. A NULL <[functions.close]> merely flushes all data 61 | then lets <> succeed. A failed close will still invalidate 62 | the stream. 63 | 64 | Read and write I/O functions are allowed to change the underlying 65 | buffer on fully buffered or line buffered streams by calling 66 | <>. They are also not required to completely fill or empty 67 | the buffer. They are not, however, allowed to change streams from 68 | unbuffered to buffered or to change the state of the line buffering 69 | flag. They must also be prepared to have read or write calls occur on 70 | buffers other than the one most recently specified. 71 | 72 | RETURNS 73 | The return value is an open FILE pointer on success. On error, 74 | <> is returned, and <> will be set to EINVAL if a 75 | function pointer is missing or <[mode]> is invalid, ENOMEM if the 76 | stream cannot be created, or EMFILE if too many streams are already 77 | open. 78 | 79 | PORTABILITY 80 | This function is a newlib extension, copying the prototype from Linux. 81 | It is not portable. See also the <> interface from BSD. 82 | 83 | Supporting OS subroutines required: <>. 84 | */ 85 | 86 | #include 87 | #include 88 | #include 89 | #include "stdioext.h" 90 | 91 | typedef struct fccookie { 92 | void *cookie; 93 | FILE *fp; 94 | ssize_t (*readfn)(void *, char *, size_t); 95 | ssize_t (*writefn)(void *, const char *, size_t); 96 | int (*seekfn)(void *, off_t *, int); 97 | int (*closefn)(void *); 98 | } fccookie; 99 | 100 | static int 101 | fcread(void *cookie, char *buf, int n) 102 | { 103 | int result; 104 | fccookie *c = (fccookie *) cookie; 105 | result = c->readfn (c->cookie, buf, n); 106 | return result; 107 | } 108 | 109 | static int 110 | fcwrite(void *cookie, const char *buf, int n) 111 | { 112 | int result; 113 | fccookie *c = (fccookie *) cookie; 114 | if (c->fp->_flags & __SAPP && c->fp->_seek) 115 | { 116 | c->fp->_seek (cookie, 0, SEEK_END); 117 | } 118 | result = c->writefn (c->cookie, buf, n); 119 | return result; 120 | } 121 | 122 | static fpos_t 123 | fcseek(void *cookie, fpos_t pos, int whence) 124 | { 125 | fccookie *c = (fccookie *) cookie; 126 | off_t offset = (off_t) pos; 127 | 128 | c->seekfn (c->cookie, &offset, whence); 129 | 130 | return (fpos_t) offset; 131 | } 132 | 133 | static int 134 | fcclose(void *cookie) 135 | { 136 | int result = 0; 137 | fccookie *c = (fccookie *) cookie; 138 | if (c->closefn) 139 | { 140 | result = c->closefn (c->cookie); 141 | } 142 | free (c); 143 | return result; 144 | } 145 | 146 | FILE * 147 | fopencookie(void *cookie, const char *mode, cookie_io_functions_t functions) 148 | { 149 | FILE *fp; 150 | fccookie *c; 151 | int flags; 152 | int dummy; 153 | 154 | if ((flags = __sflags (mode, &dummy)) == 0) 155 | return NULL; 156 | if (((flags & (__SRD | __SRW)) && !functions.read) 157 | || ((flags & (__SWR | __SRW)) && !functions.write)) 158 | { 159 | return NULL; 160 | } 161 | if ((fp = (FILE *) __sfp ()) == NULL) 162 | return NULL; 163 | if ((c = (fccookie *) malloc (sizeof *c)) == NULL) 164 | { 165 | fp->_flags = 0; 166 | return NULL; 167 | } 168 | 169 | fp->_file = -1; 170 | fp->_flags = flags; 171 | c->cookie = cookie; 172 | c->fp = fp; 173 | fp->_cookie = c; 174 | c->readfn = functions.read; 175 | fp->_read = fcread; 176 | c->writefn = functions.write; 177 | fp->_write = fcwrite; 178 | c->seekfn = functions.seek; 179 | fp->_seek = functions.seek ? fcseek : NULL; 180 | c->closefn = functions.close; 181 | fp->_close = fcclose; 182 | 183 | return fp; 184 | } 185 | 186 | 187 | -------------------------------------------------------------------------------- /src/open_memstream.c: -------------------------------------------------------------------------------- 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 | * Modifications for Android written Jul 2009 by Alan Viverette 6 | */ 7 | 8 | /* 9 | FUNCTION 10 | <>---open a write stream around an arbitrary-length string 11 | 12 | INDEX 13 | open_memstream 14 | 15 | ANSI_SYNOPSIS 16 | #include 17 | FILE *open_memstream(char **restrict <[buf]>, 18 | size_t *restrict <[size]>); 19 | 20 | DESCRIPTION 21 | <> creates a seekable <> stream that wraps an 22 | arbitrary-length buffer, created as if by <>. The current 23 | contents of *<[buf]> are ignored; this implementation uses *<[size]> 24 | as a hint of the maximum size expected, but does not fail if the hint 25 | was wrong. The parameters <[buf]> and <[size]> are later stored 26 | through following any call to <> or <>, set to the 27 | current address and usable size of the allocated string; although 28 | after fflush, the pointer is only valid until another stream operation 29 | that results in a write. Behavior is undefined if the user alters 30 | either *<[buf]> or *<[size]> prior to <>. 31 | 32 | The stream is write-only, since the user can directly read *<[buf]> 33 | after a flush; see <> for a way to wrap a string with a 34 | readable stream. The user is responsible for calling <> on 35 | the final *<[buf]> after <>. 36 | 37 | Any time the stream is flushed, a NUL byte is written at the current 38 | position (but is not counted in the buffer length), so that the string 39 | is always NUL-terminated after at most *<[size]> bytes. However, data 40 | previously written beyond the current stream offset is not lost, and 41 | the NUL byte written during a flush is restored to its previous value 42 | when seeking elsewhere in the string. 43 | 44 | RETURNS 45 | The return value is an open FILE pointer on success. On error, 46 | <> is returned, and <> will be set to EINVAL if <[buf]> 47 | or <[size]> is NULL, ENOMEM if memory could not be allocated, or 48 | EMFILE if too many streams are already open. 49 | 50 | PORTABILITY 51 | This function is being added to POSIX 200x, but is not in POSIX 2001. 52 | 53 | Supporting OS subroutines required: <>. 54 | */ 55 | 56 | #include 57 | #include 58 | #include 59 | #include 60 | #include "stdioext.h" 61 | 62 | /* Describe details of an open memstream. */ 63 | typedef struct memstream { 64 | void *storage; /* storage to free on close */ 65 | char **pbuf; /* pointer to the current buffer */ 66 | size_t *psize; /* pointer to the current size, smaller of pos or eof */ 67 | size_t pos; /* current position */ 68 | size_t eof; /* current file size */ 69 | size_t max; /* current malloc buffer size, always > eof */ 70 | char saved; /* saved character that lived at *psize before NUL */ 71 | } memstream; 72 | 73 | /* Write up to non-zero N bytes of BUF into the stream described by COOKIE, 74 | returning the number of bytes written or EOF on failure. */ 75 | static int 76 | memwrite(void *cookie, const char *buf, int n) 77 | { 78 | memstream *c = (memstream *) cookie; 79 | char *cbuf = *c->pbuf; 80 | 81 | /* size_t is unsigned, but off_t is signed. Don't let stream get so 82 | big that user cannot do ftello. */ 83 | if (sizeof (off_t) == sizeof (size_t) && (ssize_t) (c->pos + n) < 0) 84 | { 85 | return EOF; 86 | } 87 | /* Grow the buffer, if necessary. Choose a geometric growth factor 88 | to avoid quadratic realloc behavior, but use a rate less than 89 | (1+sqrt(5))/2 to accomodate malloc overhead. Overallocate, so 90 | that we can add a trailing \0 without reallocating. The new 91 | allocation should thus be max(prev_size*1.5, c->pos+n+1). */ 92 | if (c->pos + n >= c->max) 93 | { 94 | size_t newsize = c->max * 3 / 2; 95 | if (newsize < c->pos + n + 1) 96 | newsize = c->pos + n + 1; 97 | cbuf = realloc (cbuf, newsize); 98 | if (! cbuf) 99 | return EOF; /* errno already set to ENOMEM */ 100 | *c->pbuf = cbuf; 101 | c->max = newsize; 102 | } 103 | /* If we have previously done a seek beyond eof, ensure all 104 | intermediate bytes are NUL. */ 105 | if (c->pos > c->eof) 106 | memset (cbuf + c->eof, '\0', c->pos - c->eof); 107 | memcpy (cbuf + c->pos, buf, n); 108 | c->pos += n; 109 | /* If the user has previously written further, remember what the 110 | trailing NUL is overwriting. Otherwise, extend the stream. */ 111 | if (c->pos > c->eof) 112 | c->eof = c->pos; 113 | else 114 | c->saved = cbuf[c->pos]; 115 | cbuf[c->pos] = '\0'; 116 | *c->psize = c->pos; 117 | return n; 118 | } 119 | 120 | /* Seek to position POS relative to WHENCE within stream described by 121 | COOKIE; return resulting position or fail with EOF. */ 122 | static fpos_t 123 | memseek(void *cookie, fpos_t pos, int whence) 124 | { 125 | memstream *c = (memstream *) cookie; 126 | off_t offset = (off_t) pos; 127 | 128 | if (whence == SEEK_CUR) 129 | offset += c->pos; 130 | else if (whence == SEEK_END) 131 | offset += c->eof; 132 | if (offset < 0) 133 | { 134 | offset = -1; 135 | } 136 | else if ((size_t) offset != offset) 137 | { 138 | offset = -1; 139 | } 140 | else 141 | { 142 | if (c->pos < c->eof) 143 | { 144 | (*c->pbuf)[c->pos] = c->saved; 145 | c->saved = '\0'; 146 | } 147 | c->pos = offset; 148 | if (c->pos < c->eof) 149 | { 150 | c->saved = (*c->pbuf)[c->pos]; 151 | (*c->pbuf)[c->pos] = '\0'; 152 | *c->psize = c->pos; 153 | } 154 | else 155 | *c->psize = c->eof; 156 | } 157 | return (fpos_t) offset; 158 | } 159 | 160 | /* Reclaim resources used by stream described by COOKIE. */ 161 | static int 162 | memclose(void *cookie) 163 | { 164 | memstream *c = (memstream *) cookie; 165 | char *buf; 166 | 167 | /* Be nice and try to reduce any unused memory. */ 168 | buf = realloc (*c->pbuf, *c->psize + 1); 169 | if (buf) 170 | *c->pbuf = buf; 171 | free (c->storage); 172 | return 0; 173 | } 174 | 175 | /* Open a memstream that tracks a dynamic buffer in BUF and SIZE. 176 | Return the new stream, or fail with NULL. */ 177 | FILE * 178 | open_memstream(char **buf, size_t *size) 179 | { 180 | FILE *fp; 181 | memstream *c; 182 | int flags; 183 | 184 | if (!buf || !size) 185 | { 186 | return NULL; 187 | } 188 | if ((fp = (FILE *) __sfp ()) == NULL) 189 | return NULL; 190 | if ((c = (memstream *) malloc (sizeof *c)) == NULL) 191 | { 192 | fp->_flags = 0; /* release */ 193 | return NULL; 194 | } 195 | 196 | /* Use *size as a hint for initial sizing, but bound the initial 197 | malloc between 64 bytes (same as asprintf, to avoid frequent 198 | mallocs on small strings) and 64k bytes (to avoid overusing the 199 | heap if *size was garbage). */ 200 | c->max = *size; 201 | if (c->max < 64) 202 | c->max = 64; 203 | else if (c->max > 64 * 1024) 204 | c->max = 64 * 1024; 205 | *size = 0; 206 | *buf = malloc (c->max); 207 | if (!*buf) 208 | { 209 | fp->_flags = 0; /* release */ 210 | free (c); 211 | return NULL; 212 | } 213 | **buf = '\0'; 214 | 215 | c->storage = c; 216 | c->pbuf = buf; 217 | c->psize = size; 218 | c->eof = 0; 219 | c->saved = '\0'; 220 | 221 | fp->_file = -1; 222 | fp->_flags = __SWR; 223 | fp->_cookie = c; 224 | fp->_read = NULL; 225 | fp->_write = memwrite; 226 | fp->_seek = memseek; 227 | fp->_close = memclose; 228 | 229 | return fp; 230 | } 231 | 232 | --------------------------------------------------------------------------------