Subversion Repositories HelenOS

Rev

Rev 4377 | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 4377 Rev 4692
Line 28... Line 28...
28
 
28
 
29
/** @addtogroup libc
29
/** @addtogroup libc
30
 * @{
30
 * @{
31
 */
31
 */
32
/** @file
32
/** @file
33
 */
33
 */
34
 
34
 
35
#include <libc.h>
-
 
36
#include <unistd.h>
-
 
37
#include <stdio.h>
35
#include <stdio.h>
-
 
36
#include <unistd.h>
38
#include <io/io.h>
37
#include <fcntl.h>
-
 
38
#include <assert.h>
39
#include <string.h>
39
#include <string.h>
40
#include <errno.h>
40
#include <errno.h>
41
#include <console.h>
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>
-
 
47
 
-
 
48
static void _fflushbuf(FILE *stream);
-
 
49
 
-
 
50
static FILE stdin_null = {
-
 
51
    .fd = -1,
-
 
52
    .error = true,
-
 
53
    .eof = true,
-
 
54
    .klog = false,
-
 
55
    .phone = -1,
-
 
56
    .btype = _IONBF,
-
 
57
    .buf = NULL,
-
 
58
    .buf_size = 0,
-
 
59
    .buf_head = NULL
-
 
60
};
-
 
61
 
-
 
62
static FILE stdout_klog = {
-
 
63
    .fd = -1,
-
 
64
    .error = false,
-
 
65
    .eof = false,
-
 
66
    .klog = true,
-
 
67
    .phone = -1,
-
 
68
    .btype = _IOLBF,
-
 
69
    .buf = NULL,
-
 
70
    .buf_size = BUFSIZ,
-
 
71
    .buf_head = NULL
-
 
72
};
-
 
73
 
-
 
74
static FILE stderr_klog = {
-
 
75
    .fd = -1,
-
 
76
    .error = false,
-
 
77
    .eof = false,
-
 
78
    .klog = true,
-
 
79
    .phone = -1,
-
 
80
    .btype = _IONBF,
-
 
81
    .buf = NULL,
-
 
82
    .buf_size = 0,
-
 
83
    .buf_head = NULL
-
 
84
};
-
 
85
 
-
 
86
FILE *stdin = NULL;
-
 
87
FILE *stdout = NULL;
-
 
88
FILE *stderr = NULL;
42
 
89
 
43
const static char nl = '\n';
90
static LIST_INITIALIZE(files);
44
 
91
 
-
 
92
void __stdio_init(int filc, fdi_node_t *filv[])
-
 
