Subversion Repositories HelenOS

Rev

Rev 4512 | Rev 4541 | Go to most recent revision | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 4512 Rev 4514
1
/*
1
/*
2
 * Copyright (c) 2005 Martin Decky
2
 * Copyright (c) 2005 Martin Decky
3
 * All rights reserved.
3
 * All rights reserved.
4
 *
4
 *
5
 * Redistribution and use in source and binary forms, with or without
5
 * Redistribution and use in source and binary forms, with or without
6
 * modification, are permitted provided that the following conditions
6
 * modification, are permitted provided that the following conditions
7
 * are met:
7
 * are met:
8
 *
8
 *
9
 * - Redistributions of source code must retain the above copyright
9
 * - Redistributions of source code must retain the above copyright
10
 *   notice, this list of conditions and the following disclaimer.
10
 *   notice, this list of conditions and the following disclaimer.
11
 * - Redistributions in binary form must reproduce the above copyright
11
 * - Redistributions in binary form must reproduce the above copyright
12
 *   notice, this list of conditions and the following disclaimer in the
12
 *   notice, this list of conditions and the following disclaimer in the
13
 *   documentation and/or other materials provided with the distribution.
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
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.
15
 *   derived from this software without specific prior written permission.
16
 *
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
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
18
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
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
23
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
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
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.
26
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
 */
27
 */
28
 
28
 
29
/** @addtogroup libc
29
/** @addtogroup libc
30
 * @{
30
 * @{
31
 */
31
 */
32
/** @file
32
/** @file
33
 */
33
 */
34
 
34
 
35
#include <stdio.h>
35
#include <stdio.h>
36
#include <unistd.h>
36
#include <unistd.h>
37
#include <fcntl.h>
37
#include <fcntl.h>
38
#include <string.h>
38
#include <string.h>
39
#include <errno.h>
39
#include <errno.h>
40
#include <bool.h>
40
#include <bool.h>
41
#include <malloc.h>
41
#include <malloc.h>
42
#include <io/klog.h>
42
#include <io/klog.h>
43
#include <vfs/vfs.h>
43
#include <vfs/vfs.h>
44
#include <ipc/devmap.h>
44
#include <ipc/devmap.h>
45
#include <adt/list.h>
45
#include <adt/list.h>
46
 
46
 
47
static FILE stdin_null = {
47
static FILE stdin_null = {
48
    .fd = -1,
48
    .fd = -1,
49
    .error = true,
49
    .error = true,
50
    .eof = true,
50
    .eof = true,
51
    .klog = false,
51
    .klog = false,
52
    .phone = -1
52
    .phone = -1
53
};
53
};
54
 
54
 
55
static FILE stdout_klog = {
55
static FILE stdout_klog = {
56
    .fd = -1,
56
    .fd = -1,
57
    .error = false,
57
    .error = false,
58
    .eof = false,
58
    .eof = false,
59
    .klog = true,
59
    .klog = true,
60
    .phone = -1
60
    .phone = -1
61
};
61
};
62
 
62
 
63
static FILE stderr_klog = {
63
static FILE stderr_klog = {
64
    .fd = -1,
64
    .fd = -1,
65
    .error = false,
65
    .error = false,
66
    .eof = false,
66
    .eof = false,
67
    .klog = true,
67
    .klog = true,
68
    .phone = -1
68
    .phone = -1
69
};
69
};
70
 
70
 
71
FILE *stdin = NULL;
71
FILE *stdin = NULL;
72
FILE *stdout = NULL;
72
FILE *stdout = NULL;
73
FILE *stderr = NULL;
73
FILE *stderr = NULL;
74
 
74
 
75
static LIST_INITIALIZE(files);
75
static LIST_INITIALIZE(files);
76
 
76
 
77
void stdio_init(int filc, fdi_node_t *filv[])
77
void stdio_init(int filc, fdi_node_t *filv[])
78
{
78
{
79
    if (filc > 0) {
79
    if (filc > 0) {
80
        stdin = fopen_node(filv[0], "r");
80
        stdin = fopen_node(filv[0], "r");
81
    } else {
81
    } else {
82
        stdin = &stdin_null;
82
        stdin = &stdin_null;
83
        list_append(&stdin->link, &files);
83
        list_append(&stdin->link, &files);
84
    }
84
    }
85
   
85
   
86
    if (filc > 1) {
86
    if (filc > 1) {
87
        stdout = fopen_node(filv[1], "w");
87
        stdout = fopen_node(filv[1], "w");
88
    } else {
88
    } else {
89
        stdout = &stdout_klog;
89
        stdout = &stdout_klog;
90
        list_append(&stdout->link, &files);
90
        list_append(&stdout->link, &files);
91
    }
91
    }
92
   
92
   
93
    if (filc > 2) {
93
    if (filc > 2) {
94
        stderr = fopen_node(filv[2], "w");
94
        stderr = fopen_node(filv[2], "w");
95
    } else {
95
    } else {
96
        stderr = &stderr_klog;
96
        stderr = &stderr_klog;
97
        list_append(&stderr->link, &files);
97
        list_append(&stderr->link, &files);
98
    }
98
    }
99
}
99
}
100
 
