1 /*
2  * Copyright (c) 2013 Travis Geiselbrecht
3  *
4  * Use of this source code is governed by a MIT-style
5  * license that can be found in the LICENSE file or at
6  * https://opensource.org/licenses/MIT
7  */
8 #include <lk/debug.h>
9 #include <lk/err.h>
10 #include <stdio.h>
11 #include <stdarg.h>
12 #include <string.h>
13 #include <stdlib.h>
14 #include <sys/types.h>
15 #include <platform/debug.h>
16 
17 #if defined(WITH_LIB_FS)
18 #define STDIO_FIELDS .use_fs = false,
19 #else
20 #define STDIO_FIELDS
21 #endif // WITH_LIB_FS
22 
23 #define DEFINE_STDIO_DESC(id)   \
24     [(id)]  = {                 \
25         .io = &console_io,      \
26         STDIO_FIELDS            \
27     }
28 
29 // The three main standard io file descriptors that just
30 // point to the main console.
31 FILE __stdio_FILEs[3] = {
32     DEFINE_STDIO_DESC(0), /* stdin */
33     DEFINE_STDIO_DESC(1), /* stdout */
34     DEFINE_STDIO_DESC(2), /* stderr */
35 };
36 #undef DEFINE_STDIO_DESC
37 
fopen(const char * filename,const char * mode)38 FILE *fopen(const char *filename, const char *mode) {
39 #if defined(WITH_LIB_FS)
40     FILE *stream = (FILE *) malloc(sizeof(FILE));
41     if (stream == NULL) {
42         return NULL;
43     }
44 
45     // TODO: handle more open modes
46 
47     stream->use_fs = true;
48     stream->fs_handle.offset = 0;
49     stream->fs_handle.readonly = (!strchr(mode, 'w') && !strchr(mode, 'a'));
50 
51     status_t ret = fs_open_file(filename, &(stream->fs_handle.handle));
52 
53     if (ret == ERR_NOT_FOUND && !stream->fs_handle.readonly) {
54         ret = fs_create_file(filename, &(stream->fs_handle.handle), 0);
55     }
56 
57     if (ret != NO_ERROR) {
58         free(stream);
59         return NULL;
60     }
61 
62     if (strchr(mode, 'a')) {
63         struct file_stat stat;
64         if (NO_ERROR == fs_stat_file(stream->fs_handle.handle, &stat)) {
65             stream->fs_handle.offset = stat.size;
66         }
67     }
68 
69     return stream;
70 #endif // WITH_LIB_FS
71     return NULL;
72 }
73 
fclose(FILE * stream)74 int fclose(FILE *stream) {
75 #if defined(WITH_LIB_FS)
76     if (stream && !stream->use_fs) {
77         fs_close_file(stream->fs_handle.handle);
78         free(stream);
79     }
80 #endif // WITH_LIB_FS
81     return 0;
82 }
83 
84 // Inner version of fread that returns a ssize_t to include the internal error code
85 // along with the length.
fread_error(void * ptr,size_t size,size_t count,FILE * stream)86 static ssize_t fread_error(void *ptr, size_t size, size_t count, FILE *stream) {
87     if (count == 0 || size == 0) {
88         return 0;
89     }
90 #if defined(WITH_LIB_FS)
91     if (stream->use_fs) {
92         ssize_t rsize = fs_read_file(stream->fs_handle.handle, ptr,
93                              stream->fs_handle.offset, size * count);
94         if (rsize <= 0) {
95             return rsize;
96         }
97         stream->fs_handle.offset += rsize;
98         return rsize / size;
99     }
100 #endif // WITH_LIB_FS
101     ssize_t rsize = io_read(stream->io, ptr, size * count);
102     if (rsize <= 0) {
103         return rsize;
104     }
105     return rsize / size;
106 }
107 
108 // Inner version of fwrite that returns a ssize_t to include the internal error code
109 // along with the length.
fwrite_error(const void * ptr,size_t size,size_t count,FILE * stream)110 static ssize_t fwrite_error(const void *ptr, size_t size, size_t count, FILE *stream) {
111     if (count == 0 || size == 0) {
112         return 0;
113     }
114 #if defined(WITH_LIB_FS)
115     if (stream->use_fs) {
116         if (stream->fs_handle.readonly) {
117             // return error here?
118             return 0;
119         }
120 
121         // TODO: deal with append
122 
123         ssize_t wsize = fs_write_file(stream->fs_handle.handle, ptr,
124                                      stream->fs_handle.offset, size * count);
125         if (wsize <= 0) {
126             return wsize;
127         }
128         stream->fs_handle.offset += wsize;
129 
130         return wsize / size;
131     }
132 #endif
133     ssize_t wsize = io_write(stream->io, ptr, size * count);
134     if (wsize <= 0) {
135         return wsize;
136     }
137     return wsize / size;
138 }
139 
fread(void * ptr,size_t size,size_t count,FILE * stream)140 size_t fread(void *ptr, size_t size, size_t count, FILE *stream) {
141     ssize_t read = fread_error(ptr, size, count, stream);
142     if (read < 0) {
143         // TODO: save error for ferror()
144         return 0;
145     }
146     return read;
147 }
148 
fwrite(const void * ptr,size_t size,size_t count,FILE * stream)149 size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream) {
150     ssize_t written = fwrite_error(ptr, size, count, stream);
151     if (written < 0) {
152         // TODO: save error for ferror()
153         return 0;
154     }
155     return written;
156 }
157 
fflush(FILE * stream)158 int fflush(FILE *stream) {
159     return 0;
160 }
161 
feof(FILE * stream)162 int feof(FILE *stream) {
163 #if defined(WITH_LIB_FS)
164     if (!stream->use_fs) {
165         return 0;
166     }
167     struct file_stat stat;
168     if (NO_ERROR != fs_stat_file(stream->fs_handle.handle, &stat)) {
169         return 1;
170     }
171     return (uint64_t)stream->fs_handle.offset >= stat.size;
172 #endif // WITH_LIB_FS
173     return 0;
174 }
175 
clamp(off_t val,off_t min,off_t max)176 static inline off_t clamp(off_t val, off_t min, off_t max) {
177     const off_t t = (val < min) ? min : val;
178     return (t > max) ? max : t;
179 }
180 
fseek(FILE * stream,long offset,int whence)181 int fseek(FILE *stream, long offset, int whence) {
182 #if defined(WITH_LIB_FS)
183     if (!stream->use_fs) {
184         return 0;
185     }
186     struct file_stat stat;
187     if (NO_ERROR != fs_stat_file(stream->fs_handle.handle, &stat)) {
188         return -1;
189     }
190 
191     switch (whence) {
192         case SEEK_SET:
193             stream->fs_handle.offset = clamp(offset, 0, stat.size);
194             break;
195         case SEEK_CUR:
196             stream->fs_handle.offset = clamp(stream->fs_handle.offset + offset, 0, stat.size);
197             break;
198         case SEEK_END:
199             stream->fs_handle.offset = clamp(stat.size - offset, 0, stat.size);
200             break;
201         default:
202             return -1;
203     }
204 #endif // WITH_LIB_FS
205     return 0;
206 }
207 
ftell(FILE * stream)208 long ftell(FILE *stream) {
209 #if defined(WITH_LIB_FS)
210     if (!stream->use_fs) {
211         return 0;
212     }
213     return stream->fs_handle.offset;
214 #endif // WITH_LIB_FS
215     return 0;
216 }
217 
fputc(int _c,FILE * fp)218 int fputc(int _c, FILE *fp) {
219     unsigned char c = _c;
220 
221     size_t written = fwrite(&c, /*size=*/1, /*count=*/1, fp);
222     if (written == 0) {
223         return EOF;
224     }
225     return c;
226 }
227 
putchar(int c)228 int putchar(int c) {
229     return fputc(c, stdout);
230 }
231 
puts(const char * str)232 int puts(const char *str) {
233     int err = fputs(str, stdout);
234     if (err >= 0) {
235         err = fputc('\n', stdout);
236     }
237     return err;
238 }
239 
fputs(const char * s,FILE * fp)240 int fputs(const char *s, FILE *fp) {
241     size_t len = strlen(s);
242     if (len == 0) {
243         return 0;
244     }
245 
246     size_t written = fwrite(s, /*size=*/1, /*count=*/len, fp);
247     if (written == 0) {
248         return EOF;
249     }
250     return written;
251 }
252 
fgetc(FILE * fp)253 int fgetc(FILE *fp) {
254     char c;
255 
256     size_t err = fread(&c, /*size=*/1, /*count=*/1, fp);
257     if (err == 0) {
258         return EOF;
259     }
260     return c;
261 }
262 
getchar(void)263 int getchar(void) {
264     return getc(stdin);
265 }
266 
fgets(char * s,int size,FILE * stream)267 char *fgets(char *s, int size, FILE *stream) {
268     int c = -1;
269     char *cs = s;
270 
271     if (size < 1) {
272         return NULL;
273     }
274 
275     while (--size > 0 && (c = fgetc(stream)) != EOF) {
276         *cs++ = c;
277         if (c == '\n') {
278             break;
279         }
280     }
281 
282     *cs = '\0';
283     return (c == EOF && cs == s) ? NULL : s;
284 }
285 
_fprintf_output_func(const char * str,size_t len,void * state)286 int _fprintf_output_func(const char *str, size_t len, void *state) {
287     FILE *fp = (FILE *)state;
288 
289     return fwrite_error(str, /*size=*/1, /*count=*/len, fp);
290 }
291