root/src/punutil/punutil.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. mcc_to_ascii
  2. gcos_to_ascii
  3. check_for_valid_mcc_cards
  4. check_for_valid_gcos_cards
  5. convert_mcc_to_ascii
  6. convert_gcos_to_ascii
  7. print_card
  8. log_char_matrix_pattern
  9. search_glyph_patterns
  10. get_lace_char
  11. scan_card_for_glyphs
  12. allocate_card
  13. read_card
  14. save_card_in_cache
  15. print_event
  16. print_state
  17. transition_state
  18. do_state_idle
  19. do_state_starting_job
  20. do_state_scan_card_for_glyphs
  21. do_state_end_of_header
  22. do_state_cache_card
  23. do_state_end_of_deck
  24. do_state_end_of_job
  25. unexpected_event
  26. parse_card
  27. parse_cards
  28. init
  29. print_help
  30. parse_options
  31. dump_cards
  32. dump_mcc
  33. dump_gcos
  34. dump_raw
  35. main

   1 /*
   2  * vim: filetype=c:tabstop=4:ai:expandtab
   3  * SPDX-License-Identifier: ICU
   4  * scspell-id: 62d95d16-f631-11ec-9329-80ee73e9b8e7
   5  *
   6  * -------------------------------------------------------------------------
   7  *
   8  * Copyright (c) 2021-2025 The DPS8M Development Team
   9  *
  10  * This software is made available under the terms of the ICU License.
  11  * See the LICENSE.md file at the top-level directory of this distribution.
  12  *
  13  * -------------------------------------------------------------------------
  14  */
  15 
  16 //-V::1048
  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 /* if defined(_AIX) && !defined(USE_POPT) */
  59 #if defined(USE_POPT)
  60 # include <popt.h>
  61 #else
  62 # include <getopt.h>
  63 #endif /* if defined(USE_POPT) */
  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; //-V677
  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 // Card image of the banner card
 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 // Card image of the End Of Deck card
 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  *   MCC Punch Codes -> ASCII Conversion Table
 306  * The table entry is the MCC Punch Code and the
 307  * index of the entry is the ASCII character code.
 308  */
 309 
 310 static word12 mcc_punch_codes[128] =
 311     {
 312         05403, // ?   (9-12-0-8-1)  0xb03  2819
 313         04401, // ?   (9-12-1)      0x901  2305
 314         04201, // ?   (9-12-2)      0x881  2177
 315         04101, // ?   (9-12-3)      0x841  2113
 316         00005, // ?   (9-7)         0x5       5
 317         01023, // ?   (9-0-8-5)     0x213   531
 318         01013, // ?   (9-0-8-6)     0x20b   523
 319         01007, // ?   (9-0-8-7)     0x207   519
 320         02011, // ?   (9-11-6)      0x409  1033
 321         04021, // ?   (9-12-5)      0x811  2065
 322         02021, // ?   (9-11-5)      0x411  1041
 323         04103, // ?   (9-12-8-3)    0x843  2115
 324         04043, // ?   (9-12-8-4)    0x823  2083
 325         04023, // ?   (9-12-8-5)    0x813  2067
 326         04013, // ?   (9-12-8-6)    0x80b  2059
 327         04007, // ?   (9-12-8-7)    0x807  2055
 328         06403, // ?   (12-11-9-8-1) 0xd03  3331
 329         02401, // ?   (9-11-1)      0x501  1281
 330         02201, // ?   (9-11-2)      0x481  1153
 331         02101, // ?   (9-11-3)      0x441  1089
 332         00043, // ?   (9-8-4)       0x23     35
 333         00023, // ?   (9-8-5)       0x13     19
 334         00201, // ?   (9-2)         0x81    129
 335         01011, // ?   (9-0-6)       0x209   521
 336         02003, // ?   (9-11-8)      0x403  1027
 337         02403, // ?   (9-11-8-1)    0x503  1283
 338         00007, // ?   (9-8-7)       0x7       7
 339         01005, // ?   (9-0-7)       0x205   517
 340         02043, // ?   (9-11-8-4)    0x423  1059
 341         02023, // ?   (9-11-8-5)    0x413  1043
 342         02013, // ?   (9-11-8-6)    0x40b  1035
 343         02007, // ?   (9-11-8-7)    0x407  1031
 344         00000, //     ()            0x0       0
 345         02202, // !   (11-8-2)      0x482  1154
 346         00006, // "   (8-7)         0x6       6
 347         00102, // #   (8-3)         0x42     66
 348         02102, // $   (11-8-3)      0x442  1090
 349         01042, // %   (0-8-4)       0x222   546
 350         04000, // &   (12)          0x800  2048
 351         00022, // '   (8-5)         0x12     18
 352         04022, // (   (12-8-5)      0x812  2066
 353         02022, // )   (11-8-5)      0x412  1042
 354         02042, // *   (11-8-4)      0x422  1058
 355         04012, // +   (12-8-6)      0x80a  2058
 356         01102, // ,   (0-8-3)       0x242   578
 357         02000, // -   (11)          0x400  1024
 358         04102, // .   (12-8-3)      0x842  2114
 359         01400, // /   (0-1)         0x300   768
 360         01000, // 0   (0)           0x200   512
 361         00400, // 1   (1)           0x100   256
 362         00200, // 2   (2)           0x80    128
 363         00100, // 3   (3)           0x40     64
 364         00040, // 4   (4)           0x20     32
 365         00020, // 5   (5)           0x10     16
 366         00010, // 6   (6)           0x8       8
 367         00004, // 7   (7)           0x4       4
 368         00002, // 8   (8)           0x2       2
 369         00001, // 9   (9)           0x1       1
 370         00202, // :   (8-2)         0x82    130
 371         02012, // ;   (11-8-6)      0x40a  1034
 372         04042, // <   (12-8-4)      0x822  2082
 373         00012, // =   (8-6)         0xa      10
 374         01012, // >   (0-8-6)       0x20a   522
 375         01006, // ?   (0-8-7)       0x206   518
 376         00042, // @   (8-4)         0x22     34
 377         04400, // A   (12-1)        0x900  2304
 378         04200, // B   (12-2)        0x880  2176
 379         04100, // C   (12-3)        0x840  2112
 380         04040, // D   (12-4)        0x820  2080
 381         04020, // E   (12-5)        0x810  2064
 382         04010, // F   (12-6)        0x808  2056
 383         04004, // G   (12-7)        0x804  2052
 384         04002, // H   (12-8)        0x802  2050
 385         04001, // I   (12-9)        0x801  2049
 386         02400, // J   (11-1)        0x500  1280
 387         02200, // K   (11-2)        0x480  1152
 388         02100, // L   (11-3)        0x440  1088
 389         02040, // M   (11-4)        0x420  1056
 390         02020, // N   (11-5)        0x410  1040
 391         02010, // O   (11-6)        0x408  1032
 392         02004, // P   (11-7)        0x404  1028
 393         02002, // Q   (11-8)        0x402  1026
 394         02001, // R   (11-9)        0x401  1025
 395         01200, // S   (0-2)         0x280   640
 396         01100, // T   (0-3)         0x240   576
 397         01040, // U   (0-4)         0x220   544
 398         01020, // V   (0-5)         0x210   528
 399         01010, // W   (0-6)         0x208   520
 400         01004, // X   (0-7)         0x204   516
 401         01002, // Y   (0-8)         0x202   514
 402         01001, // Z   (0-9)         0x201   513
 403         05022, // [   (12-0-8-5)    0xa12  2578
 404         04202, // \   (12-8-2)      0x882  2178
 405         06022, // ]   (12-11-8-5)   0xc12  3090
 406         02006, // ^   (11-8-7)      0x406  1030
 407         01022, // _   (0-8-5)       0x212   530
 408         00402, // `   (8-1)         0x102   258
 409         05400, // a   (12-0-1)      0xb00  2816
 410         05200, // b   (12-0-2)      0xa80  2688
 411         05100, // c   (12-0-3)      0xa40  2624
 412         05040, // d   (12-0-4)      0xa20  2592
 413         05020, // e   (12-0-5)      0xa10  2576
 414         05010, // f   (12-0-6)      0xa08  2568
 415         05004, // g   (12-0-7)      0xa04  2564
 416         05002, // h   (12-0-8)      0xa02  2562
 417         05001, // i   (12-0-9)      0xa01  2561
 418         06400, // j   (12-11-1)     0xd00  3328
 419         06200, // k   (12-11-2)     0xc80  3200
 420         06100, // l   (12-11-3)     0xc40  3136
 421         06040, // m   (12-11-4)     0xc20  3104
 422         06020, // n   (12-11-5)     0xc10  3088
 423         06010, // o   (12-11-6)     0xc08  3080
 424         06004, // p   (12-11-7)     0xc04  3076
 425         06002, // q   (12-11-8)     0xc02  3074
 426         06001, // r   (12-11-9)     0xc01  3073
 427         03200, // s   (11-0-2)      0x680  1664
 428         03100, // t   (11-0-3)      0x640  1600
 429         03040, // u   (11-0-4)      0x620  1568
 430         03020, // v   (11-0-5)      0x610  1552
 431         03010, // w   (11-0-6)      0x608  1544
 432         03004, // x   (11-0-7)      0x604  1540
 433         03002, // y   (11-0-8)      0x602  1538
 434         03001, // z   (11-0-9)      0x601  1537
 435         05000, // {   (12-0)        0xa00  2560
 436         04006, // |   (12-8-7)      0x806  2054
 437         03000, // }   (11-0)        0x600  1536
 438         03400, // ~   (11-0-1)      0x700  1792
 439         04005, // ^?  (12-7-9)      0x805  2053
 440 };
 441 
 442 static word12 gcos_punch_codes[128] =
 443     {
 444         05403, // ?   (9-12-0-8-1)  0xb03  2819
 445         04401, // ?   (9-12-1)      0x901  2305
 446         04201, // ?   (9-12-2)      0x881  2177
 447         04101, // ?   (9-12-3)      0x841  2113
 448         00005, // ?   (9-7)         0x5       5
 449         01023, // ?   (9-0-8-5)     0x213   531
 450         01013, // ?   (9-0-8-6)     0x20b   523
 451         01007, // ?   (9-0-8-7)     0x207   519
 452         02011, // ?   (9-11-6)      0x409  1033
 453         04021, // ?   (9-12-5)      0x811  2065
 454         02021, // ?   (9-11-5)      0x411  1041
 455         04103, // ?   (9-12-8-3)    0x843  2115
 456         04043, // ?   (9-12-8-4)    0x823  2083
 457         04023, // ?   (9-12-8-5)    0x813  2067
 458         04013, // ?   (9-12-8-6)    0x80b  2059
 459         04007, // ?   (9-12-8-7)    0x807  2055
 460         06403, // ?   (12-11-9-8-1) 0xd03  3331
 461         02401, // ?   (9-11-1)      0x501  1281
 462         02201, // ?   (9-11-2)      0x481  1153
 463         02101, // ?   (9-11-3)      0x441  1089
 464         00043, // ?   (9-8-4)       0x23     35
 465         00023, // ?   (9-8-5)       0x13     19
 466         00201, // ?   (9-2)         0x81    129
 467         01011, // ?   (9-0-6)       0x209   521
 468         02003, // ?   (9-11-8)      0x403  1027
 469         02403, // ?   (9-11-8-1)    0x503  1283
 470         00007, // ?   (9-8-7)       0x7       7
 471         01005, // ?   (9-0-7)       0x205   517
 472         02043, // ?   (9-11-8-4)    0x423  1059
 473         02023, // ?   (9-11-8-5)    0x413  1043
 474         02013, // ?   (9-11-8-6)    0x40b  1035
 475         02007, // ?   (9-11-8-7)    0x407  1031
 476         00000, //     ()            0x0       0
 477         01006, // SPC (0-8-7)       0x206   518
 478         01012, // !   (0-8-6)       0x20a   522
 479         00102, // #   (8-3)         0x42     66
 480         02102, // $   (11-8-3)      0x442  1090
 481         01042, // %   (0-8-4)       0x222   546
 482         04000, // &   (12)          0x800  2048
 483         02006, // '   (11-8-7)      0x406  1030
 484         04022, // (   (12-8-5)      0x812  2066
 485         02022, // )   (11-8-5)      0x412  1042
 486         02042, // *   (11-8-4)      0x422  1058
 487         05000, // +   (12-0)        0xa00  2560
 488         01102, // ,   (0-8-3)       0x242   578
 489         02000, // -   (11)          0x400  1024
 490         04102, // .   (12-8-3)      0x842  2114
 491         01400, // /   (0-1)         0x300   768
 492         01000, // 0   (0)           0x200   512
 493         00400, // 1   (1)           0x100   256
 494         00200, // 2   (2)           0x80    128
 495         00100, // 3   (3)           0x40     64
 496         00040, // 4   (4)           0x20     32
 497         00020, // 5   (5)           0x10     16
 498         00010, // 6   (6)           0x8       8
 499         00004, // 7   (7)           0x4       4
 500         00002, // 8   (8)           0x2       2
 501         00001, // 9   (9)           0x1       1
 502         00022, // :   (8-5)         0x12     18
 503         02012, // ;   (11-8-6)      0x40a  1034
 504         04012, // <   (12-8-6)      0x80a  2058
 505         01022, // =   (0-8-5)       0x212   530
 506         00012, // >   (8-6)         0xa      10
 507         00006, // ?   (8-7)         0xa00  2560
 508         00042, // @   (8-4)         0x22     34
 509         04400, // A   (12-1)        0x900  2304
 510         04200, // B   (12-2)        0x880  2176
 511         04100, // C   (12-3)        0x840  2112
 512         04040, // D   (12-4)        0x820  2080
 513         04020, // E   (12-5)        0x810  2064
 514         04010, // F   (12-6)        0x808  2056
 515         04004, // G   (12-7)        0x804  2052
 516         04002, // H   (12-8)        0x802  2050
 517         04001, // I   (12-9)        0x801  2049
 518         02400, // J   (11-1)        0x500  1280
 519         02200, // K   (11-2)        0x480  1152
 520         02100, // L   (11-3)        0x440  1088
 521         02040, // M   (11-4)        0x420  1056
 522         02020, // N   (11-5)        0x410  1040
 523         02010, // O   (11-6)        0x408  1032
 524         02004, // P   (11-7)        0x404  1028
 525         02002, // Q   (11-8)        0x402  1026
 526         02001, // R   (11-9)        0x401  1025
 527         01200, // S   (0-2)         0x280   640
 528         01100, // T   (0-3)         0x240   576
 529         01040, // U   (0-4)         0x220   544
 530         01020, // V   (0-5)         0x210   528
 531         01010, // W   (0-6)         0x208   520
 532         01004, // X   (0-7)         0x204   516
 533         01002, // Y   (0-8)         0x202   514
 534         01001, // Z   (0-9)         0x201   513
 535         00202, // [   (8-2)         0x82    130
 536         04006, // \   (12-8-7)      0x806  2054
 537         04042, // ]   (12-8-4)      0x822  2082
 538         03000, // ^   (11-0)        0x600  1536
 539         01202, // _   (0-8-2)       0x282   642
 540         00402, // `   (8-1)         0x102   258
 541         05400, // a   (12-0-1)      0xb00  2816
 542         05200, // b   (12-0-2)      0xa80  2688
 543         05100, // c   (12-0-3)      0xa40  2624
 544         05040, // d   (12-0-4)      0xa20  2592
 545         05020, // e   (12-0-5)      0xa10  2576
 546         05010, // f   (12-0-6)      0xa08  2568
 547         05004, // g   (12-0-7)      0xa04  2564
 548         05002, // h   (12-0-8)      0xa02  2562
 549         05001, // i   (12-0-9)      0xa01  2561
 550         06400, // j   (12-11-1)     0xd00  3328
 551         06200, // k   (12-11-2)     0xc80  3200
 552         06100, // l   (12-11-3)     0xc40  3136
 553         06040, // m   (12-11-4)     0xc20  3104
 554         06020, // n   (12-11-5)     0xc10  3088
 555         06010, // o   (12-11-6)     0xc08  3080
 556         06004, // p   (12-11-7)     0xc04  3076
 557         06002, // q   (12-11-8)     0xc02  3074
 558         06001, // r   (12-11-9)     0xc01  3073
 559         03200, // s   (11-0-2)      0x680  1664
 560         03100, // t   (11-0-3)      0x640  1600
 561         03040, // u   (11-0-4)      0x620  1568
 562         03020, // v   (11-0-5)      0x610  1552
 563         03010, // w   (11-0-6)      0x608  1544
 564         03004, // x   (11-0-7)      0x604  1540
 565         03002, // y   (11-0-8)      0x602  1538
 566         03001, // z   (11-0-9)      0x601  1537
 567         05000, // {   (12-0)        0xa00  2560
 568         04006, // |   (12-8-7)      0x806  2054
 569         03000, // }   (11-0)        0x600  1536
 570         03400, // ~   (11-0-1)      0x700  1792
 571         04005, // ^?  (12-7-9)      0x805  2053
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 // Scans data cards to determine if they contain all valid MCC punch codes
 617 static bool check_for_valid_mcc_cards(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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  *                  Glyph Pattern Lookup
 691  * This is the parsing of the "lace" cards and extracting the ASCII characters
 692  * have been punched into the cards (as glyphs) so the operator knows how to
 693  * deliver the deck.
 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         // Asterisk
 701         {037, 037, 037, 037, 037},
 702         // Space
 703         {000, 000, 000, 000, 000},
 704         // Period
 705         {000, 003, 003, 003, 000},
 706         // >
 707         {021, 000, 012, 000, 004},
 708         // A
 709         {037, 024, 024, 024, 037},
 710         // B
 711         {037, 025, 025, 025, 012},
 712         // C
 713         {037, 021, 021, 021, 021},
 714         // D
 715         {037, 021, 021, 021, 016},
 716         // E
 717         {037, 025, 025, 025, 021},
 718         // F
 719         {037, 024, 024, 024, 020},
 720         // G
 721         {037, 021, 021, 025, 027},
 722         // H
 723         {037, 004, 004, 004, 037},
 724         // I
 725         {021, 021, 037, 021, 021},
 726         // J
 727         {003, 001, 001, 001, 037},
 728         // K
 729         {037, 004, 004, 012, 021},
 730         // L
 731         {037, 001, 001, 001, 001},
 732         // M
 733         {037, 010, 004, 010, 037},
 734         // N
 735         {037, 010, 004, 002, 037},
 736         // O
 737         {037, 021, 021, 021, 037},
 738         // P
 739         {037, 024, 024, 024, 034},
 740         // Q
 741         {037, 021, 025, 023, 037},
 742         // R
 743         {037, 024, 024, 026, 035},
 744         // S
 745         {035, 025, 025, 025, 027},
 746         // T
 747         {020, 020, 037, 020, 020},
 748         // U
 749         {037, 001, 001, 001, 037},
 750         // V
 751         {030, 006, 001, 006, 030},
 752         // W
 753         {037, 002, 004, 002, 037},
 754         // X
 755         {021, 012, 004, 012, 021},
 756         // Y
 757         {020, 010, 007, 010, 020},
 758         // Z
 759         {021, 027, 025, 035, 021},
 760         // 0
 761         {016, 021, 021, 021, 016},
 762         // 1
 763         {000, 010, 000, 037, 000},
 764         // 2
 765         {023, 025, 025, 025, 035},
 766         // 3
 767         {021, 025, 025, 025, 037},
 768         // 4
 769         {034, 004, 004, 004, 037},
 770         // 5
 771         {035, 025, 025, 025, 022},
 772         // 6
 773         {037, 005, 005, 005, 007},
 774         // 7
 775         {020, 021, 022, 024, 030},
 776         // 8
 777         {012, 025, 025, 025, 012},
 778         // 9
 779         {034, 024, 024, 024, 037},
 780         // Underscore
 781         {001, 001, 001, 001, 001},
 782         // Hyphen
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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;                                      // Top or bottom line of characters
 885     uint char_offset = (char_pos < 11) ? char_pos : char_pos - 11; // Character number in the line
 886     word12 col_buffer[5];                                          // The extracted 5 columns for the character
 887 
 888     // Extract the five 12-bit words from the main buffer that make up the character
 889     // Note that in this process we reverse the character image so it reads normally
 890     // (characters are punched in reverse)
 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     // Now shift the characters into the 5x5 matrix buffer
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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     /* cppcheck-suppress nullPointerOutOfMemory */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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             // We've hit the end of file so just bail out
 949             return NULL;
 950         }
 951         else if (ferror(in_file))
 952         {
 953             // Something bad happened, report the error and exit
 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     // Make sure we read a full card
 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     // We have a full card so allocate a card and convert the byte buffer into the card image
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
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)
     /* [previous][next][first][last][top][bottom][index][help] */
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)
     /* [previous][next][first][last][top][bottom][index][help] */
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)
     /* [previous][next][first][last][top][bottom][index][help] */
