1 /* This is a _simple_ cat program.
2  * Reads from stdin if no args are given.
3  *
4  * This shows how to use the Vstr library at it's simpelest,
5  * for easy and fast IO. Note however that all needed error detection is
6  * included.
7  *
8  * This file is more commented than normal code, so as to make it easy to follow
9  * while knowing almost nothing about Vstr or Linux IO programming.
10  */
11 #include "ex_utils.h"
12 
13 #define CONF_USE_MMAP_DEF FALSE
14 
15 /*  Keep reading on the file descriptor until there is no more data (ERR_EOF)
16  * abort if there is an error reading or writing */
ex_cat_read_fd_write_stdout(Vstr_base * s1,int fd)17 static void ex_cat_read_fd_write_stdout(Vstr_base *s1, int fd)
18 {
19   while (TRUE)
20   {
21     int io_w_state = IO_OK;
22     int io_r_state = io_get(s1, fd);
23 
24     if (io_r_state == IO_EOF)
25       break;
26 
27     io_w_state = io_put(s1, STDOUT_FILENO);
28 
29     io_limit(io_r_state, fd, io_w_state, STDOUT_FILENO, s1);
30   }
31 }
32 
ex_cat_limit(Vstr_base * s1)33 static void ex_cat_limit(Vstr_base *s1)
34 {
35   while ((s1->len >= EX_MAX_W_DATA_INCORE) || (s1->len >= EX_MAX_R_DATA_INCORE))
36   {
37     if (io_put(s1, STDOUT_FILENO) == IO_BLOCK)
38       io_block(-1, STDOUT_FILENO);
39   }
40 }
41 
42 /* This is "cat", using non-blocking IO and Vstr for buffer space */
main(int argc,char * argv[])43 int main(int argc, char *argv[])
44 {
45   Vstr_base *s1 = ex_init(NULL); /* init the library etc. */
46   int count = 1; /* skip the program name */
47   int use_mmap = CONF_USE_MMAP_DEF;
48 
49   /* parse command line arguments... */
50   while (count < argc)
51   { /* quick hack getopt_long */
52     if (!strcmp("--", argv[count]))
53     {
54       ++count;
55       break;
56     }
57     else if (!strcmp("--mmap", argv[count])) /* toggle use of mmap */
58       use_mmap = !use_mmap;
59     else if (!strcmp("--version", argv[count]))
60     { /* print version and exit */
61       vstr_add_fmt(s1, 0, "%s", "\
62 jcat 1.0.0\n\
63 Written by James Antill\n\
64 \n\
65 Uses Vstr string library.\n\
66 ");
67       goto out;
68     }
69     else if (!strcmp("--help", argv[count]))
70     { /* print version and exit */
71       vstr_add_fmt(s1, 0, "%s", "\
72 Usage: jcat [FILENAME]...\n\
73    or: jcat OPTION\n\
74 Output filenames.\n\
75 \n\
76       --help     Display this help and exit\n\
77       --version  Output version information and exit\n\
78       --mmap     Toggle use of mmap() to load input files\n\
79       --         Treat rest of cmd line as input filenames\n\
80 \n\
81 Report bugs to James Antill <james@and.org>.\n\
82 ");
83       goto out;
84     }
85     else
86       break;
87     ++count;
88   }
89 
90   /* if no arguments are given just do stdin to stdout */
91   if (count >= argc)
92   {
93     io_fd_set_o_nonblock(STDIN_FILENO);
94     ex_cat_read_fd_write_stdout(s1, STDIN_FILENO);
95   }
96 
97   /* loop through all arguments, open the file specified
98    * and do the read/write loop */
99   while (count < argc)
100   {
101     unsigned int ern = 0;
102 
103     if (use_mmap)
104       vstr_sc_mmap_file(s1, s1->len, argv[count], 0, 0, &ern);
105 
106     if (!use_mmap ||
107         (ern == VSTR_TYPE_SC_MMAP_FILE_ERR_FSTAT_ERRNO) ||
108         (ern == VSTR_TYPE_SC_MMAP_FILE_ERR_MMAP_ERRNO) ||
109         (ern == VSTR_TYPE_SC_MMAP_FILE_ERR_TOO_LARGE))
110     { /* if mmap didn't work ... do a read/alter/write loop */
111       int fd = io_open(argv[count]);
112 
113       ex_cat_read_fd_write_stdout(s1, fd);
114 
115       if (close(fd) == -1)
116         warn("close(%s)", argv[count]);
117     }
118     else if (ern && (ern != VSTR_TYPE_SC_MMAP_FILE_ERR_CLOSE_ERRNO))
119       err(EXIT_FAILURE, "add");
120     else /* mmap worked */
121       ex_cat_limit(s1);
122 
123     ++count;
124   }
125 
126   /* output all remaining data */
127  out:
128   io_put_all(s1, STDOUT_FILENO);
129 
130   exit (ex_exit(s1, NULL));
131 }
132