93
{
-
 
94
    if (filc > 0) {
-
 
95
        stdin = fopen_node(filv[0], "r");
-
 
96
    } else {
-
 
97
        stdin = &stdin_null;
-
 
98
        list_append(&stdin->link, &files);
-
 
99
    }
-
 
100
   
-
 
101
    if (filc > 1) {
-
 
102
        stdout = fopen_node(filv[1], "w");
-
 
103
    } else {
45
int puts(const char *str)
104
        stdout = &stdout_klog;
-
 
105
        list_append(&stdout->link, &files);
-
 
106
    }
-
 
107
   
-
 
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
 
-
 
116
void __stdio_done(void)
-
 
117
{
-
 
118
    link_t *link = files.next;
-
 
119
   
-
 
120
    while (link != &files) {
-
 
121
        FILE *file = list_get_instance(link, FILE, link);
-
 
122
        fclose(file);
-
 
123
        link = files.next;
-
 
124
    }
-
 
125
}
-
 
126
 
-
 
127
static bool parse_mode(const char *mode, int *flags)
46
{
128
{
-
 
129
    /* Parse mode except first character. */
-
 
130
    const char *mp = mode;
-
 
131
    if (*mp++ == 0) {
-
 
132
        errno = EINVAL;
47
    size_t count;
133
        return false;
-
 
134
    }
48
   
135
   
49
    if (str == NULL)
136
    if ((*mp == 'b') || (*mp == 't'))
50
        return putnchars("(NULL)", 6);
137
        mp++;
51
   
138
   
-
 
139
    bool plus;
52
    for (count = 0; str[count] != 0; count++);
140
    if (*mp == '+') {
-
 
141
        mp++;
-
 
142
        plus = true;
-
 
143
    } else
-
 
144
        plus = false;
53
   
145
   
54
    if (console_write((void *) str, count) == count) {
146
    if (*mp != 0) {
55
        if (console_write(&nl, 1) == 1)
147
        errno = EINVAL;
56
            return 0;
148
        return false;
57
    }
149
    }
58
   
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
   
59
    return EOF;
171
    return true;
60
}
172
}
61
 
173
 
62
/** Put count chars from buffer to stdout without adding newline
174
/** Set stream buffer. */
63
 * @param buf Buffer with size at least count bytes - NULL pointer NOT allowed!
175
void setvbuf(FILE *stream, void *buf, int mode, size_t size)
-
 
176
{
-
 
177
    stream->btype = mode;
64
 * @param count
178
    stream->buf = buf;
-
 
179
    stream->buf_size = size;
65
 * @return 0 on succes, EOF on fail
180
    stream->buf_head = stream->buf;
66
 */
181
}
-
 
182
 
67
int putnchars(const char *buf, size_t count)
183
static void _setvbuf(FILE *stream)
68
{
184
{
69
    if (console_write((void *) buf, count) == count)
185
    /* FIXME: Use more complex rules for setting buffering options. */
70
        return 0;
-
 
71
   
186
   
-
 
187
    switch (stream->fd) {
-
 
188
    case 1:
-
 
189
        setvbuf(stream, NULL, _IOLBF, BUFSIZ);
72
    return EOF;
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
    }
73
}
198
}
74
 
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
 
-
 
215
/** Open a stream.
-
 
216
 *
75
/** Same as puts, but does not print newline at end
217
 * @param path Path of the file to open.
-
 
218
 * @param mode Mode string, (r|w|a)[b|t][+].
76
 *
219
 *
77
 */
220
 */
78
int putstr(const char *str)
221
FILE *fopen(const char *path, const char *mode)
79
{
222
{
80
    size_t count;
223
    int flags;
-
 
224
    if (!parse_mode(mode, &flags))
-
 
225
        return NULL;
-
 
226
   
-
 
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
    }
81
   
240
   
-
 
241
    stream->error = false;
-
 
242
    stream->eof = false;
-
 
243
    stream->klog = false;
-
 
244
    stream->phone = -1;
82
    if (str == NULL)
245
    _setvbuf(stream);
-
 
246
   
-
 
247
    list_append(&stream->link, &files);
-
 
248
   
83
        return putnchars("(NULL)", 6);
249
    return stream;
-
 
250
}
84
 
251
 
85
    for (count = 0; str[count] != 0; count++);
252
FILE *fdopen(int fd, const char *mode)
-
 
253
{
-
 
254
    /* Open file. */
86
    if (console_write((void *) str, count) == count)
255
    FILE *stream = malloc(sizeof(FILE));
-
 
256
    if (stream == NULL) {
-
 
257
        errno = ENOMEM;
87
        return 0;
258
        return NULL;
-
 
259
    }
88
   
260
   
-
 
261
    stream->fd = fd;
-
 
262
    stream->error = false;
-
 
263
    stream->eof = false;
-
 
264
    stream->klog = false;
-
 
265
    stream->phone = -1;
-
 
266
    _setvbuf(stream);
-
 
267
   
-
 
268
    list_append(&stream->link, &files);
-
 
269
   
89
    return EOF;
270
    return stream;
90
}
271
}
91
 
272
 
92
int putchar(int c)
273
FILE *fopen_node(fdi_node_t *node, const char *mode)
93
{
274
{
-
 
275
    int flags;
-
 
276
    if (!parse_mode(mode, &flags))
-
 
277
        return NULL;
-
 
278
   
-
 
279
    /* Open file. */
-
 
280
    FILE *stream = malloc(sizeof(FILE));
94
    char buf[STR_BOUNDS(1)];
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;
95
    size_t offs;
294
    stream->eof = false;
-
 
295
    stream->klog = false;
-
 
296
    stream->phone = -1;
-
 
297
    _setvbuf(stream);
-
 
298
   
-
 
299
    list_append(&stream->link, &files);
-
 
300
   
-
 
301
    return stream;
-
 
302
}
96
 
303
 
-
 
304
int fclose(FILE *stream)
-
 
305
{
97
    offs = 0;
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)
98
    if (chr_encode(c, buf, &offs, STR_BOUNDS(1)) != EOK)
320
        && (stream != &stderr_klog))
