1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <ctype.h>
5 #include "lib/mlr_globals.h"
6 #include "lib/mlr_arch.h"
7 #include "lib/mlrutil.h"
8 #include "lib/netbsd_strptime.h"
9 #include "input/line_readers.h"
10 
11 // ----------------------------------------------------------------
12 static int        aux_list_main(int argc, char** argv);
13 static int           lecat_main(int argc, char** argv);
14 static int         termcvt_main(int argc, char** argv);
15 static int             hex_main(int argc, char** argv);
16 static int           unhex_main(int argc, char** argv);
17 static int netbsd_strptime_main(int argc, char** argv);
18 
19 static int lecat_stream(FILE* input_stream, int do_color);
20 static void hex_dump_fp(FILE *in_fp, FILE *out_fp, int do_raw);
21 static void    unhex_fp(FILE *in_fp, FILE *out_fp);
22 
23 static void        aux_list_usage(char* argv0, char* argv1, FILE* o, int exit_code);
24 static void           lecat_usage(char* argv0, char* argv1, FILE* o, int exit_code);
25 static void         termcvt_usage(char* argv0, char* argv1, FILE* o, int exit_code);
26 static void             hex_usage(char* argv0, char* argv1, FILE* o, int exit_code);
27 static void           unhex_usage(char* argv0, char* argv1, FILE* o, int exit_code);
28 static void netbsd_strptime_usage(char* argv0, char* argv1, FILE* o, int exit_code);
29 
30 // ----------------------------------------------------------------
31 typedef int aux_main_t(int argc, char**argv);
32 typedef void aux_usage_t( char* argv0, char* argv1, FILE* o, int exit_code);
33 typedef struct _aux_lookup_entry_t {
34 	char* name;
35 	aux_main_t* pmain;
36 	aux_usage_t* pusage;
37 } aux_lookup_entry_t;
38 
39 static aux_lookup_entry_t aux_lookup_table[] = {
40 
41 	{ "aux-list",        aux_list_main,        aux_list_usage        },
42 	{ "lecat",           lecat_main,           lecat_usage           },
43 	{ "termcvt",         termcvt_main,         termcvt_usage         },
44 	{ "hex",             hex_main,             hex_usage             },
45 	{ "unhex",           unhex_main,           unhex_usage           },
46 	{ "netbsd-strptime", netbsd_strptime_main, netbsd_strptime_usage },
47 
48 };
49 
50 static int aux_lookup_table_size = sizeof(aux_lookup_table) / sizeof(aux_lookup_table[0]);
51 
52 // ================================================================
do_aux_entries(int argc,char ** argv)53 void do_aux_entries(int argc, char** argv) {
54 	if (argc < 2) {
55 		return;
56 	}
57 
58 	for (int i = 0; i < aux_lookup_table_size; i++) {
59 		if (streq(argv[1], aux_lookup_table[i].name)) {
60 			exit(aux_lookup_table[i].pmain(argc, argv));
61 		}
62 	}
63 	// else return to mlrmain for the rest of Miller.
64 }
65 
show_aux_entries(FILE * fp)66 void show_aux_entries(FILE* fp) {
67 	fprintf(fp, "Available subcommands:\n");
68 	for (int i = 0; i < aux_lookup_table_size; i++) {
69 		fprintf(fp, "  %s\n", aux_lookup_table[i].name);
70 	}
71 	fprintf(fp, "For more information, please invoke %s {subcommand} --help\n", MLR_GLOBALS.bargv0);
72 }
73 
74 // ----------------------------------------------------------------
aux_list_usage(char * argv0,char * argv1,FILE * o,int exit_code)75 static void aux_list_usage(char* argv0, char* argv1, FILE* o, int exit_code) {
76 	fprintf(o, "Usage: %s %s [options]\n", argv0, argv1);
77 	fprintf(o, "Options:\n");
78 	fprintf(o, "-h or --help: print this message\n");
79 	exit(exit_code);
80 }
81 
aux_list_main(int argc,char ** argv)82 int aux_list_main(int argc, char** argv) {
83 	show_aux_entries(stdout);
84 	return 0;
85 }
86 
87 // ----------------------------------------------------------------
lecat_usage(char * argv0,char * argv1,FILE * o,int exit_code)88 static void lecat_usage(char* argv0, char* argv1, FILE* o, int exit_code) {
89 	fprintf(o, "Usage: %s %s [options] {zero or more file names}\n", argv0, argv1);
90 	fprintf(o, "Simply echoes input, but flags CR characters in red and LF characters in green.\n");
91 	fprintf(o, "If zero file names are supplied, standard input is read.\n");
92 	fprintf(o, "Options:\n");
93 	fprintf(o, "--mono: don't try to colorize the output\n");
94 	fprintf(o, "-h or --help: print this message\n");
95 	exit(exit_code);
96 }
97 
lecat_main(int argc,char ** argv)98 static int lecat_main(int argc, char** argv) {
99 	int ok = 1;
100 	int do_color = TRUE;
101 
102 	if (argc >= 3) {
103 		if (streq(argv[2], "-h") || streq(argv[2], "--help")) {
104 			lecat_usage(argv[0], argv[1], stdout, 0);
105 		}
106 	}
107 
108 	// 'mlr' and 'lecat' are already argv[0] and argv[1].
109 	int argb = 2;
110 	if (argc >= 3 && argv[argb][0] == '-') {
111 		if (streq(argv[argb], "--mono")) {
112 			do_color = FALSE;
113 			argb++;
114 		} else {
115 			fprintf(stderr, "%s %s: unrecognized option \"%s\".\n",
116 				argv[0], argv[1], argv[argb]);
117 			return 1;
118 		}
119 	}
120 
121 	if (argb == argc) {
122 		ok = ok && lecat_stream(stdin, do_color);
123 	} else {
124 		for (int argi = argb; argi < argc; argi++) {
125 			char* file_name = argv[argi];
126 			FILE* input_stream = fopen(file_name, "r");
127 			if (input_stream == NULL) {
128 				perror(file_name);
129 				exit(1);
130 			}
131 			ok = lecat_stream(input_stream, do_color);
132 			fclose(input_stream);
133 		}
134 	}
135 	return ok ? 0 : 1;
136 }
137 
lecat_stream(FILE * input_stream,int do_color)138 static int lecat_stream(FILE* input_stream, int do_color) {
139 	while (1) {
140 		int c = fgetc(input_stream);
141 		if (c == EOF)
142 			break;
143 		if (c == '\r') {
144 			if (do_color)
145 				printf("\033[31;01m"); // xterm red
146 			printf("[CR]");
147 			if (do_color)
148 				printf("\033[0m");
149 		} else if (c == '\n') {
150 			if (do_color)
151 				printf("\033[32;01m"); // xterm green
152 			printf("[LF]\n");
153 			if (do_color)
154 				printf("\033[0m");
155 		} else {
156 			putchar(c);
157 		}
158 	}
159 	return 1;
160 }
161 
162 // ================================================================
termcvt_stream(FILE * input_stream,FILE * output_stream,char * inend,char * outend)163 static int termcvt_stream(FILE* input_stream, FILE* output_stream, char* inend, char* outend) {
164 	size_t line_length = MLR_ALLOC_READ_LINE_INITIAL_SIZE;
165 	int inend_length = strlen(inend);
166 	while (1) {
167 		char* line = mlr_alloc_read_line_multiple_delimiter(input_stream, inend, inend_length, &line_length);
168 		if (line == NULL) {
169 			break;
170 		}
171 		fputs(line, output_stream);
172 		fputs(outend, output_stream);
173 		free(line);
174 	}
175 	return 1;
176 }
177 
178 // ----------------------------------------------------------------
termcvt_usage(char * argv0,char * argv1,FILE * o,int exit_code)179 static void termcvt_usage(char* argv0, char* argv1, FILE* o, int exit_code) {
180 	fprintf(o, "Usage: %s %s [option] {zero or more file names}\n", argv0, argv1);
181 	fprintf(o, "Option (exactly one is required):\n");
182 	fprintf(o, "--cr2crlf\n");
183 	fprintf(o, "--lf2crlf\n");
184 	fprintf(o, "--crlf2cr\n");
185 	fprintf(o, "--crlf2lf\n");
186 	fprintf(o, "--cr2lf\n");
187 	fprintf(o, "--lf2cr\n");
188 	fprintf(o, "-I in-place processing (default is to write to stdout)\n");
189 	fprintf(o, "-h or --help: print this message\n");
190 	fprintf(o, "Zero file names means read from standard input.\n");
191 	fprintf(o, "Output is always to standard output; files are not written in-place.\n");
192 	exit(exit_code);
193 }
194 
195 // ----------------------------------------------------------------
termcvt_main(int argc,char ** argv)196 static int termcvt_main(int argc, char** argv) {
197 	int ok = 1;
198 	char* inend  = "\n";
199 	char* outend = "\n";
200 	int do_in_place = FALSE;
201 
202 	// argv[0] is 'mlr'
203 	// argv[1] is 'termcvt'
204 	// argv[2] is '--some-option'
205 	// argv[3] and above are filenames
206 	if (argc < 2)
207 		termcvt_usage(argv[0], argv[1], stderr, 1);
208 	int argi;
209 	for (argi = 2; argi < argc; argi++) {
210 		char* opt = argv[argi];
211 
212 		if (opt[0] != '-')
213 			break;
214 
215 		if (streq(opt, "-h") || streq(opt, "--help")) {
216 			termcvt_usage(argv[0], argv[1], stdout, 0);
217 		} else if (streq(opt, "-I")) {
218 			do_in_place = TRUE;
219 		} else if (streq(opt, "--cr2crlf")) {
220 			inend  = "\r";
221 			outend = "\r\n";
222 		} else if (streq(opt, "--lf2crlf")) {
223 			inend  = "\n";
224 			outend = "\r\n";
225 		} else if (streq(opt, "--crlf2cr")) {
226 			inend  = "\r\n";
227 			outend = "\r";
228 		} else if (streq(opt, "--lf2cr")) {
229 			inend  = "\n";
230 			outend = "\r";
231 		} else if (streq(opt, "--crlf2lf")) {
232 			inend  = "\r\n";
233 			outend = "\n";
234 		} else if (streq(opt, "--cr2lf")) {
235 			inend  = "\r";
236 			outend = "\n";
237 		} else {
238 			termcvt_usage(argv[0], argv[1], stdout, 0);
239 		}
240 	}
241 
242 	int nfiles = argc - argi;
243 	if (nfiles == 0) {
244 		ok = ok && termcvt_stream(stdin, stdout, inend, outend);
245 
246 	} else if (do_in_place) {
247 		for (; argi < argc; argi++) {
248 			char* file_name = argv[argi];
249 			char* temp_name = alloc_suffixed_temp_file_name(file_name);
250 			FILE* input_stream = fopen(file_name, "r");
251 			FILE* output_stream = fopen(temp_name, "wb");
252 
253 			if (input_stream == NULL) {
254 				perror("fopen");
255 				fprintf(stderr, "%s: Could not open \"%s\" for read.\n",
256 					MLR_GLOBALS.bargv0, file_name);
257 				exit(1);
258 			}
259 			if (output_stream == NULL) {
260 				perror("fopen");
261 				fprintf(stderr, "%s: Could not open \"%s\" for write.\n",
262 					MLR_GLOBALS.bargv0, temp_name);
263 				exit(1);
264 			}
265 
266 			ok = termcvt_stream(input_stream, output_stream, inend, outend);
267 
268 			fclose(input_stream);
269 			fclose(output_stream);
270 
271 			int rc = rename(temp_name, file_name);
272 			if (rc != 0) {
273 				perror("rename");
274 				fprintf(stderr, "%s: Could not rename \"%s\" to \"%s\".\n",
275 					MLR_GLOBALS.bargv0, temp_name, file_name);
276 				exit(1);
277 			}
278 			free(temp_name);
279 		}
280 
281 	} else {
282 		for (; argi < argc; argi++) {
283 			char* file_name = argv[argi];
284 			FILE* input_stream = fopen(file_name, "r");
285 			if (input_stream == NULL) {
286 				perror(file_name);
287 				exit(1);
288 			}
289 			ok = termcvt_stream(input_stream, stdout, inend, outend);
290 			fclose(input_stream);
291 		}
292 
293 	}
294 	return ok ? 0 : 1;
295 }
296 
297 // ================================================================
298 // Copyright (c) 1998 John Kerl.
299 // ================================================================
300 // This is a simple hex dump with hex offsets to the left, hex data in the
301 // middle, and ASCII at the right.  This is a subset of the functionality of
302 // Unix od; I wrote it in my NT days.
303 //
304 // Example:
305 //
306 // $ d2h $(jot 0 128) | unhex | hex
307 // 00000000: 00 01 02 03  04 05 06 07  08 09 0a 0b  0c 0d 0e 0f |................|
308 // 00000010: 10 11 12 13  14 15 16 17  18 19 1a 1b  1c 1d 1e 1f |................|
309 // 00000020: 20 21 22 23  24 25 26 27  28 29 2a 2b  2c 2d 2e 2f | !"#$%&'()*+,-./|
310 // 00000030: 30 31 32 33  34 35 36 37  38 39 3a 3b  3c 3d 3e 3f |0123456789:;<=>?|
311 // 00000040: 40 41 42 43  44 45 46 47  48 49 4a 4b  4c 4d 4e 4f |@ABCDEFGHIJKLMNO|
312 // 00000050: 50 51 52 53  54 55 56 57  58 59 5a 5b  5c 5d 5e 5f |PQRSTUVWXYZ[\]^_|
313 // 00000060: 60 61 62 63  64 65 66 67  68 69 6a 6b  6c 6d 6e 6f |`abcdefghijklmno|
314 // 00000070: 70 71 72 73  74 75 76 77  78 79 7a 7b  7c 7d 7e 7f |pqrstuvwxyz{|}~.|
315 // ================================================================
316 
317 #define LINE_LENGTH_MAX 8192
318 
319 // ----------------------------------------------------------------
hex_usage(char * argv0,char * argv1,FILE * o,int exit_code)320 static void hex_usage(char* argv0, char* argv1, FILE* o, int exit_code) {
321 	fprintf(o, "Usage: %s %s [options] {zero or more file names}\n", argv0, argv1);
322 	fprintf(o, "Simple hex-dump.\n");
323 	fprintf(o, "If zero file names are supplied, standard input is read.\n");
324 	fprintf(o, "Options:\n");
325 	fprintf(o, "-r: print only raw hex without leading offset indicators or trailing ASCII dump.\n");
326 	fprintf(o, "-h or --help: print this message\n");
327 	exit(exit_code);
328 }
329 
330 //----------------------------------------------------------------------
331 // 'mlr' and 'hex' are already argv[0] and argv[1].
hex_main(int argc,char ** argv)332 static int hex_main(int argc, char **argv) {
333 	char * filename;
334 	FILE * in_fp;
335 	FILE * out_fp;
336 	int do_raw = 0;
337 	int argi = 2;
338 
339 	if (argc >= 3) {
340 		if (streq(argv[2], "-r")) {
341 			do_raw = 1;
342 			argi++;
343 		} else if (streq(argv[2], "-h") || streq(argv[2], "--help")) {
344 			hex_usage(argv[0], argv[1], stdout, 0);
345 		}
346 	}
347 
348 	int num_file_names = argc - argi;
349 
350 	if (num_file_names == 0) {
351 #ifdef WINDOWS
352 		setmode(fileno(stdin), O_BINARY);
353 #endif //WINDOWS
354 		hex_dump_fp(stdin, stdout, do_raw);
355 	} else {
356 		for ( ; argi < argc; argi++) {
357 			if (!do_raw) {
358 				if (num_file_names > 1)
359 					printf("%s:\n", argv[argi]);
360 			}
361 			filename = argv[argi];
362 			in_fp    = fopen(filename, "rb");
363 			out_fp   = stdout;
364 			if (in_fp == NULL) {
365 				fprintf(stderr, "Couldn't open \"%s\"; skipping.\n",
366 					filename);
367 			}
368 			else {
369 				hex_dump_fp(in_fp, out_fp, do_raw);
370 				fclose(in_fp);
371 				if (out_fp != stdout)
372 					fclose(out_fp);
373 
374 			}
375 			if (!do_raw) {
376 				if (num_file_names > 1)
377 					printf("\n");
378 			}
379 		}
380 	}
381 
382 	return 0;
383 }
384 
385 // ----------------------------------------------------------------
hex_dump_fp(FILE * in_fp,FILE * out_fp,int do_raw)386 static void hex_dump_fp(FILE *in_fp, FILE *out_fp, int do_raw) {
387 	const int bytes_per_clump = 4;
388 	const int clumps_per_line = 4;
389 	const int buffer_size = bytes_per_clump * clumps_per_line;
390 	unsigned char buf[buffer_size];
391 	long num_bytes_read;
392 	long num_bytes_total = 0;
393 	int byteno;
394 
395 	while ((num_bytes_read=fread(buf, sizeof(unsigned char),
396 		buffer_size, in_fp)) > 0)
397 	{
398 		if (!do_raw) {
399 			printf("%08lx: ", num_bytes_total);
400 		}
401 
402 		for (byteno = 0; byteno < num_bytes_read; byteno++) {
403 			unsigned int temp = buf[byteno];
404 			printf("%02x ", temp);
405 			if ((byteno % bytes_per_clump) ==
406 			(bytes_per_clump - 1))
407 			{
408 				if ((byteno > 0) && (byteno < buffer_size-1))
409 					printf(" ");
410 			}
411 		}
412 		for (byteno = num_bytes_read; byteno < buffer_size; byteno++) {
413 			printf("   ");
414 			if ((byteno % bytes_per_clump) ==
415 			(bytes_per_clump - 1))
416 			{
417 				if ((byteno > 0) && (byteno < buffer_size-1))
418 					printf(" ");
419 			}
420 		}
421 
422 		if (!do_raw) {
423 			printf("|");
424 			for (byteno = 0; byteno < num_bytes_read; byteno++) {
425 				unsigned char temp = buf[byteno];
426 				if (!isprint(temp))
427 					temp = '.';
428 				printf("%c", temp);
429 			}
430 
431 			printf("|");
432 		}
433 
434 		printf("\n");
435 		num_bytes_total += num_bytes_read;
436 	}
437 }
438 
439 // ----------------------------------------------------------------
unhex_usage(char * argv0,char * argv1,FILE * o,int exit_code)440 static void unhex_usage(char* argv0, char* argv1, FILE* o, int exit_code) {
441 	fprintf(o, "Usage: %s %s [option] {zero or more file names}\n", argv0, argv1);
442 	fprintf(o, "Options:\n");
443 	fprintf(o, "-h or --help: print this message\n");
444 	fprintf(o, "Zero file names means read from standard input.\n");
445 	fprintf(o, "Output is always to standard output; files are not written in-place.\n");
446 	exit(exit_code);
447 }
448 
449 // ----------------------------------------------------------------
unhex_main(int argc,char ** argv)450 int unhex_main(int argc, char ** argv) {
451 	// 'mlr' and 'unhex' are already argv[0] and argv[1].
452 	if (argc >= 3) {
453 		if (streq(argv[2], "-h") || streq(argv[2], "--help")) {
454 			unhex_usage(argv[0], argv[1], stdout, 0);
455 		}
456 	}
457 
458 	int exit_code = 0;
459 	if (argc == 2) {
460 		unhex_fp(stdin, stdout);
461 	} else {
462 		for (int argi = 2; argi < argc; argi++) {
463 			char* filename = argv[argi];
464 			FILE* infp = fopen(filename, "rb");
465 			if (infp == NULL) {
466 				fprintf(stderr, "%s %s: Couldn't open \"%s\"; skipping.\n",
467 					argv[0], argv[1], filename);
468 				exit_code = 1;
469 			} else {
470 				unhex_fp(infp, stdout);
471 				fclose(infp);
472 			}
473 		}
474 	}
475 
476 	return exit_code;
477 }
478 
479 // ----------------------------------------------------------------
unhex_fp(FILE * infp,FILE * outfp)480 static void unhex_fp(FILE *infp, FILE *outfp) {
481 	unsigned char byte;
482 	unsigned temp;
483 	int count;
484 	while ((count=fscanf(infp, "%x", &temp)) > 0) {
485 		byte = temp;
486 		fwrite (&byte, sizeof(byte), 1, outfp);
487 	}
488 }
489 
490 // ================================================================
netbsd_strptime_usage(char * argv0,char * argv1,FILE * o,int exit_code)491 static void netbsd_strptime_usage(char* argv0, char* argv1, FILE* o, int exit_code) {
492 	fprintf(o, "Usage: %s %s {string value} {format}\n", argv0, argv1);
493 	fprintf(o, "Standalone driver for replacement strptime for MSYS2.\n");
494 	fprintf(o, "Example string value: 2012-03-04T05:06:07Z\n");
495 	fprintf(o, "Example format: %%Y-%%m-%%dT%%H:%%M:%%SZ\n");
496 	exit(exit_code);
497 }
498 
499 //----------------------------------------------------------------------
500 #define MYBUFLEN 256
netbsd_strptime_main(int argc,char ** argv)501 static int netbsd_strptime_main(int argc, char **argv) {
502 	// 'mlr' and 'netbsd_strptime' are already argv[0] and argv[1].
503 	if (streq(argv[2], "-h") || streq(argv[2], "--help") || (argc != 4)) {
504 		netbsd_strptime_usage(argv[0], argv[1], stdout, 0);
505 	}
506 
507 	struct tm tm;
508 	char* strptime_input = argv[2];
509 	char* format = argv[3];
510 	memset(&tm, 0, sizeof(tm));
511 	char* strptime_output = netbsd_strptime(strptime_input, format, &tm);
512 	if (strptime_output == NULL) {
513 		printf("Could not strptime(\"%s\", \"%s\").\n", strptime_input, format);
514 	} else {
515 		printf("strptime: %s ->\n", strptime_input);
516 		printf("  tm_sec    = %d\n",  tm.tm_sec);
517 		printf("  tm_min    = %d\n",  tm.tm_min);
518 		printf("  tm_hour   = %d\n",  tm.tm_hour);
519 		printf("  tm_mday   = %d\n",  tm.tm_mday);
520 		printf("  tm_mon    = %d\n",  tm.tm_mon);
521 		printf("  tm_year   = %d\n",  tm.tm_year);
522 		printf("  tm_wday   = %d\n",  tm.tm_wday);
523 		printf("  tm_yday   = %d\n",  tm.tm_yday);
524 		printf("  tm_isdst  = %d\n",  tm.tm_isdst);
525 		printf("  remainder = \"%s\"\n", strptime_output);
526 	}
527 
528 	return 0;
529 }
530