13a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes/*- 23a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes * Copyright (C) 2013 Pietro Cerutti <gahr@FreeBSD.org> 33a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes * 43a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes * Redistribution and use in source and binary forms, with or without 53a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes * modification, are permitted provided that the following conditions 63a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes * are met: 73a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes * 1. Redistributions of source code must retain the above copyright 83a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes * notice, this list of conditions and the following disclaimer. 93a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes * 2. Redistributions in binary form must reproduce the above copyright 103a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes * notice, this list of conditions and the following disclaimer in the 113a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes * documentation and/or other materials provided with the distribution. 123a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes * 133a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 143a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 153a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 163a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 173a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 183a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 193a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 203a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 213a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 223a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 233a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes * SUCH DAMAGE. 243a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes */ 253a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes 263a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes#include <sys/cdefs.h> 273a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes 283a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes#include <errno.h> 293a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes#include <fcntl.h> 303a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes#include <stdio.h> 313a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes#include <stdlib.h> 323a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes#include <string.h> 333a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes 343a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes#include "local.h" 353a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes 363a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes// See http://pubs.opengroup.org/onlinepubs/9699919799/functions/fmemopen.html 373a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes// and http://man7.org/linux/man-pages/man3/fmemopen.3.html for documentation. 383a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes 393a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughesstruct fmemopen_cookie { 403a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes char* buf; 413a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes char* allocation; 423a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes size_t capacity; 433a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes size_t size; 443a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes size_t offset; 453a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes bool append; 463a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes}; 473a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes 483a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughesstatic int fmemopen_read(void* cookie, char* buf, int n) { 493a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes fmemopen_cookie* ck = static_cast<fmemopen_cookie*>(cookie); 503a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes 513a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes if (static_cast<size_t>(n) > ck->size - ck->offset) n = ck->size - ck->offset; 523a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes 533a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes if (n > 0) { 543a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes memmove(buf, ck->buf + ck->offset, n); 553a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes ck->offset += n; 563a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes } 573a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes return n; 583a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes} 593a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes 603a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughesstatic int fmemopen_write(void* cookie, const char* buf, int n) { 613a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes fmemopen_cookie* ck = static_cast<fmemopen_cookie*>(cookie); 623a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes 633a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes // We don't need to add the trailing NUL if there's already a trailing NUL 643a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes // in the data we're writing. 653a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes size_t space_for_null = (n > 0 && buf[n - 1] != '\0') ? 1 : 0; 663a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes 673a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes // Undo any seeking/reading on an append-only stream. 683a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes if (ck->append) ck->offset = ck->size; 693a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes 703a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes // How much can we actually fit? 713a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes if (static_cast<size_t>(n) + space_for_null > ck->capacity - ck->offset) { 723a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes n = ck->capacity - ck->offset - space_for_null; 733a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes // Give up if we don't even have room for one byte of userdata. 743a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes if (n <= 0) { 753a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes errno = ENOSPC; 763a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes return -1; 773a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes } 783a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes } 793a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes 803a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes if (n > 0) { 813a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes memmove(ck->buf + ck->offset, buf, n); 823a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes ck->offset += n; 833a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes // Is this the furthest we've ever been? 843a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes if (ck->offset >= ck->size) { 853a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes if (buf[n - 1] != '\0') ck->buf[ck->offset] = '\0'; 863a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes ck->size = ck->offset; 873a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes } 883a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes } 893a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes return n; 903a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes} 913a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes 923a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughesstatic fpos_t fmemopen_seek(void* cookie, fpos_t offset, int whence) { 933a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes fmemopen_cookie* ck = static_cast<fmemopen_cookie*>(cookie); 943a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes 953a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes if (whence == SEEK_SET && (offset >= 0 && static_cast<size_t>(offset) <= ck->capacity)) { 963a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes return (ck->offset = offset); 973a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes } else if (whence == SEEK_CUR && (ck->offset + offset <= ck->capacity)) { 983a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes return (ck->offset += offset); 993a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes } else if (whence == SEEK_END && (offset <= 0 && static_cast<size_t>(-offset) <= ck->size)) { 1003a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes return (ck->offset = ck->size + offset); 1013a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes } 1023a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes errno = EINVAL; 1033a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes return -1; 1043a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes} 1053a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes 1063a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughesstatic int fmemopen_close(void* cookie) { 1073a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes fmemopen_cookie* ck = static_cast<fmemopen_cookie*>(cookie); 1083a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes free(ck->allocation); 1093a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes free(ck); 1103a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes return 0; 1113a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes} 1123a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes 1133a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott HughesFILE* fmemopen(void* buf, size_t capacity, const char* mode) { 1143a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes int flags; 1153a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes if (__sflags(mode, &flags) == 0) { 1163a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes errno = EINVAL; 1173a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes return nullptr; 1183a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes } 1193a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes 1203a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes fmemopen_cookie* ck = static_cast<fmemopen_cookie*>(calloc(sizeof(fmemopen_cookie), 1)); 1213a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes if (ck == nullptr) return nullptr; 1223a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes 1233a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes ck->buf = static_cast<char*>(buf); 1243a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes ck->capacity = capacity; 1253a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes 1263a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes if (ck->buf == nullptr) ck->buf = ck->allocation = static_cast<char*>(calloc(capacity, 1)); 1273a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes if (ck->buf == nullptr) { 1283a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes free(ck); 1293a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes return nullptr; 1303a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes } 1313a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes 1323a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes FILE* fp = funopen(ck, 1333a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes (flags & O_WRONLY) ? nullptr : fmemopen_read, 1343a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes (flags & O_RDONLY) ? nullptr : fmemopen_write, 1353a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes fmemopen_seek, 1363a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes fmemopen_close); 1373a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes if (fp == nullptr) { 1383a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes fmemopen_close(ck); 1393a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes return nullptr; 1403a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes } 1413a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes 1423a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes if (mode[0] == 'a') { 1433a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes ck->size = strnlen(ck->buf, ck->capacity); 1443a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes ck->offset = ck->size; 1453a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes ck->append = true; 1463a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes } else if (mode[0] == 'r') { 1473a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes ck->size = capacity; 1483a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes ck->offset = 0; 1493a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes } else if (mode[0] == 'w') { 1503a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes ck->size = 0; 1513a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes ck->offset = 0; 1523a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes ck->buf[0] = '\0'; 1533a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes } 1543a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes 1553a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes return fp; 1563a4c45499e9b67799fdfe4693e0a4fbbb5e955e2Elliott Hughes} 157