source: mainline/uspace/lib/c/generic/io/io.c

Last change on this file was 28a5ebd, checked in by Martin Decky <martin@…>, 5 years ago

Use char32_t instead of wchat_t to represent UTF-32 strings

The intention of the native HelenOS string API has been always to
support Unicode in the UTF-8 and UTF-32 encodings as the sole character
representations and ignore the obsolete mess of older single-byte and
multibyte character encodings. Before C11, the wchar_t type has been
slightly misused for the purpose of the UTF-32 strings. The newer
char32_t type is obviously a much more suitable option. The standard
defines char32_t as uint_least32_t, thus we can take the liberty to fix
it to uint32_t.

To maintain compatilibity with the C Standard, the putwchar(wchar_t)
functions has been replaced by our custom putuchar(char32_t) functions
where appropriate.

  • Property mode set to 100644
File size: 20.4 KB
Line 
1/*
2 * Copyright (c) 2005 Martin Decky
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup libc
30 * @{
31 */
32/** @file
33 */
34
35#include <stdio.h>
36#include <assert.h>
37#include <str.h>
38#include <errno.h>
39#include <limits.h>
40#include <stdbool.h>
41#include <stdlib.h>
42#include <async.h>
43#include <io/kio.h>
44#include <vfs/vfs.h>
45#include <vfs/vfs_sess.h>
46#include <vfs/inbox.h>
47#include <ipc/loc.h>
48#include <adt/list.h>
49#include <wchar.h>
50#include <uchar.h>
51#include "../private/io.h"
52#include "../private/stdio.h"
53
54static void _ffillbuf(FILE *stream);
55static void _fflushbuf(FILE *stream);
56
57static size_t stdio_kio_read(void *, size_t, size_t, FILE *);
58static size_t stdio_kio_write(const void *, size_t, size_t, FILE *);
59static int stdio_kio_flush(FILE *);
60
61static size_t stdio_vfs_read(void *, size_t, size_t, FILE *);
62static size_t stdio_vfs_write(const void *, size_t, size_t, FILE *);
63
64static int stdio_vfs_flush(FILE *);
65
66/** KIO stream ops */
67static __stream_ops_t stdio_kio_ops = {
68 .read = stdio_kio_read,
69 .write = stdio_kio_write,
70 .flush = stdio_kio_flush
71};
72
73/** VFS stream ops */
74static __stream_ops_t stdio_vfs_ops = {
75 .read = stdio_vfs_read,
76 .write = stdio_vfs_write,
77 .flush = stdio_vfs_flush
78};
79
80static FILE stdin_null = {
81 .fd = -1,
82 .pos = 0,
83 .error = true,
84 .eof = true,
85 .ops = &stdio_vfs_ops,
86 .arg = NULL,
87 .sess = NULL,
88 .btype = _IONBF,
89 .buf = NULL,
90 .buf_size = 0,
91 .buf_head = NULL,
92 .buf_tail = NULL,
93 .buf_state = _bs_empty
94};
95
96static FILE stdout_kio = {
97 .fd = -1,
98 .pos = 0,
99 .error = false,
100 .eof = false,
101 .ops = &stdio_kio_ops,
102 .arg = NULL,
103 .sess = NULL,
104 .btype = _IOLBF,
105 .buf = NULL,
106 .buf_size = BUFSIZ,
107 .buf_head = NULL,
108 .buf_tail = NULL,
109 .buf_state = _bs_empty
110};
111
112static FILE stderr_kio = {
113 .fd = -1,
114 .pos = 0,
115 .error = false,
116 .eof = false,
117 .ops = &stdio_kio_ops,
118 .arg = NULL,
119 .sess = NULL,
120 .btype = _IONBF,
121 .buf = NULL,
122 .buf_size = 0,
123 .buf_head = NULL,
124 .buf_tail = NULL,
125 .buf_state = _bs_empty
126};
127
128FILE *stdin = NULL;
129FILE *stdout = NULL;
130FILE *stderr = NULL;
131
132static LIST_INITIALIZE(files);
133
134void __stdio_init(void)
135{
136 /*
137 * The first three standard file descriptors are assigned for compatibility.
138 * This will probably be removed later.
139 */
140 int infd = inbox_get("stdin");
141 if (infd >= 0) {
142 int stdinfd = -1;
143 (void) vfs_clone(infd, -1, false, &stdinfd);
144 assert(stdinfd == 0);
145 vfs_open(stdinfd, MODE_READ);
146 stdin = fdopen(stdinfd, "r");
147 } else {
148 stdin = &stdin_null;
149 list_append(&stdin->link, &files);
150 }
151
152 int outfd = inbox_get("stdout");
153 if (outfd >= 0) {
154 int stdoutfd = -1;
155 (void) vfs_clone(outfd, -1, false, &stdoutfd);
156 assert(stdoutfd <= 1);
157 while (stdoutfd < 1)
158 (void) vfs_clone(outfd, -1, false, &stdoutfd);
159 vfs_open(stdoutfd, MODE_APPEND);
160 stdout = fdopen(stdoutfd, "a");
161 } else {
162 stdout = &stdout_kio;
163 list_append(&stdout->link, &files);
164 }
165
166 int errfd = inbox_get("stderr");
167 if (errfd >= 0) {
168 int stderrfd = -1;
169 (void) vfs_clone(errfd, -1, false, &stderrfd);
170 assert(stderrfd <= 2);
171 while (stderrfd < 2)
172 (void) vfs_clone(errfd, -1, false, &stderrfd);
173 vfs_open(stderrfd, MODE_APPEND);
174 stderr = fdopen(stderrfd, "a");
175 } else {
176 stderr = &stderr_kio;
177 list_append(&stderr->link, &files);
178 }
179}
180
181void __stdio_done(void)
182{
183 while (!list_empty(&files)) {
184 FILE *file = list_get_instance(list_first(&files), FILE, link);
185 fclose(file);
186 }
187}
188
189static bool parse_mode(const char *fmode, int *mode, bool *create, bool *excl,
190 bool *truncate)
191{
192 /* Parse mode except first character. */
193 const char *mp = fmode;
194
195 if (*mp++ == '\0') {
196 errno = EINVAL;
197 return false;
198 }
199
200 if ((*mp == 'b') || (*mp == 't'))
201 mp++;
202
203 bool plus;
204 if (*mp == '+') {
205 mp++;
206 plus = true;
207 } else {
208 plus = false;
209 }
210
211 bool ex;
212 if (*mp == 'x') {
213 mp++;
214 ex = true;
215 } else {
216 ex = false;
217 }
218
219 if (*mp != '\0') {
220 errno = EINVAL;
221 return false;
222 }
223
224 *create = false;
225 *truncate = false;
226 *excl = false;
227
228 /* Parse first character of fmode and determine mode for vfs_open(). */
229 switch (fmode[0]) {
230 case 'r':
231 *mode = plus ? MODE_READ | MODE_WRITE : MODE_READ;
232 if (ex) {
233 errno = EINVAL;
234 return false;
235 }
236 break;
237 case 'w':
238 *mode = plus ? MODE_READ | MODE_WRITE : MODE_WRITE;
239 *create = true;
240 *excl = ex;
241 if (!plus)
242 *truncate = true;
243 break;
244 case 'a':
245 /* TODO: a+ must read from beginning, append to the end. */
246 if (plus) {
247 errno = ENOTSUP;
248 return false;
249 }
250
251 if (ex) {
252 errno = EINVAL;
253 return false;
254 }
255
256 *mode = MODE_APPEND | (plus ? MODE_READ | MODE_WRITE : MODE_WRITE);
257 *create = true;
258 break;
259 default:
260 errno = EINVAL;
261 return false;
262 }
263
264 return true;
265}
266
267/** Set stream buffer. */
268int setvbuf(FILE *stream, void *buf, int mode, size_t size)
269{
270 if (mode != _IONBF && mode != _IOLBF && mode != _IOFBF)
271 return -1;
272
273 stream->btype = mode;
274 stream->buf = buf;
275 stream->buf_size = size;
276 stream->buf_head = stream->buf;
277 stream->buf_tail = stream->buf;
278 stream->buf_state = _bs_empty;
279
280 return 0;
281}
282
283/** Set stream buffer.
284 *
285 * When @p buf is NULL, the stream is set as unbuffered, otherwise
286 * full buffering is enabled.
287 */
288void setbuf(FILE *stream, void *buf)
289{
290 if (buf == NULL) {
291 setvbuf(stream, NULL, _IONBF, BUFSIZ);
292 } else {
293 setvbuf(stream, buf, _IOFBF, BUFSIZ);
294 }
295}
296
297static void _setvbuf(FILE *stream)
298{
299 /* FIXME: Use more complex rules for setting buffering options. */
300
301 switch (stream->fd) {
302 case 1:
303 setvbuf(stream, NULL, _IOLBF, BUFSIZ);
304 break;
305 case 0:
306 case 2:
307 setvbuf(stream, NULL, _IONBF, 0);
308 break;
309 default:
310 setvbuf(stream, NULL, _IOFBF, BUFSIZ);
311 }
312}
313
314/** Allocate stream buffer. */
315static int _fallocbuf(FILE *stream)
316{
317 assert(stream->buf == NULL);
318
319 stream->buf = malloc(stream->buf_size);
320 if (stream->buf == NULL) {
321 errno = ENOMEM;
322 return EOF;
323 }
324
325 stream->buf_head = stream->buf;
326 stream->buf_tail = stream->buf;
327 return 0;
328}
329
330/** Open a stream.
331 *
332 * @param path Path of the file to open.
333 * @param mode Mode string, (r|w|a)[b|t][+][x].
334 *
335 */
336FILE *fopen(const char *path, const char *fmode)
337{
338 int mode;
339 bool create;
340 bool excl;
341 bool truncate;
342
343 if (!parse_mode(fmode, &mode, &create, &excl, &truncate))
344 return NULL;
345
346 /* Open file. */
347 FILE *stream = malloc(sizeof(FILE));
348 if (stream == NULL) {
349 errno = ENOMEM;
350 return NULL;
351 }
352
353 int flags = WALK_REGULAR;
354 if (create && excl)
355 flags |= WALK_MUST_CREATE;
356 else if (create)
357 flags |= WALK_MAY_CREATE;
358 int file;
359 errno_t rc = vfs_lookup(path, flags, &file);
360 if (rc != EOK) {
361 errno = rc;
362 free(stream);
363 return NULL;
364 }
365
366 rc = vfs_open(file, mode);
367 if (rc != EOK) {
368 errno = rc;
369 vfs_put(file);
370 free(stream);
371 return NULL;
372 }
373
374 if (truncate) {
375 rc = vfs_resize(file, 0);
376 if (rc != EOK) {
377 errno = rc;
378 vfs_put(file);
379 free(stream);
380 return NULL;
381 }
382 }
383
384 stream->fd = file;
385 stream->pos = 0;
386 stream->error = false;
387 stream->eof = false;
388 stream->ops = &stdio_vfs_ops;
389 stream->arg = NULL;
390 stream->sess = NULL;
391 stream->need_sync = false;
392 _setvbuf(stream);
393 stream->ungetc_chars = 0;
394
395 list_append(&stream->link, &files);
396
397 return stream;
398}
399
400FILE *fdopen(int fd, const char *mode)
401{
402 /* Open file. */
403 FILE *stream = malloc(sizeof(FILE));
404 if (stream == NULL) {
405 errno = ENOMEM;
406 return NULL;
407 }
408
409 stream->fd = fd;
410 stream->pos = 0;
411 stream->error = false;
412 stream->eof = false;
413 stream->ops = &stdio_vfs_ops;
414 stream->arg = NULL;
415 stream->sess = NULL;
416 stream->need_sync = false;
417 _setvbuf(stream);
418 stream->ungetc_chars = 0;
419
420 list_append(&stream->link, &files);
421
422 return stream;
423}
424
425static int _fclose_nofree(FILE *stream)
426{
427 errno_t rc = 0;
428
429 fflush(stream);
430
431 if (stream->sess != NULL)
432 async_hangup(stream->sess);
433
434 if (stream->fd >= 0)
435 rc = vfs_put(stream->fd);
436
437 list_remove(&stream->link);
438
439 if (rc != EOK) {
440 errno = rc;
441 return EOF;
442 }
443
444 return 0;
445}
446
447int fclose(FILE *stream)
448{
449 int rc = _fclose_nofree(stream);
450
451 if ((stream != &stdin_null) &&
452 (stream != &stdout_kio) &&
453 (stream != &stderr_kio))
454 free(stream);
455
456 return rc;
457}
458
459FILE *freopen(const char *path, const char *mode, FILE *stream)
460{
461 FILE *nstr;
462
463 if (path == NULL) {
464 /* Changing mode is not supported */
465 return NULL;
466 }
467
468 (void) _fclose_nofree(stream);
469 nstr = fopen(path, mode);
470 if (nstr == NULL) {
471 free(stream);
472 return NULL;
473 }
474
475 list_remove(&nstr->link);
476 *stream = *nstr;
477 list_append(&stream->link, &files);
478
479 free(nstr);
480
481 return stream;
482}
483
484/** Read from a stream (unbuffered).
485 *
486 * @param buf Destination buffer.
487 * @param size Size of each record.
488 * @param nmemb Number of records to read.
489 * @param stream Pointer to the stream.
490 *
491 * @return Number of elements successfully read. On error this is less than
492 * nmemb, stream error indicator is set and errno is set.
493 */
494static size_t _fread(void *buf, size_t size, size_t nmemb, FILE *stream)
495{
496 return stream->ops->read(buf, size, nmemb, stream);
497}
498
499/** Write to a stream (unbuffered).
500 *
501 * @param buf Source buffer.
502 * @param size Size of each record.
503 * @param nmemb Number of records to write.
504 * @param stream Pointer to the stream.
505 *
506 * @return Number of elements successfully written. On error this is less than
507 * nmemb, stream error indicator is set and errno is set.
508 */
509static size_t _fwrite(const void *buf, size_t size, size_t nmemb, FILE *stream)
510{
511 size_t nwritten;
512
513 if (size == 0 || nmemb == 0)
514 return 0;
515
516 nwritten = stream->ops->write(buf, size, nmemb, stream);
517
518 if (nwritten > 0)
519 stream->need_sync = true;
520
521 return (nwritten / size);
522}
523
524/** Read some data in stream buffer.
525 *
526 * On error, stream error indicator is set and errno is set.
527 */
528static void _ffillbuf(FILE *stream)
529{
530 errno_t rc;
531 size_t nread;
532
533 stream->buf_head = stream->buf_tail = stream->buf;
534
535 rc = vfs_read(stream->fd, &stream->pos, stream->buf, stream->buf_size,
536 &nread);
537 if (rc != EOK) {
538 errno = rc;
539 stream->error = true;
540 return;
541 }
542
543 if (nread == 0) {
544 stream->eof = true;
545 return;
546 }
547
548 stream->buf_head += nread;
549 stream->buf_state = _bs_read;
550}
551
552/** Write out stream buffer, do not sync stream. */
553static void _fflushbuf(FILE *stream)
554{
555 size_t bytes_used;
556
557 if ((!stream->buf) || (stream->btype == _IONBF) || (stream->error))
558 return;
559
560 bytes_used = stream->buf_head - stream->buf_tail;
561
562 /* If buffer has prefetched read data, we need to seek back. */
563 if (bytes_used > 0 && stream->buf_state == _bs_read)
564 stream->pos -= bytes_used;
565
566 /* If buffer has unwritten data, we need to write them out. */
567 if (bytes_used > 0 && stream->buf_state == _bs_write) {
568 (void) _fwrite(stream->buf_tail, 1, bytes_used, stream);
569 /* On error stream error indicator and errno are set by _fwrite */
570 if (stream->error)
571 return;
572 }
573
574 stream->buf_head = stream->buf;
575 stream->buf_tail = stream->buf;
576 stream->buf_state = _bs_empty;
577}
578
579/** Read from a stream.
580 *
581 * @param dest Destination buffer.
582 * @param size Size of each record.
583 * @param nmemb Number of records to read.
584 * @param stream Pointer to the stream.
585 *
586 */
587size_t fread(void *dest, size_t size, size_t nmemb, FILE *stream)
588{
589 uint8_t *dp;
590 size_t bytes_left;
591 size_t now;
592 size_t data_avail;
593 size_t total_read;
594 size_t i;
595
596 if (size == 0 || nmemb == 0)
597 return 0;
598
599 bytes_left = size * nmemb;
600 total_read = 0;
601 dp = (uint8_t *) dest;
602
603 /* Bytes from ungetc() buffer */
604 while (stream->ungetc_chars > 0 && bytes_left > 0) {
605 *dp++ = stream->ungetc_buf[--stream->ungetc_chars];
606 ++total_read;
607 --bytes_left;
608 }
609
610 /* If not buffered stream, read in directly. */
611 if (stream->btype == _IONBF) {
612 total_read += _fread(dest, 1, bytes_left, stream);
613 return total_read / size;
614 }
615
616 /* Make sure no data is pending write. */
617 if (stream->buf_state == _bs_write)
618 _fflushbuf(stream);
619
620 /* Perform lazy allocation of stream buffer. */
621 if (stream->buf == NULL) {
622 if (_fallocbuf(stream) != 0)
623 return 0; /* Errno set by _fallocbuf(). */
624 }
625
626 while ((!stream->error) && (!stream->eof) && (bytes_left > 0)) {
627 if (stream->buf_head == stream->buf_tail)
628 _ffillbuf(stream);
629
630 if (stream->error || stream->eof) {
631 /* On error errno was set by _ffillbuf() */
632 break;
633 }
634
635 data_avail = stream->buf_head - stream->buf_tail;
636
637 if (bytes_left > data_avail)
638 now = data_avail;
639 else
640 now = bytes_left;
641
642 for (i = 0; i < now; i++) {
643 dp[i] = stream->buf_tail[i];
644 }
645
646 dp += now;
647 stream->buf_tail += now;
648 bytes_left -= now;
649 total_read += now;
650 }
651
652 return (total_read / size);
653}
654
655/** Write to a stream.
656 *
657 * @param buf Source buffer.
658 * @param size Size of each record.
659 * @param nmemb Number of records to write.
660 * @param stream Pointer to the stream.
661 *
662 */
663size_t fwrite(const void *buf, size_t size, size_t nmemb, FILE *stream)
664{
665 uint8_t *data;
666 size_t bytes_left;
667 size_t now;
668 size_t buf_free;
669 size_t total_written;
670 size_t i;
671 uint8_t b;
672 bool need_flush;
673
674 if (size == 0 || nmemb == 0)
675 return 0;
676
677 /* If not buffered stream, write out directly. */
678 if (stream->btype == _IONBF) {
679 now = _fwrite(buf, size, nmemb, stream);
680 fflush(stream);
681 return now;
682 }
683
684 /* Make sure buffer contains no prefetched data. */
685 if (stream->buf_state == _bs_read)
686 _fflushbuf(stream);
687
688 /* Perform lazy allocation of stream buffer. */
689 if (stream->buf == NULL) {
690 if (_fallocbuf(stream) != 0)
691 return 0; /* Errno set by _fallocbuf(). */
692 }
693
694 data = (uint8_t *) buf;
695 bytes_left = size * nmemb;
696 total_written = 0;
697 need_flush = false;
698
699 while ((!stream->error) && (bytes_left > 0)) {
700 buf_free = stream->buf_size - (stream->buf_head - stream->buf);
701 if (bytes_left > buf_free)
702 now = buf_free;
703 else
704 now = bytes_left;
705
706 for (i = 0; i < now; i++) {
707 b = data[i];
708 stream->buf_head[i] = b;
709
710 if ((b == '\n') && (stream->btype == _IOLBF))
711 need_flush = true;
712 }
713
714 data += now;
715 stream->buf_head += now;
716 buf_free -= now;
717 bytes_left -= now;
718 total_written += now;
719 stream->buf_state = _bs_write;
720
721 if (buf_free == 0) {
722 /* Only need to drain buffer. */
723 _fflushbuf(stream);
724 if (!stream->error)
725 need_flush = false;
726 }
727 }
728
729 if (need_flush)
730 fflush(stream);
731
732 return (total_written / size);
733}
734
735wint_t fputwc(wchar_t wc, FILE *stream)
736{
737 char buf[STR_BOUNDS(1)];
738 size_t sz = 0;
739
740 if (chr_encode(wc, buf, &sz, STR_BOUNDS(1)) != EOK) {
741 errno = EILSEQ;
742 return WEOF;
743 }
744
745 size_t wr = fwrite(buf, 1, sz, stream);
746 if (wr < sz)
747 return WEOF;
748
749 return wc;
750}
751
752wint_t fputuc(char32_t wc, FILE *stream)
753{
754 char buf[STR_BOUNDS(1)];
755 size_t sz = 0;
756
757 if (chr_encode(wc, buf, &sz, STR_BOUNDS(1)) != EOK) {
758 errno = EILSEQ;
759 return WEOF;
760 }
761
762 size_t wr = fwrite(buf, 1, sz, stream);
763 if (wr < sz)
764 return WEOF;
765
766 return wc;
767}
768
769wint_t putwchar(wchar_t wc)
770{
771 return fputwc(wc, stdout);
772}
773
774wint_t putuchar(char32_t wc)
775{
776 return fputuc(wc, stdout);
777}
778
779int fputc(int c, FILE *stream)
780{
781 unsigned char b;
782 size_t wr;
783
784 b = (unsigned char) c;
785 wr = fwrite(&b, sizeof(b), 1, stream);
786 if (wr < 1)
787 return EOF;
788
789 return b;
790}
791
792int putchar(int c)
793{
794 return fputc(c, stdout);
795}
796
797int fputs(const char *str, FILE *stream)
798{
799 (void) fwrite(str, str_size(str), 1, stream);
800 if (ferror(stream))
801 return EOF;
802 return 0;
803}
804
805int puts(const char *str)
806{
807 if (fputs(str, stdout) < 0)
808 return EOF;
809 return putchar('\n');
810}
811
812int fgetc(FILE *stream)
813{
814 char c;
815
816 /* This could be made faster by only flushing when needed. */
817 if (stdout)
818 fflush(stdout);
819 if (stderr)
820 fflush(stderr);
821
822 if (fread(&c, sizeof(char), 1, stream) < sizeof(char))
823 return EOF;
824
825 return (int) c;
826}
827
828char *fgets(char *str, int size, FILE *stream)
829{
830 int c;
831 int idx;
832
833 idx = 0;
834 while (idx < size - 1) {
835 c = fgetc(stream);
836 if (c == EOF)
837 break;
838
839 str[idx++] = c;
840
841 if (c == '\n')
842 break;
843 }
844
845 if (ferror(stream))
846 return NULL;
847
848 if (idx == 0)
849 return NULL;
850
851 str[idx] = '\0';
852 return str;
853}
854
855int getchar(void)
856{
857 return fgetc(stdin);
858}
859
860int ungetc(int c, FILE *stream)
861{
862 if (c == EOF)
863 return EOF;
864
865 if (stream->ungetc_chars >= UNGETC_MAX)
866 return EOF;
867
868 stream->ungetc_buf[stream->ungetc_chars++] =
869 (uint8_t)c;
870
871 stream->eof = false;
872 return (uint8_t)c;
873}
874
875int fseek64(FILE *stream, off64_t offset, int whence)
876{
877 errno_t rc;
878
879 if (stream->error)
880 return -1;
881
882 _fflushbuf(stream);
883 if (stream->error) {
884 /* errno was set by _fflushbuf() */
885 return -1;
886 }
887
888 stream->ungetc_chars = 0;
889
890 vfs_stat_t st;
891 switch (whence) {
892 case SEEK_SET:
893 stream->pos = offset;
894 break;
895 case SEEK_CUR:
896 stream->pos += offset;
897 break;
898 case SEEK_END:
899 rc = vfs_stat(stream->fd, &st);
900 if (rc != EOK) {
901 errno = rc;
902 stream->error = true;
903 return -1;
904 }
905 stream->pos = st.size + offset;
906 break;
907 }
908
909 stream->eof = false;
910 return 0;
911}
912
913off64_t ftell64(FILE *stream)
914{
915 if (stream->error)
916 return EOF;
917
918 _fflushbuf(stream);
919 if (stream->error) {
920 /* errno was set by _fflushbuf() */
921 return EOF;
922 }
923
924 return stream->pos - stream->ungetc_chars;
925}
926
927int fseek(FILE *stream, long offset, int whence)
928{
929 return fseek64(stream, offset, whence);
930}
931
932long ftell(FILE *stream)
933{
934 off64_t off = ftell64(stream);
935
936 /* The native position is too large for the C99-ish interface. */
937 if (off > LONG_MAX) {
938 errno = EOVERFLOW;
939 return -1;
940 }
941
942 return off;
943}
944
945void rewind(FILE *stream)
946{
947 (void) fseek(stream, 0, SEEK_SET);
948}
949
950int fflush(FILE *stream)
951{
952 if (stream->error)
953 return EOF;
954
955 _fflushbuf(stream);
956 if (stream->error) {
957 /* errno was set by _fflushbuf() */
958 return EOF;
959 }
960
961 if (stream->need_sync) {
962 /**
963 * Better than syncing always, but probably still not the
964 * right thing to do.
965 */
966 if (stream->ops->flush(stream) == EOF)
967 return EOF;
968
969 stream->need_sync = false;
970 }
971
972 return 0;
973}
974
975int feof(FILE *stream)
976{
977 return stream->eof;
978}
979
980int ferror(FILE *stream)
981{
982 return stream->error;
983}
984
985void clearerr(FILE *stream)
986{
987 stream->eof = false;
988 stream->error = false;
989}
990
991int fileno(FILE *stream)
992{
993 if (stream->ops != &stdio_vfs_ops) {
994 errno = EBADF;
995 return EOF;
996 }
997
998 return stream->fd;
999}
1000
1001async_sess_t *vfs_fsession(FILE *stream, iface_t iface)
1002{
1003 if (stream->fd >= 0) {
1004 if (stream->sess == NULL)
1005 stream->sess = vfs_fd_session(stream->fd, iface);
1006
1007 return stream->sess;
1008 }
1009
1010 return NULL;
1011}
1012
1013errno_t vfs_fhandle(FILE *stream, int *handle)
1014{
1015 if (stream->fd >= 0) {
1016 *handle = stream->fd;
1017 return EOK;
1018 }
1019
1020 return ENOENT;
1021}
1022
1023/** Read from KIO stream. */
1024static size_t stdio_kio_read(void *buf, size_t size, size_t nmemb, FILE *stream)
1025{
1026 stream->eof = true;
1027 return 0;
1028}
1029
1030/** Write to KIO stream. */
1031static size_t stdio_kio_write(const void *buf, size_t size, size_t nmemb,
1032 FILE *stream)
1033{
1034 errno_t rc;
1035 size_t nwritten;
1036
1037 rc = kio_write(buf, size * nmemb, &nwritten);
1038 if (rc != EOK) {
1039 stream->error = true;
1040 nwritten = 0;
1041 }
1042
1043 return nwritten / size;
1044}
1045
1046/** Flush KIO stream. */
1047static int stdio_kio_flush(FILE *stream)
1048{
1049 kio_update();
1050 return 0;
1051}
1052
1053/** Read from VFS stream. */
1054static size_t stdio_vfs_read(void *buf, size_t size, size_t nmemb, FILE *stream)
1055{
1056 errno_t rc;
1057 size_t nread;
1058
1059 if (size == 0 || nmemb == 0)
1060 return 0;
1061
1062 rc = vfs_read(stream->fd, &stream->pos, buf, size * nmemb, &nread);
1063 if (rc != EOK) {
1064 errno = rc;
1065 stream->error = true;
1066 } else if (nread == 0) {
1067 stream->eof = true;
1068 }
1069
1070 return (nread / size);
1071}
1072
1073/** Write to VFS stream. */
1074static size_t stdio_vfs_write(const void *buf, size_t size, size_t nmemb,
1075 FILE *stream)
1076{
1077 errno_t rc;
1078 size_t nwritten;
1079
1080 rc = vfs_write(stream->fd, &stream->pos, buf, size * nmemb, &nwritten);
1081 if (rc != EOK) {
1082 errno = rc;
1083 stream->error = true;
1084 }
1085
1086 return nwritten / size;
1087}
1088
1089/** Flush VFS stream. */
1090static int stdio_vfs_flush(FILE *stream)
1091{
1092 errno_t rc;
1093
1094 rc = vfs_sync(stream->fd);
1095 if (rc != EOK) {
1096 errno = rc;
1097 return EOF;
1098 }
1099
1100 return 0;
1101}
1102
1103/** @}
1104 */
Note: See TracBrowser for help on using the repository browser.