Subversion Repositories HelenOS

Rev

Rev 4590 | 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. static void _setvbuf(FILE *stream)
  184. {
  185.     /* FIXME: Use more complex rules for setting buffering options. */
  186.    
  187.     switch (stream->fd) {
  188.     case 1:
  189.         setvbuf(stream, NULL, _IOLBF, BUFSIZ);
  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.     }
  198. }
  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.  *
  217.  * @param path Path of the file to open.
  218.  * @param mode Mode string, (r|w|a)[b|t][+].
  219.  *
  220.  */
  221. FILE *fopen(const char *path, const char *mode)
  222. {
  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.     }
  240.    
  241.     stream->error = false;
  242.     stream->eof = false;
  243.     stream->klog = false;
  244.     stream->phone = -1;
  245.     _setvbuf(stream);
  246.    
  247.     list_append(&stream->link, &files);
  248.    
  249.     return stream;
  250. }
  251.  
  252. FILE *fdopen(int fd, const char *mode)
  253. {
  254.     /* Open file. */
  255.     FILE *stream = malloc(sizeof(FILE));
  256.     if (stream == NULL) {
  257.         errno = ENOMEM;
  258.         return NULL;
  259.     }
  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.    
  270.     return stream;
  271. }
  272.  
  273. FILE *fopen_node(fdi_node_t *node, const char *mode)
  274. {
  275.     int flags;
  276.     if (!parse_mode(mode, &flags))
  277.         return NULL;
  278.    
  279.     /* Open file. */
  280.     FILE *stream = malloc(sizeof(FILE));
  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;
  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. }
  303.  
  304. int fclose(FILE *stream)
  305. {
  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)
  320.         && (stream != &stderr_klog))
  321.         free(stream);
  322.    
  323.     stream = NULL;
  324.    
  325.     if (rc != 0) {
  326.         /* errno was set by close() */
  327.         return EOF;
  328.     }
  329.    
  330.     return 0;
  331. }
  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)) {
  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 {
  357.             left -= rd;
  358.             done += rd;
  359.         }
  360.     }
  361.    
  362.     return (done / size);
  363. }
  364.  
  365. static size_t _fwrite(const void *buf, size_t size, size_t nmemb, FILE *stream)
  366. {
  367.     size_t left = size * nmemb;
  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);
  387. }
  388.  
  389. /** Drain stream buffer, do not sync stream. */
  390. static void _fflushbuf(FILE *stream)
  391. {
  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)
  414. {
  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.     }
  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.    
  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.    
  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)
  485.             return EOF;
  486.        
  487.         return (int) c;
  488.     }
  489.    
  490.     return EOF;
  491. }
  492.  
  493. int putchar(wchar_t c)
  494. {
  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. {
  510.     char c;
  511.    
  512.     /* This could be made faster by only flushing when needed. */
  513.     if (stdout)
  514.         fflush(stdout);
  515.     if (stderr)
  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.    
  539.     return 0;
  540. }
  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.  
  592. /** @}
  593.  */
  594.