source: mainline/uspace/lib/c/generic/io/io.c@ 6ff23ff

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 6ff23ff was 7c3fb9b, checked in by Jiri Svoboda <jiri@…>, 7 years ago

Fix block comment formatting (ccheck).

  • Property mode set to 100644
File size: 17.9 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 <stdbool.h>
40#include <stdlib.h>
41#include <async.h>
42#include <io/kio.h>
43#include <vfs/vfs.h>
44#include <vfs/vfs_sess.h>
45#include <vfs/inbox.h>
46#include <ipc/loc.h>
47#include <adt/list.h>
48#include "../private/io.h"
49#include "../private/stdio.h"
50
51static void _ffillbuf(FILE *stream);
52static void _fflushbuf(FILE *stream);
53
54static FILE stdin_null = {
55 .fd = -1,
56 .pos = 0,
57 .error = true,
58 .eof = true,
59 .kio = false,
60 .sess = NULL,
61 .btype = _IONBF,
62 .buf = NULL,
63 .buf_size = 0,
64 .buf_head = NULL,
65 .buf_tail = NULL,
66 .buf_state = _bs_empty
67};
68
69static FILE stdout_kio = {
70 .fd = -1,
71 .pos = 0,
72 .error = false,
73 .eof = false,
74 .kio = true,
75 .sess = NULL,
76 .btype = _IOLBF,
77 .buf = NULL,
78 .buf_size = BUFSIZ,
79 .buf_head = NULL,
80 .buf_tail = NULL,
81 .buf_state = _bs_empty
82};
83
84static FILE stderr_kio = {
85 .fd = -1,
86 .pos = 0,
87 .error = false,
88 .eof = false,
89 .kio = true,
90 .sess = NULL,
91 .btype = _IONBF,
92 .buf = NULL,
93 .buf_size = 0,
94 .buf_head = NULL,
95 .buf_tail = NULL,
96 .buf_state = _bs_empty
97};
98
99FILE *stdin = NULL;
100FILE *stdout = NULL;
101FILE *stderr = NULL;
102
103static LIST_INITIALIZE(files);
104
105void __stdio_init(void)
106{
107 /*
108 * The first three standard file descriptors are assigned for compatibility.
109 * This will probably be removed later.
110 */
111 int infd = inbox_get("stdin");
112 if (infd >= 0) {
113 int stdinfd = -1;
114 (void) vfs_clone(infd, -1, false, &stdinfd);
115 assert(stdinfd == 0);
116 vfs_open(stdinfd, MODE_READ);
117 stdin = fdopen(stdinfd, "r");
118 } else {
119 stdin = &stdin_null;
120 list_append(&stdin->link, &files);
121 }
122
123 int outfd = inbox_get("stdout");
124 if (outfd >= 0) {
125 int stdoutfd = -1;
126 (void) vfs_clone(outfd, -1, false, &stdoutfd);
127 assert(stdoutfd <= 1);
128 while (stdoutfd < 1)
129 (void) vfs_clone(outfd, -1, false, &stdoutfd);
130 vfs_open(stdoutfd, MODE_APPEND);
131 stdout = fdopen(stdoutfd, "a");
132 } else {
133 stdout = &stdout_kio;
134 list_append(&stdout->link, &files);
135 }
136
137 int errfd = inbox_get("stderr");
138 if (errfd >= 0) {
139 int stderrfd = -1;
140 (void) vfs_clone(errfd, -1, false, &stderrfd);
141 assert(stderrfd <= 2);
142 while (stderrfd < 2)
143 (void) vfs_clone(errfd, -1, false, &stderrfd);
144 vfs_open(stderrfd, MODE_APPEND);
145 stderr = fdopen(stderrfd, "a");
146 } else {
147 stderr = &stderr_kio;
148 list_append(&stderr->link, &files);
149 }
150}
151
152void __stdio_done(void)
153{
154 while (!list_empty(&files)) {
155 FILE *file = list_get_instance(list_first(&files), FILE, link);
156 fclose(file);
157 }
158}
159
160static bool parse_mode(const char *fmode, int *mode, bool *create, bool *truncate)
161{
162 /* Parse mode except first character. */
163 const char *mp = fmode;
164 if (*mp++ == 0) {
165 errno = EINVAL;
166 return false;
167 }
168
169 if ((*mp == 'b') || (*mp == 't'))
170 mp++;
171
172 bool plus;
173 if (*mp == '+') {
174 mp++;
175 plus = true;
176 } else
177 plus = false;
178
179 if (*mp != 0) {
180 errno = EINVAL;
181 return false;
182 }
183
184 *create = false;
185 *truncate = false;
186
187 /* Parse first character of fmode and determine mode for vfs_open(). */
188 switch (fmode[0]) {
189 case 'r':
190 *mode = plus ? MODE_READ | MODE_WRITE : MODE_READ;
191 break;
192 case 'w':
193 *mode = plus ? MODE_READ | MODE_WRITE : MODE_WRITE;
194 *create = true;
195 if (!plus)
196 *truncate = true;
197 break;
198 case 'a':
199 /* TODO: a+ must read from beginning, append to the end. */
200 if (plus) {
201 errno = ENOTSUP;
202 return false;
203 }
204
205 *mode = MODE_APPEND | (plus ? MODE_READ | MODE_WRITE : MODE_WRITE);
206 *create = true;
207 break;
208 default:
209 errno = EINVAL;
210 return false;
211 }
212
213 return true;
214}
215
216/** Set stream buffer. */
217void setvbuf(FILE *stream, void *buf, int mode, size_t size)
218{
219 stream->btype = mode;
220 stream->buf = buf;
221 stream->buf_size = size;
222 stream->buf_head = stream->buf;
223 stream->buf_tail = stream->buf;
224 stream->buf_state = _bs_empty;
225}
226
227/** Set stream buffer.
228 *
229 * When @p buf is NULL, the stream is set as unbuffered, otherwise
230 * full buffering is enabled.
231 */
232void setbuf(FILE *stream, void *buf)
233{
234 if (buf == NULL) {
235 setvbuf(stream, NULL, _IONBF, BUFSIZ);
236 } else {
237 setvbuf(stream, buf, _IOFBF, BUFSIZ);
238 }
239}
240
241static void _setvbuf(FILE *stream)
242{
243 /* FIXME: Use more complex rules for setting buffering options. */
244
245 switch (stream->fd) {
246 case 1:
247 setvbuf(stream, NULL, _IOLBF, BUFSIZ);
248 break;
249 case 0:
250 case 2:
251 setvbuf(stream, NULL, _IONBF, 0);
252 break;
253 default:
254 setvbuf(stream, NULL, _IOFBF, BUFSIZ);
255 }
256}
257
258/** Allocate stream buffer. */
259static int _fallocbuf(FILE *stream)
260{
261 assert(stream->buf == NULL);
262
263 stream->buf = malloc(stream->buf_size);
264 if (stream->buf == NULL) {
265 errno = ENOMEM;
266 return EOF;
267 }
268
269 stream->buf_head = stream->buf;
270 stream->buf_tail = stream->buf;
271 return 0;
272}
273
274/** Open a stream.
275 *
276 * @param path Path of the file to open.
277 * @param mode Mode string, (r|w|a)[b|t][+].
278 *
279 */
280FILE *fopen(const char *path, const char *fmode)
281{
282 int mode;
283 bool create;
284 bool truncate;
285
286 if (!parse_mode(fmode, &mode, &create, &truncate))
287 return NULL;
288
289 /* Open file. */
290 FILE *stream = malloc(sizeof(FILE));
291 if (stream == NULL) {
292 errno = ENOMEM;
293 return NULL;
294 }
295
296 int flags = WALK_REGULAR;
297 if (create)
298 flags |= WALK_MAY_CREATE;
299 int file;
300 errno_t rc = vfs_lookup(path, flags, &file);
301 if (rc != EOK) {
302 errno = rc;
303 free(stream);
304 return NULL;
305 }
306
307 rc = vfs_open(file, mode);
308 if (rc != EOK) {
309 errno = rc;
310 vfs_put(file);
311 free(stream);
312 return NULL;
313 }
314
315 if (truncate) {
316 rc = vfs_resize(file, 0);
317 if (rc != EOK) {
318 errno = rc;
319 vfs_put(file);
320 free(stream);
321 return NULL;
322 }
323 }
324
325 stream->fd = file;
326 stream->pos = 0;
327 stream->error = false;
328 stream->eof = false;
329 stream->kio = false;
330 stream->sess = NULL;
331 stream->need_sync = false;
332 _setvbuf(stream);
333 stream->ungetc_chars = 0;
334
335 list_append(&stream->link, &files);
336
337 return stream;
338}
339
340FILE *fdopen(int fd, const char *mode)
341{
342 /* Open file. */
343 FILE *stream = malloc(sizeof(FILE));
344 if (stream == NULL) {
345 errno = ENOMEM;
346 return NULL;
347 }
348
349 stream->fd = fd;
350 stream->pos = 0;
351 stream->error = false;
352 stream->eof = false;
353 stream->kio = false;
354 stream->sess = NULL;
355 stream->need_sync = false;
356 _setvbuf(stream);
357 stream->ungetc_chars = 0;
358
359 list_append(&stream->link, &files);
360
361 return stream;
362}
363
364
365static int _fclose_nofree(FILE *stream)
366{
367 errno_t rc = 0;
368
369 fflush(stream);
370
371 if (stream->sess != NULL)
372 async_hangup(stream->sess);
373
374 if (stream->fd >= 0)
375 rc = vfs_put(stream->fd);
376
377 list_remove(&stream->link);
378
379 if (rc != EOK) {
380 errno = rc;
381 return EOF;
382 }
383
384 return 0;
385}
386
387int fclose(FILE *stream)
388{
389 int rc = _fclose_nofree(stream);
390
391 if ((stream != &stdin_null) &&
392 (stream != &stdout_kio) &&
393 (stream != &stderr_kio))
394 free(stream);
395
396 return rc;
397}
398
399FILE *freopen(const char *path, const char *mode, FILE *stream)
400{
401 FILE *nstr;
402
403 if (path == NULL) {
404 /* Changing mode is not supported */
405 return NULL;
406 }
407
408 (void) _fclose_nofree(stream);
409 nstr = fopen(path, mode);
410 if (nstr == NULL) {
411 free(stream);
412 return NULL;
413 }
414
415 list_remove(&nstr->link);
416 *stream = *nstr;
417 list_append(&stream->link, &files);
418
419 free(nstr);
420
421 return stream;
422}
423
424/** Read from a stream (unbuffered).
425 *
426 * @param buf Destination buffer.
427 * @param size Size of each record.
428 * @param nmemb Number of records to read.
429 * @param stream Pointer to the stream.
430 *
431 * @return Number of elements successfully read. On error this is less than
432 * nmemb, stream error indicator is set and errno is set.
433 */
434static size_t _fread(void *buf, size_t size, size_t nmemb, FILE *stream)
435{
436 errno_t rc;
437 size_t nread;
438
439 if (size == 0 || nmemb == 0)
440 return 0;
441
442 rc = vfs_read(stream->fd, &stream->pos, buf, size * nmemb, &nread);
443 if (rc != EOK) {
444 errno = rc;
445 stream->error = true;
446 } else if (nread == 0) {
447 stream->eof = true;
448 }
449
450 return (nread / size);
451}
452
453/** Write to a stream (unbuffered).
454 *
455 * @param buf Source buffer.
456 * @param size Size of each record.
457 * @param nmemb Number of records to write.
458 * @param stream Pointer to the stream.
459 *
460 * @return Number of elements successfully written. On error this is less than
461 * nmemb, stream error indicator is set and errno is set.
462 */
463static size_t _fwrite(const void *buf, size_t size, size_t nmemb, FILE *stream)
464{
465 errno_t rc;
466 size_t nwritten;
467
468 if (size == 0 || nmemb == 0)
469 return 0;
470
471 if (stream->kio) {
472 rc = kio_write(buf, size * nmemb, &nwritten);
473 if (rc != EOK) {
474 stream->error = true;
475 nwritten = 0;
476 }
477 } else {
478 rc = vfs_write(stream->fd, &stream->pos, buf, size * nmemb,
479 &nwritten);
480 if (rc != EOK) {
481 errno = rc;
482 stream->error = true;
483 }
484 }
485
486 if (nwritten > 0)
487 stream->need_sync = true;
488
489 return (nwritten / size);
490}
491
492/** Read some data in stream buffer.
493 *
494 * On error, stream error indicator is set and errno is set.
495 */
496static void _ffillbuf(FILE *stream)
497{
498 errno_t rc;
499 size_t nread;
500
501 stream->buf_head = stream->buf_tail = stream->buf;
502
503 rc = vfs_read(stream->fd, &stream->pos, stream->buf, stream->buf_size,
504 &nread);
505 if (rc != EOK) {
506 errno = rc;
507 stream->error = true;
508 return;
509 }
510
511 if (nread == 0) {
512 stream->eof = true;
513 return;
514 }
515
516 stream->buf_head += nread;
517 stream->buf_state = _bs_read;
518}
519
520/** Write out stream buffer, do not sync stream. */
521static void _fflushbuf(FILE *stream)
522{
523 size_t bytes_used;
524
525 if ((!stream->buf) || (stream->btype == _IONBF) || (stream->error))
526 return;
527
528 bytes_used = stream->buf_head - stream->buf_tail;
529
530 /* If buffer has prefetched read data, we need to seek back. */
531 if (bytes_used > 0 && stream->buf_state == _bs_read)
532 stream->pos -= bytes_used;
533
534 /* If buffer has unwritten data, we need to write them out. */
535 if (bytes_used > 0 && stream->buf_state == _bs_write) {
536 (void) _fwrite(stream->buf_tail, 1, bytes_used, stream);
537 /* On error stream error indicator and errno are set by _fwrite */
538 if (stream->error)
539 return;
540 }
541
542 stream->buf_head = stream->buf;
543 stream->buf_tail = stream->buf;
544 stream->buf_state = _bs_empty;
545}
546
547/** Read from a stream.
548 *
549 * @param dest Destination buffer.
550 * @param size Size of each record.
551 * @param nmemb Number of records to read.
552 * @param stream Pointer to the stream.
553 *
554 */
555size_t fread(void *dest, size_t size, size_t nmemb, FILE *stream)
556{
557 uint8_t *dp;
558 size_t bytes_left;
559 size_t now;
560 size_t data_avail;
561 size_t total_read;
562 size_t i;
563
564 if (size == 0 || nmemb == 0)
565 return 0;
566
567 bytes_left = size * nmemb;
568 total_read = 0;
569 dp = (uint8_t *) dest;
570
571 /* Bytes from ungetc() buffer */
572 while (stream->ungetc_chars > 0 && bytes_left > 0) {
573 *dp++ = stream->ungetc_buf[--stream->ungetc_chars];
574 ++total_read;
575 --bytes_left;
576 }
577
578 /* If not buffered stream, read in directly. */
579 if (stream->btype == _IONBF) {
580 total_read += _fread(dest, 1, bytes_left, stream);
581 return total_read / size;
582 }
583
584 /* Make sure no data is pending write. */
585 if (stream->buf_state == _bs_write)
586 _fflushbuf(stream);
587
588 /* Perform lazy allocation of stream buffer. */
589 if (stream->buf == NULL) {
590 if (_fallocbuf(stream) != 0)
591 return 0; /* Errno set by _fallocbuf(). */
592 }
593
594 while ((!stream->error) && (!stream->eof) && (bytes_left > 0)) {
595 if (stream->buf_head == stream->buf_tail)
596 _ffillbuf(stream);
597
598 if (stream->error || stream->eof) {
599 /* On error errno was set by _ffillbuf() */
600 break;
601 }
602
603 data_avail = stream->buf_head - stream->buf_tail;
604
605 if (bytes_left > data_avail)
606 now = data_avail;
607 else
608 now = bytes_left;
609
610 for (i = 0; i < now; i++) {
611 dp[i] = stream->buf_tail[i];
612 }
613
614 dp += now;
615 stream->buf_tail += now;
616 bytes_left -= now;
617 total_read += now;
618 }
619
620 return (total_read / size);
621}
622
623
624/** Write to a stream.
625 *
626 * @param buf Source buffer.
627 * @param size Size of each record.
628 * @param nmemb Number of records to write.
629 * @param stream Pointer to the stream.
630 *
631 */
632size_t fwrite(const void *buf, size_t size, size_t nmemb, FILE *stream)
633{
634 uint8_t *data;
635 size_t bytes_left;
636 size_t now;
637 size_t buf_free;
638 size_t total_written;
639 size_t i;
640 uint8_t b;
641 bool need_flush;
642
643 if (size == 0 || nmemb == 0)
644 return 0;
645
646 /* If not buffered stream, write out directly. */
647 if (stream->btype == _IONBF) {
648 now = _fwrite(buf, size, nmemb, stream);
649 fflush(stream);
650 return now;
651 }
652
653 /* Make sure buffer contains no prefetched data. */
654 if (stream->buf_state == _bs_read)
655 _fflushbuf(stream);
656
657 /* Perform lazy allocation of stream buffer. */
658 if (stream->buf == NULL) {
659 if (_fallocbuf(stream) != 0)
660 return 0; /* Errno set by _fallocbuf(). */
661 }
662
663 data = (uint8_t *) buf;
664 bytes_left = size * nmemb;
665 total_written = 0;
666 need_flush = false;
667
668 while ((!stream->error) && (bytes_left > 0)) {
669 buf_free = stream->buf_size - (stream->buf_head - stream->buf);
670 if (bytes_left > buf_free)
671 now = buf_free;
672 else
673 now = bytes_left;
674
675 for (i = 0; i < now; i++) {
676 b = data[i];
677 stream->buf_head[i] = b;
678
679 if ((b == '\n') && (stream->btype == _IOLBF))
680 need_flush = true;
681 }
682
683 data += now;
684 stream->buf_head += now;
685 buf_free -= now;
686 bytes_left -= now;
687 total_written += now;
688 stream->buf_state = _bs_write;
689
690 if (buf_free == 0) {
691 /* Only need to drain buffer. */
692 _fflushbuf(stream);
693 if (!stream->error)
694 need_flush = false;
695 }
696 }
697
698 if (need_flush)
699 fflush(stream);
700
701 return (total_written / size);
702}
703
704int fputc(wchar_t c, FILE *stream)
705{
706 char buf[STR_BOUNDS(1)];
707 size_t sz = 0;
708
709 if (chr_encode(c, buf, &sz, STR_BOUNDS(1)) == EOK) {
710 size_t wr = fwrite(buf, 1, sz, stream);
711
712 if (wr < sz)
713 return EOF;
714
715 return (int) c;
716 }
717
718 return EOF;
719}
720
721int putchar(wchar_t c)
722{
723 return fputc(c, stdout);
724}
725
726int fputs(const char *str, FILE *stream)
727{
728 (void) fwrite(str, str_size(str), 1, stream);
729 if (ferror(stream))
730 return EOF;
731 return 0;
732}
733
734int puts(const char *str)
735{
736 if (fputs(str, stdout) < 0)
737 return EOF;
738 return putchar('\n');
739}
740
741int fgetc(FILE *stream)
742{
743 char c;
744
745 /* This could be made faster by only flushing when needed. */
746 if (stdout)
747 fflush(stdout);
748 if (stderr)
749 fflush(stderr);
750
751 if (fread(&c, sizeof(char), 1, stream) < sizeof(char))
752 return EOF;
753
754 return (int) c;
755}
756
757char *fgets(char *str, int size, FILE *stream)
758{
759 int c;
760 int idx;
761
762 idx = 0;
763 while (idx < size - 1) {
764 c = fgetc(stream);
765 if (c == EOF)
766 break;
767
768 str[idx++] = c;
769
770 if (c == '\n')
771 break;
772 }
773
774 if (ferror(stream))
775 return NULL;
776
777 if (idx == 0)
778 return NULL;
779
780 str[idx] = '\0';
781 return str;
782}
783
784int getchar(void)
785{
786 return fgetc(stdin);
787}
788
789int ungetc(int c, FILE *stream)
790{
791 if (c == EOF)
792 return EOF;
793
794 if (stream->ungetc_chars >= UNGETC_MAX)
795 return EOF;
796
797 stream->ungetc_buf[stream->ungetc_chars++] =
798 (uint8_t)c;
799
800 stream->eof = false;
801 return (uint8_t)c;
802}
803
804int fseek64(FILE *stream, off64_t offset, int whence)
805{
806 errno_t rc;
807
808 if (stream->error)
809 return -1;
810
811 _fflushbuf(stream);
812 if (stream->error) {
813 /* errno was set by _fflushbuf() */
814 return -1;
815 }
816
817 stream->ungetc_chars = 0;
818
819 vfs_stat_t st;
820 switch (whence) {
821 case SEEK_SET:
822 stream->pos = offset;
823 break;
824 case SEEK_CUR:
825 stream->pos += offset;
826 break;
827 case SEEK_END:
828 rc = vfs_stat(stream->fd, &st);
829 if (rc != EOK) {
830 errno = rc;
831 stream->error = true;
832 return -1;
833 }
834 stream->pos = st.size + offset;
835 break;
836 }
837
838 stream->eof = false;
839 return 0;
840}
841
842off64_t ftell64(FILE *stream)
843{
844 if (stream->error)
845 return EOF;
846
847 _fflushbuf(stream);
848 if (stream->error) {
849 /* errno was set by _fflushbuf() */
850 return EOF;
851 }
852
853 return stream->pos - stream->ungetc_chars;
854}
855
856int fseek(FILE *stream, long offset, int whence)
857{
858 return fseek64(stream, offset, whence);
859}
860
861long ftell(FILE *stream)
862{
863 off64_t off = ftell64(stream);
864
865 /* The native position is too large for the C99-ish interface. */
866 if (off > LONG_MAX)
867 return EOF;
868
869 return off;
870}
871
872void rewind(FILE *stream)
873{
874 (void) fseek(stream, 0, SEEK_SET);
875}
876
877int fflush(FILE *stream)
878{
879 if (stream->error)
880 return EOF;
881
882 _fflushbuf(stream);
883 if (stream->error) {
884 /* errno was set by _fflushbuf() */
885 return EOF;
886 }
887
888 if (stream->kio) {
889 kio_update();
890 return 0;
891 }
892
893 if ((stream->fd >= 0) && (stream->need_sync)) {
894 errno_t rc;
895
896 /**
897 * Better than syncing always, but probably still not the
898 * right thing to do.
899 */
900 stream->need_sync = false;
901 rc = vfs_sync(stream->fd);
902 if (rc != EOK) {
903 errno = rc;
904 return EOF;
905 }
906
907 return 0;
908 }
909
910 return 0;
911}
912
913int feof(FILE *stream)
914{
915 return stream->eof;
916}
917
918int ferror(FILE *stream)
919{
920 return stream->error;
921}
922
923void clearerr(FILE *stream)
924{
925 stream->eof = false;
926 stream->error = false;
927}
928
929int fileno(FILE *stream)
930{
931 if (stream->kio) {
932 errno = EBADF;
933 return EOF;
934 }
935
936 return stream->fd;
937}
938
939async_sess_t *vfs_fsession(FILE *stream, iface_t iface)
940{
941 if (stream->fd >= 0) {
942 if (stream->sess == NULL)
943 stream->sess = vfs_fd_session(stream->fd, iface);
944
945 return stream->sess;
946 }
947
948 return NULL;
949}
950
951errno_t vfs_fhandle(FILE *stream, int *handle)
952{
953 if (stream->fd >= 0) {
954 *handle = stream->fd;
955 return EOK;
956 }
957
958 return ENOENT;
959}
960
961/** @}
962 */
Note: See TracBrowser for help on using the repository browser.