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