Subversion Repositories HelenOS

Rev

Rev 4537 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
504 decky 1
/*
2071 jermar 2
 * Copyright (c) 2005 Martin Decky
504 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.
1653 cejka 27
 */
28
 
1866 jermar 29
/** @addtogroup libc
1653 cejka 30
 * @{
31
 */
32
/** @file
4537 trochtova 33
 */
504 decky 34
 
4537 trochtova 35
#include <stdio.h>
504 decky 36
#include <unistd.h>
4537 trochtova 37
#include <fcntl.h>
4668 trochtova 38
#include <assert.h>
4296 trochtova 39
#include <string.h>
40
#include <errno.h>
4537 trochtova 41
#include <bool.h>
42
#include <malloc.h>
43
#include <io/klog.h>
44
#include <vfs/vfs.h>
45
#include <ipc/devmap.h>
46
#include <adt/list.h>
504 decky 47
 
4668 trochtova 48
static void _fflushbuf(FILE *stream);
49
 
4537 trochtova 50
static FILE stdin_null = {
51
    .fd = -1,
52
    .error = true,
53
    .eof = true,
54
    .klog = false,
4668 trochtova 55
    .phone = -1,
56
    .btype = _IONBF,
57
    .buf = NULL,
58
    .buf_size = 0,
59
    .buf_head = NULL
4537 trochtova 60
};
713 decky 61
 
4537 trochtova 62
static FILE stdout_klog = {
63
    .fd = -1,
64
    .error = false,
65
    .eof = false,
66
    .klog = true,
4668 trochtova 67
    .phone = -1,
68
    .btype = _IOLBF,
69
    .buf = NULL,
70
    .buf_size = BUFSIZ,
71
    .buf_head = NULL
4537 trochtova 72
};
73
 
74
static FILE stderr_klog = {
75
    .fd = -1,
76
    .error = false,
77
    .eof = false,
78
    .klog = true,
4668 trochtova 79
    .phone = -1,
80
    .btype = _IONBF,
81
    .buf = NULL,
82
    .buf_size = 0,
83
    .buf_head = NULL
4537 trochtova 84
};
85
 
86
FILE *stdin = NULL;
87
FILE *stdout = NULL;
88
FILE *stderr = NULL;
89
 
90
static LIST_INITIALIZE(files);
91
 
4668 trochtova 92
void __stdio_init(int filc, fdi_node_t *filv[])
504 decky 93
{
4537 trochtova 94
    if (filc > 0) {
95
        stdin = fopen_node(filv[0], "r");
96
    } else {
97
        stdin = &stdin_null;
98
        list_append(&stdin->link, &files);
99
    }
504 decky 100
 
4537 trochtova 101
    if (filc > 1) {
102
        stdout = fopen_node(filv[1], "w");
103
    } else {
104
        stdout = &stdout_klog;
105
        list_append(&stdout->link, &files);
106
    }
1173 cejka 107
 
4537 trochtova 108
    if (filc > 2) {
109
        stderr = fopen_node(filv[2], "w");
110
    } else {
111
        stderr = &stderr_klog;
112
        list_append(&stderr->link, &files);
113
    }
114
}
115
 
4668 trochtova 116
void __stdio_done(void)
4537 trochtova 117
{
118
    link_t *link = files.next;
4420 trochtova 119
 
4537 trochtova 120
    while (link != &files) {
121
        FILE *file = list_get_instance(link, FILE, link);
122
        fclose(file);
123
        link = files.next;
713 decky 124
    }
4537 trochtova 125
}
126
 
127
static bool parse_mode(const char *mode, int *flags)
128
{
129
    /* Parse mode except first character. */
130
    const char *mp = mode;
131
    if (*mp++ == 0) {
132
        errno = EINVAL;
133
        return false;
134
    }
713 decky 135
 
4537 trochtova 136
    if ((*mp == 'b') || (*mp == 't'))
137
        mp++;
138
 
139
    bool plus;
140
    if (*mp == '+') {
141
        mp++;
142
        plus = true;
143
    } else
144
        plus = false;
145
 
146
    if (*mp != 0) {
147
        errno = EINVAL;
148
        return false;
149
    }
150
 
151
    /* Parse first character of mode and determine flags for open(). */
152
    switch (mode[0]) {
153
    case 'r':
154
        *flags = plus ? O_RDWR : O_RDONLY;
155
        break;
156
    case 'w':
157
        *flags = (O_TRUNC | O_CREAT) | (plus ? O_RDWR : O_WRONLY);
158
        break;
159
    case 'a':
160
        /* TODO: a+ must read from beginning, append to the end. */
161
        if (plus) {
162
            errno = ENOTSUP;
163
            return false;
164
        }
165
        *flags = (O_APPEND | O_CREAT) | (plus ? O_RDWR : O_WRONLY);
166
    default:
167
        errno = EINVAL;
168
        return false;
169
    }
170
 
171
    return true;
504 decky 172
}
173
 
