Subversion Repositories HelenOS

Rev

Rev 4514 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Copyright (c) 2005 Martin 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.
  27.  */
  28.  
  29. /** @addtogroup libc
  30.  * @{
  31.  */
  32. /** @file
  33.  */
  34.  
  35. #include <stdio.h>
  36. #include <unistd.h>
  37. #include <fcntl.h>
  38. #include <assert.h>
  39. #include <string.h>
  40. #include <errno.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;
  89.  
  90. static LIST_INITIALIZE(files);
  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 {
  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)
  128. {
  129.     /* Parse mode except first character. */
  130.     const char *mp = mode;
  131.     if (*mp++ == 0) {
  132.         errno = EINVAL;
  133.         return false;
  134.     }
  135.    
  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;
  172. }
  173.  
  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. /** Allocate stream buffer. */
  184. static int _fallocbuf(FILE *stream)
  185. {
  186.     assert(stream->buf == NULL);
  187.  
  188.     stream->buf = malloc(stream->buf_size);
  189.     if (stream->buf == NULL) {
  190.         errno = ENOMEM;
  191.         return -1;
  192.     }
  193.  
  194.     stream->buf_head = stream->buf;
  195.     return 0;
  196. }
  197.  
  198. /** Open a stream.
  199.  *
  200.  * @param path Path of the file to open.
  201.  * @param mode Mode string, (r|w|a)[b|t][+].
  202.  *
  203.  */
  204. FILE *fopen(const char *path, const char *mode)
  205. {
  206.     int flags;
  207.     if (!parse_mode(mode, &flags))
  208.         return NULL;
  209.    
  210.     /* Open file. */
  211.     FILE *stream = malloc(sizeof(FILE));
  212.     if (stream == NULL) {
  213.         errno = ENOMEM;
  214.         return NULL;
  215.     }
  216.    
  217.     stream->fd = open(path, flags, 0666);
  218.     if (stream->fd < 0) {
  219.         /* errno was set by open() */
  220.         free(stream);
  221.         return NULL;
  222.     }
  223.    
  224.     stream->error = false;
  225.     stream->eof = false;
  226.     stream->klog = false;
  227.     stream->phone = -1;
  228.  
  229.     /* FIXME: Should select buffering type based on what was opened. */
  230.     setvbuf(stream, NULL, _IOFBF, BUFSIZ);
  231.    
  232.     list_append(&stream->link, &files);
  233.    
  234.     return stream;
  235. }
  236.  
  237. FILE *fdopen(int fd, const char *mode)
  238. {
  239.     /* Open file. */
  240.     FILE *stream = malloc(sizeof(FILE));
  241.     if (stream == NULL) {
  242.         errno = ENOMEM;
  243.         return NULL;
  244.     }
  245.    
  246.     stream->fd = fd;
  247.     stream->error = false;
  248.     stream->eof = false;
  249.     stream->klog = false;
  250.     stream->phone = -1;
  251.  
  252.     /* FIXME: Should select buffering type based on what was opened. */
  253.     setvbuf(stream, NULL, _IOLBF, BUFSIZ);
  254.    
  255.     list_append(&stream->link, &files);
  256.    
  257.     return stream;
  258. }
  259.  
  260. FILE *fopen_node(fdi_node_t *node, const char *mode)
  261. {
  262.     int flags;
  263.     if (!parse_mode(mode, &flags))
  264.         return NULL;
  265.    
  266.     /* Open file. */
  267.     FILE *stream = malloc(sizeof(FILE));
  268.     if (stream == NULL) {
  269.         errno = ENOMEM;
  270.         return NULL;
  271.     }
  272.    
  273.     stream->fd = open_node(node, flags);
  274.     if (stream->fd < 0) {
  275.         /* errno was set by open_node() */
  276.         free(stream);
  277.         return NULL;
  278.     }
  279.    
  280.     stream->error = false;
  281.     stream->eof = false;
  282.     stream->klog = false;
  283.     stream->phone = -1;
  284.  
  285.     /* FIXME: Should select buffering type based on what was opened. */
  286.     setvbuf(stream, NULL, _IOLBF, BUFSIZ);
  287.    
  288.     list_append(&stream->link, &files);
  289.    
  290.     return stream;
  291. }
  292.  
  293. int fclose(FILE *stream)
  294. {
  295.     int rc = 0;
  296.    
  297.     fflush(stream);
  298.    
  299.     if (stream->phone >= 0)
  300.         ipc_hangup(stream->phone);
  301.    
  302.     if (stream->fd >= 0)
  303.         rc = close(stream->fd);
  304.    
  305.     list_remove(&stream->link);
  306.    
  307.     if ((stream != &stdin_null)
  308.         && (stream != &stdout_klog)
  309.         && (stream != &stderr_klog))
  310.         free(stream);
  311.    
  312.     stream = NULL;
  313.    
  314.     if (rc != 0) {
  315.         /* errno was set by close() */
  316.         return EOF;
  317.     }
  318.    
  319.     return 0;
  320. }
  321.  
  322. /** Read from a stream.
  323.  *
  324.  * @param buf    Destination buffer.
  325.  * @param size   Size of each record.
  326.  * @param nmemb  Number of records to read.
  327.  * @param stream Pointer to the stream.
  328.  *
  329.  */
  330. size_t fread(void *buf, size_t size, size_t nmemb, FILE *stream)
  331. {
  332.     size_t left = size * nmemb;
  333.     size_t done = 0;
  334.  
  335.     /* Make sure no data is pending write. */
  336.     _fflushbuf(stream);
  337.  
  338.     while ((left > 0) && (!stream->error) && (!stream->eof)) {
  339.         ssize_t rd = read(stream->fd, buf + done, left);
  340.        
  341.         if (rd < 0)
  342.             stream->error = true;
  343.         else if (rd == 0)
  344.             stream->eof = true;
  345.         else {
  346.             left -= rd;
  347.             done += rd;
  348.         }
  349.     }
  350.    
  351.     return (done / size);
  352. }
  353.  
  354. static size_t _fwrite(const void *buf, size_t size, size_t nmemb, FILE *stream)
  355. {
  356.     size_t left = size * nmemb;
  357.     size_t done = 0;
  358.    
  359.     while ((left > 0) && (!stream->error)) {
  360.         ssize_t wr;
  361.        
  362.         if (stream->klog)
  363.             wr = klog_write(buf + done, left);
  364.         else
  365.             wr = write(stream->fd, buf + done, left);
  366.        
  367.         if (wr <= 0)
  368.             stream->error = true;
  369.         else {
  370.             left -= wr;
  371.             done += wr;
  372.         }
  373.     }
  374.    
  375.     return (done / size);
  376. }
  377.  
  378. /** Drain stream buffer, do not sync stream. */
  379. static void _fflushbuf(FILE *stream)
  380. {
  381.     size_t bytes_used;
  382.  
  383.     if (!stream->buf || stream->btype == _IONBF || stream->error)
  384.         return;
  385.  
  386.     bytes_used = stream->buf_head - stream->buf;
  387.     if (bytes_used == 0)
  388.         return;
  389.  
  390.     (void) _fwrite(stream->buf, 1, bytes_used, stream);
  391.     stream->buf_head = stream->buf;
  392. }
  393.  
  394. /** Write to a stream.
  395.  *
  396.  * @param buf    Source buffer.
  397.  * @param size   Size of each record.
  398.  * @param nmemb  Number of records to write.
  399.  * @param stream Pointer to the stream.
  400.  *
  401.  */
  402. size_t fwrite(const void *buf, size_t size, size_t nmemb, FILE *stream)
  403. {
  404.     uint8_t *data;
  405.     size_t bytes_left;
  406.     size_t now;
  407.     size_t buf_free;
  408.     size_t total_written;
  409.     size_t i;
  410.     uint8_t b;
  411.     bool need_flush;
  412.  
  413.     /* If not buffered stream, write out directly. */
  414.     if (stream->btype == _IONBF)
  415.         return _fwrite(buf, size, nmemb, stream);
  416.  
  417.     /* Perform lazy allocation of stream buffer. */
  418.     if (stream->buf == NULL) {
  419.         if (_fallocbuf(stream) != 0)
  420.             return 0; /* Errno set by _fallocbuf(). */
  421.     }
  422.  
  423.     data = (uint8_t *) buf;
  424.     bytes_left = size * nmemb;
  425.     total_written = 0;
  426.     need_flush = false;
  427.  
  428.     while (!stream->error && bytes_left > 0) {
  429.  
  430.         buf_free = stream->buf_size - (stream->buf_head - stream->buf);
  431.         if (bytes_left > buf_free)
  432.             now = buf_free;
  433.         else
  434.             now = bytes_left;
  435.  
  436.         for (i = 0; i < now; i++) {
  437.             b = data[i];
  438.             stream->buf_head[i] = b;
  439.  
  440.             if (b == '\n' && stream->btype == _IOLBF)
  441.                 need_flush = true;
  442.         }
  443.  
  444.         buf += now;
  445.         stream->buf_head += now;
  446.         buf_free -= now;
  447.         bytes_left -= now;
  448.         total_written += now;
  449.  
  450.         if (buf_free == 0) {
  451.             /* Only need to drain buffer. */
  452.             _fflushbuf(stream);
  453.             need_flush = false;
  454.         }
  455.     }
  456.  
  457.     if (need_flush)
  458.         fflush(stream);
  459.  
  460.     return (total_written / size);
  461. }
  462.  
  463. int fputc(wchar_t c, FILE *stream)
  464. {
  465.     char buf[STR_BOUNDS(1)];
  466.     size_t sz = 0;
  467.    
  468.     if (chr_encode(c, buf, &sz, STR_BOUNDS(1)) == EOK) {
  469.         size_t wr = fwrite(buf, sz, 1, stream);
  470.        
  471.         if (wr < sz)
  472.             return EOF;
  473.        
  474.         return (int) c;
  475.     }
  476.    
  477.     return EOF;
  478. }
  479.  
  480. int putchar(wchar_t c)
  481. {
  482.     return fputc(c, stdout);
  483. }
  484.  
  485. int fputs(const char *str, FILE *stream)
  486. {
  487.     return fwrite(str, str_size(str), 1, stream);
  488. }
  489.  
  490. int puts(const char *str)
  491. {
  492.     return fputs(str, stdout);
  493. }
  494.  
  495. int fgetc(FILE *stream)
  496. {
  497.     char c;
  498.  
  499.     /* This could be made faster by only flushing when needed. */
  500.     if (stdout)
  501.         fflush(stdout);
  502.     if (stderr)
  503.         fflush(stderr);
  504.  
  505.     if (fread(&c, sizeof(char), 1, stream) < sizeof(char))
  506.         return EOF;
  507.    
  508.     return (int) c;
  509. }
  510.  
  511. int getchar(void)
  512. {
  513.     return fgetc(stdin);
  514. }
  515.  
  516. int fseek(FILE *stream, long offset, int origin)
  517. {
  518.     off_t rc = lseek(stream->fd, offset, origin);
  519.     if (rc == (off_t) (-1)) {
  520.         /* errno has been set by lseek. */
  521.         return -1;
  522.     }
  523.    
  524.     stream->eof = false;
  525.    
  526.     return 0;
  527. }
  528.  
  529. void rewind(FILE *stream)
  530. {
  531.     (void) fseek(stream, 0, SEEK_SET);
  532. }
  533.  
  534. int fflush(FILE *stream)
  535. {
  536.     _fflushbuf(stream);
  537.  
  538.     if (stream->klog) {
  539.         klog_update();
  540.         return EOK;
  541.     }
  542.    
  543.     if (stream->fd >= 0)
  544.         return fsync(stream->fd);
  545.    
  546.     return ENOENT;
  547. }
  548.  
  549. int feof(FILE *stream)
  550. {
  551.     return stream->eof;
  552. }
  553.  
  554. int ferror(FILE *stream)
  555. {
  556.     return stream->error;
  557. }
  558.  
  559. int fphone(FILE *stream)
  560. {
  561.     if (stream->fd >= 0) {
  562.         if (stream->phone < 0)
  563.             stream->phone = fd_phone(stream->fd);
  564.        
  565.         return stream->phone;
  566.     }
  567.    
  568.     return -1;
  569. }
  570.  
  571. int fnode(FILE *stream, fdi_node_t *node)
  572. {
  573.     if (stream->fd >= 0)
  574.         return fd_node(stream->fd, node);
  575.    
  576.     return ENOENT;
  577. }
  578.  
  579. /** @}
  580.  */
  581.