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