1 /*
2  * Copyright (c) 2016-2019 Daichi GOTO
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include "ttt.h"
29 
30 struct tttcmdargs cmdargs;
31 
32 #define SETFLAG(X,Y,Z) \
33 	Y = 1; \
34 	if (NULL != optargs && NULL != strstr(optargs, X)) { \
35 		if (':' == *(1+strstr(optargs, X))) { \
36 			if ('\0' == argv[i][2]) \
37 				Z = argv[++i]; \
38 			else \
39 				Z = &argv[i][2]; \
40 		} \
41 		else if ('!' == *(1+strstr(optargs, X))) { \
42 			if (exclusion) \
43 				usage(); \
44 			exclusion = 1; \
45 		} \
46 	} \
47 	else \
48 		usage();
49 
50 #define REALLOC_IARRAY(OLDLEN,NEWLEN,BUF) { \
51 	i_ptr = realloc(BUF, sizeof(int) * (NEWLEN)); \
52 	if (NULL == i_ptr) \
53 		err(errno, "getcmdargs#REALLOC_IARRAY"); \
54 	memset(i_ptr + sizeof(int)*(OLDLEN), 0, \
55 		sizeof(int) * ((NEWLEN)-(OLDLEN))); \
56 	BUF = i_ptr; \
57 }
58 
59 #define REALLOC_CARRAY(OLDLEN,NEWLEN,BUF) { \
60 	c_ptr = realloc(BUF, sizeof(char) * (NEWLEN)); \
61 	if (NULL == c_ptr) \
62 		err(errno, "getcmdargs#REALLOC_CARRAY"); \
63 	memset(c_ptr + sizeof(char)*(OLDLEN), 0, \
64 		sizeof(char) * ((NEWLEN)-(OLDLEN))); \
65 	BUF = c_ptr; \
66 }
67 
68 #define REALLOC_SARRAY(OLDLEN,NEWLEN,BUF) { \
69 	c_pptr = realloc(BUF, sizeof(char *) * (NEWLEN)); \
70 	if (NULL == c_pptr) \
71 		err(errno, "getcmdargs#REALLOC_SARRAY"); \
72 	BUF = c_pptr; \
73 }
74 
75 #define ARRAY_EXPANSION(OLDLEN,NEWLEN) \
76 	REALLOC_IARRAY(OLDLEN,NEWLEN,cmdargs.r_argv) \
77 	REALLOC_IARRAY(OLDLEN,NEWLEN,cmdargs.r_index_exist) \
78 	REALLOC_IARRAY(OLDLEN,NEWLEN,cmdargs.r_index_to_argv) \
79 	REALLOC_CARRAY(OLDLEN,NEWLEN,cmdargs.r_argv_delim) \
80 	REALLOC_SARRAY(OLDLEN,NEWLEN,cmdargs.r_argv_arg1) \
81 	REALLOC_SARRAY(OLDLEN,NEWLEN,cmdargs.r_argv_arg2) \
82 	REALLOC_SARRAY(OLDLEN,NEWLEN,cmdargs.r_argv_arg3)
83 
84 #define TMPDIR_NAME			".cache/ttt/tmp"
85 #define TMPFILE_NAME			"temp_XXXXXX"
86 #define R_ARGV_MAX_INIT			256
87 #define R_ARGV_ADDITION_SIZE_INIT	256
88 
89 static int r_argv_max = R_ARGV_MAX_INIT;
90 static const int r_argv_addition_size = R_ARGV_ADDITION_SIZE_INIT;
91 static int stdin_file_used = 0;
92 static char *tempfile = NULL;
93 
94 static void mktempfile(void);
95 static void stdintotempfile(void);
96 
97 int
getcmdargs(const int argc,char * argv[],const char * optargs,int flags)98 getcmdargs(const int argc, char *argv[], const char *optargs, int flags)
99 {
100 	int i, j, k, range, *i_ptr, exclusion = 0, mincol = 1;
101 	int single_delimiter_mode = 0;
102 	int double_delimiter_mode = 0;
103 	int triple_delimiter_mode = 0;
104 	char buf[BUFFER_SIZE], *p1, *p2, delim, *c_ptr, **c_pptr, o;
105 
106 	if (flags&CMDARGS_R_MINIMUMNUM_IS_0)
107 		mincol = 0;
108 
109 	if (flags&CMDARGS_DELIMITER_ONLY_1)
110 		single_delimiter_mode = 1;
111 	if (flags&CMDARGS_DELIMITER_ONLY_2)
112 		double_delimiter_mode = 1;
113 	if (flags&CMDARGS_DELIMITER_ONLY_3)
114 		triple_delimiter_mode = 1;
115 
116 	/*
117 	 * cmdargs initialization
118 	 */
119 	memset(&cmdargs, 0, sizeof(struct tttcmdargs));
120 
121 	cmdargs.f_argc = 0;
122 	cmdargs.f_argv = calloc(2, sizeof(char *));
123 
124 	cmdargs.a_argc = 0;
125 	cmdargs.a_argv = calloc(2, sizeof(char *));
126 
127 	cmdargs.r_argv_max = 0;
128 	cmdargs.r_argv = calloc(r_argv_max, sizeof(int));
129 	cmdargs.r_argv_arg1 = calloc(r_argv_max, sizeof(char *));
130 	cmdargs.r_argv_arg2 = calloc(r_argv_max, sizeof(char *));
131 	cmdargs.r_argv_arg3 = calloc(r_argv_max, sizeof(char *));
132 	cmdargs.r_argv_delim = calloc(r_argv_max, sizeof(char));
133 	cmdargs.r_index_exist = calloc(r_argv_max, sizeof(int));
134 	cmdargs.r_index_to_argv = calloc(r_argv_max, sizeof(int));
135 	for (i = 0; i < r_argv_max; i++) {
136 		cmdargs.r_argv[i] = -1;
137 		cmdargs.r_index_exist[i] = R_INDEX_IS_NOT_EXISTENCE;
138 		cmdargs.r_index_to_argv[i] = R_INDEX_IS_NONE;
139 	}
140 	cmdargs.r_index_max = 0;
141 	/*
142 	 * Command name
143 	 */
144 	cmdargs.cmdname = argv[0];
145 	cmdargs.r_argc = 0;
146 
147 	/*
148 	 * Options analysis
149 	 */
150 	i = 1;
151 	while (i < argc) {
152 		switch (argv[i][0]) {
153 		case '-':
154 			j = 1;
155 			o = argv[i][j];
156 			switch (o) {
157 			case '-':
158 				// -- : End of options
159 				++i;
160 				goto retu_analysis;
161 			case '\0':
162 				// - : stdin
163 				if (!(flags&CMDARGS_F_NONE)) {
164 					cmdargs.f_argv[1] = STDIN_FILE;
165 					stdin_file_used = 1;
166 					cmdargs.f_argc += 1;
167 					++i;
168 					goto file_analysis;
169 				}
170 				// - : arg
171 				else if (!(flags&CMDARGS_A_NONE)) {
172 					cmdargs.a_argv[1] = "-";
173 					cmdargs.a_argc += 1;
174 					++i;
175 					goto argarg_analysis;
176 				}
177 			case 'a': SETFLAG("a", FLAG_a, FLAG_a_ARG); break;
178 			case 'b': SETFLAG("b", FLAG_b, FLAG_b_ARG); break;
179 			case 'c': SETFLAG("c", FLAG_c, FLAG_c_ARG); break;
180 			case 'd': SETFLAG("d", FLAG_d, FLAG_d_ARG); break;
181 			case 'e': SETFLAG("e", FLAG_e, FLAG_e_ARG); break;
182 			case 'f': SETFLAG("f", FLAG_f, FLAG_f_ARG); break;
183 			case 'g': SETFLAG("g", FLAG_g, FLAG_g_ARG); break;
184 			case 'h': SETFLAG("h", FLAG_h, FLAG_h_ARG);
185 			          usage();                          break;
186 			case 'i': SETFLAG("i", FLAG_i, FLAG_i_ARG); break;
187 			case 'j': SETFLAG("j", FLAG_j, FLAG_j_ARG); break;
188 			case 'k': SETFLAG("k", FLAG_k, FLAG_k_ARG); break;
189 			case 'l': SETFLAG("l", FLAG_l, FLAG_l_ARG); break;
190 			case 'm': SETFLAG("m", FLAG_m, FLAG_m_ARG); break;
191 			case 'n': SETFLAG("n", FLAG_n, FLAG_n_ARG); break;
192 			case 'o': SETFLAG("o", FLAG_o, FLAG_o_ARG); break;
193 			case 'p': SETFLAG("p", FLAG_p, FLAG_p_ARG); break;
194 			case 'q': SETFLAG("q", FLAG_q, FLAG_q_ARG); break;
195 			case 'r': SETFLAG("r", FLAG_r, FLAG_r_ARG); break;
196 			case 's': SETFLAG("s", FLAG_s, FLAG_s_ARG); break;
197 			case 't': SETFLAG("t", FLAG_t, FLAG_t_ARG); break;
198 			case 'u': SETFLAG("u", FLAG_u, FLAG_u_ARG); break;
199 			case 'v': SETFLAG("v", FLAG_v, FLAG_v_ARG);
200 			          version();                        break;
201 			case 'w': SETFLAG("w", FLAG_w, FLAG_w_ARG); break;
202 			case 'x': SETFLAG("x", FLAG_x, FLAG_x_ARG); break;
203 			case 'y': SETFLAG("y", FLAG_y, FLAG_y_ARG); break;
204 			case 'z': SETFLAG("z", FLAG_z, FLAG_z_ARG); break;
205 			case 'A': SETFLAG("A", FLAG_A, FLAG_A_ARG); break;
206 			case 'B': SETFLAG("B", FLAG_B, FLAG_B_ARG); break;
207 			case 'C': SETFLAG("C", FLAG_C, FLAG_C_ARG); break;
208 			case 'D': SETFLAG("D", FLAG_D, FLAG_D_ARG); break;
209 			case 'E': SETFLAG("E", FLAG_E, FLAG_E_ARG); break;
210 			case 'F': SETFLAG("F", FLAG_F, FLAG_F_ARG); break;
211 			case 'G': SETFLAG("G", FLAG_G, FLAG_G_ARG); break;
212 			case 'H': SETFLAG("H", FLAG_H, FLAG_H_ARG); break;
213 			case 'I': SETFLAG("I", FLAG_I, FLAG_I_ARG); break;
214 			case 'J': SETFLAG("J", FLAG_J, FLAG_J_ARG); break;
215 			case 'K': SETFLAG("K", FLAG_K, FLAG_K_ARG); break;
216 			case 'L': SETFLAG("L", FLAG_L, FLAG_L_ARG); break;
217 			case 'M': SETFLAG("M", FLAG_M, FLAG_M_ARG); break;
218 			case 'N': SETFLAG("N", FLAG_N, FLAG_N_ARG); break;
219 			case 'O': SETFLAG("O", FLAG_O, FLAG_O_ARG); break;
220 			case 'P': SETFLAG("P", FLAG_P, FLAG_P_ARG); break;
221 			case 'Q': SETFLAG("Q", FLAG_Q, FLAG_Q_ARG); break;
222 			case 'R': SETFLAG("R", FLAG_R, FLAG_R_ARG); break;
223 			case 'S': SETFLAG("S", FLAG_S, FLAG_S_ARG); break;
224 			case 'T': SETFLAG("T", FLAG_T, FLAG_T_ARG); break;
225 			case 'U': SETFLAG("U", FLAG_U, FLAG_U_ARG); break;
226 			case 'V': SETFLAG("V", FLAG_V, FLAG_V_ARG); break;
227 			case 'W': SETFLAG("W", FLAG_W, FLAG_W_ARG); break;
228 			case 'X': SETFLAG("X", FLAG_X, FLAG_X_ARG); break;
229 			case 'Y': SETFLAG("Y", FLAG_Y, FLAG_Y_ARG); break;
230 			case 'Z': SETFLAG("Z", FLAG_Z, FLAG_Z_ARG); break;
231 			case '0': SETFLAG("0", FLAG_0, FLAG_0_ARG); break;
232 			case '1': SETFLAG("1", FLAG_1, FLAG_1_ARG); break;
233 			case '2': SETFLAG("2", FLAG_2, FLAG_2_ARG); break;
234 			case '3': SETFLAG("3", FLAG_3, FLAG_3_ARG); break;
235 			case '4': SETFLAG("4", FLAG_4, FLAG_4_ARG); break;
236 			case '5': SETFLAG("5", FLAG_5, FLAG_5_ARG); break;
237 			case '6': SETFLAG("6", FLAG_6, FLAG_6_ARG); break;
238 			case '7': SETFLAG("7", FLAG_7, FLAG_7_ARG); break;
239 			case '8': SETFLAG("8", FLAG_8, FLAG_8_ARG); break;
240 			case '9': SETFLAG("9", FLAG_9, FLAG_9_ARG); break;
241 			case '@': SETFLAG("@", FLAG_AT, FLAG_AT_ARG); break;
242 			}
243 			++j;
244 			++i;
245 			break;
246 		default:
247 			goto retu_analysis;
248 		}
249 	}
250 
251 	/*
252 	 * Column specific analysis
253 	 */
254 retu_analysis:
255 	if (flags&CMDARGS_R_NONE)
256 		goto argarg_analysis;
257 
258 	for (; i < argc; i++, cmdargs.r_argc++) {
259 		/*
260 		 * Array expansion
261 		 */
262 		if (cmdargs.r_argc == r_argv_max - 1) {
263 			ARRAY_EXPANSION(r_argv_max,
264 				r_argv_max + r_argv_addition_size);
265 			r_argv_max += r_argv_addition_size;
266 		}
267 
268 		switch (argv[i][0]) {
269 		case '0':
270 		case '1':
271 		case '2':
272 		case '3':
273 		case '4':
274 		case '5':
275 		case '6':
276 		case '7':
277 		case '8':
278 		case '9':
279 			/*
280 			 * Column number process
281 			 */
282 			p1 = &argv[i][0];
283 			p2 = buf;
284 			while (isdigit(*p1)) {
285 				*p2++ = *p1++;
286 			}
287 			*p2 = '\0';
288 			cmdargs.r_argv[1+cmdargs.r_argc] =
289 				(int)strtol(buf, (char **)NULL, 10);
290 			if (cmdargs.r_argv[1+cmdargs.r_argc] < mincol) {
291 				fprintf(stderr,
292 				        "%d must be 1 or over.\n",
293 				        cmdargs.r_argv[1+cmdargs.r_argc]);
294 				exit(EX_USAGE);
295 			}
296 			if (cmdargs.r_argv[1+cmdargs.r_argc] >
297 				cmdargs.r_argv_max)
298 				cmdargs.r_argv_max =
299 					cmdargs.r_argv[1+cmdargs.r_argc];
300 
301 			/*
302 			 * Array expansion
303 			 */
304 			if (cmdargs.r_argv[1+cmdargs.r_argc] >
305 				r_argv_max - 1) {
306 				ARRAY_EXPANSION(r_argv_max,
307 					r_argv_max +
308 					cmdargs.r_argv[1+cmdargs.r_argc])
309 				r_argv_max +=
310 					cmdargs.r_argv[1+cmdargs.r_argc];
311 			}
312 			cmdargs.r_index_exist
313 				[cmdargs.r_argv[1+cmdargs.r_argc]] =
314 					R_INDEX_IS_EXISTENCE;
315 			cmdargs.r_index_to_argv[
316 				cmdargs.r_argv[1+cmdargs.r_argc]] =
317 					1+cmdargs.r_argc;
318 			if (cmdargs.r_argv[1+cmdargs.r_argc] >
319 			    cmdargs.r_index_max)
320 				cmdargs.r_index_max =
321 					cmdargs.r_argv[1+cmdargs.r_argc];
322 
323 			if ('\0' == *p1) {
324 				if (flags&CMDARGS_R_ARGARG_1_NEED)
325 					usage();
326 				continue;
327 			}
328 
329 			/*
330 			 * Column range pre process
331 			 */
332 			delim = *p1++;
333 			range = 0;
334 			if ('/' == delim) {
335 				p2 = buf;
336 				if ('0' > *p1 || *p1 > '9') {
337 					fprintf(stderr,
338 					        "The range must be "
339 						"a positive number.\n");
340 					exit(EX_USAGE);
341 				}
342 				while ('0' <= *p1 && *p1 <= '9') {
343 					*p2++ = *p1++;
344 				}
345 				*p2++ = '\0';
346 				range = (int)strtol(buf, (char **)NULL, 10);
347 				if (range < 1) {
348 					fprintf(stderr,
349 					        "%d must be 1 or over.\n",
350 				        	range);
351 					exit(EX_USAGE);
352 				}
353 				if (range > cmdargs.r_index_max)
354 					cmdargs.r_index_max = range;
355 				if (range > cmdargs.r_argv_max)
356 					cmdargs.r_argv_max = range;
357 				/*
358 				 * Array expansion
359 				 */
360 				k = cmdargs.r_argc+2;
361 				if (range > r_argv_max - 1) {
362 					ARRAY_EXPANSION(r_argv_max,
363 						range + 1)
364 					r_argv_max = range + 1;
365 				}
366 
367 				if ('\0' == *p1)
368 					goto column_range_process;
369 				else
370 					delim = *p1++;
371 			}
372 
373 			/*
374 			 * 1st argument process of the column number
375 			 */
376 			if (flags&CMDARGS_R_ARGARG_NONE)
377 				usage();
378 			cmdargs.r_argv_delim[1+cmdargs.r_argc] = delim;
379 
380 			p2 = buf;
381 			if (single_delimiter_mode) {
382 				while ('\0' != *p1) {
383 					*p2++ = *p1++;
384 				}
385 			}
386 			else {
387 				while (delim != *p1 && '\0' != *p1) {
388 					*p2++ = *p1++;
389 				}
390 			}
391 			*p2++ = '\0';
392 			cmdargs.r_argv_arg1[1+cmdargs.r_argc] =
393 				calloc(p2 - buf, sizeof(char));
394 			stpncpy(cmdargs.r_argv_arg1[
395 			                1+cmdargs.r_argc], buf, p2 - buf);
396 
397 			/*
398 			 * 2nd argument process of the column number
399 			 */
400 			if (range && '\0' == *p1)
401 				goto column_range_process;
402 
403 			if ('\0' == *p1) {
404 				if (flags&CMDARGS_R_ARGARG_2_NEED)
405 					usage();
406 				continue;
407 			}
408 
409 			++p1;
410 			p2 = buf;
411 			if (double_delimiter_mode) {
412 				while ('\0' != *p1) {
413 					*p2++ = *p1++;
414 				}
415 			}
416 			else {
417 				while (delim != *p1 && '\0' != *p1) {
418 					*p2++ = *p1++;
419 				}
420 			}
421 			*p2++ = '\0';
422 			cmdargs.r_argv_arg2[1+cmdargs.r_argc] =
423 				calloc(p2 - buf, sizeof(char));
424 			stpncpy(cmdargs.r_argv_arg2[
425 			                1+cmdargs.r_argc], buf, p2 - buf);
426 
427 			/*
428 			 * 3rd argument process of the column number
429 			 */
430 			if (range && '\0' == *p1)
431 				goto column_range_process;
432 
433 			if ('\0' == *p1) {
434 				if (flags&CMDARGS_R_ARGARG_3_NEED)
435 					usage();
436 				continue;
437 			}
438 
439 			++p1;
440 			p2 = buf;
441 			while ('\0' != *p1) {
442 				*p2++ = *p1++;
443 			}
444 			*p2++ = '\0';
445 			cmdargs.r_argv_arg3[1+cmdargs.r_argc] =
446 				calloc(p2 - buf, sizeof(char));
447 			stpncpy(cmdargs.r_argv_arg3[
448 			                1+cmdargs.r_argc], buf, p2 - buf);
449 
450 			/*
451 			 * Column range pre process
452 			 */
453 column_range_process:
454 			/*
455 			 * Array expansion
456 			 */
457 			if (range) {
458 				if (cmdargs.r_argv[1+cmdargs.r_argc] <=
459 				            range) {
460 					j = cmdargs.r_argv[
461 						cmdargs.r_argc+1] + 1;
462 					if (cmdargs.r_argc >
463 					    r_argv_max - (range - j) - 2) {
464 						ARRAY_EXPANSION(r_argv_max,
465 							r_argv_max +
466 							range - j +
467 							r_argv_addition_size);
468 						r_argv_max += range - j +
469 							r_argv_addition_size;
470 					}
471 				}
472 				else {
473 					j = cmdargs.r_argv
474 						[cmdargs.r_argc+1] - 1;
475 					if (cmdargs.r_argc >
476 					    r_argv_max - (j - range) - 2) {
477 						ARRAY_EXPANSION(r_argv_max,
478 							r_argv_max +
479 							j - range +
480 							r_argv_addition_size);
481 						r_argv_max += j - range +
482 							r_argv_addition_size;
483 					}
484 				}
485 			}
486 			if (range) {
487 				k = cmdargs.r_argc+2;
488 				if (cmdargs.r_argv[1+cmdargs.r_argc] <=
489 				            range) {
490 					j = cmdargs.r_argv[
491 						cmdargs.r_argc+1] + 1;
492 					while (j <= range) {
493 						cmdargs.r_argv[k] = j;
494 						cmdargs.r_argv_delim[k] =
495 							delim;
496 						cmdargs.r_argv_arg1[k] =
497 							cmdargs.r_argv_arg1
498 							[cmdargs.r_argc+1];
499 						cmdargs.r_argv_arg2[k] =
500 							cmdargs.r_argv_arg2
501 							[cmdargs.r_argc+1];
502 						cmdargs.r_argv_arg3[k] =
503 							cmdargs.r_argv_arg3
504 							[cmdargs.r_argc+1];
505 						cmdargs.r_index_exist[j] =
506 							R_INDEX_IS_EXISTENCE;
507 						cmdargs.r_index_to_argv
508 							[j] = k;
509 						++j;
510 						++k;
511 					}
512 					cmdargs.r_argc +=
513 					        range - cmdargs.r_argv[
514 						        cmdargs.r_argc+1];
515 				}
516 				else {
517 					j = cmdargs.r_argv
518 						[cmdargs.r_argc+1] - 1;
519 					for (; j >= range; j--, k++) {
520 						cmdargs.r_argv[k] = j;
521 						cmdargs.r_argv_delim[k] =
522 							delim;
523 						cmdargs.r_argv_arg1[k] =
524 							cmdargs.r_argv_arg1
525 							[cmdargs.r_argc+1];
526 						cmdargs.r_argv_arg2[k] =
527 							cmdargs.r_argv_arg2
528 							[cmdargs.r_argc+1];
529 						cmdargs.r_argv_arg3[k] =
530 							cmdargs.r_argv_arg3
531 							[cmdargs.r_argc+1];
532 						cmdargs.r_index_exist[j] =
533 							R_INDEX_IS_EXISTENCE;
534 						cmdargs.r_index_to_argv[j] =
535 							k;
536 					}
537 					cmdargs.r_argc +=
538 					        cmdargs.r_argv
539 							[cmdargs.r_argc+1]
540 							- range;
541 				}
542 			}
543 			break;
544 		default:
545 			goto file_analysis;
546 		}
547 	}
548 
549 argarg_analysis:
550 	if (!(flags&CMDARGS_A_NEED))
551 		goto file_analysis;
552 
553 	for (; i < argc; i++, cmdargs.a_argc++) {
554 		if (cmdargs.a_argc > 0)
555 			REALLOC_SARRAY(cmdargs.a_argc+1,
556 				cmdargs.a_argc+2, cmdargs.a_argv)
557 
558 		cmdargs.a_argv[cmdargs.a_argc+1] = argv[i];
559 	}
560 	if (NULL == cmdargs.a_argv[1]) {
561 		if (flags&CMDARGS_A_NEED && !FLAG_h && !FLAG_v)
562 			fprintf(stderr,
563 			    "You must specify at least an argument.\n\n");
564 			usage();
565 	}
566 
567 	/*
568 	 * File specific analysis
569 	 */
570 file_analysis:
571 	if (flags&CMDARGS_R_NEED && 0 == cmdargs.r_argc)
572 		usage();
573 
574 	if (flags&CMDARGS_F_NONE)
575 		goto str_to_ssvstring_process;
576 
577 	for (; i < argc; i++, cmdargs.f_argc++) {
578 		if (cmdargs.f_argc > 0)
579 			REALLOC_SARRAY(cmdargs.f_argc+1,
580 				cmdargs.f_argc+2, cmdargs.f_argv)
581 
582 		switch (argv[i][0]) {
583 		case '-':
584 			switch (argv[i][1]) {
585 			case '\0':
586 				if (stdin_file_used && \
587 				    !(flags&CMDARGS_STDIN_TO_TMPFILE)) {
588 					fprintf(stderr,
589 					    "You can specify the stdin " \
590 					    "only once.\n\n");
591 					usage();
592 				}
593 				cmdargs.f_argv[cmdargs.f_argc+1] =
594 					STDIN_FILE;
595 				stdin_file_used = 1;
596 				continue;
597 				;;
598 			}
599 		default:
600 			cmdargs.f_argv[cmdargs.f_argc+1] = argv[i];
601 			break;
602 		}
603 	}
604 	if (NULL == cmdargs.f_argv[1]) {
605 		if (flags&CMDARGS_F_NEED && !FLAG_h && !FLAG_v) {
606 			fprintf(stderr,
607 			    "You must specify at least a file.\n\n");
608 			usage();
609 		}
610 		else if (stdin_file_used &&
611 		         !(flags&CMDARGS_STDIN_TO_TMPFILE)) {
612 			fprintf(stderr,
613 			    "You can specify the stdin only once.\n\n");
614 			usage();
615 		}
616 		cmdargs.f_argv[1] = STDIN_FILE;
617 		stdin_file_used = 1;
618 		++cmdargs.f_argc;
619 	}
620 
621 	/*
622 	 * convert the arg string to the ssv string
623 	 */
624 str_to_ssvstring_process:
625 	if (flags&CMDARGS_R_ARGARG_TO_SSVSTR) {
626 		char *ssvstr;
627 		int ssvstr_len = 10, arglen;
628 		ssvstr = calloc(ssvstr_len, sizeof(char));
629 #define R_ARGSTR_TO_SSVSTR(X) \
630 		if (NULL != X[i]) { \
631 			arglen = strlen(X[i]) + 1; \
632 			if (arglen * 2 - 1 > ssvstr_len) \
633 				ssvstr_len = arglen * 2 - 1; \
634 			ssvstr = calloc(ssvstr_len, sizeof(char)); \
635 			str2ssvstr(ssvstr, X[i]); \
636 			X[i] = ssvstr; \
637 		}
638 		for (i = 1; i <= cmdargs.r_argc; i++) {
639 			R_ARGSTR_TO_SSVSTR(cmdargs.r_argv_arg1)
640 			R_ARGSTR_TO_SSVSTR(cmdargs.r_argv_arg2)
641 			R_ARGSTR_TO_SSVSTR(cmdargs.r_argv_arg3)
642 		}
643 	}
644 
645 	/*
646 	 * Default options process
647 	 */
648 	if (FLAG_v)
649 		version();
650 	if (FLAG_h)
651 		usage();
652 	if ((FLAG_D && !stdin_file_used) ||
653 	    (FLAG_D && stdin_file_used &&
654 		!(flags&CMDARGS_STDIN_TO_TMPFILE)))
655 		printcmdargs();
656 
657 	/*
658 	 * Convert the stdin to a temporality file
659 	 */
660 	if (stdin_file_used && flags&CMDARGS_STDIN_TO_TMPFILE) {
661 		stdintotempfile();
662 		if (FLAG_D)
663 			printcmdargs();
664 	}
665 
666 	return 0;
667 }
668 
669 int
getcmdargs_unlinktmpf(void)670 getcmdargs_unlinktmpf(void)
671 {
672 	if (NULL == tempfile)
673 		return 0;
674 
675 	return (unlink(tempfile));
676 }
677 
678 static void
stdintotempfile(void)679 stdintotempfile(void)
680 {
681 	FILE *fp_i, *fp_o;
682 	char b;
683 
684 	mktempfile();
685 	fp_i = fopen(STDIN_FILE, "r");
686 	if (NULL == fp_i) \
687 		err(errno, "%s", STDIN_FILE); \
688 	fp_o = fopen(tempfile, "w");
689 	if (NULL == fp_o) \
690 		err(errno, "%s", tempfile); \
691 
692 	while (EOF != (b = fgetc(fp_i)))
693 		fputc(b, fp_o);
694 
695 	fclose(fp_o);
696 	fclose(fp_i);
697 
698 	for (int i = 1; i <= cmdargs.f_argc; i++)
699 		if (STDIN_FILE == cmdargs.f_argv[i])
700 			cmdargs.f_argv[i] = tempfile;
701 }
702 
703 static void
mktempfile(void)704 mktempfile(void)
705 {
706 	struct stat sb;
707 	int i, j, k, len;
708 	char *home = getenv("HOME"), *dir, *dir2;
709 	if (NULL == home || 0 == strcmp(home, "/"))
710 		home = "/tmp";
711 
712 	len = strlen(home) + strlen(TMPDIR_NAME) + 2;
713 	dir = calloc(len, sizeof(char));
714 	dir2 = calloc(len, sizeof(char));
715 	sprintf(dir, "%s/%s", home, TMPDIR_NAME);
716 
717 	/*
718 	 * Create a cache directory
719 	 */
720 	i = j = k = 0;
721 	while (1) {
722 		dir2[i] = dir[i];
723 		if ('/' == dir2[i] || '\0' == dir2[i]) {
724 			if (j == k) {
725 				if (0 != i && '/' == dir2[i])
726 					dir2[i] = '\0';
727 				if (-1 == stat(dir2, &sb))
728 					mkdir(dir2, S_IRWXU);
729 				if ('\0' == dir[i])
730 					break;
731 				k++;
732 				i = j = 0;
733 			}
734 			else {
735 				++i;
736 				++j;
737 			}
738 		}
739 		else
740 			++i;
741 	}
742 
743 	/*
744 	 * Create a temporary file
745 	 */
746 	len += strlen(TMPFILE_NAME) + 1;
747 	tempfile = calloc(len, sizeof(char));
748 	sprintf(tempfile, "%s/%s", dir, TMPFILE_NAME);
749 
750 	if (-1 == mkstemp(tempfile))
751 		err(errno, "getcmdargs#mktempfile: %s: failed", tempfile);
752 }
753