-
 
321
        free(stream);
-
 
322
   
-
 
323
    stream = NULL;
-
 
324
   
-
 
325
    if (rc != 0) {
-
 
326
        /* errno was set by close() */
99
        return EOF;
327
        return EOF;
-
 
328
    }
-
 
329
   
-
 
330
    return 0;
-
 
331
}
100
 
332
 
-
 
333
/** Read from a stream.
-
 
334
 *
-
 
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
 *
-
 
340
 */
-
 
341
size_t fread(void *buf, size_t size, size_t nmemb, FILE *stream)
-
 
342
{
-
 
343
    size_t left = size * nmemb;
-
 
344
    size_t done = 0;
-
 
345
   
-
 
346
    /* Make sure no data is pending write. */
-
 
347
    _fflushbuf(stream);
-
 
348
   
-
 
349
    while ((left > 0) && (!stream->error) && (!stream->eof)) {
101
    if (console_write((void *) buf, offs) == offs)
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 {
102
        return c;
357
            left -= rd;
-
 
358
            done += rd;
-
 
359
        }
-
 
360
    }
-
 
361
   
-
 
362
    return (done / size);
-
 
363
}
103
 
364
 
-
 
365
static size_t _fwrite(const void *buf, size_t size, size_t nmemb, FILE *stream)
-
 
366
{
-
 
367
    size_t left = size * nmemb;
104
    return EOF;
368
    size_t done = 0;
-
 
369
   
-
 
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);
105
}
387
}
106
 
388
 
-
 
389
/** Drain stream buffer, do not sync stream. */
-
 
390
static void _fflushbuf(FILE *stream)
-
 
391
{
107
int getchar(void)
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)
108
{
414
{
109
    unsigned char c;
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
    }
110
   
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
   
111
    console_flush();
470
    if (need_flush)
-
 
471
        fflush(stream);
-
 
472
   
-
 
473
    return (total_written / size);
-
 
474
}
-
 
475
 
-
 
476
int fputc(wchar_t c, FILE *stream)
-
 
477
{
-
 
478
    char buf[STR_BOUNDS(1)];
-
 
479
    size_t sz = 0;
-
 
480
   
112
    if (read_stdin((void *) &c, 1) == 1)
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)
113
        return c;
485
            return EOF;
-
 
486
       
-
 
487
        return (int) c;
-
 
488
    }
114
   
489
   
115
    return EOF;
490
    return EOF;
116
}
491
}
117
 
492
 
118
int fflush(FILE *f)
493
int putchar(wchar_t c)
119
{
494
{
120
    /* Dummy implementation */
495
    return fputc(c, stdout);
-
 
496
}
-
 
497
 
-
 
498
int fputs(const char *str, FILE *stream)
-
 
499
{
-
 
500
    return fwrite(str, str_size(str), 1, stream);
-
 
501
}
-
 
502
 
-
 
503
int puts(const char *str)
-
 
504
{
-
 
505
    return fputs(str, stdout);
-
 
506
}
-
 
507
 
-
 
508
int fgetc(FILE *stream)
-
 
509
{
121
    (void) f;
510
    char c;
-
 
511
   
-
 
512
    /* This could be made faster by only flushing when needed. */
-
 
513
    if (stdout)
-
 
514
        fflush(stdout);
-
 
515
    if (stderr)
122
    console_flush();
516
        fflush(stderr);
-
 
517
   
-
 
518
    if (fread(&c, sizeof(char), 1, stream) < sizeof(char))
-
 
519
        return EOF;
-
 
520
   
-
 
521
    return (int) c;
-
 
522
}
-
 
523
 
-
 
524
int getchar(void)
-
 
525
{
-
 
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
    }
-
 
536
   
-
 
537
    stream->eof = false;
-
 
538
   
123
    return 0;
539
    return 0;
124
}
540
}
125
 
541
 
-
 
542
void rewind(FILE *stream)
-
 
543
{
-
 
544
    (void) fseek(stream, 0, SEEK_SET);
-
 
545
}
-
 
546
 
-
 
547
int fflush(FILE *stream)
-
 
548
{
-
 
549
    _fflushbuf(stream);
-
 
550
   
-
 
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
 
126
/** @}
592
/** @}
127
 */
593
 */