source: mainline/uspace/lib/c/generic/io/io.c@ 08e103d4

Last change on this file since 08e103d4 was 08e103d4, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

Use clearer naming for string length functions

This and the following commit change the names of functions, as well as
their documentation, to use unambiguous terms "bytes" and "code points"
instead of ambiguous terms "size", "length", and "characters".

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