Start implementing CRT I/O
[monolithium.git] / crt / src / io.c
1 /*
2  * io.c
3  *
4  * Copyright (C) 2017 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 <monolithium.h>
22
23 #define FILE_READ        (1 << 0)
24 #define FILE_WRITE       (1 << 1)
25 #define FILE_APPEND      (1 << 2)
26 #define FILE_BUFFER_DIR  (1 << 3)
27 #define FILE_BUFFER_FULL (1 << 31)
28
29 struct __crt_file
30 {
31     list_entry_t link;
32     handle_t mutex;
33     uint32_t flags;
34
35     int fd;
36     long position;
37     size_t size;
38
39     uint8_t *buffer;
40     size_t buffer_size;
41     size_t buffer_start, buffer_end;
42 };
43
44 static list_entry_t open_files;
45
46 FILE *stdin  = NULL;
47 FILE *stdout = NULL;
48 FILE *stderr = NULL;
49
50 int fileno_unlocked(FILE *stream)
51 {
52     if (stream->fd == -1) errno = EBADF;
53     return stream->fd;
54 }
55
56 int fileno(FILE *stream)
57 {
58     syscall_wait_mutex(stream->mutex, NO_TIMEOUT);
59     int ret = fileno_unlocked(stream);
60     syscall_release_mutex(stream->mutex);
61     return ret;
62 }
63
64 static int __crt_fflush_stream(FILE *stream)
65 {
66     if (!(stream->flags & FILE_BUFFER_DIR))
67     {
68         stream->buffer_start = stream->buffer_end = 0;
69         stream->flags &= ~FILE_BUFFER_FULL;
70         return 0;
71     }
72
73     if (stream->fd != -1)
74     {
75         // TODO: Not implemented
76         errno = ENOSYS;
77         return EOF;
78     }
79
80     return 0;
81 }
82
83 int fflush_unlocked(FILE *stream)
84 {
85     if (stream) return __crt_fflush_stream(stream);
86
87     list_entry_t *entry;
88     int result = 0;
89
90     for (entry = open_files.next; entry != &open_files; entry = entry->next)
91     {
92         if (__crt_fflush_stream(CONTAINER_OF(entry, struct __crt_file, link)) == EOF)
93         {
94             result = EOF;
95         }
96     }
97
98     return result;
99 }
100
101 int fflush(FILE *stream)
102 {
103     syscall_wait_mutex(stream->mutex, NO_TIMEOUT);
104     int ret = fflush_unlocked(stream);
105     syscall_release_mutex(stream->mutex);
106     return ret;
107 }
108
109 inline int fgetc_unlocked(FILE *stream)
110 {
111     if (!(stream->flags & FILE_READ))
112     {
113         errno = EPERM;
114         return EOF;
115     }
116
117     if (stream->position == stream->size) return EOF;
118
119     if (stream->flags & FILE_BUFFER_DIR)
120     {
121         fflush_unlocked(stream);
122         stream->flags &= ~FILE_BUFFER_DIR;
123     }
124
125     if (stream->fd != -1
126         && stream->buffer_start == stream->buffer_end
127         && !(stream->flags & FILE_BUFFER_FULL))
128     {
129         // TODO: Not implemented
130         errno = ENOSYS;
131         return EOF;
132     }
133
134     uint8_t c = stream->buffer[stream->buffer_start];
135     stream->buffer_start++;
136     stream->buffer_end %= stream->buffer_size;
137     stream->position++;
138
139     stream->flags &= ~FILE_BUFFER_FULL;
140     return (int)((unsigned int)c);
141 }
142
143 int fgetc(FILE *stream)
144 {
145     syscall_wait_mutex(stream->mutex, NO_TIMEOUT);
146     int ret = fgetc_unlocked(stream);
147     syscall_release_mutex(stream->mutex);
148     return ret;
149 }
150
151 inline int fputc_unlocked(int c, FILE *stream)
152 {
153     if (!(stream->flags & FILE_WRITE))
154     {
155         errno = EPERM;
156         return EOF;
157     }
158
159     if (!(stream->flags & FILE_BUFFER_DIR))
160     {
161         fflush_unlocked(stream);
162         stream->flags |= FILE_BUFFER_DIR;
163     }
164
165     if ((stream->flags & FILE_APPEND) && (stream->position < stream->size))
166     {
167         fflush_unlocked(stream);
168         stream->position = stream->size;
169     }
170
171     if (stream->flags & FILE_BUFFER_FULL)
172     {
173         errno = ENOSPC;
174         return EOF;
175     }
176
177     stream->buffer[stream->buffer_end] = (uint8_t)c;
178     stream->buffer_end++;
179     stream->buffer_end %= stream->buffer_size;
180
181     stream->position++;
182     if (stream->position > stream->size) stream->size = stream->position;
183
184     if (stream->fd != -1 && ((char)c == '\n' || (stream->buffer_start == stream->buffer_end)))
185     {
186         stream->flags |= FILE_BUFFER_FULL;
187         fflush_unlocked(stream);
188     }
189
190     return 0;
191 }
192
193 int fputc(int c, FILE *stream)
194 {
195     syscall_wait_mutex(stream->mutex, NO_TIMEOUT);
196     int ret = fputc_unlocked(c, stream);
197     syscall_release_mutex(stream->mutex);
198     return ret;
199 }
200
201 char *fgets_unlocked(char *s, int size, FILE *stream)
202 {
203     int c;
204     char *ptr = s;
205
206     while (size-- > 0 && (c = fgetc_unlocked(stream)) != EOF)
207     {
208         *ptr++ = (char)c;
209         if (c == '\n') break;
210     }
211
212     return (s != ptr) ? s : NULL;
213 }
214
215 char *fgets(char *s, int size, FILE *stream)
216 {
217     syscall_wait_mutex(stream->mutex, NO_TIMEOUT);
218     char *ret = fgets_unlocked(s, size, stream);
219     syscall_release_mutex(stream->mutex);
220     return ret;
221 }
222
223 int fputs_unlocked(const char *s, FILE *stream)
224 {
225     const char *ptr = s;
226     while (*ptr) if (fputc_unlocked(*ptr++, stream) == EOF) return EOF;
227     return 0;
228 }
229
230 int fputs(const char *s, FILE *stream)
231 {
232     syscall_wait_mutex(stream->mutex, NO_TIMEOUT);
233     int ret = fputs_unlocked(s, stream);
234     syscall_release_mutex(stream->mutex);
235     return ret;
236 }
237
238 int ungetc(int c, FILE *stream)
239 {
240     syscall_wait_mutex(stream->mutex, NO_TIMEOUT);
241
242     if (stream->flags & FILE_BUFFER_DIR)
243     {
244         fflush_unlocked(stream);
245         stream->flags &= ~FILE_BUFFER_DIR;
246     }
247
248     if (stream->buffer_start == 0) stream->buffer_start = stream->buffer_size;
249     stream->buffer_start--;
250
251     stream->buffer[stream->buffer_start] = (uint8_t)c;
252     stream->position--;
253
254     syscall_release_mutex(stream->mutex);
255     return c;
256 }
257
258 char *gets(char *s)
259 {
260     char *ptr = s;
261     int c;
262     while ((c = getchar()) != EOF) *ptr++ = (char)c;
263     return ptr != s ? s : NULL;
264 }
265
266 int puts(const char *s)
267 {
268     if (fputs(s, stdout) == EOF) return EOF;
269     if (fputc('\n', stdout) == EOF) return EOF;
270     return 0;
271 }
272
273 FILE *fmemopen(void *buf, size_t size, const char *mode)
274 {
275     FILE *stream = (FILE*)malloc(sizeof(FILE));
276
277     sysret_t status = syscall_create_mutex(NULL, TRUE, &stream->mutex);
278     if (status != ERR_SUCCESS)
279     {
280         free(stream);
281         errno = __crt_translate_error(status);
282         return NULL;
283     }
284
285     stream->flags = 0;
286     stream->fd = -1;
287     stream->position = 0;
288     stream->size = size;
289     stream->buffer = buf;
290     stream->buffer_size = size;
291     stream->buffer_start = stream->buffer_end = 0;
292
293     list_append(&open_files, &stream->link);
294     return stream;
295 }
296
297 int fclose(FILE *stream)
298 {
299     list_remove(&stream->link);
300     free(stream);
301     return 0;
302 }