/*** MkMsg.c - Microsoft Make Messages Utility ******************************* * * Copyright (c) 1986-1992, Microsoft Corporation. All Rights Reserved. * * Purpose: * MkMsg.Exe is a utility that helps localize messages emitted by a utility by * padding them and placing them in message segments. * MkMsg also allows a programmer to associate symbols and Message Id to a * message text. It does this by producing a C style header file with #defines * or a Masm style include file with symbol definitions. * * Revision History: * 02-Oct-1996 DS Remove special '\\' handling in RC output. * 02-Feb-1994 HV Handle lines with only blansk. * 25-Jan-1994 HV Add -err to generate external error file in the format: * NUMBER\tMESSAGE * 24-Jan-1994 HV Allow quoted message * 21-Jan-1994 HV Nuke Lead Byte Table [[ ... ]], add !codepage directive * 21-Jan-1994 HV Field separated by white spaces * 19-Aug-1993 BW Add Lead Byte Table support * 07-Apr-1993 BW Add -hex, -c options * 13-Jul-1992 SB Add -msg option * 16-Apr-1990 SB Add header * 13-Apr-1990 SB Collated 6 different versions seperately SLM'ed on \\ODIN * 19-Apr-1989 LN add "\xhh" recognition * ??-Apr-1986 RN Created by Randy Nevin * * Syntax: * MKMSG [-h cfile] [-x xcfile] [-inc afile] [-msg cfile] [-c cfile] * [-asm srcfile [-def str] [-min|-max]] [-386] [-hex] txtfile * * Notes: * The Utility takes as input a message file (a .txt file by convention) and * produces an assembler source file. The lines in the message file have one * of the formats which are instructions to MkMsg as follows - * * 1) "<>" -- use near message segment * 2) "<>" -- use far message segment * ----- Obsolete, see 8) * 3) "[[XX, YY, ZZ]] - Use hex bytes XX, YY, ZZ as lead byte table. * Lists are cumulative, empty list clears table. * ----- * 4) "#Anything" -- comment line (ignored by mkmsg) * 5) "" -- blank line (ignored by mkmsg) * 6) "HandleNumberMessageText" -- associate MessageText with * message Id Number and the symbol Handle * 7) "NumberMessageText" -- associate MessageText with Id * Id Number * 8) "!codepage xxx" -- use this instead of [[ ... ]] * * Options: * The options can be specified in any order. When no options are specified * then the input is checked for syntactic validity. * * * -h cfile: create a C style header file cfile with * #define Handle IdNumber * If -c is also defined, a declaration for __NMSG_TEXT * and a mcros defining GET_MSG as __NMSG_TEXT is added. * -msg cfile: create a C style header file cfile with * array of struct having fields id and str * -c cfile: create a file like -msg and add a definition * of a retrieval function * -x xcfile: create a C style header file xcfile with * #define Handle MessageText * -inc afile: create a MASM style include file afile with * Handle = IdNumber * -asm srcfile: create a MASM source file srcfile with standard segment * definitions and messages in near segment (MSG) or far * segment (FAR_MSG) depending on whether <> or * <> is specified. The default is <>. * * If -min(-max) is specified then the minimum(maximum) * 0-padding is calculated and placed in srcfile. This * depends on the total length of the individual messages. * * If -def is specified the public symbol str gets declared. * This is useful if the the object file produced from the * MASM source produced is part of a library. The linker * will link in that module from the library only if it * resolves some external references. * * If -hex is specified and no Lead Byte Table is * present, all MessageText is rendered in printf-style * hexidecimal (e.g. "error" becomes * x65\x72\x72\x6f\x72"). * * if -hex is specified and a Lead Byte Table is present * even if it is empty), then all Double Byte characters * are rendering in hexidecimal. * * Future Directions: * 1> Break up main() into smaller routines * 2> Use mkmsg for handling messages * 2> Allow use of spaces as separators, -- done. HV. * 3> Allow specification of the separator * 4> Provide -? and -help options. * *****************************************************************************/ // DEFINEs // Standard INCLUDEs #include #include #include #include #include #include #include // Project INCLUDEs // MACROs // External PROTOTYPEs // TYPEDEFs typedef enum { FALSE = 0, TRUE = !0 } BOOL; typedef struct status { char *txt; char *h; char *rc; char *x; char *inc; char *msg; char *c; char *asm; char *def; char *err; int min; int max; int use32; int hex; } STATUS; // Global PROTOTYPEs void __cdecl Error(char *fmt, ...); int __cdecl main(int argc, char **argv); void parseCommandLine(unsigned argc, char **argv, STATUS *opt); void msg_fputs (unsigned char * pch, int, FILE * fp); int ReadLine (FILE * fp); BOOL ParseLine(char **ppSymbol, char **ppNumber, char **ppMessage); BOOL HandleDirectives(void); void SetCodePage (const char *pszCodePage); BOOL IsLeadByte(unsigned by); // WARNING: the following CRT function is undocumented. This call can only // be used in our internal product (like this one) only. Please // contact CRT people for more information. //void __cdecl __setmbctable(unsigned int); // // Lead Byte support // unsigned char bUseLeadByteTable = FALSE; typedef struct { unsigned uCodePage; // Codepage number unsigned byLead[12]; // Leadbyte ranges } CPTABLE; CPTABLE cpTable[] = { {932, {0x81, 0x9f, 0xe0, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, {0, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}} // trailing to end }; CPTABLE * pCP = NULL; // Local PROTOTYPEs // External VARIABLEs // Initialized VARIABLEs int cError = 0; char didnear = 0; char didfar = 0; char dir32[] = ".386\n"; char ex[] = "expected escape sequence: %s\n"; char f1[] = "FAR_HDR segment byte public \'FAR_MSG\'\nFAR_HDR ends\n"; char f2[] = "FAR_MSG segment byte public \'FAR_MSG\'\nFAR_MSG ends\n"; char f3[] = "FAR_PAD segment byte public \'FAR_MSG\'\nFAR_PAD ends\n"; char f4[] = "FAR_EPAD segment byte common \'FAR_MSG\'\nFAR_EPAD ends\n"; char f5[] = "FMGROUP group FAR_HDR,FAR_MSG,FAR_PAD,FAR_EPAD\n\n"; char n1[] = "HDR segment byte public \'MSG\'\nHDR ends\n"; char n2[] = "MSG segment byte public \'MSG\'\nMSG ends\n"; char n3[] = "PAD segment byte public \'MSG\'\nPAD ends\n"; char n4[] = "EPAD segment byte common \'MSG\'\nEPAD ends\n"; char n5[] = "DGROUP group HDR,MSG,PAD,EPAD\n\n"; char usage[] = "\nMicrosoft (R) Message Creation Utility Version %s" "\nCopyright (c) Microsoft Corp %s. All rights reserved.\n" "\nusage: MKMSG [-h cfile] [-rc rcfile] [-x xcfile] [-msg cfile]\n" "\t[-c cfile] [-err errfile] [-inc afile]\n" "\t[-asm srcfile [-def str] [-min|-max]] [-386] [-hex] txtfile\n"; char szVersionNo[] = "1.00.0011"; char szCopyRightYrs[] = "1986-1996"; // Local VARIABLEs #define MSGSTYLE_COPY 0 #define MSGSTYLE_C_HEX 1 #define MSGSTYLE_ASM_BYTE 2 #define MSGSTYLE_ASM_TEXT 3 #define FALSE 0 #define TRUE (!FALSE) #define INBUFSIZE 1024 //static char buf[INBUFSIZE]; // line buffer char *buf; // The real buffer is in ReadLine() //-------------------------------------------------------------------------- int __cdecl main(int argc, char **argv) { FILE *f; // the input file FILE *fh = NULL; // -h stream FILE *frc = NULL; // -rc stream FILE *fx = NULL; // -x stream FILE *fc = NULL; // -c stream FILE *finc = NULL; // -inc stream FILE *fmsg = NULL; // -msg stream FILE *fasm = NULL; // -asm stream FILE *ferr = NULL; // -err stream int asmstate = 0; // 0=nothing, 1=doing nmsg, 2=doing fmsg int instring; // db "... unsigned int nLineCur = 0; // Current line unsigned int cchLine; // Chars in current line char *p; int npad = 0; // cumulative amount of near padding int fpad = 0; // cumulative amount of far padding int length; double factor; double result; static STATUS opt; // Command line options parseCommandLine(argc, argv, &opt); if ((opt.def || opt.min || opt.max ) && !opt.asm) { Error( "-def/-min/-max ignored; no -asm file\n" ); opt.min = opt.max = 0; } if (opt.min && opt.max) { Error( "-min and -max are mutually exclusive; -min chosen\n" ); opt.max = 0; } if (!(f = fopen( opt.txt, "rb" ))) { Error( "can't open txtfile %s for binary reading\n", opt.txt ); return( -1 ); } if (opt.asm && !(fasm = fopen( opt.asm, "w" ))) { Error( "can't open -asm file %s for writing\n", opt.asm ); return( -1 ); } if (opt.h && !(fh = fopen( opt.h, "w" ))) { Error( "can't open -h file %s for writing\n", opt.h ); return( -1 ); } if (fh && opt.c) { fprintf (fh, "char * __NMSG_TEXT (unsigned);\n\n"); } if (opt.rc && !(frc = fopen(opt.rc, "w"))) { Error("Can't open -rc file %s for writing\n", opt.rc); return -1; } if (opt.rc) { fprintf(frc, "STRINGTABLE\nBEGIN\n"); } if (opt.x && !(fx = fopen( opt.x, "w" ))) { Error( "can't open -x file %s for writing\n", opt.x ); return( -1 ); } if (opt.inc && !(finc = fopen( opt.inc, "w" ))) { Error( "can't open -inc file %s for writing\n", opt.inc ); return( -1 ); } if (opt.msg && !(fmsg = fopen( opt.msg, "w" ))) { Error( "can't open -msg file %s for writing\n", opt.msg ); return( -1 ); } if (opt.err && !(ferr = fopen(opt.err, "w"))) { Error("Can't open -err file %s for writing\n", opt.err); return -1; } if (fmsg) { fprintf(fmsg, "typedef struct _message {\n" "\tunsigned\tid;\n" "\tchar *str;\n" "} MESSAGE;\n\n"); fprintf(fmsg, "MESSAGE __MSGTAB[] = {\n"); } if (opt.c && !(fc = fopen( opt.c, "w" ))) { Error( "can't open -c file %s for writing\n", opt.c ); return( -1 ); } if (fc) { fprintf(fc, "#include \n\n"); fprintf(fc, "typedef struct _message {\n" "\tunsigned int\tid;\n" "\tchar *str;\n" "} MESSAGE;\n\n"); fprintf(fc, "MESSAGE __MSGTAB[] = {\n"); } while ((cchLine = ReadLine ( f )) != EOF) // process lines { nLineCur++; if (buf[0] == '\0' || buf[0] == '#') { continue; } else if (HandleDirectives()) // Directive { continue; } else if (buf[0] == '[' && buf[1] == '[') // Old style leadbyte tbl { fprintf(stderr, "WARNING: Ignore leadbyte table, use !codepage instead: %s\n", buf); continue; } else if (buf[0] == '<') // <> or <> { if (!strcmp( "<>", buf )) // near msgs follow { if (asmstate == 0) { if (fasm) { if (!didnear) { didnear++; if (opt.use32) fprintf( fasm, dir32); fprintf( fasm, n1 ); fprintf( fasm, n2 ); fprintf( fasm, n3 ); fprintf( fasm, n4 ); fprintf( fasm, n5 ); } fprintf( fasm, "MSG segment\n" ); if (opt.def) { fprintf( fasm, "\tpublic\t%s\n", opt.def ); fprintf( fasm, "%s\tequ\t$\n", opt.def ); } asmstate = 1; } // if (fasm) } // if (asmstate == 0) else if (asmstate == 1) { Error( "already in nmsg\n" ); } else if (asmstate == 2 && !opt.use32) { if (fasm) { fprintf( fasm, "FAR_MSG ends\n\n" ); if (!didnear) { didnear++; fprintf( fasm, n1 ); fprintf( fasm, n2 ); fprintf( fasm, n3 ); fprintf( fasm, n4 ); fprintf( fasm, n5 ); } fprintf( fasm, "MSG segment\n" ); asmstate = 1; } // if (fasm) } // else if (asmstate == 2 ...) else { Error( "internal error\n" ); return( -1 ); } } // if near msg else if (!strcmp( "<>", buf ))//far msgs follow { if (asmstate == 0) { if (fasm) { if (!didfar) { didfar++; if (opt.use32) { fprintf( fasm, dir32); fprintf( fasm, n1 ); fprintf( fasm, n2 ); fprintf( fasm, n3 ); fprintf( fasm, n4 ); fprintf( fasm, n5 ); fprintf( fasm, "MSG segment\n" ); } else { fprintf( fasm, f1 ); fprintf( fasm, f2 ); fprintf( fasm, f3 ); fprintf( fasm, f4 ); fprintf( fasm, f5 ); fprintf( fasm, "FAR_MSG segment\n" ); } } // if (!didfar) if (opt.def) { fprintf( fasm, "\tpublic\t%s\n", opt.def ); fprintf( fasm, "%s\tequ\t$\n", opt.def ); } asmstate = 2; } // if (fasm) } // if (asmstate == 0) else if (asmstate == 1 && !opt.use32) { if (fasm) { fprintf( fasm, "MSG ends\n\n" ); if (!didfar) { didfar++; fprintf( fasm, f1 ); fprintf( fasm, f2 ); fprintf( fasm, f3 ); fprintf( fasm, f4 ); fprintf( fasm, f5 ); } fprintf( fasm, "FAR_MSG segment\n" ); asmstate = 2; } } // else if (asmstate == 1 ...) else if (asmstate == 2) { Error( "already in far_msg\n" ); } else { Error( "internal error\n" ); return( -1 ); } } // far message else // Not near, not far { Error( "ignoring bad line: %s\n", buf ); } } // if (.. < ) near/far message else if (buf[0] != '\r' && buf[0] != '\n') // something to do { char *pSymbol; char *pNumber; char *pMessage; if (!ParseLine(&pSymbol, &pNumber, &pMessage)) { fprintf( stderr, "%s(%d): error in line: \"%s\"\n", opt.txt, nLineCur, buf); continue; } if (pSymbol && opt.h) { fprintf( fh, "#define\t%s\t%s\n", pSymbol, pNumber ); } if (opt.rc) { fprintf(frc, "\t%s, \"%s\"\n", pNumber, pMessage); } if (pSymbol && opt.x) { fprintf( fx, "#define\t%s\t\"", pSymbol ); msg_fputs( pMessage , opt.hex ? MSGSTYLE_C_HEX : MSGSTYLE_COPY , fx ); putc( '\"', fx ); putc( '\n', fx ); } if (pSymbol && opt.inc) { fprintf( finc, "%s\t=\t%s\n", pSymbol, pNumber); } if (opt.msg) { fprintf( fmsg, "{%s, \"", pNumber ); msg_fputs( pMessage, opt.hex ? MSGSTYLE_C_HEX : MSGSTYLE_COPY, fmsg ); fprintf( fmsg, "\"}," ); if (opt.hex) fprintf( fmsg, " // \"%s\"", pMessage ); putc ( '\n', fmsg ); } if (opt.c) { fprintf( fc, "{%s, \"", pNumber ); msg_fputs( pMessage, opt.hex ? MSGSTYLE_C_HEX : MSGSTYLE_COPY, fc ); fprintf( fc, "\"}," ); if (opt.hex) fprintf( fc, " // \"%s\"", pMessage ); putc ( '\n', fc ); } if (opt.err) { fprintf(ferr, "%s\t\"%s\"\n", pNumber, pMessage); } // opt.err if (fasm) // write asmfile { if (asmstate == 0) { if (!didnear) { didnear++; if (opt.use32) fprintf( fasm, dir32); fprintf( fasm, n1 ); fprintf( fasm, n2 ); fprintf( fasm, n3 ); fprintf( fasm, n4 ); fprintf( fasm, n5 ); } fprintf( fasm, "MSG segment\n" ); if (opt.def) { fprintf( fasm, "\tpublic\t%s\n", opt.def ); fprintf( fasm, "%s\tequ\t$\n", opt.def ); } asmstate = 1; } // if (asmstate == 0) fprintf( fasm, "\tdw\t%s\n\tdb\t", pNumber ); instring = 0; for (p = pMessage, length = 0; *p; p++, length++) { // allocate message if (*p == '\\') { // C escape sequence switch (*++p) { case 'r': case 'n': case 't': case 'f': case 'v': case 'b': case '\'': case '"': case '\\': case 'x': if (instring) { putc( '"', fasm ); putc( ',', fasm ); instring = 0; } if (*p == 'x') { p++; if (*p && *(p+1)) fprintf ( fasm, "0%c%ch", *p, *(p+1)); else puts ("Error in Hex Constant"); p++; } else if (*p == 'r') fprintf( fasm, "13" ); else if (*p == 'n') fprintf( fasm, "10" ); else if (*p == 't') fprintf( fasm, "9" ); else if (*p == 'f') fprintf( fasm, "12" ); else if (*p == 'v') fprintf( fasm, "11" ); else if (*p == 'b') fprintf( fasm, "8" ); else if (*p == '\'') fprintf( fasm, "39" ); else if (*p == '"') fprintf( fasm, "34" ); else if (*p == '\\') fprintf( fasm, "92" ); putc( ',', fasm ); break; case '\0': //not an error, warning ... fprintf(stderr, ex, buf); p--; break; default: if (!instring) { putc( '"', fasm ); instring = 1; } putc( *p, fasm ); break; } // switch } //if (*p == '\\') else if (instring) // keep building string { putc( *p, fasm ); if (IsLeadByte(*p)) putc( *++p, fasm ); } else // start building string { putc( '"', fasm ); instring = 1; putc( *p, fasm ); if (IsLeadByte(*p)) putc( *++p, fasm ); } } // for if (instring) // close string { putc( '"', fasm ); putc( ',', fasm ); } putc( '0', fasm ); putc( '\n', fasm ); // calculate padding // depends on msg length if (opt.min || opt.max) { if (opt.min) if (length <= 10) factor = 1.01; else if (length <= 20) factor = 0.81; else if (length <= 30) factor = 0.61; else if (length <= 50) factor = 0.41; else if (length <= 70) factor = 0.31; else factor = 0.30; else if (length <= 10) factor = 2.00; else if (length <= 20) factor = 1.00; else if (length <= 30) factor = 0.80; else if (length <= 50) factor = 0.60; else if (length <= 70) factor = 0.40; else factor = 0.30; result = (double)length * factor; if (asmstate == 1 || opt.use32) { npad += (int)result; if (result > (float)((int)result)) npad++; } else if (asmstate == 2) { fpad += (int)result; if (result > (float)((int)result)) fpad++; } } // if (opt.min || opt.max) } // if (fasm)... } // Something to do } // while read line if (fmsg) { // finish up -msg stuff fprintf(fmsg, "{0, NULL}\n};\n"); } if (fc) // finish up -c stuff { fprintf(fc, "{0, NULL}\n};\n\n"); fprintf(fc, "char * __NMSG_TEXT(\n" ); fprintf(fc, "unsigned msgId\n" ); fprintf(fc, ") {\n" ); fprintf(fc, " MESSAGE *pMsg = __MSGTAB;\n" ); fprintf(fc, "\n" ); fprintf(fc, " for (;pMsg->id; pMsg++) {\n" ); fprintf(fc, " if (pMsg->id == msgId)\n" ); fprintf(fc, " break;\n" ); fprintf(fc, " }\n" ); fprintf(fc, " return pMsg->str;\n" ); fprintf(fc, "}\n" ); } // if (fc) if (fasm) // finish up asm file { if (asmstate == 1 || opt.use32) fprintf( fasm, "MSG ends\n\n"); else if (asmstate == 2) fprintf( fasm, "FAR_MSG ends\n\n"); if (npad) { // add near padding fprintf( fasm, "PAD segment\n\tdb\t%d dup(0)\n", npad ); fprintf( fasm, "PAD ends\n\n" ); } if (fpad) { // add far padding fprintf( fasm, "FAR_PAD segment\n\tdb\t%d dup(0)\n", fpad ); fprintf( fasm, "FAR_PAD ends\n\n" ); } fprintf( fasm, "\tend\n" ); fclose( fasm ); } // if (fasm) if (fh) { if (opt.c) fprintf (fh, "\n#define GET_MSG(x) __NMSG_TEXT(x)\n"); fclose( fh ); } if (frc) { fprintf(frc, "END\n"); fclose( frc ); } if (fx) fclose( fx ); if (finc) fclose( finc ); if (fmsg) fclose( fmsg ); if (fc) fclose( fc ); if (ferr) fclose( ferr ); fclose( f ); return(cError ? 1 : 0); } // main() void parseCommandLine( unsigned argc, char **argv, STATUS *opt ) { // skip argv[0] argc--; argv++; while (argc && **argv == '-') // process options { if (!strcmp("-err", *argv)) // Create .err file { argc--; argv++; if (!argc) Error("no -err file given\n"); else if (opt->err) Error("extra -err for %s ignored\n", *argv); else { opt->err = *argv; argc--; argv++; } } // -err else if (!strcmp( "-h", *argv )) { // create .h file argc--; argv++; if (!argc) Error( "no -h file given\n" ); else if (opt->h) { Error( "extra -h file %s ignored\n", *argv ); argc--; argv++; } else { // remember -h file opt->h = *argv; argc--; argv++; } } else if (!strcmp("-rc", *argv)) { // Create .rc file argc--; argv++; if (!argc) Error("no -rc file given\n"); else if (opt->rc) Error("extra -rc for %s ignored\n", *argv); else { opt->rc = *argv; argc--; argv++; } } else if (!strcmp( "-x", *argv )) { // create .h file argc--; argv++; if (!argc) Error( "no -x file given\n" ); else if (opt->x) { Error( "extra -x file %s ignored\n", *argv ); argc--; argv++; } else { // remember -x file opt->x = *argv; argc--; argv++; } } else if (!strcmp( "-inc", *argv )) { // create .inc file argc--; argv++; if (!argc) Error( "no -inc file given\n" ); else if (opt->inc) { Error( "extra -inc file %s ignored\n", *argv ); argc--; argv++; } else { // remember -inc file opt->inc = *argv; argc--; argv++; } } else if (!strcmp( "-msg", *argv )) { // create .h file with struct argc--; argv++; if (!argc) Error( "no -msg file given\n" ); else if (opt->msg) { Error( "extra -msg file %s ignored\n", *argv ); argc--; argv++; } else { // remember -msg file opt->msg = *argv; argc--; argv++; } } else if (!strcmp( "-c", *argv )) { // create .c file with struct and function argc--; argv++; if (!argc) Error( "no -c file given\n" ); else if (opt->c) { Error( "extra -c file %s ignored\n", *argv ); argc--; argv++; } else { // remember -c file opt->c = *argv; argc--; argv++; } } else if (!strcmp( "-asm", *argv )) { // create .asm file argc--; argv++; if (!argc) Error( "no -asm file given\n" ); else if (opt->asm) { Error( "extra -asm file %s ignored\n", *argv ); argc--; argv++; } else { // remember -asm file opt->asm = *argv; argc--; argv++; } } else if (!strcmp( "-def", *argv )) { argc--; argv++; if (!argc) Error( "no -def string given\n" ); else { opt->def = *argv; argc--; argv++; } } else if (!strcmp( "-min", *argv )) { // minimum padding argc--; argv++; if (opt->min) Error( "redundant -min\n" ); opt->min = 1; } else if (!strcmp( "-max", *argv )) { // maximum padding argc--; argv++; if (opt->max) Error( "redundant -max\n" ); opt->max = 1; } else if (!strcmp( "-386", *argv)) { // 32-bit segments argc--; argv++; if (opt->use32) Error( "redundant -386\n" ); opt->use32 = 1; } else if (!strcmp( "-hex", *argv)) { // hex rendering of text argc--; argv++; if (opt->hex) Error( "redundant -hex\n" ); opt->hex = 1; } else { Error( "unknown option %s ignored\n", *argv ); argc--; argv++; } } // while if (!argc) { // no arguments Error( usage, szVersionNo, szCopyRightYrs ); exit( -1 ); } if (argc != 1) // extra arguments Error( "ignoring extra arguments\n" ); opt->txt = *argv; } // ParseCommandLine() // // Read One line into global buf // int ReadLine (FILE * fp) { int i = 0; int ch; static char szBuffer[INBUFSIZE]; while ((ch = getc( fp )) != EOF && ch != '\r' && ch != '\n' && ch != '\x1A') if (i < INBUFSIZE-1) szBuffer[i++] = (char)ch; if (ch == EOF && i == 0) return EOF; if (ch == '\r') getc ( fp ); // Flush line feed szBuffer[i] = '\0'; // Skip initial space for (buf = szBuffer; *buf && isspace(*buf); buf++) i--; return i; } void __cdecl Error( char *fmt, ... ) { va_list args; va_start (args, fmt); vfprintf(stderr, fmt, args); ++cError; } ///// msg_fputs // // Purpose: // // Send string to file in the given format. // ////////////////////////////////////////////////////////////////////////// void msg_fputs( unsigned char * pch, int style, FILE * fp ) { char chbuf[8]; unsigned char bInDBCS = FALSE; static int bPrevWasHex = FALSE; switch (style) { case MSGSTYLE_COPY: fputs (pch, fp); break; case MSGSTYLE_C_HEX: for (;*pch; pch++) { // If a lead byte table was specified, we use hex // only for double-byte characters, and for hex // digits after hex output. This later is because // hex constants terminate only when a non-hex digit // (like a \) is encountered. // if (!bUseLeadByteTable || bInDBCS || IsLeadByte(*pch) || (bPrevWasHex && isxdigit(*pch))) { sprintf (chbuf, "\\x%2.2x", *pch); fputs (chbuf, fp); bInDBCS = bInDBCS ? FALSE : IsLeadByte(*pch); bPrevWasHex = TRUE; } else { fputc(*pch, fp); bPrevWasHex = FALSE; } } break; case MSGSTYLE_ASM_TEXT: // UNDONE case MSGSTYLE_ASM_BYTE: // UNDONE break; } } ///// SetCodePage // // Purpose: // // Switch to the specified codepage so that we can recognize a lead // byte using IsLeadByte(). // // Parameters: // const char *pszCodePage: Points to a buffer containing the codepage // in the the format of ".xxx" where xxx is // the codepage number. // // Note: // This function replaces the old SetLeadByteTable(), which fills // the global lead byte table with values the user supplied in the // message file. // /////////////////////////////////////////////////////////////////////////// void SetCodePage(const char *pszCodePage) { unsigned i; unsigned uCodePage; if (!setlocale(LC_ALL, pszCodePage)) // Switch to new locale { //__setmbctable(atoi(pszCodePage+1)); // Failed, use undoc'ed call // Failed, use internal codepage table uCodePage = atoi(pszCodePage+1); for (i = 0; cpTable[i].uCodePage; i++) { if (cpTable[i].uCodePage == uCodePage) // Found { pCP = &cpTable[i]; break; } } // for if (0 == cpTable[i].uCodePage) fprintf(stderr, "WARNING: unknown codepage: %s\n", pszCodePage+1); } bUseLeadByteTable = TRUE; } #ifdef VERBOSE #define DB(x) x #else #define DB(x) #endif BOOL IsLeadByte(unsigned by) { unsigned byIndex; DB(printf("IsLeadByte(0x%02x) ==> ",by)); if (!bUseLeadByteTable) { DB(printf("FALSE\n")); return FALSE; } if (!pCP) #ifdef NT_BUILD puts("Codepage support not implemented"); #else return _ismbblead(by); #endif for (byIndex = 0; pCP->byLead[byIndex]; byIndex += 2) { if (pCP->byLead[byIndex] <= by && by <= pCP->byLead[byIndex+1]) { DB(printf("TRUE\n")); return TRUE; } } DB(printf("FALSE\n")); return FALSE; } ///// ParseLine // // Purpose: // // Break the input line to 3 fields: symbol, number, and message. // The symbol field is optional: if the first non-blank char in // the line is a digit, then the symbol field is set to NULL. // // Assumption: // We assume that the input line has the following formats: // [ SYMBOL] NUMBER MESSAGE // where as the text between the square brackets ([]) is optional. // // Parameters: // char **ppSymbol: Points to the buffer containing the symbol. // *ppSymbol == NULL if there is no symbol. // char **ppNumber: Points to the buffer containing the number. // char **ppMessage: Points to the buffer containing the message. // // Use Global: // buf Contains the line to examine. // // Return Value: // TRUE: The line is a valid line. // FALSE: The line is not a valid line. // /////////////////////////////////////////////////////////////////////////// BOOL ParseLine(char **ppSymbol, char **ppNumber, char **ppMessage) { unsigned char *pBuf = buf; #define SKIP_BLANKS() for ( ; *pBuf && isspace(*pBuf); pBuf++) #define SKIP_TO_BLANKS() for ( ; *pBuf && !isspace(*pBuf); pBuf++) #define CHECK_NULL() if (!*pBuf) return FALSE; SKIP_BLANKS(); // Skip initial blanks CHECK_NULL(); // Blank line? if (!isdigit(*pBuf)) // Symbol? { *ppSymbol = pBuf; SKIP_TO_BLANKS(); CHECK_NULL(); *pBuf++ = '\0'; SKIP_BLANKS(); CHECK_NULL(); } else *ppSymbol = NULL; *ppNumber = pBuf; SKIP_TO_BLANKS(); CHECK_NULL(); *pBuf++ = '\0'; SKIP_BLANKS(); CHECK_NULL(); *ppMessage = pBuf; // Handle quoted message: if the message does not begin with quote, it is // extended to end of line. If it begins with a double quote character, // then it is extended to the closing quote, or end of line, whichever // comes first. While scanning for closing quote, we ignore the '\"', // which is the literal quote character. if ('\"' == *pBuf) // quoted message { *ppMessage = ++pBuf; while (*pBuf && '\"' != *pBuf) { if ('\\' == *pBuf || IsLeadByte(*pBuf)) pBuf++; if (*pBuf) pBuf++; } // while *pBuf = '\0'; } // if ... quoted message return TRUE; } ///// HandleDirectives // // Purpose: // // Determine if a line contains a directive, then carry out the // directive's command. // // Use Global: // buf Contains the line to examine. // // Return Value: // TRUE: The line is a directive. // FALSE: The line is not a directive // /////////////////////////////////////////////////////////////////////////// BOOL HandleDirectives(void) { register unsigned char *pBuf = buf; unsigned char *pEnd; for ( ; *pBuf && isspace(*pBuf); pBuf++) // Skip leading spaces ; if (!_strnicmp("!codepage", pBuf, 9)) // Change the codepage { for (pBuf += 9; *pBuf && isspace(*pBuf); pBuf++) // Skip spaces ; *--pBuf = '.'; for (pEnd = pBuf + 1; *pEnd && isdigit(*pEnd); pEnd++) ; *pEnd = '\0'; SetCodePage(pBuf); return TRUE; } else if ('!' == *pBuf) { Error("Unrecognized directive: '%s'\n", pBuf); return TRUE; } return FALSE; } // HandleDirectives