4668 trochtova 174
/** Set stream buffer. */
175
void setvbuf(FILE *stream, void *buf, int mode, size_t size)
176
{
177
    stream->btype = mode;
178
    stream->buf = buf;
179
    stream->buf_size = size;
180
    stream->buf_head = stream->buf;
181
}
182
 
183
static void _setvbuf(FILE *stream)
184
{
185
    /* FIXME: Use more complex rules for setting buffering options. */
186
 
187
    switch (stream->fd) {
188
    case 1:
189
        setvbuf(stream, NULL, _IOLBF, BUFSIZ);
190
        break;
191
    case 0:
192
    case 2:
193
        setvbuf(stream, NULL, _IONBF, 0);
194
        break;
195
    default:
196
        setvbuf(stream, NULL, _IOFBF, BUFSIZ);
197
    }
198
}
199
 
200
/** Allocate stream buffer. */
201
static int _fallocbuf(FILE *stream)
202
{
203
    assert(stream->buf == NULL);
204
 
205
    stream->buf = malloc(stream->buf_size);
206
    if (stream->buf == NULL) {
207
        errno = ENOMEM;
208
        return -1;
209
    }
210
 
211
    stream->buf_head = stream->buf;
212
    return 0;
213
}
214
 
4537 trochtova 215
/** Open a stream.
216
 *
217
 * @param path Path of the file to open.
218
 * @param mode Mode string, (r|w|a)[b|t][+].
219
 *
974 cejka 220
 */
4537 trochtova 221
FILE *fopen(const char *path, const char *mode)
974 cejka 222
{
4537 trochtova 223
    int flags;
224
    if (!parse_mode(mode, &flags))
225
        return NULL;
974 cejka 226
 
4537 trochtova 227
    /* Open file. */
228
    FILE *stream = malloc(sizeof(FILE));
229
    if (stream == NULL) {
230
        errno = ENOMEM;
231
        return NULL;
232
    }
233
 
234
    stream->fd = open(path, flags, 0666);
235
    if (stream->fd < 0) {
236
        /* errno was set by open() */
237
        free(stream);
238
        return NULL;
239
    }
240
 
241
    stream->error = false;
242
    stream->eof = false;
243
    stream->klog = false;
244
    stream->phone = -1;
4668 trochtova 245
    _setvbuf(stream);
4537 trochtova 246
 
247
    list_append(&stream->link, &files);
248
 
249
    return stream;
974 cejka 250
}
251
 
4537 trochtova 252
FILE *fdopen(int fd, const char *mode)
253
{
254
    /* Open file. */
255
    FILE *stream = malloc(sizeof(FILE));
256
    if (stream == NULL) {
257
        errno = ENOMEM;
258
        return NULL;
259
    }
260
 
261
    stream->fd = fd;
262
    stream->error = false;
263
    stream->eof = false;
264
    stream->klog = false;
265
    stream->phone = -1;
4668 trochtova 266
    _setvbuf(stream);
4537 trochtova 267
 
268
    list_append(&stream->link, &files);
269
 
270
    return stream;
271
}
272
 
273
FILE *fopen_node(fdi_node_t *node, const char *mode)
274
{
275
    int flags;
276
    if (!parse_mode(mode, &flags))
277
        return NULL;
278
 
279
    /* Open file. */
280
    FILE *stream = malloc(sizeof(FILE));
281
    if (stream == NULL) {
282
        errno = ENOMEM;
283
        return NULL;
284
    }
285
 
286
    stream->fd = open_node(node, flags);
287
    if (stream->fd < 0) {
288
        /* errno was set by open_node() */
289
        free(stream);
290
        return NULL;
291
    }
292
 
293
    stream->error = false;
294
    stream->eof = false;
295
    stream->klog = false;
296
    stream->phone = -1;
4668 trochtova 297
    _setvbuf(stream);
4537 trochtova 298
 
299
    list_append(&stream->link, &files);
300
 
301
    return stream;
302
}
303
 
