Subversion Repositories HelenOS

Rev

Rev 4512 | 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 <string.h>
  39. #include <errno.h>
  40. #include <bool.h>
  41. #include <malloc.h>
  42. #include <io/klog.h>
  43. #include <vfs/vfs.h>
  44. #include <ipc/devmap.h>
  45. #include <adt/list.h>
  46.  
  47. static FILE stdin_null = {
  48.     .fd = -1,
  49.     .error = true,
  50.     .eof = true,
  51.     .klog = false,
  52.     .phone = -1
  53. };
  54.  
  55. static FILE stdout_klog = {
  56.     .fd = -1,
  57.     .error = false,
  58.     .eof = false,
  59.     .klog = true,
  60.     .phone = -1
  61. };
  62.  
  63. static FILE stderr_klog = {
  64.     .fd = -1,
  65.     .error = false,
  66.     .eof = false,
  67.     .klog = true,
  68.     .phone = -1
  69. };
  70.  
  71. FILE *stdin = NULL;
  72. FILE *stdout = NULL;
  73. FILE *stderr = NULL;
  74.  
  75. static LIST_INITIALIZE(files);
  76.  
  77. void stdio_init(int filc, fdi_node_t *filv[])
  78. {
  79.     if (filc > 0) {
  80.         stdin = fopen_node(filv[0], "r");
  81.     } else {
  82.         stdin = &stdin_null;
  83.         list_append(&stdin->link, &files);
  84.     }
  85.    
  86.     if (filc > 1) {
  87.         stdout = fopen_node(filv[1], "w");
  88.     } else {
  89.         stdout = &stdout_klog;
  90.         list_append(&stdout->link, &files);
  91.     }
  92.    
  93.     if (filc > 2) {
  94.         stderr = fopen_node(filv[2], "w");
  95.     } else {
  96.         stderr = &stderr_klog;
  97.         list_append(&stderr->link, &files);
  98.     }
  99. }
  100.  
  101. void stdio_done(void)
  102. {
  103.     link_t *link = files.next;
  104.    
  105.     while (link != &files) {
  106.         FILE *file = list_get_instance(link, FILE, link);
  107.         fclose(file);
  108.         link = files.next;
  109.     }
  110. }
  111.  
  112. static bool parse_mode(const char *mode, int *flags)
  113. {
  114.     /* Parse mode except first character. */
  115.     const char *mp = mode;
  116.     if (*mp++ == 0) {
  117.         errno = EINVAL;
  118.         return false;
  119.     }
  120.    
  121.     if ((*mp == 'b') || (*mp == 't'))
  122.         mp++;
  123.    
  124.     bool plus;
  125.     if (*mp == '+') {
  126.         mp++;
  127.         plus = true;
  128.     } else
  129.         plus = false;
  130.    
  131.     if (*mp != 0) {
  132.         errno = EINVAL;
  133.         return false;
  134.     }
  135.    
  136.     /* Parse first character of mode and determine flags for open(). */
  137.     switch (mode[0]) {
  138.     case 'r':
  139.         *flags = plus ? O_RDWR : O_RDONLY;
  140.         break;
  141.     case 'w':
  142.         *flags = (O_TRUNC | O_CREAT) | (plus ? O_RDWR : O_WRONLY);
  143.         break;
  144.     case 'a':
  145.         /* TODO: a+ must read from beginning, append to the end. */
  146.         if (plus) {
  147.             errno = ENOTSUP;
  148.             return false;
  149.         }
  150.         *flags = (O_APPEND | O_CREAT) | (plus ? O_RDWR : O_WRONLY);
  151.     default:
  152.         errno = EINVAL;
  153.         return false;
  154.     }
  155.    
  156.     return true;
  157. }
  158.  
  159. /** Open a stream.
  160.  *
  161.  * @param path Path of the file to open.
  162.  * @param mode Mode string, (r|w|a)[b|t][+].
  163.  *
  164.  */
  165. FILE *fopen(const char *path, const char *mode)
  166. {
  167.     int flags;
  168.     if (!parse_mode(mode, &flags))
  169.         return NULL;
  170.    
  171.     /* Open file. */
  172.     FILE *stream = malloc(sizeof(FILE));
  173.     if (stream == NULL) {
  174.         errno = ENOMEM;
  175.         return NULL;
  176.     }
  177.    
  178.     stream->fd = open(path, flags, 0666);
  179.     if (stream->fd < 0) {
  180.         /* errno was set by open() */
  181.         free(stream);
  182.         return NULL;
  183.     }
  184.    
  185.     stream->error = false;
  186.     stream->eof = false;
  187.     stream->klog = false;
  188.     stream->phone = -1;
  189.    
  190.     list_append(&stream->link, &files);
  191.    
  192.     return stream;
  193. }
  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.  
  215. FILE *fopen_node(fdi_node_t *node, const char *mode)
  216. {
  217.     int flags;
  218.     if (!parse_mode(mode, &flags))
  219.         return NULL;
  220.    
  221.     /* Open file. */
  222.     FILE *stream = malloc(sizeof(FILE));
  223.     if (stream == NULL) {
  224.         errno = ENOMEM;
  225.         return NULL;
  226.     }
  227.    
  228.     stream->fd = open_node(node, flags);
  229.     if (stream->fd < 0) {
  230.         /* errno was set by open_node() */
  231.         free(stream);
  232.         return NULL;
  233.     }
  234.    
  235.     stream->error = false;
  236.     stream->eof = false;
  237.     stream->klog = false;
  238.     stream->phone = -1;
  239.    
  240.     list_append(&stream->link, &files);
  241.    
  242.     return stream;
  243. }
  244.  
  245. int fclose(FILE *stream)
  246. {
  247.     int rc = 0;
  248.    
  249.     fflush(stream);
  250.    
  251.     if (stream->phone >= 0)
  252.         ipc_hangup(stream->phone);
  253.    
  254.     if (stream->fd >= 0)
  255.         rc = close(stream->fd);
  256.    
  257.     list_remove(&stream->link);
  258.    
  259.     if ((stream != &stdin_null)
  260.         && (stream != &stdout_klog)
  261.         && (stream != &stderr_klog))
  262.         free(stream);
  263.    
  264.     stream = NULL;
  265.    
  266.     if (rc != 0) {
  267.         /* errno was set by close() */
  268.         return EOF;
  269.     }
  270.    
  271.     return 0;
  272. }
  273.  
  274. /** Read from a stream.
  275.  *
  276.  * @param buf    Destination buffer.
  277.  * @param size   Size of each record.
  278.  * @param nmemb  Number of records to read.
  279.  * @param stream Pointer to the stream.
  280.  *
  281.  */
  282. size_t fread(void *buf, size_t size, size_t nmemb, FILE *stream)
  283. {
  284.     size_t left = size * nmemb;
  285.     size_t done = 0;
  286.    
  287.     while ((left > 0) && (!stream->error) && (!stream->eof)) {
  288.         ssize_t rd = read(stream->fd, buf + done, left);
  289.        
  290.         if (rd < 0)
  291.             stream->error = true;
  292.         else if (rd == 0)
  293.             stream->eof = true;
  294.         else {
  295.             left -= rd;
  296.             done += rd;
  297.         }
  298.     }
  299.    
  300.     return (done / size);
  301. }
  302.  
  303. /** Write to a stream.
  304.  *
  305.  * @param buf    Source buffer.
  306.  * @param size   Size of each record.
  307.  * @param nmemb  Number of records to write.
  308.  * @param stream Pointer to the stream.
  309.  *
  310.  */
  311. size_t fwrite(const void *buf, size_t size, size_t nmemb, FILE *stream)
  312. {
  313.     size_t left = size * nmemb;
  314.     size_t done = 0;
  315.    
  316.     while ((left > 0) && (!stream->error)) {
  317.         ssize_t wr;
  318.        
  319.         if (stream->klog)
  320.             wr = klog_write(buf + done, left);
  321.         else
  322.             wr = write(stream->fd, buf + done, left);
  323.        
  324.         if (wr <= 0)
  325.             stream->error = true;
  326.         else {
  327.             left -= wr;
  328.             done += wr;
  329.         }
  330.     }
  331.    
  332.     return (done / size);
  333. }
  334.  
  335. int fputc(wchar_t c, FILE *stream)
  336. {
  337.     char buf[STR_BOUNDS(1)];
  338.     size_t sz = 0;
  339.    
  340.     if (chr_encode(c, buf, &sz, STR_BOUNDS(1)) == EOK) {
  341.         size_t wr = fwrite(buf, sz, 1, stream);
  342.        
  343.         if (wr < sz)
  344.             return EOF;
  345.        
  346.         return (int) c;
  347.     }
  348.    
  349.     return EOF;
  350. }
  351.  
  352. int putchar(wchar_t c)
  353. {
  354.     return fputc(c, stdout);
  355. }
  356.  
  357. int fputs(const char *str, FILE *stream)
  358. {
  359.     return fwrite(str, str_size(str), 1, stream);
  360. }
  361.  
  362. int puts(const char *str)
  363. {
  364.     return fputs(str, stdout);
  365. }
  366.  
  367. int fgetc(FILE *stream)
  368. {
  369.     char c;
  370.  
  371.     /* This could be made faster by only flushing when needed. */
  372.     if (stdout)
  373.         fflush(stdout);
  374.     if (stderr)
  375.         fflush(stderr);
  376.  
  377.     if (fread(&c, sizeof(char), 1, stream) < sizeof(char))
  378.         return EOF;
  379.    
  380.     return (int) c;
  381. }
  382.  
  383. int getchar(void)
  384. {
  385.     return fgetc(stdin);
  386. }
  387.  
  388. int fseek(FILE *stream, long offset, int origin)
  389. {
  390.     off_t rc = lseek(stream->fd, offset, origin);
  391.     if (rc == (off_t) (-1)) {
  392.         /* errno has been set by lseek. */
  393.         return -1;
  394.     }
  395.    
  396.     stream->eof = false;
  397.    
  398.     return 0;
  399. }
  400.  
  401. void rewind(FILE *stream)
  402. {
  403.     (void) fseek(stream, 0, SEEK_SET);
  404. }
  405.  
  406. int fflush(FILE *stream)
  407. {
  408.     if (stream->klog) {
  409.         klog_update();
  410.         return EOK;
  411.     }
  412.    
  413.     if (stream->fd >= 0)
  414.         return fsync(stream->fd);
  415.    
  416.     return ENOENT;
  417. }
  418.  
  419. int feof(FILE *stream)
  420. {
  421.     return stream->eof;
  422. }
  423.  
  424. int ferror(FILE *stream)
  425. {
  426.     return stream->error;
  427. }
  428.  
  429. int fphone(FILE *stream)
  430. {
  431.     if (stream->fd >= 0) {
  432.         if (stream->phone < 0)
  433.             stream->phone = fd_phone(stream->fd);
  434.        
  435.         return stream->phone;
  436.     }
  437.    
  438.     return -1;
  439. }
  440.  
  441. int fnode(FILE *stream, fdi_node_t *node)
  442. {
  443.     if (stream->fd >= 0)
  444.         return fd_node(stream->fd, node);
  445.    
  446.     return ENOENT;
  447. }
  448.  
  449. /** @}
  450.  */
  451.