Subversion Repositories HelenOS

Rev

Rev 4511 | 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 <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 *fopen_node(fdi_node_t *node, const char *mode)
  196. {
  197.     int flags;
  198.     if (!parse_mode(mode, &flags))
  199.         return NULL;
  200.    
  201.     /* Open file. */
  202.     FILE *stream = malloc(sizeof(FILE));
  203.     if (stream == NULL) {
  204.         errno = ENOMEM;
  205.         return NULL;
  206.     }
  207.    
  208.     stream->fd = open_node(node, flags);
  209.     if (stream->fd < 0) {
  210.         /* errno was set by open_node() */
  211.         free(stream);
  212.         return NULL;
  213.     }
  214.    
  215.     stream->error = false;
  216.     stream->eof = false;
  217.     stream->klog = false;
  218.     stream->phone = -1;
  219.    
  220.     list_append(&stream->link, &files);
  221.    
  222.     return stream;
  223. }
  224.  
  225. int fclose(FILE *stream)
  226. {
  227.     int rc = 0;
  228.    
  229.     fflush(stream);
  230.    
  231.     if (stream->phone >= 0)
  232.         ipc_hangup(stream->phone);
  233.    
  234.     if (stream->fd >= 0)
  235.         rc = close(stream->fd);
  236.    
  237.     list_remove(&stream->link);
  238.    
  239.     if ((stream != &stdin_null)
  240.         && (stream != &stdout_klog)
  241.         && (stream != &stderr_klog))
  242.         free(stream);
  243.    
  244.     stream = NULL;
  245.    
  246.     if (rc != 0) {
  247.         /* errno was set by close() */
  248.         return EOF;
  249.     }
  250.    
  251.     return 0;
  252. }
  253.  
  254. /** Read from a stream.
  255.  *
  256.  * @param buf    Destination buffer.
  257.  * @param size   Size of each record.
  258.  * @param nmemb  Number of records to read.
  259.  * @param stream Pointer to the stream.
  260.  *
  261.  */
  262. size_t fread(void *buf, size_t size, size_t nmemb, FILE *stream)
  263. {
  264.     size_t left = size * nmemb;
  265.     size_t done = 0;
  266.    
  267.     while ((left > 0) && (!stream->error) && (!stream->eof)) {
  268.         ssize_t rd = read(stream->fd, buf + done, left);
  269.        
  270.         if (rd < 0)
  271.             stream->error = true;
  272.         else if (rd == 0)
  273.             stream->eof = true;
  274.         else {
  275.             left -= rd;
  276.             done += rd;
  277.         }
  278.     }
  279.    
  280.     return (done / size);
  281. }
  282.  
  283. /** Write to a stream.
  284.  *
  285.  * @param buf    Source buffer.
  286.  * @param size   Size of each record.
  287.  * @param nmemb  Number of records to write.
  288.  * @param stream Pointer to the stream.
  289.  *
  290.  */
  291. size_t fwrite(const void *buf, size_t size, size_t nmemb, FILE *stream)
  292. {
  293.     size_t left = size * nmemb;
  294.     size_t done = 0;
  295.    
  296.     while ((left > 0) && (!stream->error)) {
  297.         ssize_t wr;
  298.        
  299.         if (stream->klog)
  300.             wr = klog_write(buf + done, left);
  301.         else
  302.             wr = write(stream->fd, buf + done, left);
  303.        
  304.         if (wr <= 0)
  305.             stream->error = true;
  306.         else {
  307.             left -= wr;
  308.             done += wr;
  309.         }
  310.     }
  311.    
  312.     return (done / size);
  313. }
  314.  
  315. int fputc(wchar_t c, FILE *stream)
  316. {
  317.     char buf[STR_BOUNDS(1)];
  318.     size_t sz = 0;
  319.    
  320.     if (chr_encode(c, buf, &sz, STR_BOUNDS(1)) == EOK) {
  321.         size_t wr = fwrite(buf, sz, 1, stream);
  322.        
  323.         if (wr < sz)
  324.             return EOF;
  325.        
  326.         return (int) c;
  327.     }
  328.    
  329.     return EOF;
  330. }
  331.  
  332. int putchar(wchar_t c)
  333. {
  334.     return fputc(c, stdout);
  335. }
  336.  
  337. int fputs(const char *str, FILE *stream)
  338. {
  339.     return fwrite(str, str_size(str), 1, stream);
  340. }
  341.  
  342. int puts(const char *str)
  343. {
  344.     return fputs(str, stdout);
  345. }
  346.  
  347. int fgetc(FILE *stream)
  348. {
  349.     char c;
  350.  
  351.     /* This could be made faster by only flushing when needed. */
  352.     if (stdout)
  353.         fflush(stdout);
  354.     if (stderr)
  355.         fflush(stderr);
  356.  
  357.     if (fread(&c, sizeof(char), 1, stream) < sizeof(char))
  358.         return EOF;
  359.    
  360.     return (int) c;
  361. }
  362.  
  363. int getchar(void)
  364. {
  365.     return fgetc(stdin);
  366. }
  367.  
  368. int fseek(FILE *stream, long offset, int origin)
  369. {
  370.     off_t rc = lseek(stream->fd, offset, origin);
  371.     if (rc == (off_t) (-1)) {
  372.         /* errno has been set by lseek. */
  373.         return -1;
  374.     }
  375.    
  376.     stream->eof = false;
  377.    
  378.     return 0;
  379. }
  380.  
  381. int fflush(FILE *stream)
  382. {
  383.     if (stream->klog) {
  384.         klog_update();
  385.         return EOK;
  386.     }
  387.    
  388.     if (stream->fd >= 0)
  389.         return fsync(stream->fd);
  390.    
  391.     return ENOENT;
  392. }
  393.  
  394. int feof(FILE *stream)
  395. {
  396.     return stream->eof;
  397. }
  398.  
  399. int ferror(FILE *stream)
  400. {
  401.     return stream->error;
  402. }
  403.  
  404. int fphone(FILE *stream)
  405. {
  406.     if (stream->fd >= 0) {
  407.         if (stream->phone < 0)
  408.             stream->phone = fd_phone(stream->fd);
  409.        
  410.         return stream->phone;
  411.     }
  412.    
  413.     return -1;
  414. }
  415.  
  416. int fnode(FILE *stream, fdi_node_t *node)
  417. {
  418.     if (stream->fd >= 0)
  419.         return fd_node(stream->fd, node);
  420.    
  421.     return ENOENT;
  422. }
  423.  
  424. /** @}
  425.  */
  426.