100
 
101
void stdio_done(void)
101
void stdio_done(void)
102
{
102
{
103
    link_t *link = files.next;
103
    link_t *link = files.next;
104
   
104
   
105
    while (link != &files) {
105
    while (link != &files) {
106
        FILE *file = list_get_instance(link, FILE, link);
106
        FILE *file = list_get_instance(link, FILE, link);
107
        fclose(file);
107
        fclose(file);
108
        link = files.next;
108
        link = files.next;
109
    }
109
    }
110
}
110
}
111
 
111
 
112
static bool parse_mode(const char *mode, int *flags)
112
static bool parse_mode(const char *mode, int *flags)
113
{
113
{
114
    /* Parse mode except first character. */
114
    /* Parse mode except first character. */
115
    const char *mp = mode;
115
    const char *mp = mode;
116
    if (*mp++ == 0) {
116
    if (*mp++ == 0) {
117
        errno = EINVAL;
117
        errno = EINVAL;
118
        return false;
118
        return false;
119
    }
119
    }
120
   
120
   
121
    if ((*mp == 'b') || (*mp == 't'))
121
    if ((*mp == 'b') || (*mp == 't'))
122
        mp++;
122
        mp++;
123
   
123
   
124
    bool plus;
124
    bool plus;
125
    if (*mp == '+') {
125
    if (*mp == '+') {
126
        mp++;
126
        mp++;
127
        plus = true;
127
        plus = true;
128
    } else
128
    } else
129
        plus = false;
129
        plus = false;
130
   
130
   
131
    if (*mp != 0) {
131
    if (*mp != 0) {
132
        errno = EINVAL;
132
        errno = EINVAL;
133
        return false;
133
        return false;
134
    }
134
    }
135
   
135
   
136
    /* Parse first character of mode and determine flags for open(). */
136
    /* Parse first character of mode and determine flags for open(). */
137
    switch (mode[0]) {
137
    switch (mode[0]) {
138
    case 'r':
138
    case 'r':
139
        *flags = plus ? O_RDWR : O_RDONLY;
139
        *flags = plus ? O_RDWR : O_RDONLY;
140
        break;
140
        break;
141
    case 'w':
141
    case 'w':
142
        *flags = (O_TRUNC | O_CREAT) | (plus ? O_RDWR : O_WRONLY);
142
        *flags = (O_TRUNC | O_CREAT) | (plus ? O_RDWR : O_WRONLY);
143
        break;
143
        break;
144
    case 'a':
144
    case 'a':
145
        /* TODO: a+ must read from beginning, append to the end. */
145
        /* TODO: a+ must read from beginning, append to the end. */
146
        if (plus) {
146
        if (plus) {
147
            errno = ENOTSUP;
147
            errno = ENOTSUP;
148
            return false;
148
            return false;
149
        }
149
        }
150
        *flags = (O_APPEND | O_CREAT) | (plus ? O_RDWR : O_WRONLY);
150
        *flags = (O_APPEND | O_CREAT) | (plus ? O_RDWR : O_WRONLY);
151
    default:
151
    default:
152
        errno = EINVAL;
152
        errno = EINVAL;
153
        return false;
153
        return false;
154
    }
154
    }
155
   
155
   
156
    return true;
156
    return true;
157
}
157
}
158
 
158
 
159
/** Open a stream.
159
/** Open a stream.
160
 *
160
 *
161
 * @param path Path of the file to open.
161
 * @param path Path of the file to open.
162
 * @param mode Mode string, (r|w|a)[b|t][+].
162
 * @param mode Mode string, (r|w|a)[b|t][+].
163
 *
163
 *
164
 */
164
 */
