1 /* this program does a character count of it's input */
2
3 #include "ex_utils.h"
4 #include <locale.h>
5 #include <ctype.h>
6
7 #define CONF_USE_MMAP_DEF FALSE
8
prnt_chrs(Vstr_base * s1,char chr,size_t * len)9 static void prnt_chrs(Vstr_base *s1, char chr, size_t *len)
10 {
11 if (isprint((unsigned char)chr))
12 vstr_add_fmt(s1, s1->len, " '%c' [%#04x] * %zu\n", chr, chr, *len);
13 else
14 vstr_add_fmt(s1, s1->len, " '?' [%#04x] * %zu\n", chr, *len);
15
16 if (s1->conf->malloc_bad) /* checks all three above */
17 errno = ENOMEM, err(EXIT_FAILURE, "adding data");
18
19 *len = 0;
20 }
21
ex_ccount_process(Vstr_base * s1,Vstr_base * s2,int last)22 static int ex_ccount_process(Vstr_base *s1, Vstr_base *s2, int last)
23 {
24 static size_t prev_len = 0;
25 static char prev_chr = 0;
26 int ret = FALSE;
27
28 if (s1->len > EX_MAX_W_DATA_INCORE)
29 return (FALSE);
30
31 if (!s2->len && last && prev_len)
32 prnt_chrs(s1, prev_chr, &prev_len);
33
34 while (s2->len)
35 {
36 char chrs[1];
37 int did_all = FALSE;
38 size_t len = 0;
39
40 chrs[0] = vstr_export_chr(s2, 1);
41 if (prev_len && (chrs[0] != prev_chr))
42 prnt_chrs(s1, prev_chr, &prev_len);
43
44 len = vstr_spn_chrs_fwd(s2, 1, s2->len, chrs, 1);
45
46 ret = TRUE;
47
48 did_all = (len == s2->len);
49 vstr_del(s2, 1, len);
50
51 prev_len += len;
52 prev_chr = chrs[0];
53
54 if (did_all && !last)
55 break;
56
57 prnt_chrs(s1, prev_chr, &prev_len);
58
59 if (s1->len > EX_MAX_W_DATA_INCORE)
60 return (FALSE);
61 }
62
63 return (ret);
64 }
65
ex_ccount_process_limit(Vstr_base * s1,Vstr_base * s2,unsigned int lim)66 static void ex_ccount_process_limit(Vstr_base *s1, Vstr_base *s2,
67 unsigned int lim)
68 {
69 while (s2->len > lim)
70 { /* Finish processing read data (try writing if we need memory) */
71 int proc_data = ex_ccount_process(s1, s2, !lim);
72
73 if (!proc_data && (io_put(s1, STDOUT_FILENO) == IO_BLOCK))
74 io_block(-1, STDOUT_FILENO);
75 }
76 }
77
ex_ccount_read_fd_write_stdout(Vstr_base * s1,Vstr_base * s2,int fd)78 static void ex_ccount_read_fd_write_stdout(Vstr_base *s1, Vstr_base *s2, int fd)
79 {
80 while (TRUE)
81 {
82 int io_w_state = IO_OK;
83 int io_r_state = io_get(s2, fd);
84
85 if (io_r_state == IO_EOF)
86 break;
87
88 ex_ccount_process(s1, s2, FALSE);
89
90 io_w_state = io_put(s1, STDOUT_FILENO);
91
92 io_limit(io_r_state, fd, io_w_state, STDOUT_FILENO, s1);
93 }
94
95 ex_ccount_process_limit(s1, s2, 0);
96 }
97
main(int argc,char * argv[])98 int main(int argc, char *argv[])
99 {
100 Vstr_base *s2 = NULL;
101 Vstr_base *s1 = ex_init(&s2); /* init the library, and create two strings */
102 int count = 1; /* skip the program name */
103 unsigned int use_mmap = CONF_USE_MMAP_DEF;
104 unsigned int ern = 0;
105
106 setlocale(LC_ALL, "");
107
108 /* parse command line arguments... */
109 while (count < argc)
110 { /* quick hack getopt_long */
111 if (!strcmp("--", argv[count]))
112 {
113 ++count;
114 break;
115 }
116 else if (!strcmp("--mmap", argv[count])) /* toggle use of mmap */
117 use_mmap = !use_mmap;
118 else if (!strcmp("--version", argv[count]))
119 { /* print version and exit */
120 vstr_add_fmt(s1, 0, "%s", "\
121 jccount 1.0.0\n\
122 Written by James Antill\n\
123 \n\
124 Uses Vstr string library.\n\
125 ");
126 goto out;
127 }
128 else if (!strcmp("--help", argv[count]))
129 { /* print version and exit */
130 vstr_add_fmt(s1, 0, "%s", "\
131 Usage: jccount [FILENAME]...\n\
132 or: jccount OPTION\n\
133 Output filenames in byte/character count format.\n\
134 \n\
135 --help Display this help and exit\n\
136 --version Output version information and exit\n\
137 --mmap Toggle use of mmap() to load input files\n\
138 -- Treat rest of cmd line as input filenames\n\
139 \n\
140 Report bugs to James Antill <james@and.org>.\n\
141 ");
142 goto out;
143 }
144 else
145 break;
146 ++count;
147 }
148
149 /* if no arguments are given just do stdin to stdout */
150 if (count >= argc)
151 {
152 io_fd_set_o_nonblock(STDIN_FILENO);
153 ex_ccount_read_fd_write_stdout(s1, s2, STDIN_FILENO);
154 }
155
156 /* loop through all arguments, open the file specified
157 * and do the read/write loop */
158 while (count < argc)
159 {
160 /* try to mmap the file */
161 if (use_mmap)
162 vstr_sc_mmap_file(s2, s2->len, argv[count], 0, 0, &ern);
163
164 if (!use_mmap ||
165 (ern == VSTR_TYPE_SC_MMAP_FILE_ERR_FSTAT_ERRNO) ||
166 (ern == VSTR_TYPE_SC_MMAP_FILE_ERR_MMAP_ERRNO) ||
167 (ern == VSTR_TYPE_SC_MMAP_FILE_ERR_TOO_LARGE))
168 { /* if mmap didn't work ... do a read/alter/write loop */
169 int fd = io_open(argv[count]);
170
171 ex_ccount_read_fd_write_stdout(s1, s2, fd);
172
173 if (close(fd) == -1)
174 warn("close(%s)", argv[count]);
175 }
176 else if (ern && (ern != VSTR_TYPE_SC_MMAP_FILE_ERR_CLOSE_ERRNO))
177 err(EXIT_FAILURE, "add");
178 else /* mmap worked so processes the entire file at once */
179 ex_ccount_process_limit(s1, s2, 0);
180
181 ++count;
182 }
183
184 /* output all remaining data */
185 out:
186 io_put_all(s1, STDOUT_FILENO);
187
188 exit (ex_exit(s1, NULL));
189 }
190