1091 {
1092     transition_state(event, Idle);
1093 
1094     // no action
1095 
1096     return NoEvent;
1097 }
1098 
1099 static enum parse_event do_state_starting_job(enum parse_event event, card_image_t *card)
     /* [previous][next][first][last][top][bottom][index][help] */
1100 {
1101     transition_state(event, StartingJob);
1102 
1103     glyph_buffer[0] = 0;                          // Clear Glyph Buffer
1104     save_card_in_cache(&banner_card_cache, card); // Save card in cache
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)
     /* [previous][next][first][last][top][bottom][index][help] */
1110 {
1111     transition_state(event, PunchGlyphLookup);
1112 
1113     scan_card_for_glyphs(card);
1114 
1115     save_card_in_cache(&banner_card_cache, card); // Save card in cache
1116 
1117     return NoEvent;
1118 }
1119 
1120 static enum parse_event do_state_end_of_header(enum parse_event event, card_image_t *card)
     /* [previous][next][first][last][top][bottom][index][help] */
1121 {
1122     transition_state(event, EndOfHeader);
1123 
1124     save_card_in_cache(&banner_card_cache, card); // Save card in cache
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)
     /* [previous][next][first][last][top][bottom][index][help] */
1135 {
1136     transition_state(event, CacheCard);
1137 
1138     save_card_in_cache(&data_card_cache, card); // Save card in cache
1139 
1140     return NoEvent;
1141 }
1142 
1143 static enum parse_event do_state_end_of_deck(enum parse_event event, card_image_t *card)
     /* [previous][next][first][last][top][bottom][index][help] */