165
FILE *fopen(const char *path, const char *mode)
165
FILE *fopen(const char *path, const char *mode)
166
{
166
{
167
    int flags;
167
    int flags;
168
    if (!parse_mode(mode, &flags))
168
    if (!parse_mode(mode, &flags))
169
        return NULL;
169
        return NULL;
170
   
170
   
171
    /* Open file. */
171
    /* Open file. */
172
    FILE *stream = malloc(sizeof(FILE));
172
    FILE *stream = malloc(sizeof(FILE));
173
    if (stream == NULL) {
173
    if (stream == NULL) {
174
        errno = ENOMEM;
174
        errno = ENOMEM;
175
        return NULL;
175
        return NULL;
176
    }
176
    }
177
   
177
   
178
    stream->fd = open(path, flags, 0666);
178
    stream->fd = open(path, flags, 0666);
179
    if (stream->fd < 0) {
179
    if (stream->fd < 0) {
180
        /* errno was set by open() */
180
        /* errno was set by open() */
181
        free(stream);
181
        free(stream);
182
        return NULL;
182
        return NULL;
183
    }
183
    }
184
   
184
   
185
    stream->error = false;
185
    stream->error = false;
186
    stream->eof = false;
186
    stream->eof = false;
187
    stream->klog = false;
187
    stream->klog = false;
188
    stream->phone = -1;
188
    stream->phone = -1;
189
   
189
   
190
    list_append(&stream->link, &files);
190
    list_append(&stream->link, &files);
191
   
191
   
192
    return stream;
192
    return stream;
193
}
193
}
194
 
194
 
-
 
195
FILE *fdopen(int fd, const char *mode)
-
 
196
{
-
 
197
    /* Open file. */
-
 
198
    FILE *stream = malloc(sizeof(FILE));
-
 
199
    if (stream == NULL) {
-
 
200
        errno = ENOMEM;
-
 
201
        return NULL;
-
 
202
    }
-
 
203
   
-
 
204
    stream->fd = fd;
-
 
205
    stream->error = false;
-
 
206
    stream->eof = false;
-
 
207
    stream->klog = false;
-
 
208
    stream->phone = -1;
-
 
209
   
-
 
210
    list_append(&stream->link, &files);
-
 
211
   
-
 
212
    return stream;
-
 
213
}
-
 
214
 
195
FILE *fopen_node(fdi_node_t *node, const char *mode)
215
FILE *fopen_node(fdi_node_t *node, const char *mode)
196
{
216
{
197
    int flags;
217
    int flags;
198
    if (!parse_mode(mode, &flags))
218
    if (!parse_mode(mode, &flags))
199
        return NULL;
219
        return NULL;
200
   
220
   
201
    /* Open file. */
221
    /* Open file. */
202
    FILE *stream = malloc(sizeof(FILE));
222
    FILE *stream = malloc(sizeof(FILE));
203
    if (stream == NULL) {
223
    if (stream == NULL) {
204
        errno = ENOMEM;
224
        errno = ENOMEM;
205
        return NULL;
225
        return NULL;
206
    }
226
    }
207
   
227
   
208
    stream->fd = open_node(node, flags);
228
    stream->fd = open_node(node, flags);
209
    if (stream->fd < 0) {
229
    if (stream->fd < 0) {
210
        /* errno was set by open_node() */
230
        /* errno was set by open_node() */
211
        free(stream);
231
        free(stream);
212
        return NULL;
232
        return NULL;
213
    }
233
    }
214
   
234
   
215
    stream->error = false;
235
    stream->error = false;
216
    stream->eof = false;
236
    stream->eof = false;
217
    stream->klog = false;
237
    stream->klog = false;
218
    stream->phone = -1;
238
    stream->phone = -1;
219
   
239
   
220
    list_append(&stream->link, &files);
240
    list_append(&stream->link, &files);
221
   
241
   
222
    return stream;
242
    return stream;
223
}
243
}
224
 
244
 
225
int fclose(FILE *stream)
245
int fclose(FILE *stream)
226
{
246
{
227
    int rc = 0;
247
    int rc = 0;
228
   
248
   
229
    fflush(stream);
249
    fflush(stream);
230
   
250
   
231
    if (stream->phone >= 0)
251
    if (stream->phone >= 0)
232
        ipc_hangup(stream->phone);
252
        ipc_hangup(stream->phone);
233
   
253
   
234
    if (stream->fd >= 0)
254
    if (stream->fd >= 0)
235
        rc = close(stream->fd);
255
        rc = close(stream->fd);
236
   
256
   
237
    list_remove(&stream->link);
257
    list_remove(&stream->link);
238
   
258
   
239
    if ((stream != &stdin_null)
259
    if ((stream != &stdin_null)
240
        && (stream != &stdout_klog)
260
        && (stream != &stdout_klog)
241
        && (stream != &stderr_klog))
261
        && (stream != &stderr_klog))
242
        free(stream);
262
        free(stream);
243
   
263
   
244
    stream = NULL;
264
    stream = NULL;
245
   
265
   
246
    if (rc != 0) {
266
    if (rc != 0) {
247
        /* errno was set by close() */
267
        /* errno was set by close() */
248
        return EOF;
268
        return EOF;
249
    }
269
    }
250
   
270
   
251
    return 0;
271
    return 0;
252
}
272
}
253
 
