1 /* needs to be big, as all enteries have to be in core */
2 #define EX_MAX_R_DATA_INCORE (16 * 1024 * 1024)
3
4 #include "ex_utils.h"
5
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <unistd.h>
9 #include <getopt.h>
10 #include <locale.h>
11
12 #include "opt.h"
13
14 #include "bag.h"
15
16 static Bag *names = NULL;
17
18 static int (*sort_cmp)(const void *, const void *) = bag_cb_sort_key_coll;
19
20
ex_dir_sort_process(Vstr_base * s1,Vstr_base * s2,int * parsed_header)21 static int ex_dir_sort_process(Vstr_base *s1, Vstr_base *s2,
22 int *parsed_header)
23 {
24 size_t scan_pos = 1;
25 size_t scan_len = s2->len;
26
27 while (scan_len)
28 {
29 size_t pos = 0;
30 size_t len = 0;
31 size_t ns1 = vstr_parse_netstr(s2, scan_pos, scan_len, &pos, &len);
32
33 if (!ns1)
34 {
35 if (s2->len > EX_MAX_R_DATA_INCORE)
36 errx(EXIT_FAILURE, "input too big");
37 if (len > EX_MAX_R_DATA_INCORE)
38 errx(EXIT_FAILURE, "bad input");
39
40 return (FALSE);
41 }
42
43 if (!*parsed_header)
44 {
45 size_t vpos = 0;
46 size_t vlen = 0;
47 size_t nst = 0;
48
49 if (!(nst = vstr_parse_netstr(s2, pos, len, &vpos, &vlen)))
50 errx(EXIT_FAILURE, "bad input");
51 pos += nst; len -= nst;
52 if (!vstr_cmp_cstr_eq(s2, vpos, vlen, "version"))
53 errx(EXIT_FAILURE, "bad input");
54 if (!(nst = vstr_parse_netstr(s2, pos, len, &vpos, &vlen)))
55 errx(EXIT_FAILURE, "bad input");
56 pos += nst; len -= nst;
57 if (!vstr_cmp_cstr_eq(s2, vpos, vlen, "1"))
58 errx(EXIT_FAILURE, "Unsupported version");
59 *parsed_header = TRUE;
60
61 vstr_add_vstr(s1, s1->len, s2, 1, ns1, 0);
62 vstr_del(s2, 1, ns1);
63 scan_pos = 1;
64 scan_len = s2->len;
65
66 continue;
67 }
68
69 while (len)
70 {
71 size_t kpos = 0;
72 size_t klen = 0;
73 size_t vpos = 0;
74 size_t vlen = 0;
75 size_t nst = 0;
76 char *key = NULL;
77 Vstr_sect_node *val = malloc(sizeof(Vstr_sect_node));
78
79 if (!val)
80 errno = ENOMEM, err(EXIT_FAILURE, "sort");
81
82 if (!(nst = vstr_parse_netstr(s2, pos, len, &kpos, &klen)))
83 errx(EXIT_FAILURE, "bad input");
84 pos += nst; len -= nst;
85
86 if (!(nst = vstr_parse_netstr(s2, pos, len, &vpos, &vlen)))
87 errx(EXIT_FAILURE, "bad input");
88 pos += nst; len -= nst;
89
90 if (!vstr_cmp_cstr_eq(s2, kpos, klen, "name"))
91 continue; /* only sort by names atm. */
92
93 if (!(key = vstr_export_cstr_malloc(s2, vpos, vlen)))
94 errno = ENOMEM, err(EXIT_FAILURE, "sort");
95
96 val->pos = scan_pos;
97 val->len = ns1;
98
99 if (!(names = bag_add_obj(names, key, val)))
100 errno = ENOMEM, err(EXIT_FAILURE, "sort");
101 break;
102 }
103
104 scan_len -= ns1;
105 scan_pos += ns1;
106 }
107
108 return (TRUE);
109 }
110
ex_dir_sort_read_fd_write_stdout(Vstr_base * s1,Vstr_base * s2,int fd)111 static void ex_dir_sort_read_fd_write_stdout(Vstr_base *s1, Vstr_base *s2,
112 int fd)
113 {
114 int parsed_header[1] = {FALSE};
115
116 while (TRUE)
117 {
118 int io_w_state = IO_OK;
119 int io_r_state = io_get(s2, fd);
120
121 if (io_r_state == IO_EOF)
122 break;
123
124 io_w_state = io_put(s1, STDOUT_FILENO);
125
126 io_limit(io_r_state, fd, io_w_state, STDOUT_FILENO, s1);
127 }
128
129 ex_dir_sort_process(s1, s2, parsed_header);
130
131 bag_sort(names, sort_cmp);
132
133 {
134 Bag_iter iter[1];
135 const Bag_obj *obj = bag_iter_beg(names, iter);
136
137 while (obj)
138 {
139 const Vstr_sect_node *val = obj->val;
140
141 vstr_add_vstr(s1, s1->len, s2, val->pos, val->len, 0);
142 obj = bag_iter_nxt(iter);
143 }
144
145 if (s1->conf->malloc_bad)
146 errno = ENOMEM, err(EXIT_FAILURE, "sort");
147 vstr_del(s2, 1, s2->len);
148 }
149
150 bag_del_all(names);
151 }
152
ex_dir_sort_init(Vstr_base * s1)153 static void ex_dir_sort_init(Vstr_base *s1)
154 {
155 if (!vstr_cntl_conf(s1->conf, VSTR_CNTL_CONF_SET_FMT_CHAR_ESC, '$') ||
156 !vstr_sc_fmt_add_all(s1->conf))
157 errno = ENOMEM, err(EXIT_FAILURE, "init");
158
159 if (!(names = bag_make(16, bag_cb_free_malloc, bag_cb_free_malloc)))
160 errno = ENOMEM, err(EXIT_FAILURE, "init");
161 names->can_resize = TRUE;
162 }
163
usage(const char * program_name,int ret,const char * prefix)164 static void usage(const char *program_name, int ret, const char *prefix)
165 {
166 Vstr_base *out = vstr_make_base(NULL);
167
168 if (!out)
169 errno = ENOMEM, err(EXIT_FAILURE, "usage");
170
171 vstr_add_fmt(out, 0, "%s\n"
172 " Usage: %s [-hV] [FILES]\n"
173 " or: %s OPTION\n"
174 " --help -h - Print this message.\n"
175 " --sort - Use cmp, case, version or collating sorting.\n"
176 " --version -V - Print the version string.\n",
177 prefix, program_name, program_name);
178
179 if (io_put_all(out, ret ? STDERR_FILENO : STDOUT_FILENO) == IO_FAIL)
180 err(EXIT_FAILURE, "write");
181
182 exit (ret);
183 }
184
ex_dir_sort_cmd_line(int * passed_argc,char ** passed_argv[])185 static void ex_dir_sort_cmd_line(int *passed_argc, char **passed_argv[])
186 {
187 int argc = *passed_argc;
188 char **argv = *passed_argv;
189 char optchar = 0;
190 const char *program_name = NULL;
191 struct option long_options[] =
192 { /* allow sorting by size etc. */
193 {"help", no_argument, NULL, 'h'},
194 {"sort", required_argument, NULL, 1},
195 {"version", no_argument, NULL, 'V'},
196 {NULL, 0, NULL, 0}
197 };
198 Vstr_base *out = vstr_make_base(NULL);
199
200 if (!out)
201 errno = ENOMEM, err(EXIT_FAILURE, "command line");
202
203 program_name = opt_program_name(argv[0], "jdir_sort");
204
205 while ((optchar = getopt_long(argc, argv, "hV",
206 long_options, NULL)) != -1)
207 {
208 switch (optchar)
209 {
210 case '?': usage(program_name, EXIT_FAILURE, "");
211 case 'h': usage(program_name, EXIT_SUCCESS, "");
212
213 case 'V':
214 vstr_add_fmt(out, 0,"\
215 %s version 1.0.0, compiled on %s.\n\
216 Written by James Antill\n\
217 \n\
218 Uses Vstr string library.\n\
219 ",
220 program_name, __DATE__);
221
222 if (io_put_all(out, STDOUT_FILENO) == IO_FAIL)
223 err(EXIT_FAILURE, "write");
224
225 exit (EXIT_SUCCESS);
226
227 case 1:
228 if (0) {}
229 else if (!strcmp(optarg, "cmp")) sort_cmp = bag_cb_sort_key_cmp;
230 else if (!strcmp(optarg, "case")) sort_cmp = bag_cb_sort_key_case;
231 else if (!strcmp(optarg, "version")) sort_cmp = bag_cb_sort_key_vers;
232 else if (!strcmp(optarg, "collating")) sort_cmp = bag_cb_sort_key_coll;
233 else
234 usage(program_name, EXIT_FAILURE, "");
235 break;
236
237 default:
238 abort();
239 }
240 }
241 vstr_free_base(out); out = NULL;
242
243 argc -= optind;
244 argv += optind;
245
246 *passed_argc = argc;
247 *passed_argv = argv;
248 }
249
main(int argc,char * argv[])250 int main(int argc, char *argv[])
251 {
252 Vstr_base *s1 = NULL;
253 Vstr_base *s2 = ex_init(&s1); /* init the library etc. */
254 int count = 0;
255
256 setlocale(LC_ALL, "");
257
258 ex_dir_sort_init(s1);
259
260 ex_dir_sort_cmd_line(&argc, &argv);
261
262 /* if no arguments are given just do stdin to stdout */
263 if (count >= argc)
264 {
265 io_fd_set_o_nonblock(STDIN_FILENO);
266 ex_dir_sort_read_fd_write_stdout(s1, s2, STDIN_FILENO);
267 }
268
269 /* loop through all arguments, open the dir specified
270 * and do the read/write loop */
271 while (count < argc)
272 {
273 int fd = io_open(argv[count]);
274
275 ex_dir_sort_read_fd_write_stdout(s1, s2, fd);
276
277 if (close(fd) == -1)
278 warn("close(%s)", argv[count]);
279
280 ++count;
281 }
282
283 bag_free(names);
284
285 /* output all remaining data */
286 io_put_all(s1, STDOUT_FILENO);
287
288 exit (ex_exit(s1, s2));
289 }
290