1*8d67ca89SAndroid Build Coastguard Worker /*-
2*8d67ca89SAndroid Build Coastguard Worker * Copyright (C) 2013 Pietro Cerutti <[email protected]>
3*8d67ca89SAndroid Build Coastguard Worker *
4*8d67ca89SAndroid Build Coastguard Worker * Redistribution and use in source and binary forms, with or without
5*8d67ca89SAndroid Build Coastguard Worker * modification, are permitted provided that the following conditions
6*8d67ca89SAndroid Build Coastguard Worker * are met:
7*8d67ca89SAndroid Build Coastguard Worker * 1. Redistributions of source code must retain the above copyright
8*8d67ca89SAndroid Build Coastguard Worker * notice, this list of conditions and the following disclaimer.
9*8d67ca89SAndroid Build Coastguard Worker * 2. Redistributions in binary form must reproduce the above copyright
10*8d67ca89SAndroid Build Coastguard Worker * notice, this list of conditions and the following disclaimer in the
11*8d67ca89SAndroid Build Coastguard Worker * documentation and/or other materials provided with the distribution.
12*8d67ca89SAndroid Build Coastguard Worker *
13*8d67ca89SAndroid Build Coastguard Worker * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14*8d67ca89SAndroid Build Coastguard Worker * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15*8d67ca89SAndroid Build Coastguard Worker * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16*8d67ca89SAndroid Build Coastguard Worker * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
17*8d67ca89SAndroid Build Coastguard Worker * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18*8d67ca89SAndroid Build Coastguard Worker * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19*8d67ca89SAndroid Build Coastguard Worker * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20*8d67ca89SAndroid Build Coastguard Worker * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21*8d67ca89SAndroid Build Coastguard Worker * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22*8d67ca89SAndroid Build Coastguard Worker * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23*8d67ca89SAndroid Build Coastguard Worker * SUCH DAMAGE.
24*8d67ca89SAndroid Build Coastguard Worker */
25*8d67ca89SAndroid Build Coastguard Worker
26*8d67ca89SAndroid Build Coastguard Worker #include <sys/cdefs.h>
27*8d67ca89SAndroid Build Coastguard Worker
28*8d67ca89SAndroid Build Coastguard Worker #include <errno.h>
29*8d67ca89SAndroid Build Coastguard Worker #include <fcntl.h>
30*8d67ca89SAndroid Build Coastguard Worker #include <stdio.h>
31*8d67ca89SAndroid Build Coastguard Worker #include <stdlib.h>
32*8d67ca89SAndroid Build Coastguard Worker #include <string.h>
33*8d67ca89SAndroid Build Coastguard Worker
34*8d67ca89SAndroid Build Coastguard Worker #include "local.h"
35*8d67ca89SAndroid Build Coastguard Worker
36*8d67ca89SAndroid Build Coastguard Worker // See https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/fmemopen.html
37*8d67ca89SAndroid Build Coastguard Worker // and https://man7.org/linux/man-pages/man3/fmemopen.3.html for documentation.
38*8d67ca89SAndroid Build Coastguard Worker
39*8d67ca89SAndroid Build Coastguard Worker struct fmemopen_cookie {
40*8d67ca89SAndroid Build Coastguard Worker char* buf;
41*8d67ca89SAndroid Build Coastguard Worker char* allocation;
42*8d67ca89SAndroid Build Coastguard Worker size_t capacity;
43*8d67ca89SAndroid Build Coastguard Worker size_t size;
44*8d67ca89SAndroid Build Coastguard Worker size_t offset;
45*8d67ca89SAndroid Build Coastguard Worker bool append;
46*8d67ca89SAndroid Build Coastguard Worker };
47*8d67ca89SAndroid Build Coastguard Worker
fmemopen_read(void * cookie,char * buf,int n)48*8d67ca89SAndroid Build Coastguard Worker static int fmemopen_read(void* cookie, char* buf, int n) {
49*8d67ca89SAndroid Build Coastguard Worker fmemopen_cookie* ck = static_cast<fmemopen_cookie*>(cookie);
50*8d67ca89SAndroid Build Coastguard Worker
51*8d67ca89SAndroid Build Coastguard Worker if (static_cast<size_t>(n) > ck->size - ck->offset) n = ck->size - ck->offset;
52*8d67ca89SAndroid Build Coastguard Worker
53*8d67ca89SAndroid Build Coastguard Worker if (n > 0) {
54*8d67ca89SAndroid Build Coastguard Worker memmove(buf, ck->buf + ck->offset, n);
55*8d67ca89SAndroid Build Coastguard Worker ck->offset += n;
56*8d67ca89SAndroid Build Coastguard Worker }
57*8d67ca89SAndroid Build Coastguard Worker return n;
58*8d67ca89SAndroid Build Coastguard Worker }
59*8d67ca89SAndroid Build Coastguard Worker
fmemopen_write(void * cookie,const char * buf,int n)60*8d67ca89SAndroid Build Coastguard Worker static int fmemopen_write(void* cookie, const char* buf, int n) {
61*8d67ca89SAndroid Build Coastguard Worker fmemopen_cookie* ck = static_cast<fmemopen_cookie*>(cookie);
62*8d67ca89SAndroid Build Coastguard Worker
63*8d67ca89SAndroid Build Coastguard Worker // We don't need to add the trailing NUL if there's already a trailing NUL
64*8d67ca89SAndroid Build Coastguard Worker // in the data we're writing.
65*8d67ca89SAndroid Build Coastguard Worker size_t space_for_null = (n > 0 && buf[n - 1] != '\0') ? 1 : 0;
66*8d67ca89SAndroid Build Coastguard Worker
67*8d67ca89SAndroid Build Coastguard Worker // Undo any seeking/reading on an append-only stream.
68*8d67ca89SAndroid Build Coastguard Worker if (ck->append) ck->offset = ck->size;
69*8d67ca89SAndroid Build Coastguard Worker
70*8d67ca89SAndroid Build Coastguard Worker // How much can we actually fit?
71*8d67ca89SAndroid Build Coastguard Worker if (static_cast<size_t>(n) + space_for_null > ck->capacity - ck->offset) {
72*8d67ca89SAndroid Build Coastguard Worker n = ck->capacity - ck->offset - space_for_null;
73*8d67ca89SAndroid Build Coastguard Worker // Give up if we don't even have room for one byte of userdata.
74*8d67ca89SAndroid Build Coastguard Worker if (n <= 0) {
75*8d67ca89SAndroid Build Coastguard Worker errno = ENOSPC;
76*8d67ca89SAndroid Build Coastguard Worker return -1;
77*8d67ca89SAndroid Build Coastguard Worker }
78*8d67ca89SAndroid Build Coastguard Worker }
79*8d67ca89SAndroid Build Coastguard Worker
80*8d67ca89SAndroid Build Coastguard Worker if (n > 0) {
81*8d67ca89SAndroid Build Coastguard Worker memmove(ck->buf + ck->offset, buf, n);
82*8d67ca89SAndroid Build Coastguard Worker ck->offset += n;
83*8d67ca89SAndroid Build Coastguard Worker // Is this the furthest we've ever been?
84*8d67ca89SAndroid Build Coastguard Worker if (ck->offset >= ck->size) {
85*8d67ca89SAndroid Build Coastguard Worker if (buf[n - 1] != '\0') ck->buf[ck->offset] = '\0';
86*8d67ca89SAndroid Build Coastguard Worker ck->size = ck->offset;
87*8d67ca89SAndroid Build Coastguard Worker }
88*8d67ca89SAndroid Build Coastguard Worker }
89*8d67ca89SAndroid Build Coastguard Worker return n;
90*8d67ca89SAndroid Build Coastguard Worker }
91*8d67ca89SAndroid Build Coastguard Worker
fmemopen_seek(void * cookie,fpos_t offset,int whence)92*8d67ca89SAndroid Build Coastguard Worker static fpos_t fmemopen_seek(void* cookie, fpos_t offset, int whence) {
93*8d67ca89SAndroid Build Coastguard Worker fmemopen_cookie* ck = static_cast<fmemopen_cookie*>(cookie);
94*8d67ca89SAndroid Build Coastguard Worker
95*8d67ca89SAndroid Build Coastguard Worker if (whence == SEEK_SET && (offset >= 0 && static_cast<size_t>(offset) <= ck->capacity)) {
96*8d67ca89SAndroid Build Coastguard Worker return (ck->offset = offset);
97*8d67ca89SAndroid Build Coastguard Worker } else if (whence == SEEK_CUR && (ck->offset + offset <= ck->capacity)) {
98*8d67ca89SAndroid Build Coastguard Worker return (ck->offset += offset);
99*8d67ca89SAndroid Build Coastguard Worker } else if (whence == SEEK_END && (offset <= 0 && static_cast<size_t>(-offset) <= ck->size)) {
100*8d67ca89SAndroid Build Coastguard Worker return (ck->offset = ck->size + offset);
101*8d67ca89SAndroid Build Coastguard Worker }
102*8d67ca89SAndroid Build Coastguard Worker errno = EINVAL;
103*8d67ca89SAndroid Build Coastguard Worker return -1;
104*8d67ca89SAndroid Build Coastguard Worker }
105*8d67ca89SAndroid Build Coastguard Worker
fmemopen_close(void * cookie)106*8d67ca89SAndroid Build Coastguard Worker static int fmemopen_close(void* cookie) {
107*8d67ca89SAndroid Build Coastguard Worker fmemopen_cookie* ck = static_cast<fmemopen_cookie*>(cookie);
108*8d67ca89SAndroid Build Coastguard Worker free(ck->allocation);
109*8d67ca89SAndroid Build Coastguard Worker free(ck);
110*8d67ca89SAndroid Build Coastguard Worker return 0;
111*8d67ca89SAndroid Build Coastguard Worker }
112*8d67ca89SAndroid Build Coastguard Worker
fmemopen(void * buf,size_t capacity,const char * mode)113*8d67ca89SAndroid Build Coastguard Worker FILE* fmemopen(void* buf, size_t capacity, const char* mode) {
114*8d67ca89SAndroid Build Coastguard Worker int flags;
115*8d67ca89SAndroid Build Coastguard Worker if (__sflags(mode, &flags) == 0) {
116*8d67ca89SAndroid Build Coastguard Worker errno = EINVAL;
117*8d67ca89SAndroid Build Coastguard Worker return nullptr;
118*8d67ca89SAndroid Build Coastguard Worker }
119*8d67ca89SAndroid Build Coastguard Worker
120*8d67ca89SAndroid Build Coastguard Worker fmemopen_cookie* ck = static_cast<fmemopen_cookie*>(calloc(sizeof(fmemopen_cookie), 1));
121*8d67ca89SAndroid Build Coastguard Worker if (ck == nullptr) return nullptr;
122*8d67ca89SAndroid Build Coastguard Worker
123*8d67ca89SAndroid Build Coastguard Worker ck->buf = static_cast<char*>(buf);
124*8d67ca89SAndroid Build Coastguard Worker ck->capacity = capacity;
125*8d67ca89SAndroid Build Coastguard Worker
126*8d67ca89SAndroid Build Coastguard Worker if (ck->buf == nullptr) ck->buf = ck->allocation = static_cast<char*>(calloc(capacity, 1));
127*8d67ca89SAndroid Build Coastguard Worker if (ck->buf == nullptr) {
128*8d67ca89SAndroid Build Coastguard Worker free(ck);
129*8d67ca89SAndroid Build Coastguard Worker return nullptr;
130*8d67ca89SAndroid Build Coastguard Worker }
131*8d67ca89SAndroid Build Coastguard Worker
132*8d67ca89SAndroid Build Coastguard Worker FILE* fp = funopen(ck,
133*8d67ca89SAndroid Build Coastguard Worker (flags & O_WRONLY) ? nullptr : fmemopen_read,
134*8d67ca89SAndroid Build Coastguard Worker (flags & O_RDONLY) ? nullptr : fmemopen_write,
135*8d67ca89SAndroid Build Coastguard Worker fmemopen_seek,
136*8d67ca89SAndroid Build Coastguard Worker fmemopen_close);
137*8d67ca89SAndroid Build Coastguard Worker if (fp == nullptr) {
138*8d67ca89SAndroid Build Coastguard Worker fmemopen_close(ck);
139*8d67ca89SAndroid Build Coastguard Worker return nullptr;
140*8d67ca89SAndroid Build Coastguard Worker }
141*8d67ca89SAndroid Build Coastguard Worker
142*8d67ca89SAndroid Build Coastguard Worker if (mode[0] == 'a') {
143*8d67ca89SAndroid Build Coastguard Worker ck->size = strnlen(ck->buf, ck->capacity);
144*8d67ca89SAndroid Build Coastguard Worker ck->offset = ck->size;
145*8d67ca89SAndroid Build Coastguard Worker ck->append = true;
146*8d67ca89SAndroid Build Coastguard Worker } else if (mode[0] == 'r') {
147*8d67ca89SAndroid Build Coastguard Worker ck->size = capacity;
148*8d67ca89SAndroid Build Coastguard Worker ck->offset = 0;
149*8d67ca89SAndroid Build Coastguard Worker } else if (mode[0] == 'w') {
150*8d67ca89SAndroid Build Coastguard Worker ck->size = 0;
151*8d67ca89SAndroid Build Coastguard Worker ck->offset = 0;
152*8d67ca89SAndroid Build Coastguard Worker if (capacity > 0) {
153*8d67ca89SAndroid Build Coastguard Worker ck->buf[0] = '\0';
154*8d67ca89SAndroid Build Coastguard Worker }
155*8d67ca89SAndroid Build Coastguard Worker }
156*8d67ca89SAndroid Build Coastguard Worker
157*8d67ca89SAndroid Build Coastguard Worker return fp;
158*8d67ca89SAndroid Build Coastguard Worker }
159