1 /*
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright © 2022 Keith Packard
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above
14  *    copyright notice, this list of conditions and the following
15  *    disclaimer in the documentation and/or other materials provided
16  *    with the distribution.
17  *
18  * 3. Neither the name of the copyright holder nor the names of its
19  *    contributors may be used to endorse or promote products derived
20  *    from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
27  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
33  * OF THE POSSIBILITY OF SUCH DAMAGE.
34  */
35 
36 #include "stdio_private.h"
37 #include <stdlib.h>
38 #include <fcntl.h>
39 #include <unistd.h>
40 
41 #define __MALL          0x01
42 
43 struct __file_mem {
44         struct __file_ext xfile;
45         char    *buf;
46         size_t  size;
47         size_t  pos;
48         uint8_t mflags;
49 };
50 
__fmem_put(char c,FILE * f)51 static int __fmem_put(char c, FILE *f)
52 {
53         struct __file_mem *mf = (struct __file_mem *) f;
54         if ((f->flags & __SWR) && mf->pos < mf->size) {
55                 mf->buf[mf->pos++] = c;
56                 return (unsigned char) c;
57         }
58         return _FDEV_ERR;
59 }
60 
__fmem_get(FILE * f)61 static int __fmem_get(FILE *f)
62 {
63         struct __file_mem *mf = (struct __file_mem *) f;
64         int c;
65         if ((f->flags & __SRD) && mf->pos < mf->size) {
66                 c = (unsigned char) mf->buf[mf->pos++];
67                 if (c == '\0')
68                         c = _FDEV_EOF;
69         } else
70                 c = _FDEV_ERR;
71         return c;
72 }
73 
__fmem_flush(FILE * f)74 static int __fmem_flush(FILE *f)
75 {
76         struct __file_mem *mf = (struct __file_mem *) f;
77         if ((f->flags & __SWR) && mf->pos < mf->size)
78                 mf->buf[mf->pos] = '\0';
79         return 0;
80 }
81 
__fmem_seek(FILE * f,off_t pos,int whence)82 static off_t __fmem_seek(FILE *f, off_t pos, int whence)
83 {
84         struct __file_mem *mf = (struct __file_mem *) f;
85 
86         switch (whence) {
87         case SEEK_SET:
88                 break;
89         case SEEK_CUR:
90                 pos += mf->pos;
91                 break;
92         case SEEK_END:
93                 pos += mf->size;
94                 break;
95         }
96         if (pos < 0 || mf->size < (size_t) pos)
97                 return EOF;
98         mf->pos = pos;
99         return pos;
100 }
101 
__fmem_close(FILE * f)102 static int __fmem_close(FILE *f)
103 {
104         struct __file_mem *mf = (struct __file_mem *) f;
105 
106         if (mf->mflags & __MALL)
107                 free (mf->buf);
108         else
109                 __fmem_flush(f);
110         free(f);
111         return 0;
112 }
113 
114 FILE *
fmemopen(void * buf,size_t size,const char * mode)115 fmemopen(void *buf, size_t size, const char *mode)
116 {
117 	int stdio_flags;
118         uint8_t mflags = 0;
119 	int open_flags;
120 	struct __file_mem *mf;
121 
122 	stdio_flags = __posix_sflags(mode, &open_flags);
123 	if (stdio_flags == 0)
124 		return NULL;
125 
126 	/* Allocate file structure and necessary buffers */
127 	mf = calloc(1, sizeof(struct __file_mem));
128 
129 	if (mf == NULL)
130 		return NULL;
131 
132         if (buf == NULL) {
133                 buf = malloc(size);
134                 if (!buf) {
135                         free(mf);
136                         return NULL;
137                 }
138                 mflags |= __MALL;
139         }
140 
141         *mf = (struct __file_mem) {
142                 .xfile = FDEV_SETUP_EXT(__fmem_put, __fmem_get, __fmem_flush, __fmem_close,
143                                         __fmem_seek, NULL, stdio_flags),
144                 .buf = buf,
145                 .size = size,
146                 .pos = 0,
147                 .mflags = mflags,
148         };
149 
150 	return &(mf->xfile.cfile.file);
151 }
152