/* * File: compmess.c * * Program to compress a file of error messages, generating suitable * arrays to hold the compressed form, and also some code to expand * them again * * Bob Eager August 2002 * */ /* * Compile time options * * Define exactly one target language name to be 1; all the rest should be 0. * */ #define C 1 /* C language */ #define IMP 0 /* IMP80 language */ #ifdef __IBMC__ #pragma strings(readonly) #endif #include #include #include #include #define MAXLETT 1000 /* Max length of 'lett' array */ #define MAXLINE 80 /* Max input line length */ #define MAXWORD 2000 /* Max length of 'word' array */ #define SPACES " " /* Type definitions */ typedef void VOID, *PVOID; typedef int BOOL, *PBOOL; typedef char CHAR, *PCHAR; typedef unsigned char UCHAR, *PUCHAR; typedef int INT, *PINT; typedef unsigned int UINT, *PUINT; typedef long LONG, *PLONG; typedef unsigned long ULONG, *PULONG; /* Forward references */ static VOID do_listing(FILE *); static VOID do_output(FILE *); static INT getword(PUCHAR *, PUCHAR); static VOID lit(PINT, PUCHAR, FILE *); static VOID mess(PUCHAR, INT); static FILE *openio(PUCHAR, PUCHAR); static INT readn(FILE *); static VOID squash(PUCHAR); static VOID usage(VOID); /* Local data */ static INT lett[MAXLETT]; /* Encoded words */ static INT next = 0; /* Index of next free slot in 'lett' array */ static INT nmax = 0; /* Highest message number seen */ static INT num = 0; /* Next free slot in 'word' array */ static INT nummax = 0; /* Pointer to last message in 'word' */ static PUCHAR progname; /* Program name */ static INT word[MAXWORD]; /* Message and word numbers */ const UCHAR intt[128] = { /* Array to map characters to 6-bit code */ 63,63,63,63,63,63,63,63, 63,63,63,63,63,63,63,63, 63,63,63,63,63,63,63,63, 63,63,63,63,63,63,63,63, 63,63,63,61,63,60,27,30, 31,32,63,63,62,28,59,29, 63,63,63,63,63,63,63,63, 63,63,63,63,63,63,63,63, 63, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, 16,17,18,19,20,21,22,23, 24,25,26,63,63,63,63,63, 63,33,34,35,36,37,38,39, 40,41,42,43,44,45,46,47, 48,49,50,51,52,53,54,55, 56,57,58,63,63,63,63,63 }; const UCHAR outtt[64] = { /* Array to map 6-bit codes to characters */ '?','A','B','C','D','E','F','G', 'H','I','J','K','L','M','N','O', 'P','Q','R','S','T','U','V','W', 'X','Y','Z','&','-','/','\'','(', ')','a','b','c','d','e','f','g', 'h','i','j','k','l','m','n','o', 'p','q','r','s','t','u','v','w', 'x','y','z','.','%','#',',','?' }; INT main(INT argc, PUCHAR argv[]) { INT n; /* Current message number */ INT ch,i; PUCHAR ptr; /* Pointer to next input word */ FILE *infp,*outfp,*listfp; /* I/O file pointers */ UCHAR input[MAXLINE]; /* Input line buffer */ UCHAR wk1[MAXLINE]; /* String work area */ /* Generate clean version of program name, for messages */ progname = argv[0]; ptr = strrchr(progname, '\\'); if(ptr != (PUCHAR) NULL) progname = ++ptr; for(i = 0; progname[i] != '\0'; i++) progname[i] = (UCHAR) tolower(progname[i]); if(argc != 4) usage(); infp = openio(argv[1], "r"); outfp = openio(argv[2], "w"); listfp = openio(argv[3], "w"); for(i = 0; i < MAXWORD; i++) word[i] = 0; lett[0] = 0; /* Initialise terminator */ /* Main loop - once for each message */ while((n = readn(infp)) != 0) { if(n > nmax) nmax = n; /* Update high water mark */ if(num >= MAXWORD) { fputs("*** Overflow of 'word' array\n", stderr); exit(EXIT_FAILURE); } word[num] = n; /* Store message number at start */ nummax = num; /* Store number of last message so far */ for(ch = fgetc(infp); ch != '\n'; ch = fgetc(infp)) { if(ch != '"') continue; /* Scan for start of message */ fscanf(infp, "%[^\"]", &input[0]); /* Read message within quotes */ while(ch != '\n') ch = fgetc(infp); /* Skip trailing junk on line */ break; } squash(&input[0]); /* Squash multiple spaces */ if((strlen(input) == 0) || (strcmp(input, " ") == 0)) continue; /* Ignore empty lines and messages */ num++; /* Point beyond message number */ fprintf(listfp, "\n%3d", n); /* Output message number to listing */ ptr = &input[0]; while(getword(&ptr, &wk1[0])) { /* Get next word in message */ if(strlen(wk1) != 0) { lit(&i, &wk1[0], listfp); /* Get word index to 'i' */ if(num >= MAXWORD) { fputs("*** Overflow of 'word' array\n", stderr); exit(EXIT_FAILURE); } word[num++] = i | 0x8000; /* Store with continuation bit */ } } } if(num >= MAXWORD) { fputs("*** Overflow of 'word' array\n", stderr); exit(EXIT_FAILURE); } word[num] = 0; /* Store word list terminator */ fputc('\n', listfp); fclose(infp); /* Generate listing of messages */ do_listing(listfp); fclose(listfp); /* Generate actual output file */ do_output(outfp); fclose(outfp); #if C fprintf(stderr, "%s: function 'message' generated\n", progname); #endif #if IMP fprintf(stderr, "%s: routine 'message' generated\n", progname); #endif return(EXIT_SUCCESS); } /* * Routine to output the listing file * */ static VOID do_listing(FILE *listfp) { UCHAR temp[MAXLINE]; INT i; fputs("\n\n", listfp); for(i = 1; i <= nmax; i++) { mess(&temp[0], i); /* Get message 'i' to 'temp' */ if(strlen(temp) != 0) { fprintf(listfp, "%3d %s\n", i, temp); } } } #if C /* * Routine to generate the main output file * * C version * */ static VOID do_output(FILE *outfp) { INT i, j; UINT k; UCHAR m[MAXLINE]; fputs("/*\n * File: message.c\n *\n", outfp); fprintf(outfp, " * This file is generated automatically" " by the '%s' program\n", progname); fputs(" *\n * It should never be edited; rather," " alter the message file then\n", outfp); fprintf(outfp, " * rerun '%s'\n *\n */\n\n", progname); fputs("#include \n\n/", outfp); for(i = 1; i <= 70; i++) fputc('*', outfp); fputs("\n * Outputs an error message stored in a" " compressed format *\n", outfp); fputs(" *", outfp); for(i = 1; i <= 68; i++) fputc(' ', outfp); fputc('*', outfp); fputc('\n', outfp); for(i = 1; i <= nmax; i++) { mess(&m[0], i); /* Get message 'i' to 'm' */ k = strlen(m); if(k != 0) { /* If message exists */ fprintf(outfp, " * %3d %s", i, m); for(j = 1; j <= 58 - k; j++) fputc(' ', outfp); fputs("*\n", outfp); } } fputs(" *", outfp); for(i = 1; i <= 68; i++) fputc(' ', outfp); fputs("*\n *", outfp); for(i = 1; i <= 69; i++) fputc('*', outfp); fputs("/\n\n", outfp); next--; /* Point to last used slot in 'lett' */ fprintf(outfp, "#define\tMWORDMAX\t%d\n", num+1); fprintf(outfp, "#define\tDEFAULT\t\t%d\n\n", nummax+1); fputs("const unsigned char outtt[64] = {\n", outfp); fputs("\t'?','A','B','C','D','E','F','G',\n", outfp); fputs("\t 'H','I','J','K','L','M','N',\n", outfp); fputs("\t 'O','P','Q','R','S','T','U',\n", outfp); fputs("\t 'V','W','X','Y','Z','&','-',\n", outfp); fputs("\t '/','\\'','(',')',\n", outfp); fputs("\t 'a','b','c','d','e','f','g',\n", outfp); fputs("\t 'h','i','j','k','l','m','n',\n", outfp); fputs("\t 'o','p','q','r','s','t','u',\n", outfp); fputs("\t 'v','w','x','y','z','.','%',\n", outfp); fputs("\t '#',',','?'\n};\n\n", outfp); fputs("const unsigned short mword[MWORDMAX+1] = {\n", outfp); fputs("\t0,\n\t", outfp); for(i = 0; i <= num; i++) { fprintf(outfp, "0x%04x", word[i]); if(i != num) fputc(',', outfp); if(((i + 1) % 8) == 0) { fputc('\n', outfp); if(i != num) fputc('\t', outfp); } } if((num+1)%8 != 0) fputc('\n', outfp); fputs("};\n", outfp); fprintf(outfp, "\nconst int mlett[%d] = {\n\t0,\n\t", next+2); for(i = 0; i <= next; i++) { fprintf(outfp, "0x%08x", lett[i]); if(i != next) fputc(',', outfp); if(((i + 1) % 4) == 0) { fputc('\n', outfp); if(i != next) fputc('\t', outfp); } } if((next+1)%4 != 0) fputc('\n', outfp); fputs("};\n\n", outfp); fprintf(outfp, "void message(unsigned char *mes, int n)\n"); fputs("{\tint i, j, k, q;\n", outfp); fputs("\tint m, sh;\n\n", outfp); fputs("\t*mes++ = \' \';\n", outfp); fputs("\t*mes = \'\\0\';\n", outfp); fputs("\tj = 0;\n", outfp); fputs("\tfor(i = 0; i < MWORDMAX+1; i++) {\n", outfp); fputs("\t\tif(n == mword[i]) {\n", outfp); fputs("\t\t\tj = 1;\n", outfp); fputs("\t\t\tbreak;\n", outfp); fputs("\t\t}\n", outfp); fputs("\t}\n\n", outfp); fputs("\tif(j == 0) {\n", outfp); fputs("\t\ti = DEFAULT;\n", outfp); fputs("\t\tj = 1;\n", outfp); fputs("\t}\n\n", outfp); fputs("\tfor(;;) {\n", outfp); fputs("\t\tk = mword[i+j];\n", outfp); fputs("\t\tif((k & 0x8000) == 0) break;\n", outfp); fputs("\t\tk &= 0x7fff;\n", outfp); fputs("\t\tif(j != 1) *mes++ = ' ';\n", outfp); fputs("\t\tdo {\n", outfp); fputs("\t\t\tm = mlett[k+1];\n", outfp); fputs("\t\t\tsh = 25;\n", outfp); fputs("\t\t\tdo {\n", outfp); fputs("\t\t\t\tq = (int) ((m >> sh) & 0x3f);\n", outfp); fputs("\t\t\t\tif(q != 0) *mes++ = outtt[q];\n", outfp); fputs("\t\t\t\tsh -= 6;\n", outfp); fputs("\t\t\t} while(sh >= 0);\n", outfp); fputs("\t\t\tk++;\n", outfp); fputs("\t\t} while((m & 1) != 0);\n", outfp); fputs("\t\tj++;\n", outfp); fputs("\t}\n", outfp); fputs("\t*mes = '\\0';\t\t\t/* Terminate string */\n", outfp); fputs("}\n", outfp); fputs("\n/*\n * End of file: message.c\n *\n */\n", outfp); } #endif #if IMP /* * Routine to generate the main output file * * IMP version * */ static VOID do_output(FILE *outfp) { INT i,j,k; UCHAR m[MAXLINE]; fputs("!\n! File: message.imp\n!\n", outfp); fprintf(outfp, "! This file is generated automatically by the '%s' program\n", progname); fputs("!\n! It should never be edited; rather," " alter the message file then\n", outfp); fprintf(outfp, "! rerun '%s'\n!\n", progname); fprintf(outfp, "%%string(%d)%%function message(%%integer n)\n!", MAXLINE); for(i = 1; i <= 71; i++) fputc('*', outfp); fputs("\n!* Outputs an error message stored in a" " compressed format *\n", outfp); fputc('!', outfp); fputc('*', outfp); for(i = 1; i <= 69; i++) fputc(' ', outfp); fputc('*', outfp); fputc('\n', outfp); for(i = 1; i <= nmax; i++) { mess(&m[0], i); /* Get message 'i' to 'm' */ k = strlen(m); if(k != 0) { /* If message exists */ fprintf(outfp,"!* %3d %s", i, m); for(j = 1; j <= 59 - k; j++) fputc(' ', outfp); fputs("*\n", outfp); } } fputc('!', outfp); fputc('*', outfp); for(i = 1; i <= 69; i++) fputc(' ', outfp); fputc('*', outfp); fputc('\n', outfp); fputc('!', outfp); for(i = 1; i <= 71; i++) fputc('*', outfp); fputs("\n!\n", outfp); next--; /* Point to last used slot in 'lett' */ fputs("%constantbyteintegerarray outtt(0:63) = " "'?','A','B','C','D','E','F','G',\n", outfp); fputs(SPACES"'H','I','J','K','L','M','N',\n", outfp); fputs(SPACES"'O','P','Q','R','S','T','U',\n", outfp); fputs(SPACES"'V','W','X','Y','Z','&','-',\n", outfp); fputs(SPACES"'/','''','(',')',\n", outfp); fputs(SPACES"'a','b','c','d','e','f','g',\n", outfp); fputs(SPACES"'h','i','j','k','l','m','n',\n", outfp); fputs(SPACES"'o','p','q','r','s','t','u',\n", outfp); fputs(SPACES"'v','w','x','y','z','.','%',\n", outfp); fputs(SPACES"'#',',','?'\n!\n", outfp); fprintf(outfp, "%%constantinteger mwordmax = %d, default = %d\n", num+1, nummax+1); fputs("%constanthalfintegerarray mword(0:mwordmax) = 0,%c\n ", outfp); for(i = 0; i <= num; i++) { fprintf(outfp, "x'%04x'", word[i]); if(i != num) fputc(',', outfp); if(((i + 1) % 8) == 0) { fputc('\n', outfp); if(i != num) fputs(" ", outfp); } } if((num+1)%8 != 0) fputc('\n', outfp); fprintf(outfp, "!\n%%constantintegerarray mlett(0:%d) = 0,%%c\n ", next+1); for(i = 0; i <= next; i++) { fprintf(outfp, "x'%08x'", lett[i]); if(i != next) fputc(',', outfp); if(((i + 1) % 4) == 0) { fputc('\n', outfp); if(i != next) fputs(" ", outfp); } } if((next+1)%4 != 0) fputc('\n', outfp); fputs("!\n", outfp); fputs("%integer i,j,k,m,q,s\n", outfp); fprintf(outfp, "%%string(%d) omess\n!\n", MAXLINE); fputs(" omess = \" \"\n", outfp); fputs(" %for i = 1,1,mwordmax - 1 %cycle\n", outfp); fputs(" -> found %if n = mword(i)\n", outfp); fputs(" %repeat\n", outfp); fputs(" i = default\n", outfp); fputs("!\nfound:\n!\n", outfp); fputs(" j = 1\n", outfp); fputs(" %cycle\n", outfp); fputs(" k = mword(i+j)\n", outfp); fputs(" %if k & x'8000' = 0 %then %exit\n", outfp); fputs(" k = k & x'7fff'\n", outfp); fputs(" omess = omess.\" \" %unless j = 1\n", outfp); fputs(" %until m & 1 = 0 %cycle\n", outfp); fputs(" m = mlett(k); s = 25\n", outfp); fputs(" %until s < 0 %cycle\n", outfp); fputs(" q = m >> s & 63\n", outfp); fputs(" %if q \\= 0 %then omess = " "omess.tostring(outtt(q))\n", outfp); fputs(" s = s - 6\n", outfp); fputs(" %repeat\n", outfp); fputs(" k = k + 1\n", outfp); fputs(" %repeat\n", outfp); fputs(" j = j + 1\n", outfp); fputs(" %repeat\n", outfp); fputs(" %result = omess\n", outfp); fputs("%end; ! of mess\n", outfp); fputs("!\n! End of file: message.imp\n!\n", outfp); } #endif /* * Function to extract the next word from the string 's' and place * it in 'word'. Returns zero if no words left, otherwise returns 1. * */ static INT getword(PUCHAR *s, PUCHAR word) { UCHAR ch; PUCHAR p = *s; /* Get working copy of pointer */ if(*p == '\0') return(0); /* String empty */ for(;;) { /* Lose leading spaces */ ch = *p++; if(ch != ' ') break; } while((ch != ' ') && (ch != '\0')) { *word++ = ch; ch = *p++; } if(ch == '\0') p--; /* Back off to null terminator */ *word = '\0'; /* Add terminator */ /* 'p' now points beyond the space. */ *s = p; /* Copy back pointer */ return(1); /* A word has been read */ } /* * This routine searches for the word 'txt' in the current word list. * If found, it returns the word index in 'p'. * If not found, it adds the word to the word list and again returns * the word index in 'p'. * */ static VOID lit(PINT p, PUCHAR txt, FILE *listfp) { INT ch = 0; INT i,j; INT l = 0; UINT txtlen = strlen(txt); INT w = 0; INT sh = 25; while(ch < txtlen) { i = txt[ch++]; /* Get next character in word */ i = intt[i]; /* Convert to 6-bit code */ w = w | (i << sh); sh -= 6; if(sh >= 0) continue; if(ch < txtlen) w |= 1; if(next+l >= MAXLETT) { fputs("Overflow of 'lett' array\n", stderr); exit(EXIT_FAILURE); } lett[next+l] = w; w = 0; sh = 25; l++; } if(sh != 25) { if(next+l >= MAXLETT) { fputs("Overflow of 'lett' array\n", stderr); exit(EXIT_FAILURE); } lett[next+l] = w; l++; } /* Store any remainder */ for(i = 0; i < next; i++) { for(j = 0; j < l; j++) { if(lett[i+j] != lett[next+j]) goto fail; } goto found; fail:; } *p = next; next += l; fprintf(listfp, " word entered"); return; found: *p = i; fprintf(listfp, " word found "); } /* * Routine to return a string corresponding to message 'n'. * The string is stored in the character array 'mes'. * */ static VOID mess(PUCHAR mes, INT n) { INT i, j, k, q; INT m, sh; j = 0; for(i = 0; i < num; i++) { if(n == word[i]) { j = 1; break; } } if(j == 0) { *mes = '\0'; return; } for(;;) { k = word[i+j]; if((k & 0x8000) == 0) break; k &= 0x7fff; if(j != 1) *mes++ = ' '; do { m = lett[k]; sh = 25; do { q = (int) ((m >> sh) & 0x3f); if(q != 0) *mes++ = outtt[q]; sh -= 6; } while(sh >= 0); k++; } while((m & 1) != 0); j++; } *mes = '\0'; /* Terminate string */ } /* * Function to open a file in a specified mode * Does not return if there is a failure, but outputs an error * message and exits * */ static FILE *openio(PUCHAR file, PUCHAR mode) { FILE *fp; fp = fopen(file, mode); if(fp == (FILE *) NULL) { fprintf(stderr, "%s: cannot open '%s'\n", progname, file); exit(EXIT_FAILURE); } return(fp); } /* * Function to read a number from input 'fp' and return its value. * */ static INT readn(FILE *fp) { INT res; INT ch = '0'; for(res = 0; isdigit(ch); ch = fgetc(fp)) { res = res*10 + (ch - '0'); } return(res); } /* * Routine to convert multiple spaces to single spaces in the string 's'. * */ static VOID squash(PUCHAR s) { UCHAR ch; UCHAR lastch = 'x'; INT i; INT j = 0; for(i = 0; s[i] != '\0'; i++) { ch = s[i]; if(!((ch == ' ') && (lastch == ' '))) { s[j++] = ch; lastch = ch; } } s[j] = '\0'; } /* * Routine to output brief usage information, then exit. * */ static VOID usage(VOID) { fprintf(stderr,"Usage: %s input output listing\n",progname); exit(EXIT_SUCCESS); } /* * End of file: compmess.c * */