io.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2005 Martin Decky
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  *
00009  * - Redistributions of source code must retain the above copyright
00010  *   notice, this list of conditions and the following disclaimer.
00011  * - Redistributions in binary form must reproduce the above copyright
00012  *   notice, this list of conditions and the following disclaimer in the
00013  *   documentation and/or other materials provided with the distribution.
00014  * - The name of the author may not be used to endorse or promote products
00015  *   derived from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
00018  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00019  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
00020  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
00021  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
00022  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00023  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00024  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00025  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
00026  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
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         /* Parse mode except first character. */
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         /* Parse first character of mode and determine flags for open(). */
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                 /* TODO: a+ must read from beginning, append to the end. */
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         /* FIXME: Use more complex rules for setting buffering options. */
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         /* Open file. */
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                 /* errno was set by open() */
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         /* Open file. */
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         /* Open file. */
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                 /* errno was set by open_node() */
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                 /* errno was set by close() */
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         /* If buffer has prefetched read data, we need to seek back. */
00457         if (stream->buf_state == _bs_read)
00458                 lseek(stream->fd, - (ssize_t) bytes_used, SEEK_CUR);
00459 
00460         /* If buffer has unwritten data, we need to write them out. */
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         /* If not buffered stream, read in directly. */
00490         if (stream->btype == _IONBF) {
00491                 now = _fread(dest, size, nmemb, stream);
00492                 return now;
00493         }
00494 
00495         /* Make sure no data is pending write. */
00496         if (stream->buf_state == _bs_write)
00497                 _fflushbuf(stream);
00498 
00499         /* Perform lazy allocation of stream buffer. */
00500         if (stream->buf == NULL) {
00501                 if (_fallocbuf(stream) != 0)
00502                         return 0; /* Errno set by _fallocbuf(). */
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         /* If not buffered stream, write out directly. */
00560         if (stream->btype == _IONBF) {
00561                 now = _fwrite(buf, size, nmemb, stream);
00562                 fflush(stream);
00563                 return now;
00564         }
00565 
00566         /* Make sure buffer contains no prefetched data. */
00567         if (stream->buf_state == _bs_read)
00568                 _fflushbuf(stream);
00569 
00570 
00571         /* Perform lazy allocation of stream buffer. */
00572         if (stream->buf == NULL) {
00573                 if (_fallocbuf(stream) != 0)
00574                         return 0; /* Errno set by _fallocbuf(). */
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                         /* Only need to drain buffer. */
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         /* This could be made faster by only flushing when needed. */
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                 /* errno has been set by lseek64. */
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 

Generated on Thu Jun 2 07:45:46 2011 for HelenOS/USB by  doxygen 1.4.7