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