xref: /aosp_15_r20/bionic/libc/stdio/fmemopen.cpp (revision 8d67ca893c1523eb926b9080dbe4e2ffd2a27ba1)
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