273
 
254
/** Read from a stream.
274
/** Read from a stream.
255
 *
275
 *
256
 * @param buf    Destination buffer.
276
 * @param buf    Destination buffer.
257
 * @param size   Size of each record.
277
 * @param size   Size of each record.
258
 * @param nmemb  Number of records to read.
278
 * @param nmemb  Number of records to read.
259
 * @param stream Pointer to the stream.
279
 * @param stream Pointer to the stream.
260
 *
280
 *
261
 */
281
 */
262
size_t fread(void *buf, size_t size, size_t nmemb, FILE *stream)
282
size_t fread(void *buf, size_t size, size_t nmemb, FILE *stream)
263
{
283
{
264
    size_t left = size * nmemb;
284
    size_t left = size * nmemb;
265
    size_t done = 0;
285
    size_t done = 0;
266
   
286
   
267
    while ((left > 0) && (!stream->error) && (!stream->eof)) {
287
    while ((left > 0) && (!stream->error) && (!stream->eof)) {
268
        ssize_t rd = read(stream->fd, buf + done, left);
288
        ssize_t rd = read(stream->fd, buf + done, left);
269
       
289
       
270
        if (rd < 0)
290
        if (rd < 0)
271
            stream->error = true;
291
            stream->error = true;
272
        else if (rd == 0)
292
        else if (rd == 0)
273
            stream->eof = true;
293
            stream->eof = true;
274
        else {
294
        else {
275
            left -= rd;
295
            left -= rd;
276
            done += rd;
296
            done += rd;
277
        }
297
        }
278
    }
298
    }
279
   
299
   
280
    return (done / size);
300
    return (done / size);
281
}
301
}
282
 
302
 
283
/** Write to a stream.
303
/** Write to a stream.
284
 *
304
 *
285
 * @param buf    Source buffer.
305
 * @param buf    Source buffer.
286
 * @param size   Size of each record.
306
 * @param size   Size of each record.
287
 * @param nmemb  Number of records to write.
307
 * @param nmemb  Number of records to write.
288
 * @param stream Pointer to the stream.
308
 * @param stream Pointer to the stream.
289
 *
309
 *
290
 */
310
 */
291
size_t fwrite(const void *buf, size_t size, size_t nmemb, FILE *stream)
311
size_t fwrite(const void *buf, size_t size, size_t nmemb, FILE *stream)
292
{
312
{
293
    size_t left = size * nmemb;
313
    size_t left = size * nmemb;
294
    size_t done = 0;
314
    size_t done = 0;
295
   
315
   
296
    while ((left > 0) && (!stream->error)) {
316
    while ((left > 0) && (!stream->error)) {
297
        ssize_t wr;
317
        ssize_t wr;
298
       
318
       
299
        if (stream->klog)
319
        if (stream->klog)
300
            wr = klog_write(buf + done, left);
320
            wr = klog_write(buf + done, left);
301
        else
321
        else
302
            wr = write(stream->fd, buf + done, left);
322
            wr = write(stream->fd, buf + done, left);
303
       
323
       
304
        if (wr <= 0)
324
        if (wr <= 0)
305
            stream->error = true;
325
            stream->error = true;
306
        else {
326
        else {
307
            left -= wr;
327
            left -= wr;
308
            done += wr;
328
            done += wr;
309
        }
329
        }
310
    }
330
    }
311
   
331
   
312
    return (done / size);
332
    return (done / size);
313
}
333
}
314
 
334
 
315
int fputc(wchar_t c, FILE *stream)
335
int fputc(wchar_t c, FILE *stream)
316
{
336
{
317
    char buf[STR_BOUNDS(1)];
337
    char buf[STR_BOUNDS(1)];
318
    size_t sz = 0;
338
    size_t sz = 0;
319
   
339
   
320
    if (chr_encode(c, buf, &sz, STR_BOUNDS(1)) == EOK) {
340
    if (chr_encode(c, buf, &sz, STR_BOUNDS(1)) == EOK) {
321
        size_t wr = fwrite(buf, sz, 1, stream);
341
        size_t wr = fwrite(buf, sz, 1, stream);
322
       
342
       
323
        if (wr < sz)
343
        if (wr < sz)
324
            return EOF;
344
            return EOF;
325
       
345
       
326
        return (int) c;
346
        return (int) c;
327
    }
347
    }
328
   
348
   
329
    return EOF;
349
    return EOF;
330
}
350
}
331
 
