1 /* Unix nl command */
2 #include "ex_utils.h"
3 
4 #define CONF_USE_MMAP_DEF FALSE
5 
6 #define EX_NL_SECTS_LOOP 32 /* how many sections to split into per loop */
7 #define EX_NL_USE_SRCH_MOV 0
8 #define EX_NL_USE_SPLIT 0
9 
10 #if EX_NL_USE_SPLIT /* this version using split shouldn't be any slower */
ex_nl_process(Vstr_base * s1,Vstr_base * s2,int last)11 static int ex_nl_process(Vstr_base *s1, Vstr_base *s2, int last)
12 {
13   static unsigned int count = 0;
14   int ret = FALSE;
15 
16   /* we don't want to create more data, if we are over our limit */
17   if (s1->len > EX_MAX_W_DATA_INCORE)
18     return (FALSE);
19 
20   while (s2->len)
21   {
22     const int flags = (VSTR_FLAG_SPLIT_REMAIN |
23                        VSTR_FLAG_SPLIT_BEG_NULL |
24                        VSTR_FLAG_SPLIT_MID_NULL |
25                        VSTR_FLAG_SPLIT_END_NULL |
26                        VSTR_FLAG_SPLIT_NO_RET);
27     VSTR_SECTS_DECL(sects, EX_NL_SECTS_LOOP);
28     unsigned int num = 0;
29 
30     VSTR_SECTS_DECL_INIT(sects);
31     vstr_split_buf(s2, 1, s2->len, "\n", 1, sects, sects->sz, flags);
32 
33     if ((sects->num != sects->sz) && !last)
34       --sects->num; /* last one isn't full line */
35 
36     if (!sects->num)
37       return (ret);
38 
39     while (++num <= sects->num)
40     {
41       size_t split_pos = VSTR_SECTS_NUM(sects, num)->pos;
42       size_t split_len = VSTR_SECTS_NUM(sects, num)->len;
43 
44       vstr_add_fmt(s1, s1->len, "% 6d\t", ++count);
45       if (split_len)
46         vstr_add_vstr(s1, s1->len, s2, split_pos, split_len,
47                       VSTR_TYPE_ADD_BUF_REF);
48       vstr_add_cstr_buf(s1, s1->len, "\n");
49 
50       if (s1->conf->malloc_bad) /* checks all three above */
51         errno = ENOMEM, err(EXIT_FAILURE, "adding data");
52     }
53 
54     if (sects->num != sects->sz)
55       vstr_del(s2, 1, s2->len);
56     else
57     {
58       size_t pos = VSTR_SECTS_NUM(sects, sects->num)->pos;
59       size_t len = VSTR_SECTS_NUM(sects, sects->num)->len;
60       vstr_del(s2, 1, vstr_sc_poslast(pos, len + 1));
61     }
62 
63     ret = TRUE;
64 
65     if (s1->len > EX_MAX_W_DATA_INCORE)
66       return (ret);
67   }
68 
69   return (FALSE);
70 }
71 #else
ex_nl_process(Vstr_base * s1,Vstr_base * s2,int last)72 static int ex_nl_process(Vstr_base *s1, Vstr_base *s2, int last)
73 {
74   static unsigned int count = 0;
75   size_t pos = 0;
76   int ret = FALSE;
77 
78   /* we don't want to create more data, if we are over our limit */
79   if (s1->len > EX_MAX_W_DATA_INCORE)
80     return (FALSE);
81 
82   if (!s2->len)
83     return (FALSE);
84 
85   while ((pos = vstr_srch_chr_fwd(s2, 1, s2->len, '\n')))
86   {
87     ret = TRUE;
88 
89     vstr_add_fmt(s1, s1->len, "% 6d\t", ++count);
90 
91     if (EX_NL_USE_SRCH_MOV)
92       vstr_mov(s1, s1->len, s2, 1, pos);
93     else
94     { /* The flag turns _BUF nodes into sharable _REF nodes */
95       vstr_add_vstr(s1, s1->len, s2, 1, pos, VSTR_TYPE_ADD_BUF_REF);
96       vstr_del(s2, 1, pos);
97     }
98 
99     if (s1->conf->malloc_bad) /* checks all three above */
100       errno = ENOMEM, err(EXIT_FAILURE, "adding data");
101 
102     if (s1->len > EX_MAX_W_DATA_INCORE)
103       return (ret);
104   }
105 
106   if (s2->len && last)
107   {
108     ret = TRUE;
109 
110     vstr_add_fmt(s1, s1->len, "% 6d\t", ++count);
111     if (s2->len)
112       vstr_mov(s1, s1->len, s2, 1, s2->len);
113     vstr_add_cstr_buf(s1, s1->len, "\n");
114 
115     if (s1->conf->malloc_bad) /* checks all three above */
116       errno = ENOMEM, err(EXIT_FAILURE, "adding data");
117   }
118 
119   return (ret);
120 }
121 #endif
122 
123 /* files are merged */
ex_nl_read_fd_write_stdout(Vstr_base * s1,Vstr_base * s2,int fd)124 static void ex_nl_read_fd_write_stdout(Vstr_base *s1, Vstr_base *s2, int fd)
125 {
126   while (TRUE)
127   {
128     int io_w_state = IO_OK;
129     int io_r_state = io_get(s2, fd);
130 
131     if (io_r_state == IO_EOF)
132       break;
133 
134     ex_nl_process(s1, s2, FALSE);
135 
136     io_w_state = io_put(s1, 1);
137 
138     io_limit(io_r_state, fd, io_w_state, STDOUT_FILENO, s1);
139   }
140 }
141 
ex_nl_process_limit(Vstr_base * s1,Vstr_base * s2,unsigned int lim)142 static void ex_nl_process_limit(Vstr_base *s1, Vstr_base *s2, unsigned int lim)
143 {
144   while (s2->len > lim)
145   {
146     int proc_data = ex_nl_process(s1, s2, !lim);
147     if (!proc_data && (io_put(s1, STDOUT_FILENO) == IO_BLOCK))
148       io_block(-1, STDOUT_FILENO);
149   }
150 }
151 
main(int argc,char * argv[])152 int main(int argc, char *argv[])
153 {
154   Vstr_base *s2 = NULL;
155   Vstr_base *s1 = ex_init(&s2);
156   int count = 1;
157   int use_mmap = CONF_USE_MMAP_DEF;
158 
159   /* parse command line arguments... */
160   while (count < argc)
161   { /* quick hack getopt_long */
162     if (!strcmp("--", argv[count]))
163     {
164       ++count;
165       break;
166     }
167     else if (!strcmp("--mmap", argv[count])) /* toggle use of mmap */
168       use_mmap = !use_mmap;
169     else if (!strcmp("--version", argv[count]))
170     { /* print version and exit */
171       vstr_add_fmt(s1, 0, "%s", "\
172 jnl 1.0.0\n\
173 Written by James Antill\n\
174 \n\
175 Uses Vstr string library.\n\
176 ");
177       goto out;
178     }
179     else if (!strcmp("--help", argv[count]))
180     { /* print version and exit */
181       vstr_add_fmt(s1, 0, "%s", "\
182 Usage: jnl [FILENAME]...\n\
183    or: jnl OPTION\n\
184 Output filenames.\n\
185 \n\
186       --help     Display this help and exit\n\
187       --version  Output version information and exit\n\
188       --mmap     Toggle use of mmap() to load input files\n\
189       --         Treat rest of cmd line as input filenames\n\
190 \n\
191 Report bugs to James Antill <james@and.org>.\n\
192 ");
193       goto out;
194     }
195     else
196       break;
197     ++count;
198   }
199 
200   /* if no arguments are given just do stdin to stdout */
201   if (count >= argc)
202   {
203     io_fd_set_o_nonblock(STDIN_FILENO);
204     ex_nl_read_fd_write_stdout(s1, s2, STDIN_FILENO);
205   }
206 
207   /* loop through all arguments, open the file specified
208    * and do the read/write loop */
209   while (count < argc)
210   {
211     unsigned int ern = 0;
212 
213     if (use_mmap)
214       vstr_sc_mmap_file(s2, s2->len, argv[count], 0, 0, &ern);
215 
216     if (!use_mmap ||
217         (ern == VSTR_TYPE_SC_MMAP_FILE_ERR_FSTAT_ERRNO) ||
218         (ern == VSTR_TYPE_SC_MMAP_FILE_ERR_MMAP_ERRNO) ||
219         (ern == VSTR_TYPE_SC_MMAP_FILE_ERR_TOO_LARGE))
220     {
221       int fd = io_open(argv[count]);
222 
223       ex_nl_read_fd_write_stdout(s1, s2, fd);
224 
225       if (close(fd) == -1)
226         warn("close(%s)", argv[count]);
227     }
228     else if (ern && (ern != VSTR_TYPE_SC_MMAP_FILE_ERR_CLOSE_ERRNO))
229       err(EXIT_FAILURE, "add");
230     else
231       ex_nl_process_limit(s1, s2, EX_MAX_R_DATA_INCORE);
232 
233     ++count;
234   }
235 
236   ex_nl_process_limit(s1, s2, 0);
237 
238  out:
239   io_put_all(s1, STDOUT_FILENO);
240 
241   exit (ex_exit(s1, s2));
242 }
243