1144 {
1145     transition_state(event, EndOfDeck);
1146 
1147     save_card_in_cache(&trailer_card_cache, card); // Save card in cache
1148 
1149     return NoEvent;
1150 }
1151 
1152 static enum parse_event do_state_end_of_job(enum parse_event event, card_image_t *card)
     /* [previous][next][first][last][top][bottom][index][help] */
1153 {
1154     transition_state(event, EndOfJob);
1155 
1156     save_card_in_cache(&trailer_card_cache, card); // Save card in cache
1157 
1158     return Done;
1159 }
1160 
1161 static void unexpected_event(enum parse_event event)
     /* [previous][next][first][last][top][bottom][index][help] */
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)
     /* [previous][next][first][last][top][bottom][index][help] */
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); //-V1037
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); //-V1037
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)
     /* [previous][next][first][last][top][bottom][index][help] */
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)
     /* [previous][next][first][last][top][bottom][index][help] */
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)
     /* [previous][next][first][last][top][bottom][index][help] */
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 /* if defined(USE_POPT) */
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 /* if defined(USE_POPT) */
1403 };
1404 
1405 static void parse_options(int argc, char *argv[])
     /* [previous][next][first][last][top][bottom][index][help] */
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 /* if defined(USE_POPT) */
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 /* if defined(USE_POPT) */
1514     }
1515 
1516     // Verify selected options
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)
     /* [previous][next][first][last][top][bottom][index][help] */
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)
     /* [previous][next][first][last][top][bottom][index][help] */
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)
     /* [previous][next][first][last][top][bottom][index][help] */
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)
     /* [previous][next][first][last][top][bottom][index][help] */
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[])
     /* [previous][next][first][last][top][bottom][index][help] */
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 }

/* [previous][next][first][last][top][bottom][index][help] */