351
 
332
int putchar(wchar_t c)
352
int putchar(wchar_t c)
333
{
353
{
334
    return fputc(c, stdout);
354
    return fputc(c, stdout);
335
}
355
}
336
 
356
 
337
int fputs(const char *str, FILE *stream)
357
int fputs(const char *str, FILE *stream)
338
{
358
{
339
    return fwrite(str, str_size(str), 1, stream);
359
    return fwrite(str, str_size(str), 1, stream);
340
}
360
}
341
 
361
 
342
int puts(const char *str)
362
int puts(const char *str)
343
{
363
{
344
    return fputs(str, stdout);
364
    return fputs(str, stdout);
345
}
365
}
346
 
366
 
347
int fgetc(FILE *stream)
367
int fgetc(FILE *stream)
348
{
368
{
349
    char c;
369
    char c;
350
 
370
 
351
    /* This could be made faster by only flushing when needed. */
371
    /* This could be made faster by only flushing when needed. */
352
    if (stdout)
372
    if (stdout)
353
        fflush(stdout);
373
        fflush(stdout);
354
    if (stderr)
374
    if (stderr)
355
        fflush(stderr);
375
        fflush(stderr);
356
 
376
 
357
    if (fread(&c, sizeof(char), 1, stream) < sizeof(char))
377
    if (fread(&c, sizeof(char), 1, stream) < sizeof(char))
358
        return EOF;
378
        return EOF;
359
   
379
   
360
    return (int) c;
380
    return (int) c;
361
}
381
}
362
 
382
 
363
int getchar(void)
383
int getchar(void)
364
{
384
{
365
    return fgetc(stdin);
385
    return fgetc(stdin);
366
}
386
}
367
 
387
 
368
int fseek(FILE *stream, long offset, int origin)
388
int fseek(FILE *stream, long offset, int origin)
369
{
389
{
370
    off_t rc = lseek(stream->fd, offset, origin);
390
    off_t rc = lseek(stream->fd, offset, origin);
371
    if (rc == (off_t) (-1)) {
391
    if (rc == (off_t) (-1)) {
372
        /* errno has been set by lseek. */
392
        /* errno has been set by lseek. */
373
        return -1;
393
        return -1;
374
    }
394
    }
375
   
395
   
376
    stream->eof = false;
396
    stream->eof = false;
377
   
397
   
378
    return 0;
398
    return 0;
379
}
399
}
-
 
400
 
-
 
401
void rewind(FILE *stream)
-
 
402
{
-
 
403
    (void) fseek(stream, 0, SEEK_SET);
-
 
404
}
380
 
405
 
381
int fflush(FILE *stream)
406
int fflush(FILE *stream)
382
{
407
{
383
    if (stream->klog) {
408
    if (stream->klog) {
384
        klog_update();
409
        klog_update();
385
        return EOK;
410
        return EOK;
386
    }
411
    }
387
   
412
   
388
    if (stream->fd >= 0)
413
    if (stream->fd >= 0)
389
        return fsync(stream->fd);
414
        return fsync(stream->fd);
390
   
415
   
391
    return ENOENT;
416
    return ENOENT;
392
}
417
}
393
 
418
 
394
int feof(FILE *stream)
419
int feof(FILE *stream)
395
{
420
{
396
    return stream->eof;
421
    return stream->eof;
397
}
422
}
398
 
423
 
399
int ferror(FILE *stream)
424
int ferror(FILE *stream)
400
{
425
{
401
    return stream->error;
426
    return stream->error;
402
}
427
}
403
 
428
 
404
int fphone(FILE *stream)
429
int fphone(FILE *stream)
405
{
430
{
406
    if (stream->fd >= 0) {
431
    if (stream->fd >= 0) {
407
        if (stream->phone < 0)
432
        if (stream->phone < 0)
408
            stream->phone = fd_phone(stream->fd);
433
            stream->phone = fd_phone(stream->fd);
409
       
434
       
410
        return stream->phone;
435
        return stream->phone;
411
    }
436
    }
412
   
437
   
413
    return -1;
438
    return -1;
414
}
439
}
415
 
440
 
416
int fnode(FILE *stream, fdi_node_t *node)
441
int fnode(FILE *stream, fdi_node_t *node)
417
{
442
{
418
    if (stream->fd >= 0)
443
    if (stream->fd >= 0)
419
        return fd_node(stream->fd, node);
444
        return fd_node(stream->fd, node);
420
   
445
   
421
    return ENOENT;
446
    return ENOENT;
422
}
447
}
423
 
448
 
424
/** @}
449
/** @}
425
 */
450
 */
426
 
451