This source file includes following definitions.
- xstrerror_l
- ts_add_ns
- monotonic_nssleep
- mach_clock_nanosleep
- openbsd_clock_nanosleep
- monotonic_sleep_until
- monotonic_nssleep
- dts_trim
- print_help
- parse_options
- init_writer
- write_word12
- getCardLine
- lookup_hcode
- convert_card_image
- write_card
- emit_card
- process_stream
- put_buf_func
- daemon_loop
- main
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33 #if !defined(_POSIX_C_SOURCE)
34 # define _POSIX_C_SOURCE 200809L
35 #endif
36
37 #if !defined(_GNU_SOURCE)
38 # define _GNU_SOURCE
39 #endif
40
41 #if !defined(_NETBSD_SOURCE)
42 # define _NETBSD_SOURCE
43 #endif
44
45 #if !defined(_OPENBSD_SOURCE)
46 # define _OPENBSD_SOURCE
47 #endif
48
49 #if !defined(CARDUTIL_VERSION_MAJOR)
50 # define CARDUTIL_VERSION_MAJOR (0)
51 #endif
52
53 #if !defined(CARDUTIL_VERSION_MINOR)
54 # define CARDUTIL_VERSION_MINOR (2)
55 #endif
56
57 #if !defined(CARDUTIL_VERSION_PATCH)
58 # define CARDUTIL_VERSION_PATCH (1)
59 #endif
60
61 #if defined(__GLIBC__)
62 # if !defined(_LARGEFILE64_SOURCE)
63 # define _LARGEFILE64_SOURCE 1
64 # endif
65 #endif
66
67 #include <stdio.h>
68 #include <stdlib.h>
69 #include <string.h>
70 #include <ctype.h>
71 #include <unistd.h>
72 #include <stdbool.h>
73 #include <sys/types.h>
74 #include <dirent.h>
75 #include <errno.h>
76 #include <sys/stat.h>
77 #include <fcntl.h>
78 #include <stddef.h>
79 #include <stdint.h>
80 #include <inttypes.h>
81 #include <time.h>
82 #include <libgen.h>
83 #include <limits.h>
84 #include <math.h>
85 #include <float.h>
86
87 #if !defined(NO_LOCALE)
88 # include <locale.h>
89 #endif
90
91 #if defined(__APPLE__) && defined(__MACH__)
92 # include <mach/mach_time.h>
93 # include <xlocale.h>
94 # if !defined(TIMER_ABSTIME)
95 # define TIMER_ABSTIME 1
96 # endif
97 #endif
98
99 #if defined(__MINGW32__) || defined(__MINGW64__) || defined(CROSS_MINGW32) || defined(CROSS_MINGW64)
100 # include <windows.h>
101 #endif
102
103 #if defined(_AIX) && !defined(USE_POPT)
104 # define USE_POPT
105 #endif
106 #if defined(USE_POPT)
107 # include <popt.h>
108 #else
109 # include <getopt.h>
110 #endif
111
112 #include "miniz_config.h"
113 #include "miniz.h"
114
115 #if defined(FREE)
116 # undef FREE
117 #endif
118 #define FREE(p) do \
119 { \
120 free ((p)); \
121 (p) = NULL; \
122 } \
123 while (0)
124
125 #define CARD_COL_COUNT (80)
126 #define DEFAULT_POLL_RATE (0.5)
127
128 typedef unsigned char uint8;
129 typedef unsigned short uint16;
130 typedef uint16 word12;
131
132 typedef struct
133 {
134 word12 column [CARD_COL_COUNT];
135 } card_image_t;
136
137 typedef struct
138 {
139 int fd;
140 unsigned char residue;
141 bool residue_present;
142 } writer_state_t;
143
144 enum state_t
145 {
146 StartDeck,
147 ReadCards,
148 NoMoreCards,
149 DoneReading
150 };
151
152 #if !defined(NO_LOCALE)
153 # define XSTR_EMAXLEN 32767
154
155 static const char *
156 xstrerror_l (int errnum)
157 {
158 int saved = errno;
159 const char * ret = NULL;
160 static char buf [XSTR_EMAXLEN];
161
162 # if defined(__APPLE__) || defined(_AIX) || defined(__MINGW32__) || \
163 defined(__MINGW64__) || defined(CROSS_MINGW32) || defined(CROSS_MINGW64)
164 # if defined(__MINGW32__) || defined(__MINGW64__) || defined(CROSS_MINGW32) || defined(CROSS_MINGW64)
165 if (0 == strerror_s (buf, sizeof (buf), errnum)) ret = buf;
166 # else
167 if (0 == strerror_r (errnum, buf, sizeof (buf))) ret = buf;
168 # endif
169 # else
170 # if defined(__NetBSD__)
171 locale_t loc = LC_GLOBAL_LOCALE;
172 # else
173 locale_t loc = uselocale ((locale_t)0);
174 # endif
175 locale_t copy = loc;
176 if (LC_GLOBAL_LOCALE == copy)
177 copy = duplocale (copy);
178
179 if ((locale_t)0 != copy)
180 {
181 ret = strerror_l (errnum, copy);
182 if (LC_GLOBAL_LOCALE == loc)
183 freelocale (copy);
184 }
185 # endif
186
187 if (! ret)
188 {
189 int n_buf = snprintf (buf, sizeof (buf), "Unknown error %d", errnum);
190 if (0 > n_buf || (size_t)n_buf >= sizeof (buf))
191 {
192 (void)fprintf (stderr, "FATAL: snprintf buffer overflow at %s[%s:%d]\n",
193 __func__, __FILE__, __LINE__);
194 exit (EXIT_FAILURE);
195 }
196
197 ret = buf;
198 }
199
200 errno = saved;
201 return ret;
202 }
203 #else
204 # define xstrerror_l strerror
205 #endif
206
207 #define MAX_POLL_RATE ((double)INT64_MAX / 1e9 - 1.0)
208
209 #if !defined(CARDUTIL_MAXPATH)
210 # if defined(PATH_MAX)
211 # define CARDUTIL_MAXPATH PATH_MAX
212 # elif defined(MAXPATHLEN)
213 # define CARDUTIL_MAXPATH MAXPATHLEN
214 # else
215 # define CARDUTIL_MAXPATH 1024
216 # endif
217 #endif
218
219 #if !defined(__MINGW32__) && !defined(__MINGW64__) && !defined(CROSS_MINGW32) && !defined(CROSS_MINGW64)
220 # if defined(CLOCK_UPTIME)
221 # define CARDUTIL_INTERVALCLOCK CLOCK_UPTIME
222 # elif defined(CLOCK_BOOTTIME) && !defined(__EMSCRIPTEN__)
223 # define CARDUTIL_INTERVALCLOCK CLOCK_BOOTTIME
224 # elif defined(CLOCK_HIGHRES)
225 # define CARDUTIL_INTERVALCLOCK CLOCK_HIGHRES
226 # elif defined(CLOCK_MONOTONIC)
227 # define CARDUTIL_INTERVALCLOCK CLOCK_MONOTONIC
228 # else
229 # define CARDUTIL_INTERVALCLOCK CLOCK_REALTIME
230 # endif
231
232 static struct timespec
233 ts_add_ns (struct timespec a, int64_t ns)
234 {
235 int64_t sec_delta = ns / 1000000000LL;
236 long nsec_delta = ns % 1000000000LL;
237 long temp_nsec = a . tv_nsec + nsec_delta;
238 int64_t carry = 0;
239 long final_nsec = temp_nsec;
240
241 if (1000000000L <= temp_nsec)
242 {
243 carry = temp_nsec / 1000000000L;
244 final_nsec = temp_nsec % 1000000000L;
245 }
246 else if (0 > temp_nsec)
247 {
248 carry = temp_nsec / 1000000000L;
249
250 if (0 != temp_nsec % 1000000000L)
251 carry -= 1;
252
253 final_nsec = temp_nsec - carry * 1000000000L;
254 }
255
256 int64_t total_sec_delta = sec_delta + carry;
257 int64_t rs = (int64_t)a . tv_sec;
258
259 if (0 < total_sec_delta && INT64_MAX - total_sec_delta < rs)
260 {
261 struct timespec ts;
262 ts . tv_sec = (time_t)INT64_MAX;
263 ts . tv_nsec = 999999999L;
264 return ts;
265 }
266 else if (0 > total_sec_delta && INT64_MIN - total_sec_delta > rs)
267 {
268 struct timespec ts;
269 ts . tv_sec = (time_t)INT64_MIN;
270 ts . tv_nsec = 0L;
271 return ts;
272 }
273 else
274 {
275 a . tv_sec = (time_t)(rs + total_sec_delta);
276 a . tv_nsec = final_nsec;
277 return a;
278 }
279 }
280 #endif
281
282 #if defined(__MINGW32__) || defined(__MINGW64__) || defined(CROSS_MINGW32) || defined(CROSS_MINGW64)
283 static int
284 monotonic_nssleep (uint64_t ns)
285 {
286 if ((uint64_t)INT64_MAX < ns)
287 return EINVAL;
288
289 DWORD ms = (DWORD)(ns / 1000000ULL);
290
291 if (0 < ms)
292 Sleep (ms);
293
294 return 0;
295 }
296 #else
297
298 # if defined(__APPLE__) && defined(__MACH__)
299 static int
300 mach_clock_nanosleep (clockid_t clock_id, int flags, const struct timespec * request, struct timespec * remain)
301 {
302 mach_timebase_info_data_t tbi;
303 mach_timebase_info (& tbi);
304
305 if (TIMER_ABSTIME == flags)
306 {
307 struct timespec now;
308
309 if (0 != clock_gettime (clock_id, & now))
310 return errno;
311
312 uint64_t now_ns = (uint64_t)now . tv_sec * 1000000000ULL + now . tv_nsec;
313 uint64_t target_ns = (uint64_t)request -> tv_sec * 1000000000ULL + request -> tv_nsec;
314
315 if (now_ns >= target_ns)
316 return 0;
317
318 uint64_t diff_ns = target_ns - now_ns;
319 uint64_t diff_abs = diff_ns * tbi . denom / tbi . numer;
320 uint64_t now_abs = mach_absolute_time ();
321
322 return mach_wait_until (now_abs + diff_abs) == KERN_SUCCESS ? 0 : errno;
323 }
324 else
325 {
326 uint64_t diff_ns = (uint64_t)request -> tv_sec * 1000000000ULL + request -> tv_nsec;
327 uint64_t diff_abs = diff_ns * tbi . denom / tbi . numer;
328 uint64_t now_abs = mach_absolute_time ();
329
330 return mach_wait_until (now_abs + diff_abs) == KERN_SUCCESS ? 0 : errno;
331 }
332 }
333 # define clock_nanosleep mach_clock_nanosleep
334 # endif
335
336 # if defined(__OpenBSD__)
337 static int
338 openbsd_clock_nanosleep (clockid_t clk_id, int flags, const struct timespec * req, struct timespec * rem)
339 {
340 if (flags & TIMER_ABSTIME)
341 {
342 struct timespec now;
343
344 if (0 != clock_gettime (clk_id, & now))
345 return errno;
346
347 struct timespec rel;
348 rel . tv_sec = req -> tv_sec - now . tv_sec;
349 rel . tv_nsec = req -> tv_nsec - now . tv_nsec;
350
351 if (0 > rel . tv_nsec)
352 {
353 rel . tv_nsec += 1000000000L;
354 rel . tv_sec -= 1;
355 }
356
357 if (0 > rel . tv_sec)
358 return 0;
359
360 return nanosleep (& rel, rem);
361 }
362 else
363 return nanosleep (req, rem);
364 }
365 # define clock_nanosleep openbsd_clock_nanosleep
366 # endif
367
368 static int
369 monotonic_sleep_until (struct timespec deadline)
370 {
371 for (;;)
372 {
373 int rc = clock_nanosleep (CARDUTIL_INTERVALCLOCK, TIMER_ABSTIME, & deadline, NULL);
374
375 if (0 == rc)
376 return 0;
377
378 if (EINTR == rc)
379 continue;
380
381 return rc;
382 }
383 }
384
385 static int
386 monotonic_nssleep (uint64_t ns)
387 {
388 if ((uint64_t)INT64_MAX < ns)
389 return EINVAL;
390
391 int64_t signed_ns = (int64_t)ns;
392 struct timespec now;
393
394 if (0 != clock_gettime (CARDUTIL_INTERVALCLOCK, & now))
395 return errno;
396
397 struct timespec deadline = ts_add_ns (now, signed_ns);
398
399 return monotonic_sleep_until (deadline);
400 }
401 #endif
402
403 #define MAX_DBL_FMTSTR (DBL_MAX_10_EXP + 19)
404 #define MAX_DBL_FMTGRW 32
405
406 static char *
407 dts_trim (double value)
408 {
409 size_t bufsize = MAX_DBL_FMTSTR;
410 char *buffer = NULL;
411
412 for (;;)
413 {
414 buffer = malloc (bufsize);
415 if (! buffer)
416 {
417 (void)fprintf (stderr, "FATAL: Out of memory, aborting at %s[%s:%d]\n",
418 __func__, __FILE__, __LINE__);
419 abort ();
420 }
421
422 int n = snprintf (buffer, bufsize, "%.15f", value);
423 if (0 > n)
424 {
425 (void)fprintf (stderr, "FATAL: snprintf encoding failure at %s[%s:%d]\n",
426 __func__, __FILE__, __LINE__);
427 FREE (buffer);
428 exit (EXIT_FAILURE);
429 }
430
431 if ((size_t)n < bufsize)
432 break;
433
434 FREE (buffer);
435 if (bufsize > SIZE_MAX - MAX_DBL_FMTGRW)
436 {
437 (void)fprintf (stderr, "FATAL: buffer size overflow at %s[%s:%d]\n",
438 __func__, __FILE__, __LINE__);
439 exit (EXIT_FAILURE);
440 }
441
442 bufsize += MAX_DBL_FMTGRW;
443 }
444
445 #if defined(NO_LOCALE)
446 const char * dp = ".";
447 #else
448 struct lconv * lc = localeconv ();
449 const char * dp = (lc && lc -> decimal_point && * lc -> decimal_point) ? lc -> decimal_point : ".";
450 #endif
451
452 char * dot = strstr (buffer, dp);
453 if (dot)
454 {
455 char * end = buffer + strlen (buffer) - 1;
456
457 while (dot < end && '0' == * end)
458 * end-- = '\0';
459
460 size_t dp_len = strlen (dp);
461 size_t buf_len = strlen (buffer);
462
463 if (buf_len >= dp_len && 0 == strcmp (buffer + buf_len - dp_len, dp))
464 * (buffer + buf_len - dp_len) = '\0';
465 }
466
467 char * out = strdup (buffer);
468 FREE (buffer);
469
470 return out;
471 }
472
473 static void
474 print_help (char * program, double defpoll)
475 {
476 char * default_poll = dts_trim (defpoll);
477
478 if (NULL == default_poll)
479 {
480 (void)fprintf (stderr, "FATAL: Out of memory, aborting at %s[%s:%d]\n",
481 __func__, __FILE__, __LINE__);
482 abort ();
483 }
484
485 (void)fprintf (stderr, "\nMultics Card Utility Program");
486 (void)fprintf (stderr, "\n");
487 (void)fprintf (stderr, "\nInvoke as:\n");
488 (void)fprintf (stderr, " %s -m <infile >outfile\n", program);
489 (void)fprintf (stderr, " %s -g <infile >outfile\n", program);
490 (void)fprintf (stderr, " %s -d [ options ] -i indir -o outdir [ -a arcdir [ -z ] ]\n", program);
491 (void)fprintf (stderr, "\n");
492 (void)fprintf (stderr, "Where options are:\n");
493 (void)fprintf (stderr, " -h, --help = Output this help message\n");
494 (void)fprintf (stderr, " -v, --version = Output version information\n");
495 (void)fprintf (stderr, "\n");
496 (void)fprintf (stderr, "Data conversion options:\n");
497 (void)fprintf (stderr, " -u, --upper-jcl = Enable conversion of input JCL to upper-case\n");
498 (void)fprintf (stderr, " [If GBCD mode is enabled, ONLY upper-cases JCL]\n");
499 (void)fprintf (stderr, " -U, --upper-all = Enable conversion of all input to upper-case\n");
500 (void)fprintf (stderr, " [If GBCD mode is enabled, DISABLES upper-casing]\n");
501 (void)fprintf (stderr, "\n");
502 (void)fprintf (stderr, "Output mode options:\n");
503 (void)fprintf (stderr, " -m, --mcc = Write the cards using MCC Punch Codes\n");
504 (void)fprintf (stderr, " -g, --gcos = Write the cards using GCOS BCD Punch Codes\n");
505 (void)fprintf (stderr, " [In GBCD mode, input is upper-cased by default]\n");
506 (void)fprintf (stderr, "\n");
507 (void)fprintf (stderr, "Daemon mode options:\n");
508 (void)fprintf (stderr, " -d, --daemon = Run in daemon mode\n");
509 (void)fprintf (stderr, " -p, --poll <rate> = Polling rate in seconds (default: %s)\n", default_poll);
510 (void)fprintf (stderr, " -i, --input <indir> = Input directory\n");
511 (void)fprintf (stderr, " -o, --output <outdir> = Output directory\n");
512 (void)fprintf (stderr, " -a, --archive <arcdir> = Archive directory\n");
513 (void)fprintf (stderr, " -z, --compress = Compress archived files\n");
514 (void)fprintf (stderr, "\n");
515 (void)fprintf (stderr, "Standard operation:\n");
516 (void)fprintf (stderr, " This program will convert standard input to Hollerith-coded card format\n");
517 (void)fprintf (stderr, " on standard output for use with the DPS8M Simulator punch card reader.\n");
518 (void)fprintf (stderr, "\n");
519 (void)fprintf (stderr, "Daemon mode operation:\n");
520 (void)fprintf (stderr, " When daemon mode ('--daemon', '-d') is specified, this program will convert\n");
521 (void)fprintf (stderr, " any files in the input directory (defined by '--input' or '-i') that end in\n");
522 (void)fprintf (stderr, " \".input\" to Hollerith-coded card format files in the output directory\n");
523 (void)fprintf (stderr, " (defined by '--output' or '-o'). Input files are removed after conversion\n");
524 (void)fprintf (stderr, " unless the '--archive' (or '-a') option is specified. Archived input files\n");
525 (void)fprintf (stderr, " may be optionally compressed by specifying the '--compress' ('-z') option.\n");
526 (void)fprintf (stderr, "\n");
527 (void)fprintf (stderr, "If no output mode is explicitly selected for daemon mode, then MCC is assumed.\n");
528 (void)fprintf (stderr, "Note that only one output mode may be selected per execution. If more than\n");
529 (void)fprintf (stderr, "one output mode option is specified, then the last option is effective.\n");
530 (void)fprintf (stderr, "\n");
531 (void)fprintf (stderr, "This software is made available under the terms of the MIT License.\n");
532
533 FREE (default_poll);
534 }
535
536 static char cardImage [CARD_COL_COUNT + 1] = "";
537 static int total_card_count = 0;
538 static int card_count = 0;
539 static card_image_t card;
540
541 #if defined(USE_POPT)
542 static struct poptOption long_options [] = {
543 #else
544 static struct option long_options [] = {
545 #endif
546 #if defined(USE_POPT)
547 { "gcos", 'g', POPT_ARG_NONE, NULL, 'g', "gcos", "GCOS" },
548 { "mcc", 'm', POPT_ARG_NONE, NULL, 'm', "mcc", "MCC" },
549 { "upper-jcl",'u', POPT_ARG_NONE, NULL, 'u', "upper-jcl", "UPPER-JCL" },
550 { "upper-all",'U', POPT_ARG_NONE, NULL, 'U', "upper-all", "UPPER-ALL" },
551 { "daemon", 'd', POPT_ARG_NONE, NULL, 'd', "daemon", "DAEMON" },
552 { "poll", 'p', POPT_ARG_STRING, NULL, 'p', "poll", "POLL" },
553 { "input", 'i', POPT_ARG_STRING, NULL, 'i', "input", "INPUT" },
554 { "output", 'o', POPT_ARG_STRING, NULL, 'o', "output", "OUTPUT" },
555 { "archive", 'a', POPT_ARG_STRING, NULL, 'a', "archive", "ARCHIVE" },
556 { "compress", 'z', POPT_ARG_NONE, NULL, 'z', "compress", "COMPRESS" },
557 { "version", 'v', POPT_ARG_NONE, NULL, 'v', "version", "VERSION" },
558 { "help", 'h', POPT_ARG_NONE, NULL, 'h', "help", "HELP" }
559 #else
560 { "gcos", no_argument, 0, 'g' },
561 { "mcc", no_argument, 0, 'm' },
562 { "upper-jcl",no_argument, 0, 'u' },
563 { "upper-all",no_argument, 0, 'U' },
564 { "daemon", no_argument, 0, 'd' },
565 { "poll", required_argument, 0, 'p' },
566 { "input", required_argument, 0, 'i' },
567 { "output", required_argument, 0, 'o' },
568 { "archive", required_argument, 0, 'a' },
569 { "compress", no_argument, 0, 'z' },
570 { "version", no_argument, 0, 'v' },
571 { "help", no_argument, 0, 'h' },
572 { 0, 0, 0, 0 }
573 #endif
574 };
575
576
577 static bool use_ascii_encoding = true;
578 static bool use_gcos_encoding = false;
579 static bool compress_archive = false;
580 static bool crlf_warned = false;
581 static bool daemon_mode = false;
582 static bool upper_jcl = false;
583 static bool upper_all = false;
584 static char * archive_dir = NULL;
585 static char * input_dir = NULL;
586 static char * output_dir = NULL;
587 static double poll_rate = DEFAULT_POLL_RATE;
588
589 static void
590 parse_options (int argc, char * argv [])
591 {
592 int c;
593 char * endptr;
594 char * poll_arg;
595 #if !defined(USE_POPT)
596 bool done = false;
597 #endif
598
599 #if defined(USE_POPT)
600 poptContext opt_con;
601 opt_con = poptGetContext (NULL, argc, (const char **)argv, long_options, 0);
602 while (0 <= (c = poptGetNextOpt (opt_con)))
603 {
604 optind += 1;
605 #else
606 while (! done)
607 {
608 int option_index = 0;
609 c = getopt_long (argc, argv, "gmvuUdi:o:a:p:zh?", long_options, & option_index);
610 #endif
611
612 switch (c)
613 {
614 #if !defined(USE_POPT)
615 case -1:
616 done = true;
617 break;
618 #endif
619
620 case 'm':
621 #if defined(TESTING)
622 (void)fprintf (stderr, "*** Switched to ASCII (MCC) mode\n");
623 #endif
624 use_ascii_encoding = true;
625 use_gcos_encoding = false;
626 break;
627
628 case 'g':
629 #if defined(TESTING)
630 (void)fprintf (stderr, "*** Switched to GCOS (GBCD) mode\n");
631 #endif
632 use_ascii_encoding = false;
633 use_gcos_encoding = true;
634 break;
635
636 case 'u':
637 #if defined(TESTING)
638 (void)fprintf (stderr, "*** Uppercase JCL enabled\n");
639 #endif
640 upper_jcl = true;
641 break;
642
643 case 'U':
644 #if defined(TESTING)
645 (void)fprintf (stderr, "*** Uppercase all enabled\n");
646 #endif
647 upper_all = true;
648 break;
649
650 case 'd':
651 #if defined(TESTING)
652 (void)fprintf (stderr, "*** Daemon mode enabled\n");
653 #endif
654 daemon_mode = true;
655 break;
656
657 case 'i':
658 #if defined(TESTING)
659 (void)fprintf (stderr, "*** Set input directory\n");
660 #endif
661 #if defined(USE_POPT)
662 input_dir = poptGetOptArg (opt_con);
663 #else
664 input_dir = optarg;
665 #endif
666 break;
667
668 case 'o':
669 #if defined(TESTING)
670 (void)fprintf (stderr, "*** Set output directory\n");
671 #endif
672 #if defined(USE_POPT)
673 output_dir = poptGetOptArg (opt_con);
674 #else
675 output_dir = optarg;
676 #endif
677 break;
678
679 case 'a':
680 #if defined(TESTING)
681 (void)fprintf (stderr, "*** Set archive directory\n");
682 #endif
683 #if defined(USE_POPT)
684 archive_dir = poptGetOptArg (opt_con);
685 #else
686 archive_dir = optarg;
687 #endif
688 break;
689
690 case 'z':
691 compress_archive = true;
692 break;
693
694 case 'p':
695 {
696 poll_arg =
697 #if defined(USE_POPT)
698 poptGetOptArg (opt_con);
699 #else
700 optarg;
701 #endif
702 if (! poll_arg || '\0' == * poll_arg)
703 {
704 (void)fprintf (stderr, "ERROR: Empty polling rate specified.\n");
705 exit (EXIT_FAILURE);
706 }
707
708 errno = 0;
709 double parsed_rate = strtod (poll_arg, & endptr);
710
711 if (ERANGE == errno)
712 {
713 (void)fprintf (stderr, "ERROR: Polling rate out of range.\n");
714 exit (EXIT_FAILURE);
715 }
716
717 if ('\0' != * endptr)
718 {
719 (void)fprintf (stderr, "ERROR: Invalid polling rate: '%s'\n", poll_arg);
720 exit (EXIT_FAILURE);
721 }
722
723 if (0.01 > parsed_rate || MAX_POLL_RATE <= parsed_rate)
724 {
725 (void)fprintf (stderr, "ERROR: Polling rate must be between 0.01 and %.0f seconds.\n",
726 MAX_POLL_RATE - 1.0F);
727 exit (EXIT_FAILURE);
728 }
729
730 poll_rate = parsed_rate;
731 #if defined(TESTING)
732 (void)fprintf (stderr, "*** Set polling rate: %f\n", poll_rate);
733 #endif
734 }
735 break;
736
737 case 'v':
738 #if defined(TESTING)
739 (void)fprintf (stderr, "*** Display version\n");
740 #endif
741 (void)fprintf (stderr, "Version %d.%d.%d\n", CARDUTIL_VERSION_MAJOR, CARDUTIL_VERSION_MINOR, CARDUTIL_VERSION_PATCH);
742 exit (EXIT_SUCCESS);
743
744 case 'h':
745 case '?':
746 #if defined(TESTING)
747 (void)fprintf (stderr, "*** Display help\n");
748 #endif
749 print_help (argv [0], DEFAULT_POLL_RATE);
750 exit (EXIT_SUCCESS);
751
752 default:
753 (void)fprintf (stderr, "*** Internal Error: did not recognize option '%d'\n", c);
754 abort ();
755 }
756 }
757 }
758
759 static void
760 init_writer (writer_state_t * writer_state, int fd)
761 {
762 writer_state -> fd = fd;
763 writer_state -> residue = 0;
764 writer_state -> residue_present = false;
765 }
766
767 static int
768 write_word12 (writer_state_t * writer_state, word12 code)
769 {
770 int rc = 0;
771
772 if (! writer_state -> residue_present)
773 {
774 unsigned char first_nibble = (code & 0xf00) >> 8;
775 unsigned char second_nibble = (code & 0x0f0) >> 4;
776 unsigned char third_nibble = (code & 0x00F);
777 unsigned char to_write = (first_nibble << 4) | second_nibble;
778 writer_state -> residue = third_nibble;
779 writer_state -> residue_present = true;
780
781 rc = write (writer_state -> fd, & to_write, 1);
782 }
783 else
784 {
785 unsigned char first_nibble = writer_state -> residue;
786 writer_state -> residue_present = false;
787 unsigned char second_nibble = (code & 0xf00) >> 8;
788 unsigned int to_write = (first_nibble << 4) | second_nibble;
789
790 rc = write (writer_state -> fd, (unsigned char *) & to_write, 1);
791 if (0 < rc)
792 {
793 to_write = (code & 0x0ff);
794 rc = write (writer_state -> fd, & to_write, 1);
795 }
796 }
797
798 return rc;
799 }
800
801 static ssize_t
802 getCardLine (int fd, char * buffer, size_t cap, int line_num)
803 {
804 size_t n = 0;
805 for (;;)
806 {
807 unsigned char ch;
808 ssize_t rc = read (fd, & ch, 1);
809
810 if (0 > rc)
811 return rc;
812 else if (0 == rc)
813 return -1;
814 else
815 {
816 if ('\n' == ch)
817 {
818 if (0 < n && '\r' == buffer [n - 1])
819 {
820 if (! crlf_warned)
821 {
822 (void)fprintf (stderr, "WARNING: <CRLF> line ending detected, converting to <LF>\n");
823 crlf_warned = true;
824 }
825
826 n--;
827 }
828
829 buffer [n] = '\0';
830 return (ssize_t)n;
831 }
832
833 if (n < cap - 1)
834 buffer [n++] = ch;
835 else
836 {
837 (void)fprintf (stderr, "WARNING: Line %d exceeds %d characters, input truncated\n",
838 line_num, CARD_COL_COUNT);
839 buffer [cap - 1] = '\0';
840
841 do
842 rc = read (fd, & ch, 1);
843 while (1 == rc && '\n' != ch);
844
845 return (ssize_t)n;
846 }
847 }
848 }
849 }
850
851 static uint16 ascii_table [128] =
852 {
853 05403, 04401, 04201, 04101, 00005, 01023, 01013, 01007,
854 02011, 04021, 02021, 04103, 04043, 04023, 04013, 04007,
855 06403, 02401, 02201, 02101, 00043, 00023, 00201, 01011,
856 02003, 02403, 00007, 01005, 02043, 02023, 02013, 02007,
857 00000, 02202, 00006, 00102, 02102, 01042, 04000, 00022,
858 04022, 02022, 02042, 04012, 01102, 02000, 04102, 01400,
859 01000, 00400, 00200, 00100, 00040, 00020, 00010, 00004,
860 00002, 00001, 00202, 02012, 04042, 00012, 01012, 01006,
861 00042, 04400, 04200, 04100, 04040, 04020, 04010, 04004,
862 04002, 04001, 02400, 02200, 02100, 02040, 02020, 02010,
863 02004, 02002, 02001, 01200, 01100, 01040, 01020, 01010,
864 01004, 01002, 01001, 05022, 04202, 06022, 02006, 01022,
865 00402, 05400, 05200, 05100, 05040, 05020, 05010, 05004,
866 05002, 05001, 06400, 06200, 06100, 06040, 06020, 06010,
867 06004, 06002, 06001, 03200, 03100, 03040, 03020, 03010,
868 03004, 03002, 03001, 05000, 04006, 03000, 03400, 00000
869 };
870
871 static uint16 gcos_table [128] =
872 {
873 05403, 04401, 04201, 04101, 00005, 01023, 01013, 01007,
874 02011, 04021, 02021, 04103, 04043, 04023, 04013, 04007,
875 06403, 02401, 02201, 02101, 00043, 00023, 00201, 01011,
876 02003, 02403, 00007, 01005, 02043, 02023, 02013, 02007,
877 00000, 01006, 01012, 00102, 02102, 01042, 04000, 02006,
878 04022, 02022, 02042, 05000, 01102, 02000, 04102, 01400,
879 01000, 00400, 00200, 00100, 00040, 00020, 00010, 00004,
880 00002, 00001, 00022, 02012, 04012, 01022, 00012, 00006,
881 00042, 04400, 04200, 04100, 04040, 04020, 04010, 04004,
882 04002, 04001, 02400, 02200, 02100, 02040, 02020, 02010,
883 02004, 02002, 02001, 01200, 01100, 01040, 01020, 01010,
884 01004, 01002, 01001, 00202, 04006, 04042, 03000, 01202,
885 00402, 05400, 05200, 05100, 05040, 05020, 05010, 05004,
886 05002, 05001, 06400, 06200, 06100, 06040, 06020, 06010,
887 06004, 06002, 06001, 03200, 03100, 03040, 03020, 03010,
888 03004, 03002, 03001, 05000, 04006, 03000, 03400, 00000
889 };
890
891 static int
892 lookup_hcode (unsigned char c, word12 * h)
893 {
894 if (128 <= c)
895 return -1;
896
897 if (use_ascii_encoding)
898 * h = ascii_table [c];
899 else
900 * h = gcos_table [c];
901
902 return 0;
903 }
904
905 static void
906 convert_card_image (const char * s, size_t len, card_image_t * card)
907 {
908 size_t l = len < CARD_COL_COUNT ? len : CARD_COL_COUNT;
909
910 for (size_t i = 0; i < l; i++)
911 {
912 word12 hcode = 0;
913 if (0 != lookup_hcode (s [i], & hcode))
914 {
915 if (daemon_mode)
916 (void)fprintf (stderr, "WARNING: ");
917 else
918 (void)fprintf (stderr, "ERROR: ");
919
920 (void)fprintf (stderr, "Could not translate character 0x%x at column %" PRIuMAX "\n",
921 (unsigned)(unsigned char)s [i], (uintmax_t)i + 1);
922
923 if (! daemon_mode)
924 exit (EXIT_FAILURE);
925 }
926
927 card -> column [i] = hcode;
928 }
929
930 for (size_t i = l; i < CARD_COL_COUNT; i++)
931 card -> column [i] = 0;
932 }
933
934 static int
935 write_card (writer_state_t * writer_state, card_image_t * card)
936 {
937 for (size_t i = 0; i < CARD_COL_COUNT; i++)
938 {
939 int rc = write_word12 (writer_state, card -> column [i]);
940 if (0 >= rc)
941 {
942 (void)fprintf (stderr, "WARNING: Error writing Hollerith code at %s[%s:%d]\n",
943 __func__, __FILE__, __LINE__);
944 return rc;
945 }
946 }
947
948 return 0;
949 }
950
951 static int
952 emit_card (char * contents, char * cardImage, size_t cardImageLen, writer_state_t * writer_state)
953 {
954 total_card_count += 1;
955 (void)fprintf (stderr, "Adding %s card\n", contents);
956
957 (void)strncpy (cardImage, contents, cardImageLen);
958 cardImage [sizeof (cardImage) - 1] = '\0';
959
960 convert_card_image (cardImage, (size_t)strlen (cardImage), & card);
961
962 if (write_card (writer_state, & card))
963 {
964 (void)fprintf (stderr, "ERROR: write_card failure at %s[%s:%d]\n",
965 __func__, __FILE__, __LINE__);
966 return 1;
967 }
968
969 return 0;
970 }
971
972 static int
973 process_stream (int ifd, int ofd)
974 {
975 writer_state_t writer_state;
976 init_writer (& writer_state, ofd);
977 ssize_t len;
978 bool read_one = false;
979 int line_num = 0;
980 enum state_t state = StartDeck;
981
982 while (state != DoneReading)
983 {
984 switch (state)
985 {
986 case StartDeck:
987 if (use_ascii_encoding)
988 {
989 int rc = emit_card ("++EOF", cardImage, sizeof (cardImage) - 1, & writer_state);
990 if (rc)
991 return -1;
992
993 rc = emit_card ("++UID 1", cardImage, sizeof (cardImage) - 1, & writer_state);
994 if (rc)
995 return -1;
996 }
997
998 state = ReadCards;
999 break;
1000
1001 case ReadCards:
1002 line_num++;
1003 len = getCardLine (ifd, cardImage, sizeof (cardImage), line_num);
1004 if (0 <= len)
1005 {
1006 if (! read_one)
1007 read_one = true;
1008
1009 if (upper_all)
1010 for (char * p = cardImage; * p; ++p)
1011 * p = toupper (* p);
1012 else if (upper_jcl && cardImage [0] == '$')
1013 for (char * p = cardImage; * p; ++p)
1014 * p = toupper (* p);
1015
1016 card_count += 1;
1017 total_card_count += 1;
1018 (void)fprintf (stderr, "Converting card %d\n", card_count);
1019 convert_card_image (cardImage, (size_t)len, & card);
1020
1021 if (write_card (& writer_state, & card))
1022 {
1023 (void)fprintf (stderr, "write_card failure, exiting at %s[%s:%d]\n",
1024 __func__, __FILE__, __LINE__);
1025 return -1;
1026 }
1027 }
1028 else
1029 {
1030 if (read_one)
1031 state = NoMoreCards;
1032 else
1033 {
1034 if (! daemon_mode)
1035 {
1036 (void)fprintf (stderr, "ERROR: could not read from stdin\n");
1037 exit (EXIT_FAILURE);
1038 }
1039 else
1040 (void)fprintf (stderr, "WARNING: could not read file data\n");
1041 }
1042 }
1043 break;
1044
1045 case NoMoreCards:
1046 if (use_gcos_encoding)
1047 {
1048 int rc = emit_card ("***EOF", cardImage, sizeof (cardImage) - 1, & writer_state);
1049 if (rc)
1050 return -1;
1051
1052 state = DoneReading;
1053 }
1054 else
1055 {
1056 int rc = emit_card ("++EOF", cardImage, sizeof (cardImage) - 1, & writer_state);
1057 if (rc)
1058 return -1;
1059
1060 rc = emit_card ("++UID 1", cardImage, sizeof (cardImage) - 1, & writer_state);
1061 if (rc)
1062 return -1;
1063
1064 state = DoneReading;
1065 }
1066 (void)fprintf (stderr, "Emitted %d total cards\n", total_card_count);
1067 card_count = total_card_count = 0;
1068 break;
1069
1070 case DoneReading:
1071
1072 break;
1073 }
1074 }
1075
1076 return 0;
1077 }
1078
1079 static mz_bool
1080 put_buf_func (const void * pBuf, int len, void * pUser)
1081 {
1082 return (fwrite (pBuf, 1, len, (FILE *)pUser) == (size_t)len);
1083 }
1084
1085 static void
1086 daemon_loop (const char * in_dir, const char * out_dir, const char * archive_dir, bool compress_archive)
1087 {
1088 const char * safe_chars = "cdhkmnprswxyzCDFGJKMNPRSTXY57";
1089 int safe_chars_len = strlen (safe_chars);
1090
1091 srand (time (NULL));
1092 while (1)
1093 {
1094 DIR * d;
1095 struct dirent * dir;
1096 d = opendir (in_dir);
1097 time_t rawtime;
1098 struct tm * timelocal;
1099 char timebuffer [128];
1100
1101 if (d)
1102 {
1103 while (NULL != (dir = readdir (d)))
1104 {
1105 const char * ext = strrchr (dir -> d_name, '.');
1106
1107 if (ext && 0 == strcmp (ext, ".input"))
1108 {
1109 (void)time (& rawtime);
1110 timelocal = localtime (& rawtime);
1111
1112 if (strftime (timebuffer, sizeof (timebuffer), "%A, %B %d %Y, %H:%M:%S %Z",
1113 timelocal))
1114 (void)fprintf (stderr, "\nConversion job started %s\n", timebuffer);
1115
1116 (void)fprintf (stderr, "Converting input file '%s'\n", dir -> d_name);
1117
1118 char in_path [CARDUTIL_MAXPATH];
1119 int n_in_path = snprintf (in_path, sizeof (in_path), "%s/%s", in_dir, dir -> d_name);
1120 if (0 > n_in_path || (size_t)n_in_path >= sizeof (in_path))
1121 {
1122 (void)fprintf (stderr, "FATAL: snprintf input path overflow at %s[%s:%d]\n",
1123 __func__, __FILE__, __LINE__);
1124 exit (EXIT_FAILURE);
1125 }
1126
1127 int in_fd = open (in_path, O_RDONLY);
1128 if (0 > in_fd)
1129 {
1130 (void)fprintf (stderr, "open input failure: '%s' (Error %d)\n",
1131 xstrerror_l (errno), errno);
1132 continue;
1133 }
1134
1135 time_t t = time (NULL);
1136 if ((time_t)-1 == t)
1137 {
1138 (void)fprintf (stderr, "failed to read system time; using fallback\n");
1139 t = 0;
1140 }
1141 else if (0 > t)
1142 {
1143 (void)fprintf (stderr, "absurd system time returned; using fallback\n");
1144 t = 0;
1145 }
1146
1147 char out_name [256];
1148 int n_out_name = snprintf (out_name, sizeof (out_name), "%ld%c%c%c%c", (long)t,
1149 safe_chars[rand () % safe_chars_len],
1150 safe_chars[rand () % safe_chars_len],
1151 safe_chars[rand () % safe_chars_len],
1152 safe_chars[rand () % safe_chars_len]);
1153 if (0 > n_out_name || (size_t)n_out_name >= sizeof (out_name))
1154 {
1155 (void)fprintf (stderr, "FATAL: snprintf out_name overflow at %s[%s:%d]\n",
1156 __func__, __FILE__, __LINE__);
1157 exit (EXIT_FAILURE);
1158 }
1159
1160 char open_path [CARDUTIL_MAXPATH];
1161 int n_open_path = snprintf (open_path, sizeof (open_path), "%s/OPEN.%s", out_dir, out_name);
1162 if (0 > n_open_path || (size_t)n_open_path >= sizeof (open_path))
1163 {
1164 (void)fprintf (stderr, "FATAL: snprintf open_path overflow at %s[%s:%d]\n",
1165 __func__, __FILE__, __LINE__);
1166 exit (EXIT_FAILURE);
1167 }
1168
1169 int out_fd = open (open_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
1170 if (0 > out_fd)
1171 {
1172 (void)fprintf (stderr, "open output failure: '%s' (Error %d)\n",
1173 xstrerror_l (errno), errno);
1174 (void)close (in_fd);
1175 continue;
1176 }
1177
1178 struct stat st_buf;
1179 if (0 > fstat (in_fd, & st_buf))
1180 {
1181 (void)fprintf (stderr, "fstat failure: '%s' (Error %d)\n",
1182 xstrerror_l (errno), errno);
1183 (void)close (in_fd);
1184 (void)close (out_fd);
1185 continue;
1186 }
1187
1188 if (0 == st_buf . st_size)
1189 {
1190 (void)fprintf (stderr, "WARNING: Input file '%s' is empty, removing\n", in_path);
1191 (void)close (in_fd);
1192 (void)close (out_fd);
1193 (void)unlink (open_path);
1194 if (0 != unlink (in_path))
1195 (void)fprintf (stderr, "unlink failure: '%s' (Error %d)\n",
1196 xstrerror_l (errno), errno);
1197 else
1198 (void)fprintf (stderr, "Processed and removed '%s'\n", in_path);
1199 continue;
1200 }
1201
1202 crlf_warned = false;
1203
1204 if (0 != process_stream (in_fd, out_fd))
1205 {
1206 (void)fprintf (stderr, "ERROR: process_stream failed for '%s'\n", in_path);
1207 (void)close (in_fd);
1208 (void)close (out_fd);
1209 (void)unlink (open_path);
1210 continue;
1211 }
1212
1213 (void)close (in_fd);
1214 (void)close (out_fd);
1215
1216 char final_path [CARDUTIL_MAXPATH];
1217 struct stat st;
1218
1219 for (;;)
1220 {
1221 int n_final_path = snprintf (final_path, sizeof (final_path), "%s/bdeck.%s",
1222 out_dir, out_name);
1223 if (0 > n_final_path || (size_t)n_final_path >= sizeof (final_path))
1224 {
1225 (void)fprintf (stderr, "FATAL: snprintf final_path overflow at %s[%s:%d]\n",
1226 __func__, __FILE__, __LINE__);
1227 exit (EXIT_FAILURE);
1228 }
1229 if (0 > stat (final_path, & st))
1230 {
1231 if (ENOENT == errno)
1232 break;
1233 }
1234
1235 size_t out_name_len = strlen (out_name);
1236 if (out_name_len + 1 < sizeof (out_name))
1237 {
1238 out_name [out_name_len] = safe_chars [rand () % safe_chars_len];
1239 out_name [out_name_len + 1] = '\0';
1240 }
1241 }
1242
1243 if (0 != rename (open_path, final_path))
1244 {
1245 (void)fprintf (stderr, "rename failure: '%s' (Error %d)\n",
1246 xstrerror_l (errno), errno);
1247 (void)unlink (open_path);
1248 }
1249 else
1250 (void)fprintf (stderr, "Wrote output file '%s'\n", final_path);
1251
1252 if (archive_dir)
1253 {
1254 struct stat st;
1255 if (0 != stat (archive_dir, & st) || ! S_ISDIR (st . st_mode))
1256 {
1257 (void)fprintf (stderr, "ERROR: Archive directory '%s': '%s' (Error %d)\n",
1258 archive_dir, xstrerror_l (errno), errno);
1259 if (0 != unlink (in_path))
1260 (void)fprintf (stderr, "unlink failure: '%s' (Error %d)\n",
1261 xstrerror_l (errno), errno);
1262 else
1263 (void)fprintf (stderr, "Processed and removed '%s'\n", in_path);
1264 }
1265 else
1266 {
1267 char archive_path [CARDUTIL_MAXPATH];
1268
1269 for (;;)
1270 {
1271 long int archive_time = time (NULL);
1272 char rand_chars [5];
1273
1274 for (int i = 0; 4 > i; i++)
1275 rand_chars [i] = safe_chars [rand () % safe_chars_len];
1276
1277 rand_chars [4] = '\0';
1278
1279 char * in_path_dup = strdup (in_path);
1280 char * bname = basename (in_path_dup);
1281 int n_archive_path = snprintf (archive_path, sizeof (archive_path), "%s/%ld%s.%s",
1282 archive_dir, archive_time, rand_chars, bname);
1283 if (0 > n_archive_path || (size_t)n_archive_path >= sizeof (archive_path))
1284 {
1285 (void)fprintf (stderr, "FATAL: snprintf archive_path overflow at %s[%s:%d]\n",
1286 __func__, __FILE__, __LINE__);
1287 exit (EXIT_FAILURE);
1288 }
1289 FREE (in_path_dup);
1290
1291 if (0 > stat (archive_path, & st))
1292 if (ENOENT == errno)
1293 break;
1294 }
1295
1296 if (0 != rename (in_path, archive_path))
1297 (void)fprintf (stderr, "rename failure: %s (Error %d)\n",
1298 xstrerror_l (errno), errno);
1299 else
1300 {
1301 (void)fprintf (stderr, "Archived '%s' to '%s'\n",
1302 in_path, archive_path);
1303 if (compress_archive)
1304 {
1305 char gz_path [CARDUTIL_MAXPATH];
1306 size_t maxpathlen = CARDUTIL_MAXPATH - 4;
1307
1308 if (strlen (archive_path) > maxpathlen)
1309 {
1310 (void)fprintf (stderr, "path too long (>%" PRIuMAX ") to append '.gz' extension!\n",
1311 (uintmax_t)maxpathlen);
1312 continue;
1313 }
1314
1315 int mpl_limit = (maxpathlen > (size_t)INT_MAX) ? INT_MAX : (int)maxpathlen;
1316 int n_gz_path = snprintf (gz_path, sizeof (gz_path), "%.*s.gz", mpl_limit, archive_path);
1317 if (0 > n_gz_path || (size_t)n_gz_path >= sizeof (gz_path))
1318 {
1319 (void)fprintf (stderr, "FATAL: snprintf gz_path overflow at %s[%s:%d]\n",
1320 __func__, __FILE__, __LINE__);
1321 exit (EXIT_FAILURE);
1322 }
1323
1324 FILE * f_in = fopen (archive_path, "rb");
1325 if (NULL == f_in)
1326 {
1327 (void)fprintf (stderr, "fopen failure: %s (Error %d)\n",
1328 xstrerror_l (errno), errno);
1329 continue;
1330 }
1331
1332
1333 (void)fseek (f_in, 0, SEEK_END);
1334 long f_size = ftell (f_in);
1335 (void)fseek (f_in, 0, SEEK_SET);
1336
1337
1338 if (0 > f_size)
1339 {
1340 (void)fprintf (stderr, "WARNING: ftell failed: %s (Error %d)\n",
1341 xstrerror_l (errno), errno);
1342 (void)fclose (f_in);
1343 continue;
1344 }
1345
1346 if (0 == f_size)
1347 {
1348 (void)fclose (f_in);
1349 continue;
1350 }
1351
1352 #if defined(__GNUC__) && (defined(__MINGW32__) || defined(__MINGW64__) || defined(CROSS_MINGW32) || defined(CROSS_MINGW64))
1353 # pragma GCC diagnostic push
1354 # pragma GCC diagnostic ignored "-Wtype-limits"
1355 #endif
1356 if (SSIZE_MAX < f_size)
1357 {
1358 (void)fprintf (stderr, "WARNING: input exceeds %" PRIdMAX " bytes\n",
1359 (intmax_t)SSIZE_MAX);
1360 (void)fclose (f_in);
1361 continue;
1362 }
1363 #if defined(__GNUC__) && (defined(__MINGW32__) || defined(__MINGW64__) || defined(CROSS_MINGW32) || defined(CROSS_MINGW64))
1364 # pragma GCC diagnostic pop
1365 #endif
1366
1367 unsigned char * p_in_buf = malloc ((size_t)f_size);
1368 if (NULL == p_in_buf)
1369 {
1370 (void)fprintf (stderr, "FATAL: Out of memory, aborting at %s[%s:%d]\n",
1371 __func__, __FILE__, __LINE__);
1372 (void)fclose (f_in);
1373 abort ();
1374 }
1375
1376
1377 if (1 != fread (p_in_buf, f_size, 1, f_in))
1378 {
1379 (void)fprintf (stderr, "WARNING: fread failed");
1380
1381 if (feof (f_in))
1382 (void)fprintf (stderr, ": unexpected EOF\n");
1383 else if (ferror (f_in))
1384 {
1385 (void)fprintf (stderr, ": I/O error\n");
1386 clearerr (f_in);
1387 }
1388 else
1389 (void)fprintf (stderr, "\n");
1390
1391 FREE (p_in_buf);
1392
1393 (void)fclose (f_in);
1394 continue;
1395 }
1396
1397 (void)fclose (f_in);
1398
1399 tdefl_compressor * p_comp = tdefl_compressor_alloc ();
1400 if (! p_comp)
1401 {
1402 (void)fprintf (stderr, "WARNING: tdefl_compressor_alloc failure!\n");
1403 FREE (p_in_buf);
1404 continue;
1405 }
1406
1407 FILE * f_out = fopen (gz_path, "wb");
1408 if (NULL == f_out)
1409 {
1410 (void)fprintf (stderr, "fopen failure: %s (Error %d)\n",
1411 xstrerror_l (errno), errno);
1412 FREE (p_in_buf);
1413 tdefl_compressor_free (p_comp);
1414 continue;
1415 }
1416
1417
1418 (void)fwrite ("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff", 1, 10, f_out);
1419
1420 (void)tdefl_init (p_comp, put_buf_func, f_out, tdefl_create_comp_flags_from_zip_params
1421 (MZ_DEFAULT_COMPRESSION, -15, MZ_DEFAULT_STRATEGY));
1422
1423 if (! tdefl_compress_buffer (p_comp, p_in_buf, f_size, TDEFL_FINISH))
1424 {
1425 tdefl_status st = tdefl_get_prev_return_status (p_comp);
1426 (void)fprintf (stderr, "WARNING: tdefl_compress_buffer failure: status %d\n", (int)st);
1427 (void)fclose (f_out);
1428 tdefl_compressor_free (p_comp);
1429 FREE (p_in_buf);
1430 (void)unlink (gz_path);
1431 continue;
1432 }
1433
1434 mz_uint32 crc = mz_crc32 (0, p_in_buf, f_size);
1435
1436 (void)fwrite (& crc, 4, 1, f_out);
1437 (void)fwrite (& f_size, 4, 1, f_out);
1438
1439 (void)fclose (f_out);
1440
1441
1442 tdefl_compressor_free (p_comp);
1443
1444 (void)fprintf (stderr, "Compressed '%s' to '%s'\n",
1445 archive_path, gz_path);
1446
1447 (void)unlink (archive_path);
1448
1449 FREE (p_in_buf);
1450 }
1451 }
1452 }
1453 }
1454 else
1455 {
1456 if (0 != unlink (in_path))
1457 (void)fprintf (stderr, "unlink failure: %s (Error %d)\n",
1458 xstrerror_l (errno), errno);
1459 else
1460 (void)fprintf (stderr, "Processed and removed '%s'\n", in_path);
1461 }
1462
1463 (void)time (& rawtime);
1464 timelocal = localtime (& rawtime);
1465
1466 if (strftime (timebuffer, sizeof (timebuffer), "%A, %B %d %Y, %H:%M:%S %Z", timelocal))
1467 (void)fprintf (stderr, "Conversion job ended %s\n", timebuffer);
1468 }
1469 }
1470
1471 (void)closedir (d);
1472 }
1473
1474 int rc = monotonic_nssleep ((uint64_t)(poll_rate * 1000000000.0));
1475 if (EINTR == rc)
1476 (void)fprintf (stderr, "WARNING: monotonic_nssleep interrupted\n");
1477 else if (0 != rc)
1478 {
1479 (void)fprintf (stderr, "FATAL: monotonic_nssleep failure");
1480 if (errno)
1481 (void)fprintf (stderr, ": %s (Error %d)\n", xstrerror_l (errno), errno);
1482 else
1483 (void)fprintf (stderr, "\n");
1484 abort ();
1485 }
1486 }
1487 }
1488
1489 int
1490 main (int argc, char * argv [])
1491 {
1492 #if !defined(NO_LOCALE)
1493 (void)setlocale (LC_ALL, "");
1494 #endif
1495
1496 parse_options (argc, argv);
1497
1498 if (! use_ascii_encoding && use_gcos_encoding)
1499 {
1500 if (upper_all)
1501 {
1502 #if defined(TESTING)
1503 (void)fprintf (stderr, "*** GBCD upper-casing disabled");
1504 #endif
1505 upper_all = false;
1506 }
1507 else if (upper_jcl)
1508 {
1509 #if defined(TESTING)
1510 (void)fprintf (stderr, "*** GBCD JCL-only upper-casing enabled");
1511 #endif
1512 upper_jcl = true;
1513 upper_all = false;
1514 }
1515 else
1516 {
1517 #if defined(TESTING)
1518 (void)fprintf (stderr, "*** GBCD upper-casing enabled");
1519 #endif
1520 upper_all = true;
1521 }
1522 }
1523
1524 if (! daemon_mode)
1525 {
1526 if (fabs (poll_rate - DEFAULT_POLL_RATE) > DBL_EPSILON)
1527 {
1528 (void)fprintf (stderr, "ERROR: --poll option requires --daemon\n");
1529 exit (EXIT_FAILURE);
1530 }
1531
1532 if (input_dir)
1533 {
1534 (void)fprintf (stderr, "ERROR: --input option requires --daemon\n");
1535 exit (EXIT_FAILURE);
1536 }
1537
1538 if (output_dir)
1539 {
1540 (void)fprintf (stderr, "ERROR: --output option requires --daemon\n");
1541 exit (EXIT_FAILURE);
1542 }
1543
1544 if (archive_dir)
1545 {
1546 (void)fprintf (stderr, "ERROR: --archive option requires --daemon\n");
1547 exit (EXIT_FAILURE);
1548 }
1549
1550 if (compress_archive)
1551 {
1552 (void)fprintf (stderr, "ERROR: --compress option requires --daemon and --archive\n");
1553 exit (EXIT_FAILURE);
1554 }
1555 }
1556
1557 if (daemon_mode)
1558 {
1559 if ((input_dir && '-' == * input_dir) ||
1560 (output_dir && '-' == * output_dir) ||
1561 (archive_dir && '-' == * archive_dir))
1562 {
1563 (void)fprintf (stderr, "ERROR: Directory names should not begin with a dash ('-')\n");
1564 exit (EXIT_FAILURE);
1565 }
1566
1567 if (NULL == input_dir || NULL == output_dir)
1568 {
1569 (void)fprintf (stderr, "ERROR: Daemon mode requires -i <input_dir> and -o <output_dir>\n");
1570 exit (EXIT_FAILURE);
1571 }
1572
1573 if (compress_archive && NULL == archive_dir)
1574 {
1575 (void)fprintf (stderr, "ERROR: --compress option requires --archive <archive_dir>\n");
1576 exit (EXIT_FAILURE);
1577 }
1578
1579 struct stat st;
1580
1581 if (0 != stat (input_dir, & st) || ! S_ISDIR (st . st_mode))
1582 {
1583 (void)fprintf (stderr, "ERROR: Input directory not found: '%s'\n", input_dir);
1584 exit (EXIT_FAILURE);
1585 }
1586
1587 if (0 != access (input_dir, R_OK | X_OK))
1588 {
1589 (void)fprintf (stderr, "ERROR: Input directory is not readable: '%s'\n", input_dir);
1590 exit (EXIT_FAILURE);
1591 }
1592
1593 if (0 != stat (output_dir, & st) || ! S_ISDIR (st . st_mode))
1594 {
1595 (void)fprintf (stderr, "ERROR: Output directory not found: '%s'\n", output_dir);
1596 exit (EXIT_FAILURE);
1597 }
1598
1599 if (0 != access (output_dir, W_OK))
1600 {
1601 (void)fprintf (stderr, "ERROR: Output directory not writable: '%s'\n", output_dir);
1602 exit (EXIT_FAILURE);
1603 }
1604
1605 if (archive_dir)
1606 {
1607 if (0 != stat (archive_dir, & st) || ! S_ISDIR (st . st_mode))
1608 {
1609 (void)fprintf (stderr, "ERROR: Archive directory not found: '%s'\n", archive_dir);
1610 exit (EXIT_FAILURE);
1611 }
1612
1613 if (0 != access (archive_dir, W_OK))
1614 {
1615 (void)fprintf (stderr, "ERROR: Archive directory not writable: '%s'\n", archive_dir);
1616 exit (EXIT_FAILURE);
1617 }
1618 }
1619
1620 char r_input_dir [CARDUTIL_MAXPATH];
1621 char r_output_dir [CARDUTIL_MAXPATH];
1622 char r_archive_dir [CARDUTIL_MAXPATH];
1623
1624 #if !defined(__MINGW32__) && !defined(__MINGW64__) && !defined(CROSS_MINGW32) && !defined(CROSS_MINGW64)
1625 if (! realpath (input_dir, r_input_dir))
1626 {
1627 (void)fprintf (stderr, "ERROR: Cannot canonicalize input directory '%s': %s (Error %d)\n",
1628 input_dir, xstrerror_l (errno), errno);
1629 exit (EXIT_FAILURE);
1630 }
1631
1632 if (! realpath (output_dir, r_output_dir))
1633 {
1634 (void)fprintf (stderr, "ERROR: Cannot canonicalize output directory '%s': %s (Error %d)\n",
1635 output_dir, xstrerror_l (errno), errno);
1636 exit (EXIT_FAILURE);
1637 }
1638
1639 if (archive_dir && ! realpath (archive_dir, r_archive_dir))
1640 {
1641 (void)fprintf (stderr, "ERROR: Cannot canonicalize archive directory '%s': %s (Error %d)\n",
1642 archive_dir, xstrerror_l (errno), errno);
1643 exit (EXIT_FAILURE);
1644 }
1645 #else
1646 (void)strncpy (r_input_dir, input_dir, sizeof (r_input_dir));
1647 r_input_dir [sizeof (r_input_dir) - 1] = '\0';
1648
1649 (void)strncpy (r_output_dir, output_dir, sizeof (r_output_dir));
1650 r_output_dir [sizeof (r_output_dir) - 1] = '\0';
1651
1652 if (archive_dir)
1653 {
1654 (void)strncpy (r_archive_dir, archive_dir, sizeof (r_archive_dir));
1655 r_archive_dir [sizeof (r_archive_dir) - 1] = '\0';
1656 }
1657 else
1658 r_archive_dir[0] = '\0';
1659 #endif
1660
1661 if (0 == strcmp (r_input_dir, ".") || 0 == strcmp (r_input_dir, ".."))
1662 {
1663 (void)fprintf (stderr, "ERROR: Input directory should not be specified as '.' or '..'\n");
1664 exit (EXIT_FAILURE);
1665 }
1666
1667 if (0 == strcmp (r_output_dir, ".") || 0 == strcmp (r_output_dir, ".."))
1668 {
1669 (void)fprintf (stderr, "ERROR: Output directory should not be specified as '.' or '..'\n");
1670 exit (EXIT_FAILURE);
1671 }
1672
1673 if (archive_dir && (0 == strcmp (r_archive_dir, ".") || 0 == strcmp (r_archive_dir, "..")))
1674 {
1675 (void)fprintf (stderr, "ERROR: Archive directory should not be specified as '.' or '..'\n");
1676 exit (EXIT_FAILURE);
1677 }
1678
1679 if (0 == strcmp (r_input_dir, r_output_dir))
1680 {
1681 (void)fprintf (stderr, "ERROR: Input and output directories cannot be the same.\n");
1682 exit (EXIT_FAILURE);
1683 }
1684
1685 if (archive_dir && 0 == strcmp (r_input_dir, r_archive_dir))
1686 {
1687 (void)fprintf (stderr, "ERROR: Input and archive directories cannot be the same.\n");
1688 exit (EXIT_FAILURE);
1689 }
1690
1691 if (archive_dir && 0 == strcmp (r_output_dir, r_archive_dir))
1692 {
1693 (void)fprintf (stderr, "ERROR: Output and archive directories cannot be the same.\n");
1694 exit (EXIT_FAILURE);
1695 }
1696
1697 if (0 == stat (input_dir, & st))
1698 if (0 != (st . st_mode & S_IWOTH))
1699 (void)fprintf (stderr, "NOTE: The input directory specified is world-writable!\n");
1700
1701 bool output_world_writable = 0 != (st . st_mode & S_IWOTH);
1702 bool archive_world_writable = false;
1703
1704 if (archive_dir)
1705 if (0 == stat (archive_dir, & st))
1706 archive_world_writable = 0 != (st . st_mode & S_IWOTH);
1707
1708 if (output_world_writable && archive_world_writable)
1709 (void)fprintf (stderr, "WARNING: World writable output and archive directories are considered insecure!\n");
1710 else if (output_world_writable)
1711 (void)fprintf (stderr, "WARNING: World writable output directory is considered insecure!\n");
1712 else if (archive_world_writable)
1713 (void)fprintf (stderr, "WARNING: World writable archive directory is considered insecure!\n");
1714
1715 char * daemon_label;
1716 if (use_ascii_encoding)
1717 daemon_label = "MCC";
1718 else if (use_gcos_encoding)
1719 daemon_label = "GBCD";
1720 else
1721 {
1722 (void)fprintf (stderr, "FATAL: Unknown output format, aborting!\n");
1723 abort ();
1724 }
1725
1726 #if defined(TESTING)
1727 (void)fprintf (stderr, "\n");
1728 #endif
1729
1730 pid_t pid = getpid ();
1731 if (0 == pid)
1732 (void)fprintf (stderr, "****\nCard Utility (%s Daemon Mode)\n****\n",
1733 daemon_label);
1734 else
1735 (void)fprintf (stderr, "****\nCard Utility (%s Daemon Mode) [PID %" PRIdMAX "]\n****\n",
1736 daemon_label, (intmax_t)pid);
1737
1738 #if defined(TESTING)
1739 (void)fprintf (stderr, "\n");
1740 (void)fprintf (stderr, "* Input directory : %s\n", input_dir);
1741 (void)fprintf (stderr, "* Output directory : %s\n", output_dir);
1742 (void)fprintf (stderr, "* Archive directory : %s\n", archive_dir ? archive_dir : "disabled");
1743 (void)fprintf (stderr, "* Archive compression : %s\n", compress_archive ? "enabled" : "disabled");
1744 (void)fprintf (stderr, "* Daemon polling rate : %f\n", poll_rate);
1745 #endif
1746
1747 daemon_loop (input_dir, output_dir, archive_dir, compress_archive);
1748 }
1749 else
1750 {
1751 if (2 > argc)
1752 {
1753 print_help (argv [0], DEFAULT_POLL_RATE);
1754 return EXIT_FAILURE;
1755 }
1756 #if defined(TESTING)
1757 (void)fprintf (stderr, "\n");
1758 #endif
1759 (void)fprintf (stderr, "****\nCard Utility\n****\n");
1760
1761 int ifd = STDIN_FILENO;
1762 int ofd = STDOUT_FILENO;
1763
1764 crlf_warned = false;
1765
1766 if (0 != process_stream (ifd, ofd))
1767 {
1768 (void)fprintf (stderr, "ERROR: process_stream failed at %s[%s:%d]\n",
1769 __func__, __FILE__, __LINE__);
1770 exit (EXIT_FAILURE);
1771 }
1772
1773 (void)close (ifd);
1774 (void)close (ofd);
1775 }
1776
1777 (void)fprintf (stderr, "Done\n");
1778
1779 return EXIT_SUCCESS;
1780 }