304
int fclose(FILE *stream)
305
{
306
    int rc = 0;
307
 
308
    fflush(stream);
309
 
310
    if (stream->phone >= 0)
311
        ipc_hangup(stream->phone);
312
 
313
    if (stream->fd >= 0)
314
        rc = close(stream->fd);
315
 
316
    list_remove(&stream->link);
317
 
318
    if ((stream != &stdin_null)
319
        && (stream != &stdout_klog)
320
        && (stream != &stderr_klog))
321
        free(stream);
322
 
323
    stream = NULL;
324
 
325
    if (rc != 0) {
326
        /* errno was set by close() */
327
        return EOF;
328
    }
329
 
330
    return 0;
331
}
332
 
333
/** Read from a stream.
974 cejka 334
 *
4537 trochtova 335
 * @param buf    Destination buffer.
336
 * @param size   Size of each record.
337
 * @param nmemb  Number of records to read.
338
 * @param stream Pointer to the stream.
339
 *
974 cejka 340
 */
4537 trochtova 341
size_t fread(void *buf, size_t size, size_t nmemb, FILE *stream)
974 cejka 342
{
4537 trochtova 343
    size_t left = size * nmemb;
344
    size_t done = 0;
974 cejka 345
 
4668 trochtova 346
    /* Make sure no data is pending write. */
347
    _fflushbuf(stream);
348
 
4537 trochtova 349
    while ((left > 0) && (!stream->error) && (!stream->eof)) {
350
        ssize_t rd = read(stream->fd, buf + done, left);
351
 
352
        if (rd < 0)
353
            stream->error = true;
354
        else if (rd == 0)
355
            stream->eof = true;
356
        else {
357
            left -= rd;
358
            done += rd;
359
        }
360
    }
361
 
362
    return (done / size);
363
}
1010 cejka 364
 
4668 trochtova 365
static size_t _fwrite(const void *buf, size_t size, size_t nmemb, FILE *stream)
4537 trochtova 366
{
367
    size_t left = size * nmemb;
368
    size_t done = 0;
974 cejka 369
 
4537 trochtova 370
    while ((left > 0) && (!stream->error)) {
371
        ssize_t wr;
372
 
373
        if (stream->klog)
374
            wr = klog_write(buf + done, left);
375
        else
376
            wr = write(stream->fd, buf + done, left);
377
 
378
        if (wr <= 0)
379
            stream->error = true;
380
        else {
381
            left -= wr;
382
            done += wr;
383
        }
384
    }
385
 
386
    return (done / size);
974 cejka 387
}
388
 
4668 trochtova 389
/** Drain stream buffer, do not sync stream. */
390
static void _fflushbuf(FILE *stream)
391
{
392
    size_t bytes_used;
393
 
394
    if ((!stream->buf) || (stream->btype == _IONBF) || (stream->error))
395
        return;
396
 
397
    bytes_used = stream->buf_head - stream->buf;
398
    if (bytes_used == 0)
399
        return;
400
 
401
    (void) _fwrite(stream->buf, 1, bytes_used, stream);
402
    stream->buf_head = stream->buf;
403
}
404
 
405
/** Write to a stream.
406
 *
407
 * @param buf    Source buffer.
408
 * @param size   Size of each record.
409
 * @param nmemb  Number of records to write.
410
 * @param stream Pointer to the stream.
411
 *
412
 */
413
size_t fwrite(const void *buf, size_t size, size_t nmemb, FILE *stream)
414
{
415
    uint8_t *data;
416
    size_t bytes_left;
417
    size_t now;
418
    size_t buf_free;
419
    size_t total_written;
420
    size_t i;
421
    uint8_t b;
422
    bool need_flush;
423
 
424
    /* If not buffered stream, write out directly. */
425
    if (stream->btype == _IONBF) {
426
        now = _fwrite(buf, size, nmemb, stream);
427
        fflush(stream);
428
        return now;
429
    }
430
 
431
    /* Perform lazy allocation of stream buffer. */
432
    if (stream->buf == NULL) {
433
        if (_fallocbuf(stream) != 0)
434
            return 0; /* Errno set by _fallocbuf(). */
435
    }
436
 
437
    data = (uint8_t *) buf;
438
    bytes_left = size * nmemb;
439
    total_written = 0;
440
    need_flush = false;
441
 
442
    while ((!stream->error) && (bytes_left > 0)) {
443
        buf_free = stream->buf_size - (stream->buf_head - stream->buf);
444
        if (bytes_left > buf_free)
445
            now = buf_free;
446
        else
447
            now = bytes_left;
448
 
449
        for (i = 0; i < now; i++) {
450
            b = data[i];
451
            stream->buf_head[i] = b;
452
 
453
            if ((b == '\n') && (stream->btype == _IOLBF))
454
                need_flush = true;
455
        }
456
 
457
        buf += now;
458
        stream->buf_head += now;
459
        buf_free -= now;
460
        bytes_left -= now;
461
        total_written += now;
462
 
463
        if (buf_free == 0) {
464
            /* Only need to drain buffer. */
465
            _fflushbuf(stream);
466
            need_flush = false;
467
        }
468
    }
469
 
470
    if (need_flush)
471
        fflush(stream);
472
 
473
    return (total_written / size);
474
}
475
 
