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
RevLine 
[b861b58]1/*
[df4ed85]2 * Copyright (c) 2005 Martin Decky
[b861b58]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.
[b2951e2]27 */
28
[fadd381]29/** @addtogroup libc
[b2951e2]30 * @{
31 */
32/** @file
[2595dab]33 */
[b861b58]34
[cc6f688]35#include <stdio.h>
[ef8bcc6]36#include <assert.h>
[19f857a]37#include <str.h>
[56fa418]38#include <errno.h>
[3f7fe9e]39#include <limits.h>
[3e6a98c5]40#include <stdbool.h>
[38d150e]41#include <stdlib.h>
[64d2b10]42#include <async.h>
[6fa9a99d]43#include <io/kio.h>
[2595dab]44#include <vfs/vfs.h>
[79ae36dd]45#include <vfs/vfs_sess.h>
[bb9ec2d]46#include <vfs/inbox.h>
[15f3c3f]47#include <ipc/loc.h>
[d9c8c81]48#include <adt/list.h>
[ed88c8e]49#include <wchar.h>
[28a5ebd]50#include <uchar.h>
[47b7006]51#include "../private/io.h"
[79ae36dd]52#include "../private/stdio.h"
[b861b58]53
[facebd56]54static void _ffillbuf(FILE *stream);
[ef8bcc6]55static void _fflushbuf(FILE *stream);
56
[01cc7b4]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
[a68f737]80static FILE stdin_null = {
[2595dab]81 .fd = -1,
[1b20da0]82 .pos = 0,
[2595dab]83 .error = true,
84 .eof = true,
[01cc7b4]85 .ops = &stdio_vfs_ops,
86 .arg = NULL,
[79ae36dd]87 .sess = NULL,
[ef8bcc6]88 .btype = _IONBF,
89 .buf = NULL,
90 .buf_size = 0,
[facebd56]91 .buf_head = NULL,
92 .buf_tail = NULL,
[c70703a]93 .buf_state = _bs_empty
[2595dab]94};
[350514c]95
[6fa9a99d]96static FILE stdout_kio = {
[2595dab]97 .fd = -1,
[1b20da0]98 .pos = 0,
[2595dab]99 .error = false,
100 .eof = false,
[01cc7b4]101 .ops = &stdio_kio_ops,
102 .arg = NULL,
[79ae36dd]103 .sess = NULL,
[ef8bcc6]104 .btype = _IOLBF,
105 .buf = NULL,
106 .buf_size = BUFSIZ,
[facebd56]107 .buf_head = NULL,
108 .buf_tail = NULL,
109 .buf_state = _bs_empty
[2595dab]110};
111
[6fa9a99d]112static FILE stderr_kio = {
[a68f737]113 .fd = -1,
[1b20da0]114 .pos = 0,
[a68f737]115 .error = false,
116 .eof = false,
[01cc7b4]117 .ops = &stdio_kio_ops,
118 .arg = NULL,
[79ae36dd]119 .sess = NULL,
[ef8bcc6]120 .btype = _IONBF,
121 .buf = NULL,
122 .buf_size = 0,
[facebd56]123 .buf_head = NULL,
124 .buf_tail = NULL,
125 .buf_state = _bs_empty
[a68f737]126};
127
128FILE *stdin = NULL;
129FILE *stdout = NULL;
130FILE *stderr = NULL;
131
132static LIST_INITIALIZE(files);
133
[bb9ec2d]134void __stdio_init(void)
[a68f737]135{
[7c3fb9b]136 /*
137 * The first three standard file descriptors are assigned for compatibility.
[bb9ec2d]138 * This will probably be removed later.
139 */
140 int infd = inbox_get("stdin");
141 if (infd >= 0) {
[f77c1c9]142 int stdinfd = -1;
143 (void) vfs_clone(infd, -1, false, &stdinfd);
[bb9ec2d]144 assert(stdinfd == 0);
[b19e892]145 vfs_open(stdinfd, MODE_READ);
[bb9ec2d]146 stdin = fdopen(stdinfd, "r");
[a68f737]147 } else {
148 stdin = &stdin_null;
149 list_append(&stdin->link, &files);
150 }
[a35b458]151
[bb9ec2d]152 int outfd = inbox_get("stdout");
153 if (outfd >= 0) {
[f77c1c9]154 int stdoutfd = -1;
155 (void) vfs_clone(outfd, -1, false, &stdoutfd);
[bb9ec2d]156 assert(stdoutfd <= 1);
[fcab7ef]157 while (stdoutfd < 1)
[f77c1c9]158 (void) vfs_clone(outfd, -1, false, &stdoutfd);
[b19e892]159 vfs_open(stdoutfd, MODE_APPEND);
[bb9ec2d]160 stdout = fdopen(stdoutfd, "a");
[a68f737]161 } else {
[6fa9a99d]162 stdout = &stdout_kio;
[a68f737]163 list_append(&stdout->link, &files);
164 }
[a35b458]165
[bb9ec2d]166 int errfd = inbox_get("stderr");
167 if (errfd >= 0) {
[f77c1c9]168 int stderrfd = -1;
169 (void) vfs_clone(errfd, -1, false, &stderrfd);
[bb9ec2d]170 assert(stderrfd <= 2);
[fcab7ef]171 while (stderrfd < 2)
[f77c1c9]172 (void) vfs_clone(errfd, -1, false, &stderrfd);
[b19e892]173 vfs_open(stderrfd, MODE_APPEND);
[bb9ec2d]174 stderr = fdopen(stderrfd, "a");
[a68f737]175 } else {
[6fa9a99d]176 stderr = &stderr_kio;
[a68f737]177 list_append(&stderr->link, &files);
178 }
179}
180
[db24058]181void __stdio_done(void)
[a68f737]182{
[b72efe8]183 while (!list_empty(&files)) {
184 FILE *file = list_get_instance(list_first(&files), FILE, link);
[a68f737]185 fclose(file);
186 }
187}
[2595dab]188
[7ab7075f]189static bool parse_mode(const char *fmode, int *mode, bool *create, bool *excl,
190 bool *truncate)
[b861b58]191{
[2595dab]192 /* Parse mode except first character. */
[b19e892]193 const char *mp = fmode;
[7ab7075f]194
195 if (*mp++ == '\0') {
[2595dab]196 errno = EINVAL;
197 return false;
198 }
[a35b458]199
[2595dab]200 if ((*mp == 'b') || (*mp == 't'))
201 mp++;
[a35b458]202
[2595dab]203 bool plus;
204 if (*mp == '+') {
205 mp++;
206 plus = true;
[7ab7075f]207 } else {
[2595dab]208 plus = false;
[7ab7075f]209 }
210
211 bool ex;
212 if (*mp == 'x') {
213 mp++;
214 ex = true;
215 } else {
216 ex = false;
217 }
[a35b458]218
[7ab7075f]219 if (*mp != '\0') {
[2595dab]220 errno = EINVAL;
221 return false;
[350514c]222 }
[b19e892]223
224 *create = false;
225 *truncate = false;
[7ab7075f]226 *excl = false;
[a35b458]227
[b19e892]228 /* Parse first character of fmode and determine mode for vfs_open(). */
229 switch (fmode[0]) {
[2595dab]230 case 'r':
[b19e892]231 *mode = plus ? MODE_READ | MODE_WRITE : MODE_READ;
[7ab7075f]232 if (ex) {
233 errno = EINVAL;
234 return false;
235 }
[2595dab]236 break;
237 case 'w':
[b19e892]238 *mode = plus ? MODE_READ | MODE_WRITE : MODE_WRITE;
239 *create = true;
[7ab7075f]240 *excl = ex;
[b19e892]241 if (!plus)
242 *truncate = true;
[2595dab]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 }
[b19e892]250
[7ab7075f]251 if (ex) {
252 errno = EINVAL;
253 return false;
254 }
255
[b19e892]256 *mode = MODE_APPEND | (plus ? MODE_READ | MODE_WRITE : MODE_WRITE);
257 *create = true;
[82582e4]258 break;
[2595dab]259 default:
260 errno = EINVAL;
261 return false;
262 }
[a35b458]263
[2595dab]264 return true;
[cc6f688]265}
[b861b58]266
[ef8bcc6]267/** Set stream buffer. */
[58daded]268int setvbuf(FILE *stream, void *buf, int mode, size_t size)
[ef8bcc6]269{
[58daded]270 if (mode != _IONBF && mode != _IOLBF && mode != _IOFBF)
271 return -1;
272
[ef8bcc6]273 stream->btype = mode;
274 stream->buf = buf;
275 stream->buf_size = size;
276 stream->buf_head = stream->buf;
[facebd56]277 stream->buf_tail = stream->buf;
278 stream->buf_state = _bs_empty;
[58daded]279
280 return 0;
[ef8bcc6]281}
282
[7699c21]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
[bfd247f]297static void _setvbuf(FILE *stream)
298{
299 /* FIXME: Use more complex rules for setting buffering options. */
[a35b458]300
[bfd247f]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
[ef8bcc6]314/** Allocate stream buffer. */
315static int _fallocbuf(FILE *stream)
316{
317 assert(stream->buf == NULL);
[a35b458]318
[ef8bcc6]319 stream->buf = malloc(stream->buf_size);
320 if (stream->buf == NULL) {
321 errno = ENOMEM;
[6afc9d7]322 return EOF;
[ef8bcc6]323 }
[a35b458]324
[ef8bcc6]325 stream->buf_head = stream->buf;
[facebd56]326 stream->buf_tail = stream->buf;
[ef8bcc6]327 return 0;
328}
329
[2595dab]330/** Open a stream.
331 *
332 * @param path Path of the file to open.
[7ab7075f]333 * @param mode Mode string, (r|w|a)[b|t][+][x].
[2595dab]334 *
[4e2cf8b]335 */
[b19e892]336FILE *fopen(const char *path, const char *fmode)
[4e2cf8b]337{
[b19e892]338 int mode;
339 bool create;
[7ab7075f]340 bool excl;
[b19e892]341 bool truncate;
342
[7ab7075f]343 if (!parse_mode(fmode, &mode, &create, &excl, &truncate))
[2595dab]344 return NULL;
[a35b458]345
[2595dab]346 /* Open file. */
347 FILE *stream = malloc(sizeof(FILE));
348 if (stream == NULL) {
349 errno = ENOMEM;
350 return NULL;
351 }
[b19e892]352
353 int flags = WALK_REGULAR;
[7ab7075f]354 if (create && excl)
355 flags |= WALK_MUST_CREATE;
356 else if (create)
[b19e892]357 flags |= WALK_MAY_CREATE;
[f77c1c9]358 int file;
[b7fd2a0]359 errno_t rc = vfs_lookup(path, flags, &file);
[f77c1c9]360 if (rc != EOK) {
361 errno = rc;
[b19e892]362 free(stream);
363 return NULL;
364 }
365
[f77c1c9]366 rc = vfs_open(file, mode);
[b19e892]367 if (rc != EOK) {
368 errno = rc;
[9c4cf0d]369 vfs_put(file);
[2595dab]370 free(stream);
371 return NULL;
372 }
[a35b458]373
[b19e892]374 if (truncate) {
375 rc = vfs_resize(file, 0);
376 if (rc != EOK) {
377 errno = rc;
[9c4cf0d]378 vfs_put(file);
[b19e892]379 free(stream);
380 return NULL;
381 }
382 }
383
384 stream->fd = file;
[58898d1d]385 stream->pos = 0;
[2595dab]386 stream->error = false;
387 stream->eof = false;
[01cc7b4]388 stream->ops = &stdio_vfs_ops;
389 stream->arg = NULL;
[79ae36dd]390 stream->sess = NULL;
[facebd56]391 stream->need_sync = false;
[bfd247f]392 _setvbuf(stream);
[e86a617a]393 stream->ungetc_chars = 0;
[a35b458]394
[a68f737]395 list_append(&stream->link, &files);
[a35b458]396
[2595dab]397 return stream;
398}
399
[080ad7f]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 }
[a35b458]408
[080ad7f]409 stream->fd = fd;
[58898d1d]410 stream->pos = 0;
[080ad7f]411 stream->error = false;
412 stream->eof = false;
[01cc7b4]413 stream->ops = &stdio_vfs_ops;
414 stream->arg = NULL;
[79ae36dd]415 stream->sess = NULL;
[facebd56]416 stream->need_sync = false;
[bfd247f]417 _setvbuf(stream);
[e86a617a]418 stream->ungetc_chars = 0;
[a35b458]419
[080ad7f]420 list_append(&stream->link, &files);
[a35b458]421
[080ad7f]422 return stream;
423}
424
[80bee81]425static int _fclose_nofree(FILE *stream)
[2595dab]426{
[b7fd2a0]427 errno_t rc = 0;
[a35b458]428
[2595dab]429 fflush(stream);
[a35b458]430
[79ae36dd]431 if (stream->sess != NULL)
432 async_hangup(stream->sess);
[a35b458]433
[2595dab]434 if (stream->fd >= 0)
[9c4cf0d]435 rc = vfs_put(stream->fd);
[a35b458]436
[a68f737]437 list_remove(&stream->link);
[a35b458]438
[f77c1c9]439 if (rc != EOK) {
440 errno = rc;
[80bee81]441 return EOF;
442 }
[a35b458]443
[80bee81]444 return 0;
445}
446
447int fclose(FILE *stream)
448{
449 int rc = _fclose_nofree(stream);
[a35b458]450
[3bacee1]451 if ((stream != &stdin_null) &&
452 (stream != &stdout_kio) &&
453 (stream != &stderr_kio))
[2595dab]454 free(stream);
[a35b458]455
[80bee81]456 return rc;
457}
458
459FILE *freopen(const char *path, const char *mode, FILE *stream)
460{
461 FILE *nstr;
[a35b458]462
[80bee81]463 if (path == NULL) {
464 /* Changing mode is not supported */
465 return NULL;
[2595dab]466 }
[a35b458]467
[80bee81]468 (void) _fclose_nofree(stream);
469 nstr = fopen(path, mode);
470 if (nstr == NULL) {
471 free(stream);
472 return NULL;
473 }
[a35b458]474
[80bee81]475 list_remove(&nstr->link);
476 *stream = *nstr;
477 list_append(&stream->link, &files);
[a35b458]478
[80bee81]479 free(nstr);
[a35b458]480
[80bee81]481 return stream;
[4e2cf8b]482}
483
[facebd56]484/** Read from a stream (unbuffered).
[2595dab]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.
[6afc9d7]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.
[4e2cf8b]493 */
[facebd56]494static size_t _fread(void *buf, size_t size, size_t nmemb, FILE *stream)
[4e2cf8b]495{
[01cc7b4]496 return stream->ops->read(buf, size, nmemb, stream);
[2595dab]497}
[55cff86]498
[facebd56]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.
[6afc9d7]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.
[facebd56]508 */
[ef8bcc6]509static size_t _fwrite(const void *buf, size_t size, size_t nmemb, FILE *stream)
[2595dab]510{
[8e3498b]511 size_t nwritten;
512
[002252a]513 if (size == 0 || nmemb == 0)
514 return 0;
515
[01cc7b4]516 nwritten = stream->ops->write(buf, size, nmemb, stream);
[facebd56]517
[8e3498b]518 if (nwritten > 0)
[facebd56]519 stream->need_sync = true;
[8e3498b]520
521 return (nwritten / size);
[4e2cf8b]522}
523
[6afc9d7]524/** Read some data in stream buffer.
525 *
526 * On error, stream error indicator is set and errno is set.
527 */
[facebd56]528static void _ffillbuf(FILE *stream)
529{
[b7fd2a0]530 errno_t rc;
[8e3498b]531 size_t nread;
[facebd56]532
533 stream->buf_head = stream->buf_tail = stream->buf;
534
[8e3498b]535 rc = vfs_read(stream->fd, &stream->pos, stream->buf, stream->buf_size,
536 &nread);
537 if (rc != EOK) {
[ce04ea44]538 errno = rc;
[facebd56]539 stream->error = true;
540 return;
541 }
542
[8e3498b]543 if (nread == 0) {
[facebd56]544 stream->eof = true;
545 return;
546 }
547
[8e3498b]548 stream->buf_head += nread;
[facebd56]549 stream->buf_state = _bs_read;
550}
551
552/** Write out stream buffer, do not sync stream. */
[ef8bcc6]553static void _fflushbuf(FILE *stream)
554{
555 size_t bytes_used;
[facebd56]556
[bfd247f]557 if ((!stream->buf) || (stream->btype == _IONBF) || (stream->error))
[ef8bcc6]558 return;
[facebd56]559
560 bytes_used = stream->buf_head - stream->buf_tail;
561
562 /* If buffer has prefetched read data, we need to seek back. */
[58898d1d]563 if (bytes_used > 0 && stream->buf_state == _bs_read)
564 stream->pos -= bytes_used;
[facebd56]565
566 /* If buffer has unwritten data, we need to write them out. */
[6afc9d7]567 if (bytes_used > 0 && stream->buf_state == _bs_write) {
[facebd56]568 (void) _fwrite(stream->buf_tail, 1, bytes_used, stream);
[6afc9d7]569 /* On error stream error indicator and errno are set by _fwrite */
570 if (stream->error)
571 return;
572 }
[facebd56]573
[ef8bcc6]574 stream->buf_head = stream->buf;
[facebd56]575 stream->buf_tail = stream->buf;
576 stream->buf_state = _bs_empty;
[ef8bcc6]577}
578
[facebd56]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
[bd5414e]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;
[a955fcc]607 --bytes_left;
[bd5414e]608 }
609
[facebd56]610 /* If not buffered stream, read in directly. */
611 if (stream->btype == _IONBF) {
[bd5414e]612 total_read += _fread(dest, 1, bytes_left, stream);
613 return total_read / size;
[facebd56]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
[6afc9d7]630 if (stream->error || stream->eof) {
631 /* On error errno was set by _ffillbuf() */
[facebd56]632 break;
[6afc9d7]633 }
[facebd56]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
[ef8bcc6]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;
[002252a]673
674 if (size == 0 || nmemb == 0)
675 return 0;
676
[ef8bcc6]677 /* If not buffered stream, write out directly. */
[bfd247f]678 if (stream->btype == _IONBF) {
679 now = _fwrite(buf, size, nmemb, stream);
680 fflush(stream);
681 return now;
682 }
[facebd56]683
684 /* Make sure buffer contains no prefetched data. */
685 if (stream->buf_state == _bs_read)
686 _fflushbuf(stream);
687
[ef8bcc6]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 }
[a35b458]693
[ef8bcc6]694 data = (uint8_t *) buf;
695 bytes_left = size * nmemb;
696 total_written = 0;
697 need_flush = false;
[a35b458]698
[bfd247f]699 while ((!stream->error) && (bytes_left > 0)) {
[ef8bcc6]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;
[a35b458]705
[ef8bcc6]706 for (i = 0; i < now; i++) {
707 b = data[i];
708 stream->buf_head[i] = b;
[a35b458]709
[bfd247f]710 if ((b == '\n') && (stream->btype == _IOLBF))
[ef8bcc6]711 need_flush = true;
712 }
[a35b458]713
[0803134]714 data += now;
[ef8bcc6]715 stream->buf_head += now;
716 buf_free -= now;
717 bytes_left -= now;
718 total_written += now;
[0803134]719 stream->buf_state = _bs_write;
[a35b458]720
[ef8bcc6]721 if (buf_free == 0) {
722 /* Only need to drain buffer. */
723 _fflushbuf(stream);
[6afc9d7]724 if (!stream->error)
725 need_flush = false;
[ef8bcc6]726 }
727 }
[facebd56]728
[ef8bcc6]729 if (need_flush)
730 fflush(stream);
[a35b458]731
[ef8bcc6]732 return (total_written / size);
733}
734
[ed88c8e]735wint_t fputwc(wchar_t wc, FILE *stream)
[c9857c6]736{
[56fa418]737 char buf[STR_BOUNDS(1)];
[2595dab]738 size_t sz = 0;
[a35b458]739
[ed88c8e]740 if (chr_encode(wc, buf, &sz, STR_BOUNDS(1)) != EOK) {
741 errno = EILSEQ;
742 return WEOF;
743 }
[a35b458]744
[ed88c8e]745 size_t wr = fwrite(buf, 1, sz, stream);
746 if (wr < sz)
747 return WEOF;
[a35b458]748
[ed88c8e]749 return wc;
750}
751
[28a5ebd]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
[ed88c8e]769wint_t putwchar(wchar_t wc)
770{
771 return fputwc(wc, stdout);
772}
773
[28a5ebd]774wint_t putuchar(char32_t wc)
775{
776 return fputuc(wc, stdout);
777}
778
[ed88c8e]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;
[a35b458]788
[ed88c8e]789 return b;
[2595dab]790}
[56fa418]791
[ed88c8e]792int putchar(int c)
[2595dab]793{
794 return fputc(c, stdout);
795}
[56fa418]796
[2595dab]797int fputs(const char *str, FILE *stream)
798{
[7db5cfd]799 (void) fwrite(str, str_size(str), 1, stream);
800 if (ferror(stream))
801 return EOF;
802 return 0;
[2595dab]803}
[56fa418]804
[2595dab]805int puts(const char *str)
806{
[1abcf1d]807 if (fputs(str, stdout) < 0)
808 return EOF;
809 return putchar('\n');
[c9857c6]810}
[b27a97bb]811
[2595dab]812int fgetc(FILE *stream)
[b27a97bb]813{
[2595dab]814 char c;
[a35b458]815
[bac82eeb]816 /* This could be made faster by only flushing when needed. */
[b8e57e8c]817 if (stdout)
818 fflush(stdout);
819 if (stderr)
820 fflush(stderr);
[a35b458]821
[2595dab]822 if (fread(&c, sizeof(char), 1, stream) < sizeof(char))
823 return EOF;
[a35b458]824
[2595dab]825 return (int) c;
826}
827
[c62d2e1]828char *fgets(char *str, int size, FILE *stream)
829{
[a634485]830 int c;
[c62d2e1]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
[2595dab]855int getchar(void)
856{
857 return fgetc(stdin);
[b27a97bb]858}
859
[bd5414e]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
[1c7f381]875int fseek64(FILE *stream, off64_t offset, int whence)
[d2cc7e1]876{
[b7fd2a0]877 errno_t rc;
[23a0368]878
[6afc9d7]879 if (stream->error)
[58898d1d]880 return -1;
[6afc9d7]881
[facebd56]882 _fflushbuf(stream);
[6afc9d7]883 if (stream->error) {
884 /* errno was set by _fflushbuf() */
[58898d1d]885 return -1;
[6afc9d7]886 }
887
[bd5414e]888 stream->ungetc_chars = 0;
[facebd56]889
[39330200]890 vfs_stat_t st;
[58898d1d]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:
[23a0368]899 rc = vfs_stat(stream->fd, &st);
900 if (rc != EOK) {
901 errno = rc;
[58898d1d]902 stream->error = true;
[1b20da0]903 return -1;
[58898d1d]904 }
905 stream->pos = st.size + offset;
906 break;
[2595dab]907 }
[facebd56]908
[2595dab]909 stream->eof = false;
[566f4cfb]910 return 0;
[d2cc7e1]911}
912
[1c7f381]913off64_t ftell64(FILE *stream)
[08232ee]914{
[6afc9d7]915 if (stream->error)
916 return EOF;
[a35b458]917
[8ad496d2]918 _fflushbuf(stream);
[6afc9d7]919 if (stream->error) {
920 /* errno was set by _fflushbuf() */
921 return EOF;
922 }
923
[58898d1d]924 return stream->pos - stream->ungetc_chars;
[08232ee]925}
926
[1c7f381]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. */
[ef84413]937 if (off > LONG_MAX) {
938 errno = EOVERFLOW;
939 return -1;
940 }
[1c7f381]941
942 return off;
943}
944
[080ad7f]945void rewind(FILE *stream)
946{
947 (void) fseek(stream, 0, SEEK_SET);
948}
949
[2595dab]950int fflush(FILE *stream)
951{
[6afc9d7]952 if (stream->error)
953 return EOF;
[a35b458]954
[ef8bcc6]955 _fflushbuf(stream);
[6afc9d7]956 if (stream->error) {
957 /* errno was set by _fflushbuf() */
958 return EOF;
959 }
[a35b458]960
[01cc7b4]961 if (stream->need_sync) {
[facebd56]962 /**
963 * Better than syncing always, but probably still not the
964 * right thing to do.
965 */
[01cc7b4]966 if (stream->ops->flush(stream) == EOF)
[6afc9d7]967 return EOF;
968
[01cc7b4]969 stream->need_sync = false;
[facebd56]970 }
[a35b458]971
[6afc9d7]972 return 0;
[2595dab]973}
974
975int feof(FILE *stream)
976{
977 return stream->eof;
978}
979
980int ferror(FILE *stream)
981{
982 return stream->error;
983}
984
[c77a64f]985void clearerr(FILE *stream)
986{
987 stream->eof = false;
988 stream->error = false;
989}
990
[3629481]991int fileno(FILE *stream)
992{
[01cc7b4]993 if (stream->ops != &stdio_vfs_ops) {
[3629481]994 errno = EBADF;
[6afc9d7]995 return EOF;
[3629481]996 }
[a35b458]997
[3629481]998 return stream->fd;
999}
1000
[6afc9d7]1001async_sess_t *vfs_fsession(FILE *stream, iface_t iface)
[2595dab]1002{
1003 if (stream->fd >= 0) {
[79ae36dd]1004 if (stream->sess == NULL)
[6afc9d7]1005 stream->sess = vfs_fd_session(stream->fd, iface);
[a35b458]1006
[79ae36dd]1007 return stream->sess;
[2595dab]1008 }
[a35b458]1009
[79ae36dd]1010 return NULL;
[2595dab]1011}
1012
[b7fd2a0]1013errno_t vfs_fhandle(FILE *stream, int *handle)
[2595dab]1014{
[7171760]1015 if (stream->fd >= 0) {
1016 *handle = stream->fd;
1017 return EOK;
1018 }
[a35b458]1019
[a68f737]1020 return ENOENT;
[2595dab]1021}
1022
[01cc7b4]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
[fadd381]1103/** @}
[b2951e2]1104 */
Note: See TracBrowser for help on using the repository browser.