00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00035 #include <stdio.h>
00036 #include <unistd.h>
00037 #include <fcntl.h>
00038 #include <assert.h>
00039 #include <str.h>
00040 #include <errno.h>
00041 #include <bool.h>
00042 #include <malloc.h>
00043 #include <async.h>
00044 #include <io/klog.h>
00045 #include <vfs/vfs.h>
00046 #include <ipc/devmap.h>
00047 #include <adt/list.h>
00048 #include "../private/io.h"
00049
00050 static void _ffillbuf(FILE *stream);
00051 static void _fflushbuf(FILE *stream);
00052
00053 static FILE stdin_null = {
00054 .fd = -1,
00055 .error = true,
00056 .eof = true,
00057 .klog = false,
00058 .phone = -1,
00059 .btype = _IONBF,
00060 .buf = NULL,
00061 .buf_size = 0,
00062 .buf_head = NULL,
00063 .buf_tail = NULL,
00064 .buf_state = _bs_empty
00065 };
00066
00067 static FILE stdout_klog = {
00068 .fd = -1,
00069 .error = false,
00070 .eof = false,
00071 .klog = true,
00072 .phone = -1,
00073 .btype = _IOLBF,
00074 .buf = NULL,
00075 .buf_size = BUFSIZ,
00076 .buf_head = NULL,
00077 .buf_tail = NULL,
00078 .buf_state = _bs_empty
00079 };
00080
00081 static FILE stderr_klog = {
00082 .fd = -1,
00083 .error = false,
00084 .eof = false,
00085 .klog = true,
00086 .phone = -1,
00087 .btype = _IONBF,
00088 .buf = NULL,
00089 .buf_size = 0,
00090 .buf_head = NULL,
00091 .buf_tail = NULL,
00092 .buf_state = _bs_empty
00093 };
00094
00095 FILE *stdin = NULL;
00096 FILE *stdout = NULL;
00097 FILE *stderr = NULL;
00098
00099 static LIST_INITIALIZE(files);
00100
00101 void __stdio_init(int filc, fdi_node_t *filv[])
00102 {
00103 if (filc > 0) {
00104 stdin = fopen_node(filv[0], "r");
00105 } else {
00106 stdin = &stdin_null;
00107 list_append(&stdin->link, &files);
00108 }
00109
00110 if (filc > 1) {
00111 stdout = fopen_node(filv[1], "w");
00112 } else {
00113 stdout = &stdout_klog;
00114 list_append(&stdout->link, &files);
00115 }
00116
00117 if (filc > 2) {
00118 stderr = fopen_node(filv[2], "w");
00119 } else {
00120 stderr = &stderr_klog;
00121 list_append(&stderr->link, &files);
00122 }
00123 }
00124
00125 void __stdio_done(void)
00126 {
00127 link_t *link = files.next;
00128
00129 while (link != &files) {
00130 FILE *file = list_get_instance(link, FILE, link);
00131 fclose(file);
00132 link = files.next;
00133 }
00134 }
00135
00136 static bool parse_mode(const char *mode, int *flags)
00137 {
00138
00139 const char *mp = mode;
00140 if (*mp++ == 0) {
00141 errno = EINVAL;
00142 return false;
00143 }
00144
00145 if ((*mp == 'b') || (*mp == 't'))
00146 mp++;
00147
00148 bool plus;
00149 if (*mp == '+') {
00150 mp++;
00151 plus = true;
00152 } else
00153 plus = false;
00154
00155 if (*mp != 0) {
00156 errno = EINVAL;
00157 return false;
00158 }
00159
00160
00161 switch (mode[0]) {
00162 case 'r':
00163 *flags = plus ? O_RDWR : O_RDONLY;
00164 break;
00165 case 'w':
00166 *flags = (O_TRUNC | O_CREAT) | (plus ? O_RDWR : O_WRONLY);
00167 break;
00168 case 'a':
00169
00170 if (plus) {
00171 errno = ENOTSUP;
00172 return false;
00173 }
00174 *flags = (O_APPEND | O_CREAT) | (plus ? O_RDWR : O_WRONLY);
00175 break;
00176 default:
00177 errno = EINVAL;
00178 return false;
00179 }
00180
00181 return true;
00182 }
00183
00185 void setvbuf(FILE *stream, void *buf, int mode, size_t size)
00186 {
00187 stream->btype = mode;
00188 stream->buf = buf;
00189 stream->buf_size = size;
00190 stream->buf_head = stream->buf;
00191 stream->buf_tail = stream->buf;
00192 stream->buf_state = _bs_empty;
00193 }
00194
00195 static void _setvbuf(FILE *stream)
00196 {
00197
00198
00199 switch (stream->fd) {
00200 case 1:
00201 setvbuf(stream, NULL, _IOLBF, BUFSIZ);
00202 break;
00203 case 0:
00204 case 2:
00205 setvbuf(stream, NULL, _IONBF, 0);
00206 break;
00207 default:
00208 setvbuf(stream, NULL, _IOFBF, BUFSIZ);
00209 }
00210 }
00211
00213 static int _fallocbuf(FILE *stream)
00214 {
00215 assert(stream->buf == NULL);
00216
00217 stream->buf = malloc(stream->buf_size);
00218 if (stream->buf == NULL) {
00219 errno = ENOMEM;
00220 return -1;
00221 }
00222
00223 stream->buf_head = stream->buf;
00224 stream->buf_tail = stream->buf;
00225 return 0;
00226 }
00227
00234 FILE *fopen(const char *path, const char *mode)
00235 {
00236 int flags;
00237 if (!parse_mode(mode, &flags))
00238 return NULL;
00239
00240
00241 FILE *stream = malloc(sizeof(FILE));
00242 if (stream == NULL) {
00243 errno = ENOMEM;
00244 return NULL;
00245 }
00246
00247 stream->fd = open(path, flags, 0666);
00248 if (stream->fd < 0) {
00249
00250 free(stream);
00251 return NULL;
00252 }
00253
00254 stream->error = false;
00255 stream->eof = false;
00256 stream->klog = false;
00257 stream->phone = -1;
00258 stream->need_sync = false;
00259 _setvbuf(stream);
00260
00261 list_append(&stream->link, &files);
00262
00263 return stream;
00264 }
00265
00266 FILE *fdopen(int fd, const char *mode)
00267 {
00268
00269 FILE *stream = malloc(sizeof(FILE));
00270 if (stream == NULL) {
00271 errno = ENOMEM;
00272 return NULL;
00273 }
00274
00275 stream->fd = fd;
00276 stream->error = false;
00277 stream->eof = false;
00278 stream->klog = false;
00279 stream->phone = -1;
00280 stream->need_sync = false;
00281 _setvbuf(stream);
00282
00283 list_append(&stream->link, &files);
00284
00285 return stream;
00286 }
00287
00288 FILE *fopen_node(fdi_node_t *node, const char *mode)
00289 {
00290 int flags;
00291 if (!parse_mode(mode, &flags))
00292 return NULL;
00293
00294
00295 FILE *stream = malloc(sizeof(FILE));
00296 if (stream == NULL) {
00297 errno = ENOMEM;
00298 return NULL;
00299 }
00300
00301 stream->fd = open_node(node, flags);
00302 if (stream->fd < 0) {
00303
00304 free(stream);
00305 return NULL;
00306 }
00307
00308 stream->error = false;
00309 stream->eof = false;
00310 stream->klog = false;
00311 stream->phone = -1;
00312 stream->need_sync = false;
00313 _setvbuf(stream);
00314
00315 list_append(&stream->link, &files);
00316
00317 return stream;
00318 }
00319
00320 int fclose(FILE *stream)
00321 {
00322 int rc = 0;
00323
00324 fflush(stream);
00325
00326 if (stream->phone >= 0)
00327 async_hangup(stream->phone);
00328
00329 if (stream->fd >= 0)
00330 rc = close(stream->fd);
00331
00332 list_remove(&stream->link);
00333
00334 if ((stream != &stdin_null)
00335 && (stream != &stdout_klog)
00336 && (stream != &stderr_klog))
00337 free(stream);
00338
00339 stream = NULL;
00340
00341 if (rc != 0) {
00342
00343 return EOF;
00344 }
00345
00346 return 0;
00347 }
00348
00356 static size_t _fread(void *buf, size_t size, size_t nmemb, FILE *stream)
00357 {
00358 size_t left, done;
00359
00360 if (size == 0 || nmemb == 0)
00361 return 0;
00362
00363 left = size * nmemb;
00364 done = 0;
00365
00366 while ((left > 0) && (!stream->error) && (!stream->eof)) {
00367 ssize_t rd = read(stream->fd, buf + done, left);
00368
00369 if (rd < 0)
00370 stream->error = true;
00371 else if (rd == 0)
00372 stream->eof = true;
00373 else {
00374 left -= rd;
00375 done += rd;
00376 }
00377 }
00378
00379 return (done / size);
00380 }
00381
00389 static size_t _fwrite(const void *buf, size_t size, size_t nmemb, FILE *stream)
00390 {
00391 size_t left;
00392 size_t done;
00393
00394 if (size == 0 || nmemb == 0)
00395 return 0;
00396
00397 left = size * nmemb;
00398 done = 0;
00399
00400 while ((left > 0) && (!stream->error)) {
00401 ssize_t wr;
00402
00403 if (stream->klog)
00404 wr = klog_write(buf + done, left);
00405 else
00406 wr = write(stream->fd, buf + done, left);
00407
00408 if (wr <= 0)
00409 stream->error = true;
00410 else {
00411 left -= wr;
00412 done += wr;
00413 }
00414 }
00415
00416 if (done > 0)
00417 stream->need_sync = true;
00418
00419 return (done / size);
00420 }
00421
00423 static void _ffillbuf(FILE *stream)
00424 {
00425 ssize_t rc;
00426
00427 stream->buf_head = stream->buf_tail = stream->buf;
00428
00429 rc = read(stream->fd, stream->buf, stream->buf_size);
00430 if (rc < 0) {
00431 stream->error = true;
00432 return;
00433 }
00434
00435 if (rc == 0) {
00436 stream->eof = true;
00437 return;
00438 }
00439
00440 stream->buf_head += rc;
00441 stream->buf_state = _bs_read;
00442 }
00443
00445 static void _fflushbuf(FILE *stream)
00446 {
00447 size_t bytes_used;
00448
00449 if ((!stream->buf) || (stream->btype == _IONBF) || (stream->error))
00450 return;
00451
00452 bytes_used = stream->buf_head - stream->buf_tail;
00453 if (bytes_used == 0)
00454 return;
00455
00456
00457 if (stream->buf_state == _bs_read)
00458 lseek(stream->fd, - (ssize_t) bytes_used, SEEK_CUR);
00459
00460
00461 if (stream->buf_state == _bs_write)
00462 (void) _fwrite(stream->buf_tail, 1, bytes_used, stream);
00463
00464 stream->buf_head = stream->buf;
00465 stream->buf_tail = stream->buf;
00466 stream->buf_state = _bs_empty;
00467 }
00468
00477 size_t fread(void *dest, size_t size, size_t nmemb, FILE *stream)
00478 {
00479 uint8_t *dp;
00480 size_t bytes_left;
00481 size_t now;
00482 size_t data_avail;
00483 size_t total_read;
00484 size_t i;
00485
00486 if (size == 0 || nmemb == 0)
00487 return 0;
00488
00489
00490 if (stream->btype == _IONBF) {
00491 now = _fread(dest, size, nmemb, stream);
00492 return now;
00493 }
00494
00495
00496 if (stream->buf_state == _bs_write)
00497 _fflushbuf(stream);
00498
00499
00500 if (stream->buf == NULL) {
00501 if (_fallocbuf(stream) != 0)
00502 return 0;
00503 }
00504
00505 bytes_left = size * nmemb;
00506 total_read = 0;
00507 dp = (uint8_t *) dest;
00508
00509 while ((!stream->error) && (!stream->eof) && (bytes_left > 0)) {
00510 if (stream->buf_head == stream->buf_tail)
00511 _ffillbuf(stream);
00512
00513 if (stream->error || stream->eof)
00514 break;
00515
00516 data_avail = stream->buf_head - stream->buf_tail;
00517
00518 if (bytes_left > data_avail)
00519 now = data_avail;
00520 else
00521 now = bytes_left;
00522
00523 for (i = 0; i < now; i++) {
00524 dp[i] = stream->buf_tail[i];
00525 }
00526
00527 dp += now;
00528 stream->buf_tail += now;
00529 bytes_left -= now;
00530 total_read += now;
00531 }
00532
00533 return (total_read / size);
00534 }
00535
00536
00545 size_t fwrite(const void *buf, size_t size, size_t nmemb, FILE *stream)
00546 {
00547 uint8_t *data;
00548 size_t bytes_left;
00549 size_t now;
00550 size_t buf_free;
00551 size_t total_written;
00552 size_t i;
00553 uint8_t b;
00554 bool need_flush;
00555
00556 if (size == 0 || nmemb == 0)
00557 return 0;
00558
00559
00560 if (stream->btype == _IONBF) {
00561 now = _fwrite(buf, size, nmemb, stream);
00562 fflush(stream);
00563 return now;
00564 }
00565
00566
00567 if (stream->buf_state == _bs_read)
00568 _fflushbuf(stream);
00569
00570
00571
00572 if (stream->buf == NULL) {
00573 if (_fallocbuf(stream) != 0)
00574 return 0;
00575 }
00576
00577 data = (uint8_t *) buf;
00578 bytes_left = size * nmemb;
00579 total_written = 0;
00580 need_flush = false;
00581
00582 while ((!stream->error) && (bytes_left > 0)) {
00583 buf_free = stream->buf_size - (stream->buf_head - stream->buf);
00584 if (bytes_left > buf_free)
00585 now = buf_free;
00586 else
00587 now = bytes_left;
00588
00589 for (i = 0; i < now; i++) {
00590 b = data[i];
00591 stream->buf_head[i] = b;
00592
00593 if ((b == '\n') && (stream->btype == _IOLBF))
00594 need_flush = true;
00595 }
00596
00597 buf += now;
00598 stream->buf_head += now;
00599 buf_free -= now;
00600 bytes_left -= now;
00601 total_written += now;
00602
00603 if (buf_free == 0) {
00604
00605 _fflushbuf(stream);
00606 need_flush = false;
00607 }
00608 }
00609
00610 if (total_written > 0)
00611 stream->buf_state = _bs_write;
00612
00613 if (need_flush)
00614 fflush(stream);
00615
00616 return (total_written / size);
00617 }
00618
00619 int fputc(wchar_t c, FILE *stream)
00620 {
00621 char buf[STR_BOUNDS(1)];
00622 size_t sz = 0;
00623
00624 if (chr_encode(c, buf, &sz, STR_BOUNDS(1)) == EOK) {
00625 size_t wr = fwrite(buf, 1, sz, stream);
00626
00627 if (wr < sz)
00628 return EOF;
00629
00630 return (int) c;
00631 }
00632
00633 return EOF;
00634 }
00635
00636 int putchar(wchar_t c)
00637 {
00638 return fputc(c, stdout);
00639 }
00640
00641 int fputs(const char *str, FILE *stream)
00642 {
00643 return fwrite(str, str_size(str), 1, stream);
00644 }
00645
00646 int puts(const char *str)
00647 {
00648 return fputs(str, stdout);
00649 }
00650
00651 int fgetc(FILE *stream)
00652 {
00653 char c;
00654
00655
00656 if (stdout)
00657 fflush(stdout);
00658 if (stderr)
00659 fflush(stderr);
00660
00661 if (fread(&c, sizeof(char), 1, stream) < sizeof(char))
00662 return EOF;
00663
00664 return (int) c;
00665 }
00666
00667 char *fgets(char *str, int size, FILE *stream)
00668 {
00669 int c;
00670 int idx;
00671
00672 idx = 0;
00673 while (idx < size - 1) {
00674 c = fgetc(stream);
00675 if (c == EOF)
00676 break;
00677
00678 str[idx++] = c;
00679
00680 if (c == '\n')
00681 break;
00682 }
00683
00684 if (ferror(stream))
00685 return NULL;
00686
00687 if (idx == 0)
00688 return NULL;
00689
00690 str[idx] = '\0';
00691 return str;
00692 }
00693
00694 int getchar(void)
00695 {
00696 return fgetc(stdin);
00697 }
00698
00699 int fseek(FILE *stream, off64_t offset, int whence)
00700 {
00701 off64_t rc;
00702
00703 _fflushbuf(stream);
00704
00705 rc = lseek(stream->fd, offset, whence);
00706 if (rc == (off64_t) (-1)) {
00707
00708 return -1;
00709 }
00710
00711 stream->eof = false;
00712 return 0;
00713 }
00714
00715 off64_t ftell(FILE *stream)
00716 {
00717 return lseek(stream->fd, 0, SEEK_CUR);
00718 }
00719
00720 void rewind(FILE *stream)
00721 {
00722 (void) fseek(stream, 0, SEEK_SET);
00723 }
00724
00725 int fflush(FILE *stream)
00726 {
00727 _fflushbuf(stream);
00728
00729 if (stream->klog) {
00730 klog_update();
00731 return EOK;
00732 }
00733
00734 if (stream->fd >= 0 && stream->need_sync) {
00739 stream->need_sync = false;
00740 return fsync(stream->fd);
00741 }
00742
00743 return ENOENT;
00744 }
00745
00746 int feof(FILE *stream)
00747 {
00748 return stream->eof;
00749 }
00750
00751 int ferror(FILE *stream)
00752 {
00753 return stream->error;
00754 }
00755
00756 void clearerr(FILE *stream)
00757 {
00758 stream->eof = false;
00759 stream->error = false;
00760 }
00761
00762 int fileno(FILE *stream)
00763 {
00764 if (stream->klog) {
00765 errno = EBADF;
00766 return -1;
00767 }
00768
00769 return stream->fd;
00770 }
00771
00772 int fphone(FILE *stream)
00773 {
00774 if (stream->fd >= 0) {
00775 if (stream->phone < 0)
00776 stream->phone = fd_phone(stream->fd);
00777
00778 return stream->phone;
00779 }
00780
00781 return -1;
00782 }
00783
00784 int fnode(FILE *stream, fdi_node_t *node)
00785 {
00786 if (stream->fd >= 0)
00787 return fd_node(stream->fd, node);
00788
00789 return ENOENT;
00790 }
00791