This source file includes following definitions.
- mcc_to_ascii
- gcos_to_ascii
- check_for_valid_mcc_cards
- check_for_valid_gcos_cards
- convert_mcc_to_ascii
- convert_gcos_to_ascii
- print_card
- log_char_matrix_pattern
- search_glyph_patterns
- get_lace_char
- scan_card_for_glyphs
- allocate_card
- read_card
- save_card_in_cache
- print_event
- print_state
- transition_state
- do_state_idle
- do_state_starting_job
- do_state_scan_card_for_glyphs
- do_state_end_of_header
- do_state_cache_card
- do_state_end_of_deck
- do_state_end_of_job
- unexpected_event
- parse_card
- parse_cards
- init
- print_help
- parse_options
- dump_cards
- dump_mcc
- dump_gcos
- dump_raw
- main
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 #if !defined(_POSIX_C_SOURCE)
19 # define _POSIX_C_SOURCE 200809L
20 #endif
21
22 #if !defined(_GNU_SOURCE)
23 # define _GNU_SOURCE
24 #endif
25
26 #if !defined(_NETBSD_SOURCE)
27 # define _NETBSD_SOURCE
28 #endif
29
30 #if !defined(_OPENBSD_SOURCE)
31 # define _OPENBSD_SOURCE
32 #endif
33
34 #if !defined(PUNUTIL_VERSION_MAJOR)
35 # define PUNUTIL_VERSION_MAJOR (0)
36 #endif
37
38 #if !defined(PUNUTIL_VERSION_MINOR)
39 # define PUNUTIL_VERSION_MINOR (3)
40 #endif
41
42 #if !defined(PUNUTIL_VERSION_PATCH)
43 # define PUNUTIL_VERSION_PATCH (1)
44 #endif
45
46 #include <stdio.h>
47 #include <string.h>
48 #include <stdlib.h>
49 #include <ctype.h>
50 #include <unistd.h>
51 #include <stdbool.h>
52 #if defined(__APPLE__)
53 # include <xlocale.h>
54 #endif
55 #include <locale.h>
56 #if defined(_AIX) && !defined(USE_POPT)
57 # define USE_POPT
58 #endif
59 #if defined(USE_POPT)
60 # include <popt.h>
61 #else
62 # include <getopt.h>
63 #endif
64
65 #define CARD_COL_COUNT 80
66 #define NIBBLES_PER_COL 3
67 #define NIBBLES_PER_CARD (CARD_COL_COUNT * NIBBLES_PER_COL)
68 #define BYTES_PER_CARD (NIBBLES_PER_CARD / 2)
69 #define CHAR_MATRIX_BYTES 5
70 #define GLYPHS_PER_CARD 22
71 #define MAX_GLYPH_BUFFER_LEN 1024
72
73 typedef unsigned char uint8;
74 typedef unsigned short uint16;
75 typedef uint16 word12;
76 typedef unsigned int uint;
77
78 typedef struct
79 {
80 word12 column[CARD_COL_COUNT];
81 } card_image_t;
82
83 enum parse_state
84 {
85 Idle,
86 StartingJob,
87 PunchGlyphLookup,
88 EndOfHeader,
89 CacheCard,
90 EndOfDeck,
91 EndOfJob
92 };
93
94 enum parse_event
95 {
96 NoEvent,
97 BannerCard,
98 EndOfDeckCard,
99 Card,
100 Done
101 };
102
103 typedef struct card_cache_node CARD_CACHE_ENTRY;
104
105 struct card_cache_node
106 {
107 card_image_t *card;
108 CARD_CACHE_ENTRY *next_entry;
109 };
110
111 typedef struct
112 {
113 CARD_CACHE_ENTRY *first_cache_card;
114 CARD_CACHE_ENTRY *last_cache_card;
115 } CARD_CACHE;
116
117 static bool output_auto = true;
118 static bool output_glyphs = false;
119 static bool output_mcc = false;
120 static bool output_gcos = false;
121 static bool output_cards = false;
122 static bool output_flip = false;
123 static bool output_raw = false;
124 static bool output_debug = false;
125
126 static enum parse_state current_state = Idle;
127
128 static char glyph_buffer[MAX_GLYPH_BUFFER_LEN];
129
130 static CARD_CACHE banner_card_cache;
131 static CARD_CACHE data_card_cache;
132 static CARD_CACHE trailer_card_cache;
133
134
135 static word12 banner_card[] =
136 {
137 00005,
138 00000,
139 00000,
140 07700,
141 07704,
142 07704,
143 07704,
144 07700,
145 00000,
146 00000,
147 00077,
148 00477,
149 00477,
150 00477,
151 00077,
152 00000,
153 00000,
154 07700,
155 07704,
156 07704,
157 07704,
158 07700,
159 00000,
160 00000,
161 00077,
162 00477,
163 00477,
164 00477,
165 00077,
166 00000,
167 00000,
168 07700,
169 07704,
170 07704,
171 07704,
172 07700,
173 00000,
174 00000,
175 00077,
176 00477,
177 00477,
178 00477,
179 00077,
180 00000,
181 00000,
182 07700,
183 07704,
184 07704,
185 07704,
186 07700,
187 00000,
188 00000,
189 00077,
190 00477,
191 00477,
192 00477,
193 00077,
194 00000,
195 00000,
196 07700,
197 07704,
198 07704,
199 07704,
200 07700,
201 00000,
202 00000,
203 00077,
204 00477,
205 00477,
206 00477,
207 00077,
208 00000,
209 00000,
210 07700,
211 07704,
212 07704,
213 07704,
214 07700,
215 00000,
216 00005,
217 };
218
219
220 static word12 end_of_deck_card[] =
221 {
222 00005,
223 00000,
224 00000,
225 00000,
226 00000,
227 00000,
228 00000,
229 00000,
230 00000,
231 00000,
232 00000,
233 00000,
234 00000,
235 00000,
236 00000,
237 00000,
238 00000,
239 02000,
240 02400,
241 02400,
242 02400,
243 03700,
244 00000,
245 00000,
246 03721,
247 02112,
248 02104,
249 02104,
250 03737,
251 00000,
252 00000,
253 00021,
254 00021,
255 00021,
256 00021,
257 00037,
258 00000,
259 00000,
260 01621,
261 02125,
262 02125,
263 02125,
264 03737,
265 00000,
266 00000,
267 03716,
268 00221,
269 00421,
270 01021,
271 03737,
272 00000,
273 00000,
274 02100,
275 02500,
276 02500,
277 02500,
278 03700,
279 00000,
280 00000,
281 00000,
282 00000,
283 00000,
284 00000,
285 00000,
286 00000,
287 00000,
288 00000,
289 00000,
290 00000,
291 00000,
292 00000,
293 00000,
294 00000,
295 00000,
296 00000,
297 00000,
298 00000,
299 00000,
300 00000,
301 00005,
302 };
303
304
305
306
307
308
309
310 static word12 mcc_punch_codes[128] =
311 {
312 05403,
313 04401,
314 04201,
315 04101,
316 00005,
317 01023,
318 01013,
319 01007,
320 02011,
321 04021,
322 02021,
323 04103,
324 04043,
325 04023,
326 04013,
327 04007,
328 06403,
329 02401,
330 02201,
331 02101,
332 00043,
333 00023,
334 00201,
335 01011,
336 02003,
337 02403,
338 00007,
339 01005,
340 02043,
341 02023,
342 02013,
343 02007,
344 00000,
345 02202,
346 00006,
347 00102,
348 02102,
349 01042,
350 04000,
351 00022,
352 04022,
353 02022,
354 02042,
355 04012,
356 01102,
357 02000,
358 04102,
359 01400,
360 01000,
361 00400,
362 00200,
363 00100,
364 00040,
365 00020,
366 00010,
367 00004,
368 00002,
369 00001,
370 00202,
371 02012,
372 04042,
373 00012,
374 01012,
375 01006,
376 00042,
377 04400,
378 04200,
379 04100,
380 04040,
381 04020,
382 04010,
383 04004,
384 04002,
385 04001,
386 02400,
387 02200,
388 02100,
389 02040,
390 02020,
391 02010,
392 02004,
393 02002,
394 02001,
395 01200,
396 01100,
397 01040,
398 01020,
399 01010,
400 01004,
401 01002,
402 01001,
403 05022,
404 04202,
405 06022,
406 02006,
407 01022,
408 00402,
409 05400,
410 05200,
411 05100,
412 05040,
413 05020,
414 05010,
415 05004,
416 05002,
417 05001,
418 06400,
419 06200,
420 06100,
421 06040,
422 06020,
423 06010,
424 06004,
425 06002,
426 06001,
427 03200,
428 03100,
429 03040,
430 03020,
431 03010,
432 03004,
433 03002,
434 03001,
435 05000,
436 04006,
437 03000,
438 03400,
439 04005,
440 };
441
442 static word12 gcos_punch_codes[128] =
443 {
444 05403,
445 04401,
446 04201,
447 04101,
448 00005,
449 01023,
450 01013,
451 01007,
452 02011,
453 04021,
454 02021,
455 04103,
456 04043,
457 04023,
458 04013,
459 04007,
460 06403,
461 02401,
462 02201,
463 02101,
464 00043,
465 00023,
466 00201,
467 01011,
468 02003,
469 02403,
470 00007,
471 01005,
472 02043,
473 02023,
474 02013,
475 02007,
476 00000,
477 01006,
478 01012,
479 00102,
480 02102,
481 01042,
482 04000,
483 02006,
484 04022,
485 02022,
486 02042,
487 05000,
488 01102,
489 02000,
490 04102,
491 01400,
492 01000,
493 00400,
494 00200,
495 00100,
496 00040,
497 00020,
498 00010,
499 00004,
500 00002,
501 00001,
502 00022,
503 02012,
504 04012,
505 01022,
506 00012,
507 00006,
508 00042,
509 04400,
510 04200,
511 04100,
512 04040,
513 04020,
514 04010,
515 04004,
516 04002,
517 04001,
518 02400,
519 02200,
520 02100,
521 02040,
522 02020,
523 02010,
524 02004,
525 02002,
526 02001,
527 01200,
528 01100,
529 01040,
530 01020,
531 01010,
532 01004,
533 01002,
534 01001,
535 00202,
536 04006,
537 04042,
538 03000,
539 01202,
540 00402,
541 05400,
542 05200,
543 05100,
544 05040,
545 05020,
546 05010,
547 05004,
548 05002,
549 05001,
550 06400,
551 06200,
552 06100,
553 06040,
554 06020,
555 06010,
556 06004,
557 06002,
558 06001,
559 03200,
560 03100,
561 03040,
562 03020,
563 03010,
564 03004,
565 03002,
566 03001,
567 05000,
568 04006,
569 03000,
570 03400,
571 04005,
572 };
573
574 static word12 row_bit_masks[] =
575 {
576 0x800,
577 0x400,
578 0x200,
579 0x100,
580 0x080,
581 0x040,
582 0x020,
583 0x010,
584 0x008,
585 0x004,
586 0x002,
587 0x001,
588 };
589
590 static int mcc_to_ascii(word12 punch_code)
591 {
592 for (uint i = 0; i < 128; i++)
593 {
594 if (mcc_punch_codes[i] == punch_code)
595 {
596 return i;
597 }
598 }
599
600 return -1;
601 }
602
603 static int gcos_to_ascii(word12 punch_code)
604 {
605 for (uint i = 0; i < 128; i++)
606 {
607 if (gcos_punch_codes[i] == punch_code)
608 {
609 return i;
610 }
611 }
612
613 return -1;
614 }
615
616
617 static bool check_for_valid_mcc_cards(void)
618 {
619 CARD_CACHE_ENTRY *current_card = data_card_cache.first_cache_card;
620 while (current_card != NULL)
621 {
622 for (uint col = 0; col < CARD_COL_COUNT; col++)
623 {
624 if (mcc_to_ascii(current_card->card->column[col]) == -1)
625 {
626 return false;
627 }
628 }
629
630 current_card = current_card->next_entry;
631 }
632 return true;
633 }
634
635 static bool check_for_valid_gcos_cards(void)
636 {
637 CARD_CACHE_ENTRY *current_card = data_card_cache.first_cache_card;
638 while (current_card != NULL)
639 {
640 for (uint col = 0; col < CARD_COL_COUNT; col++)
641 {
642 if (gcos_to_ascii(current_card->card->column[col]) == -1)
643 {
644 return false;
645 }
646 }
647
648 current_card = current_card->next_entry;
649 }
650 return true;
651 }
652
653 static void convert_mcc_to_ascii(word12 *buffer, char *ascii_string)
654 {
655 for (uint i = 0; i < CARD_COL_COUNT; i++)
656 {
657 int c = mcc_to_ascii(buffer[i]);
658 if (output_debug)
659 {
660 (void)fprintf(stderr, "+++ Punch Code %04o = '%c'\n", buffer[i], c);
661 }
662 if (c == -1)
663 {
664 c = ' ';
665 }
666 ascii_string[i] = (int)c;
667 }
668 ascii_string[CARD_COL_COUNT] = 0;
669 }
670
671 static void convert_gcos_to_ascii(word12 *buffer, char *ascii_string)
672 {
673 for (uint i = 0; i < CARD_COL_COUNT; i++)
674 {
675 int c = gcos_to_ascii(buffer[i]);
676 if (output_debug)
677 {
678 (void)fprintf(stderr, "+++ Punch Code %04o = '%c'\n", buffer[i], c);
679 }
680 if (c == -1)
681 {
682 c = ' ';
683 }
684 ascii_string[i] = (int)c;
685 }
686 ascii_string[CARD_COL_COUNT] = 0;
687 }
688
689
690
691
692
693
694
695
696 #define NUM_GLYPH_CHAR_PATTERNS 45
697
698 static uint8 glyph_char_patterns[NUM_GLYPH_CHAR_PATTERNS][CHAR_MATRIX_BYTES] =
699 {
700
701 {037, 037, 037, 037, 037},
702
703 {000, 000, 000, 000, 000},
704
705 {000, 003, 003, 003, 000},
706
707 {021, 000, 012, 000, 004},
708
709 {037, 024, 024, 024, 037},
710
711 {037, 025, 025, 025, 012},
712
713 {037, 021, 021, 021, 021},
714
715 {037, 021, 021, 021, 016},
716
717 {037, 025, 025, 025, 021},
718
719 {037, 024, 024, 024, 020},
720
721 {037, 021, 021, 025, 027},
722
723 {037, 004, 004, 004, 037},
724
725 {021, 021, 037, 021, 021},
726
727 {003, 001, 001, 001, 037},
728
729 {037, 004, 004, 012, 021},
730
731 {037, 001, 001, 001, 001},
732
733 {037, 010, 004, 010, 037},
734
735 {037, 010, 004, 002, 037},
736
737 {037, 021, 021, 021, 037},
738
739 {037, 024, 024, 024, 034},
740
741 {037, 021, 025, 023, 037},
742
743 {037, 024, 024, 026, 035},
744
745 {035, 025, 025, 025, 027},
746
747 {020, 020, 037, 020, 020},
748
749 {037, 001, 001, 001, 037},
750
751 {030, 006, 001, 006, 030},
752
753 {037, 002, 004, 002, 037},
754
755 {021, 012, 004, 012, 021},
756
757 {020, 010, 007, 010, 020},
758
759 {021, 027, 025, 035, 021},
760
761 {016, 021, 021, 021, 016},
762
763 {000, 010, 000, 037, 000},
764
765 {023, 025, 025, 025, 035},
766
767 {021, 025, 025, 025, 037},
768
769 {034, 004, 004, 004, 037},
770
771 {035, 025, 025, 025, 022},
772
773 {037, 005, 005, 005, 007},
774
775 {020, 021, 022, 024, 030},
776
777 {012, 025, 025, 025, 012},
778
779 {034, 024, 024, 024, 037},
780
781 {001, 001, 001, 001, 001},
782
783 {000, 004, 004, 004, 000},
784
785 {000, 004, 012, 021, 000},
786
787 {000, 021, 012, 004, 000},
788
789 {001, 002, 004, 010, 020}};
790
791 static char glyph_chars[NUM_GLYPH_CHAR_PATTERNS] =
792 {
793 '*', ' ', '.', '>', 'A', 'B', 'C', 'D', 'E', 'F',
794 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
795 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
796 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
797 '_', '-', '(', ')', '/'};
798
799 static uint8 glyph_starting_column[11] =
800 {
801 73, 66, 59, 52, 45, 38, 31, 24, 17, 10, 3};
802
803 static char row_prefix_chars[] = "&-0123456789";
804
805 static void print_card(FILE *out_file, card_image_t *card, bool flip_card)
806 {
807 if (output_cards)
808 {
809 (void)fprintf(out_file, " +--------------------------------------------------------------------------------+\n");
810 for (int row = 0; row < 12; row++)
811 {
812 (void)fprintf(out_file, "%c|", row_prefix_chars[row]);
813 if (flip_card)
814 {
815 for (int col = CARD_COL_COUNT - 1; col >= 0; col--)
816 {
817 (void)fprintf(out_file, card->column[col] & row_bit_masks[row] ? "*" : " ");
818 }
819 }
820 else
821 {
822 for (int col = 0; col < CARD_COL_COUNT; col++)
823 {
824 (void)fprintf(out_file, card->column[col] & row_bit_masks[row] ? "*" : " ");
825 }
826 }
827 (void)fprintf(out_file, "|\n");
828 }
829 (void)fprintf(out_file, " +--------------------------------------------------------------------------------+\n\n");
830 }
831 }
832
833 static void log_char_matrix_pattern(uint8 *char_matrix)
834 {
835 (void)fprintf(stderr, "\nChar Matrix\n");
836 for (uint col_offset = 0; col_offset < CHAR_MATRIX_BYTES; col_offset++)
837 {
838 (void)fprintf(stderr, " %03o\n", char_matrix[col_offset]);
839 }
840
841 (void)fprintf(stderr, "\r\n");
842 for (uint row = 0; row < 5; row++)
843 {
844 for (uint col = 0; col < CHAR_MATRIX_BYTES; col++)
845 {
846 if ((char_matrix[col] >> (4 - row)) & 0x1)
847 {
848 (void)fprintf(stderr, "*");
849 }
850 else
851 {
852 (void)fprintf(stderr, " ");
853 }
854 }
855 (void)fprintf(stderr, "\r\n");
856 }
857 (void)fprintf(stderr, "\r\n");
858 }
859
860 static char search_glyph_patterns(uint8 *matrix)
861 {
862 for (int pattern = 0; pattern < NUM_GLYPH_CHAR_PATTERNS; pattern++)
863 {
864 if (memcmp(matrix, &glyph_char_patterns[pattern], CHAR_MATRIX_BYTES) == 0)
865 {
866 return glyph_chars[pattern];
867 }
868 }
869
870 (void)fprintf(stderr, "*** Warning: Punch found unknown block character pattern\n");
871 log_char_matrix_pattern(matrix);
872
873 return ' ';
874 }
875
876 static char get_lace_char(const word12 *buffer, uint char_pos)
877 {
878 if (char_pos >= GLYPHS_PER_CARD)
879 {
880 (void)fprintf(stderr, "*** Error: Attempt to read punch block character out of range (%u)\n", char_pos);
881 _Exit(4);
882 }
883
884 bool top = char_pos < 11;
885 uint char_offset = (char_pos < 11) ? char_pos : char_pos - 11;
886 word12 col_buffer[5];
887
888
889
890
891 for (uint col_offset = 0; col_offset < 5; col_offset++)
892 {
893 col_buffer[4 - col_offset] = buffer[glyph_starting_column[char_offset] + col_offset];
894 }
895
896
897 uint8 char_matrix[CHAR_MATRIX_BYTES];
898
899 for (uint col_offset = 0; col_offset < CHAR_MATRIX_BYTES; col_offset++)
900 {
901 char_matrix[col_offset] = (col_buffer[col_offset] >> (top ? 6 : 0)) & 0x1F;
902 }
903
904 return search_glyph_patterns(char_matrix);
905 }
906
907 static void scan_card_for_glyphs(card_image_t *card)
908 {
909 for (uint c_pos = 0; c_pos < 22; c_pos++)
910 {
911 char c = get_lace_char(card->column, c_pos);
912 uint current_length = strlen(glyph_buffer);
913 if (current_length < (sizeof(glyph_buffer) - 1))
914 {
915 glyph_buffer[current_length++] = c;
916 glyph_buffer[current_length] = 0;
917 }
918 }
919 }
920
921 static card_image_t *allocate_card(void)
922 {
923 card_image_t *card = malloc(sizeof(card_image_t));
924 if (card == NULL)
925 {
926 (void)fprintf(stderr, "*** Error: Failed to allocate memory for card image!\r\n");
927 (void)fprintf(stderr, "\r\nFATAL: Bugcheck! Aborting at %s[%s:%d]\r\n",
928 __func__, __FILE__, __LINE__);
929 abort();
930 }
931
932
933 (void)memset(card, 0, sizeof(card_image_t));
934
935 return card;
936 }
937
938 static card_image_t *read_card(FILE *in_file)
939 {
940 uint8 byte_buffer[BYTES_PER_CARD];
941
942 int bytes_read = fread(&byte_buffer, 1, BYTES_PER_CARD, in_file);
943
944 if (bytes_read == 0)
945 {
946 if (feof(in_file))
947 {
948
949 return NULL;
950 }
951 else if (ferror(in_file))
952 {
953
954 perror("Reading card file\n");
955 _Exit(2);
956 }
957 else
958 {
959 (void)fprintf(stderr, "*** Error: fread returned zero but failed to set the error or eof flags!\n");
960 _Exit(2);
961 }
962 }
963
964
965 if (bytes_read != BYTES_PER_CARD)
966 {
967 (void)fprintf(stderr, "*** Error: failed to read a full card (only read %d of %d bytes)\n", bytes_read, BYTES_PER_CARD);
968 _Exit(3);
969 }
970
971
972 card_image_t *card = allocate_card();
973
974 for (int current_nibble = 0; current_nibble < NIBBLES_PER_CARD; current_nibble++)
975 {
976 int byte_offset = current_nibble / 2;
977 int nibble_offset = (2 - (current_nibble % 3)) * 4;
978 int col = current_nibble / 3;
979
980 word12 nibble = (current_nibble & 0x1) ? byte_buffer[byte_offset] : (byte_buffer[byte_offset] >> 4);
981 nibble &= 0x00000F;
982 card->column[col] |= (nibble << nibble_offset);
983 }
984
985 if (output_debug)
986 {
987 print_card(stderr, card, false);
988 }
989
990 return card;
991 }
992
993 static void save_card_in_cache(CARD_CACHE *card_cache, card_image_t *card)
994 {
995 CARD_CACHE_ENTRY *new_entry = malloc(sizeof(CARD_CACHE_ENTRY));
996 if (!new_entry)
997 {
998 (void)fprintf(stderr, "\rFATAL: Out of memory, aborting at %s[%s:%d]\r\n",
999 __func__, __FILE__, __LINE__);
1000 abort();
1001 }
1002
1003 new_entry->card = card;
1004 new_entry->next_entry = NULL;
1005
1006 if (card_cache->first_cache_card == NULL)
1007 {
1008 card_cache->first_cache_card = new_entry;
1009 card_cache->last_cache_card = new_entry;
1010 }
1011 else
1012 {
1013 card_cache->last_cache_card->next_entry = new_entry;
1014 card_cache->last_cache_card = new_entry;
1015 }
1016 }
1017
1018 static void print_event(enum parse_event event)
1019 {
1020 switch (event)
1021 {
1022 case NoEvent:
1023 (void)fprintf(stderr, "[No Event]");
1024 break;
1025 case BannerCard:
1026 (void)fprintf(stderr, "[Banner Card]");
1027 break;
1028 case EndOfDeckCard:
1029 (void)fprintf(stderr, "[End Of Deck Card]");
1030 break;
1031 case Card:
1032 (void)fprintf(stderr, "[Card]");
1033 break;
1034 case Done:
1035 (void)fprintf(stderr, "[Done]");
1036 break;
1037 default:
1038 (void)fprintf(stderr, "[unknown event %d]", event);
1039 break;
1040 }
1041 }
1042
1043 static void print_state(enum parse_state state)
1044 {
1045 switch (state)
1046 {
1047 case Idle:
1048 (void)fprintf(stderr, "[Idle]");
1049 break;
1050 case StartingJob:
1051 (void)fprintf(stderr, "[Starting Job]");
1052 break;
1053 case PunchGlyphLookup:
1054 (void)fprintf(stderr, "[Punch Glyph Lookup]");
1055 break;
1056 case EndOfHeader:
1057 (void)fprintf(stderr, "[End Of Header]");
1058 break;
1059 case CacheCard:
1060 (void)fprintf(stderr, "[Cache Card]");
1061 break;
1062 case EndOfDeck:
1063 (void)fprintf(stderr, "[End Of Deck]");
1064 break;
1065 case EndOfJob:
1066 (void)fprintf(stderr, "[End Of Job]");
1067 break;
1068 default:
1069 (void)fprintf(stderr, "[unknown state %d]", state);
1070 break;
1071 }
1072 }
1073
1074 static void transition_state(enum parse_event event, enum parse_state new_state)
1075 {
1076 if (output_debug)
1077 {
1078 (void)fprintf(stderr, ">>> State Transition: ");
1079 print_event(event);
1080 (void)fprintf(stderr, " = ");
1081 print_state(current_state);
1082 (void)fprintf(stderr, " -> ");
1083 print_state(new_state);
1084 (void)fprintf(stderr, "\r\n");
1085 }
1086
1087 current_state = new_state;
1088 }
1089
1090 static enum parse_event do_state_idle(enum parse_event event)
1091 {
1092 transition_state(event, Idle);
1093
1094
1095
1096 return NoEvent;
1097 }
1098
1099 static enum parse_event do_state_starting_job(enum parse_event event, card_image_t *card)
1100 {
1101 transition_state(event, StartingJob);
1102
1103 glyph_buffer[0] = 0;
1104 save_card_in_cache(&banner_card_cache, card);
1105
1106 return NoEvent;
1107 }
1108
1109 static enum parse_event do_state_scan_card_for_glyphs(enum parse_event event, card_image_t *card)
1110 {
1111 transition_state(event, PunchGlyphLookup);
1112
1113 scan_card_for_glyphs(card);
1114
1115 save_card_in_cache(&banner_card_cache, card);
1116
1117 return NoEvent;
1118 }
1119
1120 static enum parse_event do_state_end_of_header(enum parse_event event, card_image_t *card)
1121 {
1122 transition_state(event, EndOfHeader);
1123
1124 save_card_in_cache(&banner_card_cache, card);
1125
1126 if (output_debug)
1127 {
1128 (void)fprintf(stderr, "\n++++ Glyph Buffer ++++\n'%s'\n", glyph_buffer);
1129 }
1130
1131 return NoEvent;
1132 }
1133
1134 static enum parse_event do_state_cache_card(enum parse_event event, card_image_t *card)
1135 {
1136 transition_state(event, CacheCard);
1137
1138 save_card_in_cache(&data_card_cache, card);
1139
1140 return NoEvent;
1141 }
1142
1143 static enum parse_event do_state_end_of_deck(enum parse_event event, card_image_t *card)
1144 {
1145 transition_state(event, EndOfDeck);
1146
1147 save_card_in_cache(&trailer_card_cache, card);
1148
1149 return NoEvent;
1150 }
1151
1152 static enum parse_event do_state_end_of_job(enum parse_event event, card_image_t *card)
1153 {
1154 transition_state(event, EndOfJob);
1155
1156 save_card_in_cache(&trailer_card_cache, card);
1157
1158 return Done;
1159 }
1160
1161 static void unexpected_event(enum parse_event event)
1162 {
1163 if (output_debug)
1164 {
1165 (void)fprintf(stderr, "*** Unexpected event ");
1166 print_event(event);
1167
1168 (void)fprintf(stderr, " in state ");
1169 print_state(current_state);
1170
1171 (void)fprintf(stderr, "***\n");
1172 }
1173 }
1174
1175 static void parse_card(card_image_t *card)
1176 {
1177 enum parse_event event = Card;
1178
1179 if (memcmp(card, end_of_deck_card, sizeof(end_of_deck_card)) == 0)
1180 {
1181 if (output_debug)
1182 {
1183 (void)fprintf(stderr, "*** Found End Of Deck Card ***\n");
1184 }
1185 event = EndOfDeckCard;
1186 }
1187
1188 if (memcmp(card, banner_card, sizeof(banner_card)) == 0)
1189 {
1190 if (output_debug)
1191 {
1192 (void)fprintf(stderr, "*** Found Banner Card ***\n");
1193 }
1194 event = BannerCard;
1195 }
1196
1197 while (event != NoEvent)
1198 {
1199 enum parse_event current_event = event;
1200 event = NoEvent;
1201
1202 switch (current_event)
1203 {
1204 case BannerCard:
1205 switch (current_state)
1206 {
1207 case Idle:
1208 event = do_state_starting_job(current_event, card);
1209 break;
1210
1211 case PunchGlyphLookup:
1212 event = do_state_end_of_header(current_event, card);
1213 break;
1214
1215 case EndOfDeck:
1216 event = do_state_end_of_job(current_event, card);
1217 break;
1218
1219 default:
1220 unexpected_event(current_event);
1221 break;
1222 }
1223 break;
1224
1225 case EndOfDeckCard:
1226 switch (current_state)
1227 {
1228 case StartingJob:
1229 event = do_state_end_of_deck(current_event, card);
1230 break;
1231
1232 case PunchGlyphLookup:
1233 event = do_state_end_of_deck(current_event, card);
1234 break;
1235
1236 case EndOfHeader:
1237 event = do_state_end_of_deck(current_event, card);
1238 break;
1239
1240 case CacheCard:
1241 event = do_state_end_of_deck(current_event, card);
1242 break;
1243
1244 case EndOfDeck:
1245 event = do_state_end_of_deck(current_event, card);
1246 break;
1247
1248 default:
1249 unexpected_event(current_event);
1250 break;
1251 }
1252 break;
1253
1254 case Card:
1255 switch (current_state)
1256 {
1257 case StartingJob:
1258 event = do_state_scan_card_for_glyphs(current_event, card);
1259 break;
1260
1261 case PunchGlyphLookup:
1262 event = do_state_scan_card_for_glyphs(current_event, card);
1263 break;
1264
1265 case EndOfHeader:
1266 event = do_state_cache_card(current_event, card);
1267 break;
1268
1269 case CacheCard:
1270 event = do_state_cache_card(current_event, card);
1271 break;
1272
1273 case EndOfDeck:
1274 event = do_state_cache_card(current_event, card);
1275 break;
1276
1277 case Idle:
1278 event = do_state_cache_card(current_event, card);
1279 break;
1280
1281 default:
1282 unexpected_event(current_event);
1283 break;
1284 }
1285 break;
1286
1287 case Done:
1288 switch (current_state)
1289 {
1290 case EndOfJob:
1291 event = do_state_idle(current_event);
1292 break;
1293
1294 default:
1295 unexpected_event(current_event);
1296 break;
1297 }
1298 break;
1299
1300 default:
1301 (void)fprintf(stderr, "*** Error: Punch received unknown event!\n");
1302 break;
1303 }
1304 }
1305 }
1306
1307 static void parse_cards(FILE *in_file)
1308 {
1309 card_image_t *card;
1310 while ( (card = ( read_card(in_file) ) ) )
1311 {
1312 parse_card(card);
1313 if (output_debug)
1314 {
1315 (void)fprintf(stderr, "\n");
1316 }
1317 }
1318 }
1319
1320 static void init(void)
1321 {
1322 (void)memset(&banner_card_cache, 0, sizeof(banner_card_cache));
1323 (void)memset(&data_card_cache, 0, sizeof(data_card_cache));
1324 (void)memset(&trailer_card_cache, 0, sizeof(trailer_card_cache));
1325 }
1326
1327 static void print_help(char *program)
1328 {
1329 (void)printf("\nMultics Punch Utility Program");
1330 (void)printf("\n");
1331 (void)printf("\nInvoke as: %s [options]\n", program);
1332 (void)printf("\n");
1333 (void)printf("Where options are:\n");
1334 (void)printf(" -h, --help = Output this message\n");
1335 (void)printf(" -a, --auto = Attempt to automatically determine the type of card deck\n");
1336 (void)printf(" (Default; disables any previously enabled output options)\n");
1337 (void)printf(" -n, --no-auto = Disable auto selection of the card format\n");
1338 (void)printf(" (You must specify output control options)\n");
1339 (void)printf(" -v, --version = Add version information to stderr output\n");
1340 (void)printf("\n");
1341 (void)printf("Output control options:\n");
1342 (void)printf(" By default 'auto' mode is active, where a scan attempts to determine the\n");
1343 (void)printf(" type of deck. The scan order is: 'MCC', '7punch', 'gcos', and 'raw' (if\n");
1344 (void)printf(" none of the first three seem to apply). Note that the options below\n");
1345 (void)printf(" are mutually exclusive.\n");
1346 (void)printf("\n");
1347 (void)printf(" -7, --7punch = Interpret the cards as 7punch data and output the data\n");
1348 (void)printf(" (currently not supported!)\n");
1349 (void)printf(" -c, --cards = Output an ASCII art form of the punched cards with an '*'\n");
1350 (void)printf(" representing the punches\n");
1351 (void)printf(" -f, --flip = If --cards is specified, the banner cards will be 'flipped'\n");
1352 (void)printf(" so they can be read normally\n");
1353 (void)printf(" -g, --glyphs = Output the glyphs parsed from the banner cards as ASCII\n");
1354 (void)printf(" -m, --mcc = Interpret the cards as MCC Punch Codes\n");
1355 (void)printf(" (Invalid punch codes will be converted to spaces)\n");
1356 (void)printf(" -G, --gcos = Interpret the cards as GCOS BCD Punch Codes\n");
1357 (void)printf(" (Invalid punch codes will be converted to spaces)\n");
1358 (void)printf(" -r, --raw = Dump the raw card data as 12-bit words in column order");
1359 (void)printf("\n");
1360 (void)printf("\nThis program will read a card spool file produced by the DPS8M Simulator,");
1361 (void)printf("\nparse it, and produce the requested output on standard output.");
1362 (void)printf("\n");
1363 (void)printf("\nNote that only one output mode may be selected per execution.");
1364 (void)printf("\n");
1365 (void)printf("\nThis software is made available under the terms of the ICU License.");
1366 (void)printf("\nFor complete license details, see the LICENSE file included with the");
1367 (void)printf("\nsoftware or https://gitlab.com/dps8m/dps8m/-/blob/master/LICENSE.md");
1368 (void)printf("\n");
1369 }
1370
1371 #if defined(USE_POPT)
1372 static struct poptOption long_options[] = {
1373 #else
1374 static struct option long_options[] = {
1375 #endif
1376 #if defined(USE_POPT)
1377 { "7punch", '7', POPT_ARG_NONE, NULL, '7', "7punch", "7PUNCH" },
1378 { "auto", 'a', POPT_ARG_NONE, NULL, 'a', "auto", "AUTO" },
1379 { "cards", 'c', POPT_ARG_NONE, NULL, 'c', "cards", "CARDS" },
1380 { "debug", 'd', POPT_ARG_NONE, NULL, 'd', "debug", "DEBUG" },
1381 { "flip", 'f', POPT_ARG_NONE, NULL, 'f', "flip", "FLIP" },
1382 { "glyphs", 'g', POPT_ARG_NONE, NULL, 'g', "glyphs", "GLYPHS" },
1383 { "help", 'h', POPT_ARG_NONE, NULL, 'h', "help", "HELP" },
1384 { "mcc", 'm', POPT_ARG_NONE, NULL, 'm', "mcc", "MCC" },
1385 { "gcos", 'G', POPT_ARG_NONE, NULL, 'G', "gcos", "GCOS" },
1386 { "no-auto", 'n', POPT_ARG_NONE, NULL, 'n', "no-auto", "NOAUTO" },
1387 { "raw", 'r', POPT_ARG_NONE, NULL, 'r', "raw", "RAW" },
1388 { "version", 'v', POPT_ARG_NONE, NULL, 'v', "version", "VERSION" }
1389 #else
1390 { "7punch", no_argument, 0, '7' },
1391 { "auto", no_argument, 0, 'a' },
1392 { "cards", no_argument, 0, 'c' },
1393 { "debug", no_argument, 0, 'd' },
1394 { "flip", no_argument, 0, 'f' },
1395 { "glyphs", no_argument, 0, 'g' },
1396 { "help", no_argument, 0, 'h' },
1397 { "mcc", no_argument, 0, 'm' },
1398 { "no-auto", no_argument, 0, 'n' },
1399 { "raw", no_argument, 0, 'r' },
1400 { "version", no_argument, 0, 'v' },
1401 { 0, 0, 0, 0 }
1402 #endif
1403 };
1404
1405 static void parse_options(int argc, char *argv[])
1406 {
1407 int c;
1408 bool done = false;
1409
1410 while (!done)
1411 {
1412 #if defined(USE_POPT)
1413 poptContext opt_con;
1414 opt_con = poptGetContext(NULL, argc, (const char **)argv, long_options, 0);
1415 while ((c = poptGetNextOpt(opt_con)) >= 0) {
1416 #else
1417 int option_index = 0;
1418 c = getopt_long(argc, argv, "7acdfghmGnrvV", long_options, &option_index);
1419 #endif
1420
1421 switch (c)
1422 {
1423 case -1:
1424 done = true;
1425 break;
1426
1427 case '7':
1428 (void)fprintf(stderr, "*** Sorry: 7punch format is not yet supported!\n");
1429 _Exit(1);
1430
1431 case 'a':
1432 output_auto = true;
1433 output_cards = false;
1434 output_glyphs = false;
1435 output_mcc = false;
1436 output_gcos = false;
1437 output_raw = false;
1438 break;
1439
1440 case 'c':
1441 output_auto = false;
1442 output_cards = true;
1443 output_glyphs = false;
1444 output_mcc = false;
1445 output_gcos = false;
1446 output_raw = false;
1447 break;
1448
1449 case 'd':
1450 output_debug = true;
1451 break;
1452
1453 case 'f':
1454 output_flip = true;
1455 break;
1456
1457 case 'g':
1458 output_auto = false;
1459 output_cards = false;
1460 output_glyphs = true;
1461 output_mcc = false;
1462 output_gcos = false;
1463 output_raw = false;
1464 break;
1465
1466 case 'm':
1467 output_auto = false;
1468 output_cards = false;
1469 output_glyphs = false;
1470 output_mcc = true;
1471 output_gcos = false;
1472 output_raw = false;
1473 break;
1474
1475 case 'G':
1476 output_auto = false;
1477 output_cards = false;
1478 output_glyphs = false;
1479 output_mcc = false;
1480 output_gcos = true;
1481 output_raw = false;
1482 break;
1483
1484 case 'n':
1485 output_auto = false;
1486 break;
1487
1488 case 'r':
1489 output_auto = false;
1490 output_cards = false;
1491 output_glyphs = false;
1492 output_mcc = false;
1493 output_gcos = false;
1494 output_raw = true;
1495 break;
1496
1497 case 'v':
1498 (void)fprintf(stderr, "\nVersion %d.%d.%d\n",
1499 PUNUTIL_VERSION_MAJOR, PUNUTIL_VERSION_MINOR, PUNUTIL_VERSION_PATCH);
1500 break;
1501
1502 case 'h':
1503 case '?':
1504 print_help(argv[0]);
1505 exit(0);
1506
1507 default:
1508 (void)fprintf(stderr, "*** Internal Error: did not recognize option when parsing options, got %d\n", c);
1509 _Exit(1);
1510 }
1511 #if defined(USE_POPT)
1512 }
1513 #endif
1514 }
1515
1516
1517 bool override_in_effect = output_cards || output_glyphs || output_mcc || output_raw || output_gcos;;
1518
1519 if (!output_auto && !override_in_effect)
1520 {
1521 (void)fprintf(stderr, "*** Error: auto mode was disabled with no override mode selected!\n");
1522 _Exit(1);
1523 }
1524 }
1525
1526 static void dump_cards(FILE *out_file)
1527 {
1528 CARD_CACHE_ENTRY *current_entry = banner_card_cache.first_cache_card;
1529 while (current_entry != NULL)
1530 {
1531 print_card(out_file, current_entry->card, output_flip);
1532 current_entry = current_entry->next_entry;
1533 }
1534
1535 current_entry = data_card_cache.first_cache_card;
1536 while (current_entry != NULL)
1537 {
1538 print_card(out_file, current_entry->card, false);
1539 current_entry = current_entry->next_entry;
1540 }
1541
1542 current_entry = trailer_card_cache.first_cache_card;
1543 while (current_entry != NULL)
1544 {
1545 print_card(out_file, current_entry->card, output_flip);
1546 current_entry = current_entry->next_entry;
1547 }
1548 }
1549
1550 static void dump_mcc(FILE *out_file)
1551 {
1552 char ascii_string[CARD_COL_COUNT + 1];
1553 CARD_CACHE_ENTRY *current_entry = data_card_cache.first_cache_card;
1554 while (current_entry != NULL)
1555 {
1556 convert_mcc_to_ascii(current_entry->card->column, ascii_string);
1557 (void)fprintf(out_file, "%s\n", ascii_string);
1558 current_entry = current_entry->next_entry;
1559 }
1560 }
1561
1562 static void dump_gcos(FILE *out_file)
1563 {
1564 char ascii_string[CARD_COL_COUNT + 1];
1565 CARD_CACHE_ENTRY *current_entry = data_card_cache.first_cache_card;
1566 while (current_entry != NULL)
1567 {
1568 convert_gcos_to_ascii(current_entry->card->column, ascii_string);
1569 (void)fprintf(out_file, "%s\n", ascii_string);
1570 current_entry = current_entry->next_entry;
1571 }
1572 }
1573
1574 static void dump_raw(FILE *out_file)
1575 {
1576 CARD_CACHE_ENTRY *current_entry = data_card_cache.first_cache_card;
1577 while (current_entry != NULL)
1578 {
1579 if (output_debug)
1580 {
1581 (void)fprintf(stderr, "\nCard:\n");
1582 for (uint col = 0; col < CARD_COL_COUNT; col++)
1583 {
1584 (void)fprintf(stderr, " 0x%03X\n", current_entry->card->column[col]);
1585 }
1586 }
1587 for (int current_nibble = 0; current_nibble < NIBBLES_PER_CARD; current_nibble += 2)
1588 {
1589 uint8 byte = 0;
1590
1591 uint col = current_nibble / 3;
1592 uint nibble_offset = 2 - (current_nibble % 3);
1593
1594 uint nibble = (current_entry->card->column[col] >> (nibble_offset * 4)) & 0x00F;
1595 byte |= nibble << 4;
1596
1597 col = (current_nibble + 1) / 3;
1598 nibble_offset = 2 - ((current_nibble + 1) % 3);
1599
1600 nibble = (current_entry->card->column[col] >> (nibble_offset * 4)) & 0x00F;
1601 byte |= nibble;
1602
1603 if (fwrite(&byte, 1, 1, out_file) != 1)
1604 {
1605 perror("Failed to write output file\n");
1606 _Exit(5);
1607 }
1608 }
1609 current_entry = current_entry->next_entry;
1610 }
1611 }
1612
1613 int main(int argc, char *argv[])
1614 {
1615 #if !defined(NO_LOCALE)
1616 (void)setlocale(LC_ALL, "");
1617 #endif
1618
1619 (void)fprintf(stderr, "****\nPunch File Utility\n****\n");
1620
1621 parse_options(argc, argv);
1622
1623 init();
1624
1625 parse_cards(stdin);
1626
1627 if (output_auto)
1628 {
1629 if (check_for_valid_mcc_cards())
1630 {
1631 output_mcc = true;
1632 (void)fprintf(stderr, ">> Auto-detected MCC Punch Codes <<\n");
1633 }
1634 else if (check_for_valid_gcos_cards())
1635 {
1636 output_gcos = true;
1637 (void)fprintf(stderr, ">> Auto-detected GCOS Punch Codes <<\n");
1638 }
1639 else
1640 {
1641 output_raw = true;
1642 (void)fprintf(stderr, ">> Auto-detection did not recognize format so output mode set to raw <<\n");
1643 }
1644 }
1645
1646 if (output_cards)
1647 {
1648 (void)fprintf(stderr, "\n*****\nWriting ASCII Art Card Images (banner cards%s flipped)\n*****\n",
1649 output_flip ? "" : " not");
1650 dump_cards(stdout);
1651 }
1652
1653 if (output_glyphs)
1654 {
1655 (void)fprintf(stderr, "\n*****\nWriting Banner Glyphs as ASCII\n*****\n");
1656 (void)fprintf(stdout, "%s\n", glyph_buffer);
1657 }
1658
1659 if (output_raw)
1660 {
1661 (void)fprintf(stderr, "\n*****\nWriting raw output\n*****\n");
1662 dump_raw(stdout);
1663 }
1664
1665 if (output_mcc)
1666 {
1667 (void)fprintf(stderr, "\n*****\nWriting MCC output\n*****\n");
1668 dump_mcc(stdout);
1669 }
1670
1671 if (output_gcos)
1672 {
1673 (void)fprintf(stderr, "\n*****\nWriting GCOS output\n*****\n");
1674 dump_gcos(stdout);
1675 }
1676
1677 }