|
|
/*** 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) "<<NMSG>>" -- use near message segment * 2) "<<FMSG>>" -- 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) "Handle<White space>Number<White space>MessageText" -- associate MessageText with * message Id Number and the symbol Handle * 7) "Number<White space>MessageText" -- 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 <<NMSG>> or * <<FMSG>> is specified. The default is <<NMSG>>. * * 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 <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <locale.h>
#include <mbctype.h>
// 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 <stdio.h>\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] == '<') // <<NMSG>> or <<FMSG>>
{ if (!strcmp( "<<NMSG>>", 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( "<<FMSG>>", 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:
// [<White space> SYMBOL] <White space> NUMBER <White space> 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
|