From 213b3636bb2989f2429f6eaaebc14e0541541753 Mon Sep 17 00:00:00 2001 From: Jonas 'Sortie' Termansen Date: Thu, 11 Jun 2015 21:48:37 +0200 Subject: [PATCH] Add open_memstream(3). --- libc/Makefile | 1 + libc/include/stdio.h | 2 +- libc/stdio/open_memstream.cpp | 122 ++++++++++++++++++++++++++++++++++ 3 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 libc/stdio/open_memstream.cpp diff --git a/libc/Makefile b/libc/Makefile index 59b1d496..bd50424f 100644 --- a/libc/Makefile +++ b/libc/Makefile @@ -132,6 +132,7 @@ stdio/fwrite.o \ stdio/fwrite_unlocked.o \ stdio/getdelim.o \ stdio/getline.o \ +stdio/open_memstream.o \ stdio/rewind.o \ stdio/setbuf.o \ stdio/setvbuf.o \ diff --git a/libc/include/stdio.h b/libc/include/stdio.h index b34ed77a..b9fe3290 100644 --- a/libc/include/stdio.h +++ b/libc/include/stdio.h @@ -242,7 +242,7 @@ int dprintf(int fildes, const char* __restrict format, ...) __attribute__((__format__ (printf, 2, 3))); FILE* fmemopen(void* __restrict, size_t, const char* __restrict); ssize_t getdelim(char** __restrict lineptr, size_t* __restrict n, int delimiter, FILE* __restrict stream); -/* TODO: open_memstream */ +FILE* open_memstream(char**, size_t*); ssize_t getline(char** __restrict lineptr, size_t* __restrict n, FILE* __restrict stream); int renameat(int oldfd, const char* oldname, int newfd, const char* newname); int vdprintf(int fildes, const char* __restrict format, __gnuc_va_list ap) diff --git a/libc/stdio/open_memstream.cpp b/libc/stdio/open_memstream.cpp new file mode 100644 index 00000000..1ac30ee1 --- /dev/null +++ b/libc/stdio/open_memstream.cpp @@ -0,0 +1,122 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2015. + + This file is part of the Sortix C Library. + + The Sortix C Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + + The Sortix C Library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with the Sortix C Library. If not, see . + + libc/stdio/open_memstream.cpp + Open a memory stream. + +*******************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +struct memstream +{ + char** string_out; + size_t* used_out; + char* string; + size_t offset; + size_t used; + size_t size; +}; + +static ssize_t memstream_write(void* ms_ptr, const void* src, size_t count) +{ + struct memstream* ms = (struct memstream*) ms_ptr; + if ( (size_t) SSIZE_MAX < count ) + count = SSIZE_MAX; + if ( ms->offset == SIZE_MAX && 1 <= count ) + return errno = EOVERFLOW, -1; + if ( (size_t) SSIZE_MAX - ms->offset < count ) + count = SIZE_MAX - ms->offset; + size_t required_size = ms->offset + count; + if ( ms->size < required_size ) + { + // TODO: Multiplication overflow. + size_t new_size = 2 * ms->size; + if ( new_size < 16 ) + new_size = 16; + if ( new_size < required_size ) + new_size = required_size; + if ( SIZE_MAX - new_size < 1 ) + return errno = EOVERFLOW, -1; + char* new_string = (char*) realloc(ms->string, new_size + 1); + if ( !new_string ) + return -1; + ms->string = new_string; + ms->size = new_size; + } + memcpy(ms->string + ms->offset, src, count); + ms->offset += count; + if ( ms->used < required_size ) + { + ms->used = required_size; + ms->string[ms->used] = '\0'; + } + *ms->string_out = ms->string; + *ms->used_out = ms->used; + return count; +} + +static off_t memstream_seek(void* ms_ptr, off_t offset, int whence) +{ + struct memstream* ms = (struct memstream*) ms_ptr; + off_t base; + switch ( whence ) + { + case SEEK_SET: base = 0; break; + case SEEK_CUR: base = (off_t) ms->offset; break; + case SEEK_END: base = (off_t) ms->used; break; + default: return errno = EINVAL, -1; + } + if ( offset < -base || base - SSIZE_MAX < offset ) + return errno = EOVERFLOW, -1; + return (off_t) (ms->offset = (size_t) (base + offset)); +} + +extern "C" FILE* open_memstream(char** string_out, size_t* used_out) +{ + char* string = (char*) malloc(1); + if ( !string ) + return NULL; + string[0] = '\0'; + struct memstream* ms = (struct memstream*) malloc(sizeof(struct memstream)); + if ( !ms ) + return free(string), (FILE*) NULL; + FILE* fp = fnewfile(); + if ( !fp ) + return free(ms), free(string), (FILE*) NULL; + ms->string_out = string_out; + ms->used_out = used_out; + ms->string= string; + ms->offset = 0; + ms->used = 0; + ms->size = 0; + fp->flags |= _FILE_WRITABLE; + fp->user = ms; + fp->write_func = memstream_write; + fp->seek_func = memstream_seek; + *string_out = ms->string; + *used_out = ms->used; + return fp; +}