4537 trochtova 476
int fputc(wchar_t c, FILE *stream)
1173 cejka 477
{
4296 trochtova 478
    char buf[STR_BOUNDS(1)];
4537 trochtova 479
    size_t sz = 0;
480
 
481
    if (chr_encode(c, buf, &sz, STR_BOUNDS(1)) == EOK) {
482
        size_t wr = fwrite(buf, sz, 1, stream);
483
 
484
        if (wr < sz)
485
            return EOF;
486
 
487
        return (int) c;
488
    }
489
 
490
    return EOF;
491
}
4296 trochtova 492
 
4537 trochtova 493
int putchar(wchar_t c)
494
{
495
    return fputc(c, stdout);
496
}
4296 trochtova 497
 
4537 trochtova 498
int fputs(const char *str, FILE *stream)
499
{
500
    return fwrite(str, str_size(str), 1, stream);
501
}
4296 trochtova 502
 
4537 trochtova 503
int puts(const char *str)
504
{
505
    return fputs(str, stdout);
1173 cejka 506
}
1465 cejka 507
 
4537 trochtova 508
int fgetc(FILE *stream)
509
{
510
    char c;
4668 trochtova 511
 
4537 trochtova 512
    /* This could be made faster by only flushing when needed. */
513
    if (stdout)
514
        fflush(stdout);
515
    if (stderr)
516
        fflush(stderr);
4668 trochtova 517
 
4537 trochtova 518
    if (fread(&c, sizeof(char), 1, stream) < sizeof(char))
519
        return EOF;
520
 
521
    return (int) c;
522
}
523
 
1465 cejka 524
int getchar(void)
525
{
4537 trochtova 526
    return fgetc(stdin);
527
}
528
 
529
int fseek(FILE *stream, long offset, int origin)
530
{
531
    off_t rc = lseek(stream->fd, offset, origin);
532
    if (rc == (off_t) (-1)) {
533
        /* errno has been set by lseek. */
534
        return -1;
535
    }
4420 trochtova 536
 
4537 trochtova 537
    stream->eof = false;
1465 cejka 538
 
4537 trochtova 539
    return 0;
1465 cejka 540
}
541
 
4537 trochtova 542
void rewind(FILE *stream)
4201 trochtova 543
{
4537 trochtova 544
    (void) fseek(stream, 0, SEEK_SET);
4201 trochtova 545
}
546
 
4537 trochtova 547
int fflush(FILE *stream)
548
{
4668 trochtova 549
    _fflushbuf(stream);
550
 
4537 trochtova 551
    if (stream->klog) {
552
        klog_update();
553
        return EOK;
554
    }
555
 
556
    if (stream->fd >= 0)
557
        return fsync(stream->fd);
558
 
559
    return ENOENT;
560
}
561
 
562
int feof(FILE *stream)
563
{
564
    return stream->eof;
565
}
566
 
567
int ferror(FILE *stream)
568
{
569
    return stream->error;
570
}
571
 
572
int fphone(FILE *stream)
573
{
574
    if (stream->fd >= 0) {
575
        if (stream->phone < 0)
576
            stream->phone = fd_phone(stream->fd);
577
 
578
        return stream->phone;
579
    }
580
 
581
    return -1;
582
}
583
 
584
int fnode(FILE *stream, fdi_node_t *node)
585
{
586
    if (stream->fd >= 0)
587
        return fd_node(stream->fd, node);
588
 
589
    return ENOENT;
590
}
591
 
1866 jermar 592
/** @}
1653 cejka 593
 */