Use macros to allow prefixing C runtime library symbols
[monolithium.git] / libraries / mlcrt / src / io.c
1 /*
2  * io.c
3  *
4  * Copyright (C) 2019 Aleksandar Andrejevic <theflash@sdf.lonestar.org>
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Affero General Public License as
8  * published by the Free Software Foundation, either version 3 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Affero General Public License for more details.
15  *
16  * You should have received a copy of the GNU Affero General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <stdio.h>
21 #include <unistd.h>
22 #include "crt_priv.h"
23 #include "io_priv.h"
24
25 static list_entry_t open_files;
26
27 FILE *stdin  = NULL;
28 FILE *stdout = NULL;
29 FILE *stderr = NULL;
30
31 int __CRT_PUBLIC(fileno_unlocked)(FILE *stream)
32 {
33     if (stream->fd == -1) __CRT_PUBLIC(errno) = EBADF;
34     return stream->fd;
35 }
36
37 int __CRT_PUBLIC(fileno)(FILE *stream)
38 {
39     syscall_wait_mutex(stream->mutex, NO_TIMEOUT);
40     int ret = __CRT_PUBLIC(fileno_unlocked)(stream);
41     syscall_release_mutex(stream->mutex);
42     return ret;
43 }
44
45 static int __CRT_PRIVATE(fflush_stream)(FILE *stream)
46 {
47     if (!stream->buffer) return EOF;
48
49     if (!(stream->flags & FILE_BUFFER_DIR))
50     {
51         stream->buffer_start = stream->buffer_end = 0;
52         stream->flags &= ~FILE_BUFFER_FULL;
53         return 0;
54     }
55
56     if (stream->fd == -1) return 0;
57
58     if (stream->buffer_start < stream->buffer_end)
59     {
60         ssize_t written = __CRT_PUBLIC(write)(stream->fd, &stream->buffer[stream->buffer_start], stream->buffer_end - stream->buffer_start);
61         if (written < 0) return EOF;
62
63         stream->buffer_start += written;
64         stream->buffer_start %= stream->buffer_size;
65         if (written) stream->flags &= ~FILE_BUFFER_FULL;
66     }
67     else
68     {
69         if (stream->buffer_start < stream->buffer_size)
70         {
71             ssize_t written = __CRT_PUBLIC(write)(stream->fd, &stream->buffer[stream->buffer_start], stream->buffer_size - stream->buffer_start);
72             if (written < 0) return EOF;
73
74             stream->buffer_start += written;
75             stream->buffer_start %= stream->buffer_size;
76             if (written) stream->flags &= ~FILE_BUFFER_FULL;
77         }
78
79         if (stream->buffer_end)
80         {
81             ssize_t written =__CRT_PUBLIC(write)(stream->fd, stream->buffer, stream->buffer_end);
82             if (written < 0) return EOF;
83
84             stream->buffer_start += written;
85             stream->buffer_start %= stream->buffer_size;
86             if (written) stream->flags &= ~FILE_BUFFER_FULL;
87         }
88     }
89
90     return 0;
91 }
92
93 int __CRT_PUBLIC(fflush_unlocked)(FILE *stream)
94 {
95     if (stream) return __CRT_PRIVATE(fflush_stream)(stream);
96
97     list_entry_t *entry;
98     int result = 0;
99
100     for (entry = open_files.next; entry != &open_files; entry = entry->next)
101     {
102         if (__CRT_PRIVATE(fflush_stream)(CONTAINER_OF(entry, struct __crt_file, link)) == EOF)
103         {
104             result = EOF;
105         }
106     }
107
108     return result;
109 }
110
111 int __CRT_PUBLIC(fflush)(FILE *stream)
112 {
113     syscall_wait_mutex(stream->mutex, NO_TIMEOUT);
114     int ret = __CRT_PUBLIC(fflush_unlocked)(stream);
115     syscall_release_mutex(stream->mutex);
116     return ret;
117 }
118
119 int __CRT_PUBLIC(setvbuf)(FILE *stream, char *buf, int mode, size_t size)
120 {
121     int ret = EOF;
122     syscall_wait_mutex(stream->mutex, NO_TIMEOUT);
123
124     switch (mode)
125     {
126     case _IONBF:
127         if (__CRT_PUBLIC(fflush_unlocked)(stream) == 0)
128         {
129             stream->buffer = NULL;
130             stream->buffer_size = 0;
131             ret = 0;
132         }
133         break;
134
135     case _IOLBF:
136         if (!buf || __CRT_PUBLIC(fflush_unlocked)(stream) == 0)
137         {
138             if (buf)
139             {
140                 stream->buffer = buf;
141                 stream->buffer_size = size;
142             }
143
144             stream->flags |= FILE_BUFFER_LINE;
145             ret = 0;
146         }
147         break;
148
149     case _IOFBF:
150         if (!buf || __CRT_PUBLIC(fflush_unlocked)(stream) == 0)
151         {
152             if (buf)
153             {
154                 stream->buffer = buf;
155                 stream->buffer_size = size;
156             }
157
158             stream->flags &= ~FILE_BUFFER_LINE;
159             ret = 0;
160         }
161         break;
162     }
163
164     syscall_release_mutex(stream->mutex);
165     return ret;
166 }
167
168 void __CRT_PUBLIC(setbuf)(FILE *stream, char *buf)
169 {
170     __CRT_PUBLIC(setvbuf)(stream, buf, buf ? _IOFBF : _IONBF, BUFSIZ);
171 }
172
173 void __CRT_PUBLIC(setbuffer)(FILE *stream, char *buf, size_t size)
174 {
175     __CRT_PUBLIC(setvbuf)(stream, buf, buf ? _IOFBF : _IONBF, size);
176 }
177
178 void __CRT_PUBLIC(setlinebuf)(FILE *stream)
179 {
180     __CRT_PUBLIC(setvbuf)(stream, NULL, _IOLBF, 0);
181 }
182
183 inline int __CRT_PUBLIC(fgetc_unlocked)(FILE *stream)
184 {
185     char c;
186
187     if (!(stream->flags & FILE_READ))
188     {
189         __CRT_PUBLIC(errno) = EPERM;
190         return EOF;
191     }
192
193     if (stream->buffer)
194     {
195         if (stream->flags & FILE_BUFFER_DIR)
196         {
197             __CRT_PUBLIC(fflush_unlocked)(stream);
198             stream->flags &= ~FILE_BUFFER_DIR;
199         }
200
201         if (stream->buffer_start == stream->buffer_end && !(stream->flags & FILE_BUFFER_FULL))
202         {
203             if (stream->fd == -1) return EOF;
204
205             if (stream->buffer_end < stream->buffer_size)
206             {
207                 ssize_t amount = __CRT_PUBLIC(read)(stream->fd, &stream->buffer[stream->buffer_end], stream->buffer_size - stream->buffer_end);
208                 if (amount < 0) return EOF;
209
210                 stream->buffer_end += amount;
211                 stream->buffer_end %= stream->buffer_size;
212                 if (amount && (stream->buffer_start == stream->buffer_end)) stream->flags |= FILE_BUFFER_FULL;
213             }
214
215             if (stream->buffer_end < stream->buffer_start)
216             {
217                 ssize_t amount = __CRT_PUBLIC(read)(stream->fd, &stream->buffer[stream->buffer_end], stream->buffer_start - stream->buffer_end);
218                 if (amount < 0) return EOF;
219
220                 stream->buffer_end += amount;
221                 stream->buffer_end %= stream->buffer_size;
222                 if (amount && (stream->buffer_start == stream->buffer_end)) stream->flags |= FILE_BUFFER_FULL;
223             }
224         }
225
226         c = stream->buffer[stream->buffer_start];
227         stream->buffer_start++;
228         stream->buffer_end %= stream->buffer_size;
229
230         stream->flags &= ~FILE_BUFFER_FULL;
231     }
232     else
233     {
234         if (__CRT_PUBLIC(read)(stream->fd, &c, 1) != 1) c = EOF;
235     }
236
237     return (int)((unsigned int)c);
238 }
239
240 int __CRT_PUBLIC(fgetc)(FILE *stream)
241 {
242     syscall_wait_mutex(stream->mutex, NO_TIMEOUT);
243     int ret = __CRT_PUBLIC(fgetc_unlocked)(stream);
244     syscall_release_mutex(stream->mutex);
245     return ret;
246 }
247
248 inline int __CRT_PUBLIC(fputc_unlocked)(int c, FILE *stream)
249 {
250     if (!(stream->flags & FILE_WRITE))
251     {
252         __CRT_PUBLIC(errno) = EPERM;
253         return EOF;
254     }
255
256     if (stream->buffer)
257     {
258         if (!(stream->flags & FILE_BUFFER_DIR))
259         {
260             __CRT_PUBLIC(fflush_unlocked)(stream);
261             stream->flags |= FILE_BUFFER_DIR;
262         }
263
264         if (stream->flags & FILE_BUFFER_FULL)
265         {
266             __CRT_PUBLIC(errno) = ENOSPC;
267             return EOF;
268         }
269
270         stream->buffer[stream->buffer_end] = (uint8_t)c;
271         stream->buffer_end++;
272         stream->buffer_end %= stream->buffer_size;
273
274         if (stream->buffer_start == stream->buffer_end)
275         {
276             stream->flags |= FILE_BUFFER_FULL;
277         }
278
279         if (stream->fd != -1 && ((stream->flags & FILE_BUFFER_FULL) || ((stream->flags & FILE_BUFFER_LINE) && (char)c == '\n')))
280         {
281             __CRT_PUBLIC(fflush_unlocked)(stream);
282         }
283     }
284     else
285     {
286         if (__CRT_PUBLIC(write)(stream->fd, &c, 1) != 1) return EOF;
287     }
288
289     return 0;
290 }
291
292 int __CRT_PUBLIC(fputc)(int c, FILE *stream)
293 {
294     syscall_wait_mutex(stream->mutex, NO_TIMEOUT);
295     int ret = __CRT_PUBLIC(fputc_unlocked)(c, stream);
296     syscall_release_mutex(stream->mutex);
297     return ret;
298 }
299
300 char *__CRT_PUBLIC(fgets_unlocked)(char *s, int size, FILE *stream)
301 {
302     int c;
303     char *ptr = s;
304
305     while (size-- > 0 && (c = __CRT_PUBLIC(fgetc_unlocked)(stream)) != EOF)
306     {
307         *ptr++ = (char)c;
308         if (c == '\n') break;
309     }
310
311     return (s != ptr) ? s : NULL;
312 }
313
314 char *__CRT_PUBLIC(fgets)(char *s, int size, FILE *stream)
315 {
316     syscall_wait_mutex(stream->mutex, NO_TIMEOUT);
317     char *ret = __CRT_PUBLIC(fgets_unlocked)(s, size, stream);
318     syscall_release_mutex(stream->mutex);
319     return ret;
320 }
321
322 int __CRT_PUBLIC(fputs_unlocked)(const char *s, FILE *stream)
323 {
324     const char *ptr = s;
325     while (*ptr) if (__CRT_PUBLIC(fputc_unlocked)(*ptr++, stream) == EOF) return EOF;
326     return 0;
327 }
328
329 int __CRT_PUBLIC(fputs)(const char *s, FILE *stream)
330 {
331     syscall_wait_mutex(stream->mutex, NO_TIMEOUT);
332     int ret = __CRT_PUBLIC(fputs_unlocked)(s, stream);
333     syscall_release_mutex(stream->mutex);
334     return ret;
335 }
336
337 int __CRT_PUBLIC(ungetc)(int c, FILE *stream)
338 {
339     syscall_wait_mutex(stream->mutex, NO_TIMEOUT);
340
341     if (stream->flags & FILE_BUFFER_DIR)
342     {
343         __CRT_PUBLIC(fflush_unlocked)(stream);
344         stream->flags &= ~FILE_BUFFER_DIR;
345     }
346
347     if (stream->buffer_start == 0) stream->buffer_start = stream->buffer_size;
348     stream->buffer_start--;
349
350     stream->buffer[stream->buffer_start] = (uint8_t)c;
351
352     syscall_release_mutex(stream->mutex);
353     return c;
354 }
355
356 char *__CRT_PUBLIC(gets)(char *s)
357 {
358     char *ptr = s;
359     int c;
360     while ((c = __CRT_PUBLIC(getchar)()) != EOF) *ptr++ = (char)c;
361     return ptr != s ? s : NULL;
362 }
363
364 int __CRT_PUBLIC(puts)(const char *s)
365 {
366     if (__CRT_PUBLIC(fputs)(s, __CRT_PUBLIC(stdout)) == EOF) return EOF;
367     if (__CRT_PUBLIC(fputc)('\n', __CRT_PUBLIC(stdout)) == EOF) return EOF;
368     return 0;
369 }
370
371 FILE *__CRT_PUBLIC(fdopen)(int fd, const char *mode)
372 {
373     FILE *stream = (FILE*)__CRT_PUBLIC(malloc)(sizeof(FILE) + BUFSIZ);
374
375     if (stream == NULL)
376     {
377         __CRT_PUBLIC(errno) = ENOMEM;
378         return NULL;
379     }
380
381     sysret_t status = syscall_create_mutex(NULL, TRUE, &stream->mutex);
382     if (status != ERR_SUCCESS)
383     {
384         __CRT_PUBLIC(free)(stream);
385         __CRT_PUBLIC(errno) = __CRT_PRIVATE(translate_error)(status);
386         return NULL;
387     }
388
389     stream->flags = 0;
390
391     const char *ptr;
392     for (ptr = mode; *ptr; ptr++)
393     {
394         switch (*ptr)
395         {
396         case 'r':
397             stream->flags |= FILE_READ;
398             break;
399         case 'w':
400             stream->flags |= FILE_WRITE;
401             break;
402         case 'a':
403             stream->flags |= FILE_APPEND;
404             break;
405         case '+':
406             stream->flags |= FILE_READ | FILE_WRITE;
407             break;
408         }
409     }
410
411     stream->fd = fd;
412
413     char *buffer = (char*)((uintptr_t)stream + sizeof(FILE));
414     __CRT_PUBLIC(setbuf)(stream, buffer);
415     return stream;
416 }
417
418 FILE *__CRT_PUBLIC(fmemopen)(void *buf, size_t size, const char *mode)
419 {
420     FILE *stream = (FILE*)__CRT_PUBLIC(malloc)(sizeof(FILE));
421     if (stream == NULL)
422     {
423         __CRT_PUBLIC(errno) = ENOMEM;
424         return NULL;
425     }
426
427     sysret_t status = syscall_create_mutex(NULL, TRUE, &stream->mutex);
428     if (status != ERR_SUCCESS)
429     {
430         __CRT_PUBLIC(free)(stream);
431         __CRT_PUBLIC(errno) = __CRT_PRIVATE(translate_error)(status);
432         return NULL;
433     }
434
435     stream->flags = 0;
436
437     const char *ptr;
438     for (ptr = mode; *ptr; ptr++)
439     {
440         switch (*ptr)
441         {
442         case 'r':
443             stream->flags |= FILE_READ;
444             break;
445         case 'w':
446             stream->flags |= FILE_WRITE;
447             break;
448         case 'a':
449             stream->flags |= FILE_APPEND;
450             break;
451         case '+':
452             stream->flags |= FILE_READ | FILE_WRITE;
453             break;
454         }
455     }
456
457     stream->fd = -1;
458     stream->buffer = buf;
459     stream->buffer_size = size;
460     stream->buffer_start = stream->buffer_end = 0;
461
462     list_append(&open_files, &stream->link);
463     return stream;
464 }
465
466 FILE *__CRT_PUBLIC(fopen)(const char *pathname, const char *mode)
467 {
468     int open_flags = 0;
469
470     const char *ptr;
471     for (ptr = mode; *ptr; ptr++)
472     {
473         switch (*ptr)
474         {
475         case 'r':
476             open_flags = (open_flags & ~3) | O_RDONLY;
477             break;
478         case 'w':
479             open_flags = (open_flags & ~3) | O_WRONLY;
480             break;
481         case 'a':
482             open_flags |= O_APPEND;
483             break;
484         case '+':
485             open_flags = (open_flags & ~3) | O_RDWR;
486             break;
487         }
488     }
489
490     int fd = __CRT_PUBLIC(open)(pathname, open_flags);
491     if (fd < 0) return NULL;
492
493     FILE *stream = __CRT_PUBLIC(fdopen)(fd, mode);
494     if (!stream) __CRT_PUBLIC(close)(fd);
495     return NULL;
496 }
497
498 int __CRT_PUBLIC(fclose)(FILE *stream)
499 {
500     __CRT_PUBLIC(fflush)(stream);
501     list_remove(&stream->link);
502     syscall_wait_mutex(stream->mutex, NO_TIMEOUT);
503
504     if (stream->fd) __CRT_PUBLIC(close)(stream->fd);
505
506     syscall_close_object(stream->mutex);
507     __CRT_PUBLIC(free)(stream);
508     return 0;
509 }