open_memstream.c revision cf63d5d00f5a631a2905da7812b5c029b5211cf6
1cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden/* 2cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * Copyright (C) 2010 The Android Open Source Project 3cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * 4cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * Licensed under the Apache License, Version 2.0 (the "License"); 5cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * you may not use this file except in compliance with the License. 6cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * You may obtain a copy of the License at 7cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * 8cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * http://www.apache.org/licenses/LICENSE-2.0 9cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * 10cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * Unless required by applicable law or agreed to in writing, software 11cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * distributed under the License is distributed on an "AS IS" BASIS, 12cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * See the License for the specific language governing permissions and 14cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * limitations under the License. 15cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden */ 16cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 17cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden#ifndef HAVE_OPEN_MEMSTREAM 18cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 19cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden/* 20cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * Implementation of the POSIX open_memstream() function, which Linux has 21cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * but BSD lacks. 22cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * 23cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * Summary: 24cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * - Works like a file-backed FILE* opened with fopen(name, "w"), but the 25cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * backing is a chunk of memory rather than a file. 26cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * - The buffer expands as you write more data. Seeking past the end 27cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * of the file and then writing to it zero-fills the gap. 28cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * - The values at "*bufp" and "*sizep" should be considered read-only, 29cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * and are only valid immediately after an fflush() or fclose(). 30cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * - A '\0' is maintained just past the end of the file. This is not included 31cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * in "*sizep". (The behavior w.r.t. fseek() is not clearly defined. 32cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * The spec says the null byte is written when a write() advances EOF, 33cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * but it looks like glibc ensures the null byte is always found at EOF, 34cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * even if you just seeked backwards. The example on the opengroup.org 35cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * page suggests that this is the expected behavior. The null must be 36cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * present after a no-op fflush(), which we can't see, so we have to save 37cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * and restore it. Annoying, but allows file truncation.) 38cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * - After fclose(), the caller must eventually free(*bufp). 39cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * 40cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * This is built out of funopen(), which BSD has but Linux lacks. There is 41cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * no flush() operator, so we need to keep the user pointers up to date 42cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * after each operation. 43cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * 44cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * I don't think Windows has any of the above, but we don't need to use 45cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * them there, so we just supply a stub. 46cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden */ 47cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden#include <cutils/open_memstream.h> 48cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden#include <stdlib.h> 49cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden#include <stdio.h> 50cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden#include <string.h> 51cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden#include <errno.h> 52cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden#include <assert.h> 53cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 54cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden#if 0 55cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden# define DBUG(x) printf x 56cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden#else 57cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden# define DBUG(x) ((void)0) 58cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden#endif 59cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 60cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden#ifdef HAVE_FUNOPEN 61cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 62cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden/* 63cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * Definition of a seekable, write-only memory stream. 64cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden */ 65cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFaddentypedef struct { 66cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden char** bufp; /* pointer to buffer pointer */ 67cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden size_t* sizep; /* pointer to eof */ 68cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 69cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden size_t allocSize; /* size of buffer */ 70cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden size_t eof; /* furthest point we've written to */ 71cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden size_t offset; /* current write offset */ 72cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden char saved; /* required by NUL handling */ 73cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden} MemStream; 74cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 75cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden#define kInitialSize 1024 76cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 77cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden/* 78cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * Ensure that we have enough storage to write "size" bytes at the 79cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * current offset. We also have to take into account the extra '\0' 80cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * that we maintain just past EOF. 81cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * 82cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * Returns 0 on success. 83cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden */ 84cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFaddenstatic int ensureCapacity(MemStream* stream, int writeSize) 85cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden{ 86cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden DBUG(("+++ ensureCap off=%d size=%d\n", stream->offset, writeSize)); 87cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 88cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden size_t neededSize = stream->offset + writeSize + 1; 89cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden if (neededSize <= stream->allocSize) 90cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden return 0; 91cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 92cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden size_t newSize; 93cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 94cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden if (stream->allocSize == 0) { 95cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden newSize = kInitialSize; 96cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden } else { 97cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden newSize = stream->allocSize; 98cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden newSize += newSize / 2; /* expand by 3/2 */ 99cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden } 100cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 101cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden if (newSize < neededSize) 102cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden newSize = neededSize; 103cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden DBUG(("+++ realloc %p->%p to size=%d\n", 104cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden stream->bufp, *stream->bufp, newSize)); 105cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden char* newBuf = (char*) realloc(*stream->bufp, newSize); 106cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden if (newBuf == NULL) 107cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden return -1; 108cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 109cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden *stream->bufp = newBuf; 110cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden stream->allocSize = newSize; 111cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden return 0; 112cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden} 113cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 114cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden/* 115cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * Write data to a memstream, expanding the buffer if necessary. 116cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * 117cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * If we previously seeked beyond EOF, zero-fill the gap. 118cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * 119cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * Returns the number of bytes written. 120cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden */ 121cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFaddenstatic int write_memstream(void* cookie, const char* buf, int size) 122cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden{ 123cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden MemStream* stream = (MemStream*) cookie; 124cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 125cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden if (ensureCapacity(stream, size) < 0) 126cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden return -1; 127cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 128cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden /* seeked past EOF earlier? */ 129cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden if (stream->eof < stream->offset) { 130cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden DBUG(("+++ zero-fill gap from %d to %d\n", 131cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden stream->eof, stream->offset-1)); 132cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden memset(*stream->bufp + stream->eof, '\0', 133cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden stream->offset - stream->eof); 134cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden } 135cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 136cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden /* copy data, advance write pointer */ 137cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden memcpy(*stream->bufp + stream->offset, buf, size); 138cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden stream->offset += size; 139cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 140cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden if (stream->offset > stream->eof) { 141cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden /* EOF has advanced, update it and append null byte */ 142cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden DBUG(("+++ EOF advanced to %d, appending nul\n", stream->offset)); 143cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden assert(stream->offset < stream->allocSize); 144cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden stream->eof = stream->offset; 145cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden } else { 146cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden /* within previously-written area; save char we're about to stomp */ 147cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden DBUG(("+++ within written area, saving '%c' at %d\n", 148cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden *(*stream->bufp + stream->offset), stream->offset)); 149cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden stream->saved = *(*stream->bufp + stream->offset); 150cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden } 151cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden *(*stream->bufp + stream->offset) = '\0'; 152cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden *stream->sizep = stream->offset; 153cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 154cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden return size; 155cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden} 156cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 157cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden/* 158cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * Seek within a memstream. 159cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * 160cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * Returns the new offset, or -1 on failure. 161cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden */ 162cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFaddenstatic fpos_t seek_memstream(void* cookie, fpos_t offset, int whence) 163cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden{ 164cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden MemStream* stream = (MemStream*) cookie; 165cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden off_t newPosn = (off_t) offset; 166cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 167cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden if (whence == SEEK_CUR) { 168cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden newPosn += stream->offset; 169cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden } else if (whence == SEEK_END) { 170cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden newPosn += stream->eof; 171cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden } 172cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 173cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden if (newPosn < 0 || ((fpos_t)((size_t) newPosn)) != newPosn) { 174cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden /* bad offset - negative or huge */ 175cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden DBUG(("+++ bogus seek offset %ld\n", (long) newPosn)); 176cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden errno = EINVAL; 177cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden return (fpos_t) -1; 178cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden } 179cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 180cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden if (stream->offset < stream->eof) { 181cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden /* 182cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * We were pointing to an area we'd already written to, which means 183cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * we stomped on a character and must now restore it. 184cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden */ 185cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden DBUG(("+++ restoring char '%c' at %d\n", 186cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden stream->saved, stream->offset)); 187cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden *(*stream->bufp + stream->offset) = stream->saved; 188cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden } 189cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 190cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden stream->offset = (size_t) newPosn; 191cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 192cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden if (stream->offset < stream->eof) { 193cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden /* 194cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * We're seeked backward into the stream. Preserve the character 195cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * at EOF and stomp it with a NUL. 196cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden */ 197cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden stream->saved = *(*stream->bufp + stream->offset); 198cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden *(*stream->bufp + stream->offset) = '\0'; 199cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden *stream->sizep = stream->offset; 200cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden } else { 201cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden /* 202cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * We're positioned at, or possibly beyond, the EOF. We want to 203cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * publish the current EOF, not the current position. 204cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden */ 205cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden *stream->sizep = stream->eof; 206cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden } 207cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 208cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden return newPosn; 209cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden} 210cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 211cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden/* 212cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * Close the memstream. We free everything but the data buffer. 213cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden */ 214cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFaddenstatic int close_memstream(void* cookie) 215cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden{ 216cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden free(cookie); 217cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden return 0; 218cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden} 219cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 220cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden/* 221cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * Prepare a memstream. 222cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden */ 223cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFaddenFILE* open_memstream(char** bufp, size_t* sizep) 224cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden{ 225cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden FILE* fp; 226cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden MemStream* stream; 227cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 228cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden if (bufp == NULL || sizep == NULL) { 229cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden errno = EINVAL; 230cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden return NULL; 231cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden } 232cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 233cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden stream = (MemStream*) calloc(1, sizeof(MemStream)); 234cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden if (stream == NULL) 235cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden return NULL; 236cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 237cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden fp = funopen(stream, 238cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden NULL, write_memstream, seek_memstream, close_memstream); 239cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden if (fp == NULL) { 240cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden free(stream); 241cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden return NULL; 242cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden } 243cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 244cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden *sizep = 0; 245cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden *bufp = NULL; 246cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden stream->bufp = bufp; 247cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden stream->sizep = sizep; 248cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 249cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden return fp; 250cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden} 251cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 252cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden#else /*not HAVE_FUNOPEN*/ 253cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFaddenFILE* open_memstream(char** bufp, size_t* sizep) 254cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden{ 255cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden abort(); 256cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden} 257cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden#endif /*HAVE_FUNOPEN*/ 258cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 259cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 260cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 261cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden#if 0 262cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden#define _GNU_SOURCE 263cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden#include <stdio.h> 264cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden#include <stdlib.h> 265cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden#include <string.h> 266cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 267cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden/* 268cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * Simple regression test. 269cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * 270cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * To test on desktop Linux with valgrind, it's possible to make a simple 271cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * change to open_memstream() to use fopencookie instead: 272cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * 273cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * cookie_io_functions_t iofuncs = 274cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * { NULL, write_memstream, seek_memstream, close_memstream }; 275cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * fp = fopencookie(stream, "w", iofuncs); 276cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * 277cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * (Some tweaks to seek_memstream are also required, as that takes a 278cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden * pointer to an offset rather than an offset, and returns 0 or -1.) 279cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden */ 280cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFaddenint testMemStream(void) 281cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden{ 282cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden FILE *stream; 283cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden char *buf; 284cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden size_t len; 285cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden off_t eob; 286cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 287cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden printf("Test1\n"); 288cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 289cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden /* std example */ 290cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden stream = open_memstream(&buf, &len); 291cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden fprintf(stream, "hello my world"); 292cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden fflush(stream); 293cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden printf("buf=%s, len=%zu\n", buf, len); 294cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden eob = ftello(stream); 295cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden fseeko(stream, 0, SEEK_SET); 296cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden fprintf(stream, "good-bye"); 297cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden fseeko(stream, eob, SEEK_SET); 298cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden fclose(stream); 299cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden printf("buf=%s, len=%zu\n", buf, len); 300cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden free(buf); 301cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 302cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden printf("Test2\n"); 303cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 304cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden /* std example without final seek-to-end */ 305cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden stream = open_memstream(&buf, &len); 306cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden fprintf(stream, "hello my world"); 307cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden fflush(stream); 308cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden printf("buf=%s, len=%zu\n", buf, len); 309cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden eob = ftello(stream); 310cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden fseeko(stream, 0, SEEK_SET); 311cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden fprintf(stream, "good-bye"); 312cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden //fseeko(stream, eob, SEEK_SET); 313cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden fclose(stream); 314cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden printf("buf=%s, len=%zu\n", buf, len); 315cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden free(buf); 316cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 317cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden printf("Test3\n"); 318cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 319cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden /* fancy example; should expand buffer with writes */ 320cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden static const int kCmpLen = 1024 + 128; 321cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden char* cmp = malloc(kCmpLen); 322cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden memset(cmp, 0, 1024); 323cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden memset(cmp+1024, 0xff, kCmpLen-1024); 324cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden sprintf(cmp, "This-is-a-tes1234"); 325cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden sprintf(cmp + 1022, "abcdef"); 326cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 327cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden stream = open_memstream (&buf, &len); 328cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden setvbuf(stream, NULL, _IONBF, 0); /* note: crashes in glibc with this */ 329cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden fprintf(stream, "This-is-a-test"); 330cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden fseek(stream, -1, SEEK_CUR); /* broken in glibc; can use {13,SEEK_SET} */ 331cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden fprintf(stream, "1234"); 332cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden fseek(stream, 1022, SEEK_SET); 333cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden fputc('a', stream); 334cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden fputc('b', stream); 335cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden fputc('c', stream); 336cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden fputc('d', stream); 337cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden fputc('e', stream); 338cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden fputc('f', stream); 339cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden fflush(stream); 340cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 341cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden if (memcmp(buf, cmp, len+1) != 0) { 342cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden printf("mismatch\n"); 343cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden } else { 344cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden printf("match\n"); 345cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden } 346cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 347cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden printf("Test4\n"); 348cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden stream = open_memstream (&buf, &len); 349cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden fseek(stream, 5000, SEEK_SET); 350cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden fseek(stream, 4096, SEEK_SET); 351cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden fseek(stream, -1, SEEK_SET); /* should have no effect */ 352cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden fputc('x', stream); 353cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden if (ftell(stream) == 4097) 354cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden printf("good\n"); 355cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden else 356cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden printf("BAD: offset is %ld\n", ftell(stream)); 357cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 358cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden printf("DONE\n"); 359cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 360cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden return 0; 361cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden} 362cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 363cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden/* expected output: 364cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFaddenTest1 365cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFaddenbuf=hello my world, len=14 366cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFaddenbuf=good-bye world, len=14 367cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFaddenTest2 368cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFaddenbuf=hello my world, len=14 369cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFaddenbuf=good-bye, len=8 370cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFaddenTest3 371cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFaddenmatch 372cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFaddenTest4 373cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFaddengood 374cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFaddenDONE 375cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden*/ 376cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 377cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden#endif 378cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden 379cf63d5d00f5a631a2905da7812b5c029b5211cf6Andy McFadden#endif /*!HAVE_OPEN_MEMSTREAM*/ 380