Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1250 lines
52 KiB

  1. /*** MkMsg.c - Microsoft Make Messages Utility *******************************
  2. *
  3. * Copyright (c) 1986-1992, Microsoft Corporation. All Rights Reserved.
  4. *
  5. * Purpose:
  6. * MkMsg.Exe is a utility that helps localize messages emitted by a utility by
  7. * padding them and placing them in message segments.
  8. * MkMsg also allows a programmer to associate symbols and Message Id to a
  9. * message text. It does this by producing a C style header file with #defines
  10. * or a Masm style include file with symbol definitions.
  11. *
  12. * Revision History:
  13. * 02-Oct-1996 DS Remove special '\\' handling in RC output.
  14. * 02-Feb-1994 HV Handle lines with only blansk.
  15. * 25-Jan-1994 HV Add -err to generate external error file in the format:
  16. * NUMBER\tMESSAGE
  17. * 24-Jan-1994 HV Allow quoted message
  18. * 21-Jan-1994 HV Nuke Lead Byte Table [[ ... ]], add !codepage directive
  19. * 21-Jan-1994 HV Field separated by white spaces
  20. * 19-Aug-1993 BW Add Lead Byte Table support
  21. * 07-Apr-1993 BW Add -hex, -c options
  22. * 13-Jul-1992 SB Add -msg option
  23. * 16-Apr-1990 SB Add header
  24. * 13-Apr-1990 SB Collated 6 different versions seperately SLM'ed on \\ODIN
  25. * 19-Apr-1989 LN add "\xhh" recognition
  26. * ??-Apr-1986 RN Created by Randy Nevin
  27. *
  28. * Syntax:
  29. * MKMSG [-h cfile] [-x xcfile] [-inc afile] [-msg cfile] [-c cfile]
  30. * [-asm srcfile [-def str] [-min|-max]] [-386] [-hex] txtfile
  31. *
  32. * Notes:
  33. * The Utility takes as input a message file (a .txt file by convention) and
  34. * produces an assembler source file. The lines in the message file have one
  35. * of the formats which are instructions to MkMsg as follows -
  36. *
  37. * 1) "<<NMSG>>" -- use near message segment
  38. * 2) "<<FMSG>>" -- use far message segment
  39. * ----- Obsolete, see 8)
  40. * 3) "[[XX, YY, ZZ]] - Use hex bytes XX, YY, ZZ as lead byte table.
  41. * Lists are cumulative, empty list clears table.
  42. * -----
  43. * 4) "#Anything" -- comment line (ignored by mkmsg)
  44. * 5) "" -- blank line (ignored by mkmsg)
  45. * 6) "Handle<White space>Number<White space>MessageText" -- associate MessageText with
  46. * message Id Number and the symbol Handle
  47. * 7) "Number<White space>MessageText" -- associate MessageText with Id
  48. * Id Number
  49. * 8) "!codepage xxx" -- use this instead of [[ ... ]]
  50. *
  51. * Options:
  52. * The options can be specified in any order. When no options are specified
  53. * then the input is checked for syntactic validity.
  54. *
  55. *
  56. * -h cfile: create a C style header file cfile with
  57. * #define Handle IdNumber
  58. * If -c is also defined, a declaration for __NMSG_TEXT
  59. * and a mcros defining GET_MSG as __NMSG_TEXT is added.
  60. * -msg cfile: create a C style header file cfile with
  61. * array of struct having fields id and str
  62. * -c cfile: create a file like -msg and add a definition
  63. * of a retrieval function
  64. * -x xcfile: create a C style header file xcfile with
  65. * #define Handle MessageText
  66. * -inc afile: create a MASM style include file afile with
  67. * Handle = IdNumber
  68. * -asm srcfile: create a MASM source file srcfile with standard segment
  69. * definitions and messages in near segment (MSG) or far
  70. * segment (FAR_MSG) depending on whether <<NMSG>> or
  71. * <<FMSG>> is specified. The default is <<NMSG>>.
  72. *
  73. * If -min(-max) is specified then the minimum(maximum)
  74. * 0-padding is calculated and placed in srcfile. This
  75. * depends on the total length of the individual messages.
  76. *
  77. * If -def is specified the public symbol str gets declared.
  78. * This is useful if the the object file produced from the
  79. * MASM source produced is part of a library. The linker
  80. * will link in that module from the library only if it
  81. * resolves some external references.
  82. *
  83. * If -hex is specified and no Lead Byte Table is
  84. * present, all MessageText is rendered in printf-style
  85. * hexidecimal (e.g. "error" becomes
  86. * x65\x72\x72\x6f\x72").
  87. *
  88. * if -hex is specified and a Lead Byte Table is present
  89. * even if it is empty), then all Double Byte characters
  90. * are rendering in hexidecimal.
  91. *
  92. * Future Directions:
  93. * 1> Break up main() into smaller routines
  94. * 2> Use mkmsg for handling messages
  95. * 2> Allow use of spaces as separators, -- done. HV.
  96. * 3> Allow specification of the separator
  97. * 4> Provide -? and -help options.
  98. *
  99. *****************************************************************************/
  100. // DEFINEs
  101. // Standard INCLUDEs
  102. #include <stdarg.h>
  103. #include <stdio.h>
  104. #include <stdlib.h>
  105. #include <string.h>
  106. #include <ctype.h>
  107. #include <locale.h>
  108. #include <mbctype.h>
  109. // Project INCLUDEs
  110. // MACROs
  111. // External PROTOTYPEs
  112. // TYPEDEFs
  113. typedef enum { FALSE = 0, TRUE = !0 } BOOL;
  114. typedef struct status {
  115. char *txt;
  116. char *h;
  117. char *rc;
  118. char *x;
  119. char *inc;
  120. char *msg;
  121. char *c;
  122. char *asm;
  123. char *def;
  124. char *err;
  125. int min;
  126. int max;
  127. int use32;
  128. int hex;
  129. } STATUS;
  130. // Global PROTOTYPEs
  131. void __cdecl Error(char *fmt, ...);
  132. int __cdecl main(int argc, char **argv);
  133. void parseCommandLine(unsigned argc, char **argv, STATUS *opt);
  134. void msg_fputs (unsigned char * pch, int, FILE * fp);
  135. int ReadLine (FILE * fp);
  136. BOOL ParseLine(char **ppSymbol, char **ppNumber, char **ppMessage);
  137. BOOL HandleDirectives(void);
  138. void SetCodePage (const char *pszCodePage);
  139. BOOL IsLeadByte(unsigned by);
  140. // WARNING: the following CRT function is undocumented. This call can only
  141. // be used in our internal product (like this one) only. Please
  142. // contact CRT people for more information.
  143. //void __cdecl __setmbctable(unsigned int);
  144. //
  145. // Lead Byte support
  146. //
  147. unsigned char bUseLeadByteTable = FALSE;
  148. typedef struct
  149. {
  150. unsigned uCodePage; // Codepage number
  151. unsigned byLead[12]; // Leadbyte ranges
  152. } CPTABLE;
  153. CPTABLE cpTable[] =
  154. {
  155. {932, {0x81, 0x9f, 0xe0, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
  156. {0, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}} // trailing to end
  157. };
  158. CPTABLE * pCP = NULL;
  159. // Local PROTOTYPEs
  160. // External VARIABLEs
  161. // Initialized VARIABLEs
  162. int cError = 0;
  163. char didnear = 0;
  164. char didfar = 0;
  165. char dir32[] = ".386\n";
  166. char ex[] = "expected escape sequence: %s\n";
  167. char f1[] = "FAR_HDR segment byte public \'FAR_MSG\'\nFAR_HDR ends\n";
  168. char f2[] = "FAR_MSG segment byte public \'FAR_MSG\'\nFAR_MSG ends\n";
  169. char f3[] = "FAR_PAD segment byte public \'FAR_MSG\'\nFAR_PAD ends\n";
  170. char f4[] = "FAR_EPAD segment byte common \'FAR_MSG\'\nFAR_EPAD ends\n";
  171. char f5[] = "FMGROUP group FAR_HDR,FAR_MSG,FAR_PAD,FAR_EPAD\n\n";
  172. char n1[] = "HDR segment byte public \'MSG\'\nHDR ends\n";
  173. char n2[] = "MSG segment byte public \'MSG\'\nMSG ends\n";
  174. char n3[] = "PAD segment byte public \'MSG\'\nPAD ends\n";
  175. char n4[] = "EPAD segment byte common \'MSG\'\nEPAD ends\n";
  176. char n5[] = "DGROUP group HDR,MSG,PAD,EPAD\n\n";
  177. char usage[] = "\nMicrosoft (R) Message Creation Utility Version %s"
  178. "\nCopyright (c) Microsoft Corp %s. All rights reserved.\n"
  179. "\nusage: MKMSG [-h cfile] [-rc rcfile] [-x xcfile] [-msg cfile]\n"
  180. "\t[-c cfile] [-err errfile] [-inc afile]\n"
  181. "\t[-asm srcfile [-def str] [-min|-max]] [-386] [-hex] txtfile\n";
  182. char szVersionNo[] = "1.00.0011";
  183. char szCopyRightYrs[] = "1986-1996";
  184. // Local VARIABLEs
  185. #define MSGSTYLE_COPY 0
  186. #define MSGSTYLE_C_HEX 1
  187. #define MSGSTYLE_ASM_BYTE 2
  188. #define MSGSTYLE_ASM_TEXT 3
  189. #define FALSE 0
  190. #define TRUE (!FALSE)
  191. #define INBUFSIZE 1024
  192. //static char buf[INBUFSIZE]; // line buffer
  193. char *buf; // The real buffer is in ReadLine()
  194. //--------------------------------------------------------------------------
  195. int __cdecl
  196. main(int argc, char **argv)
  197. {
  198. FILE *f; // the input file
  199. FILE *fh = NULL; // -h stream
  200. FILE *frc = NULL; // -rc stream
  201. FILE *fx = NULL; // -x stream
  202. FILE *fc = NULL; // -c stream
  203. FILE *finc = NULL; // -inc stream
  204. FILE *fmsg = NULL; // -msg stream
  205. FILE *fasm = NULL; // -asm stream
  206. FILE *ferr = NULL; // -err stream
  207. int asmstate = 0; // 0=nothing, 1=doing nmsg, 2=doing fmsg
  208. int instring; // db "...
  209. unsigned int nLineCur = 0; // Current line
  210. unsigned int cchLine; // Chars in current line
  211. char *p;
  212. int npad = 0; // cumulative amount of near padding
  213. int fpad = 0; // cumulative amount of far padding
  214. int length;
  215. double factor;
  216. double result;
  217. static STATUS opt; // Command line options
  218. parseCommandLine(argc, argv, &opt);
  219. if ((opt.def || opt.min || opt.max ) && !opt.asm) {
  220. Error( "-def/-min/-max ignored; no -asm file\n" );
  221. opt.min = opt.max = 0;
  222. }
  223. if (opt.min && opt.max) {
  224. Error( "-min and -max are mutually exclusive; -min chosen\n" );
  225. opt.max = 0;
  226. }
  227. if (!(f = fopen( opt.txt, "rb" ))) {
  228. Error( "can't open txtfile %s for binary reading\n", opt.txt );
  229. return( -1 );
  230. }
  231. if (opt.asm && !(fasm = fopen( opt.asm, "w" ))) {
  232. Error( "can't open -asm file %s for writing\n", opt.asm );
  233. return( -1 );
  234. }
  235. if (opt.h && !(fh = fopen( opt.h, "w" ))) {
  236. Error( "can't open -h file %s for writing\n", opt.h );
  237. return( -1 );
  238. }
  239. if (fh && opt.c) {
  240. fprintf (fh, "char * __NMSG_TEXT (unsigned);\n\n");
  241. }
  242. if (opt.rc && !(frc = fopen(opt.rc, "w")))
  243. {
  244. Error("Can't open -rc file %s for writing\n", opt.rc);
  245. return -1;
  246. }
  247. if (opt.rc) {
  248. fprintf(frc, "STRINGTABLE\nBEGIN\n");
  249. }
  250. if (opt.x && !(fx = fopen( opt.x, "w" ))) {
  251. Error( "can't open -x file %s for writing\n", opt.x );
  252. return( -1 );
  253. }
  254. if (opt.inc && !(finc = fopen( opt.inc, "w" ))) {
  255. Error( "can't open -inc file %s for writing\n", opt.inc );
  256. return( -1 );
  257. }
  258. if (opt.msg && !(fmsg = fopen( opt.msg, "w" ))) {
  259. Error( "can't open -msg file %s for writing\n", opt.msg );
  260. return( -1 );
  261. }
  262. if (opt.err && !(ferr = fopen(opt.err, "w")))
  263. {
  264. Error("Can't open -err file %s for writing\n", opt.err);
  265. return -1;
  266. }
  267. if (fmsg) {
  268. fprintf(fmsg, "typedef struct _message {\n"
  269. "\tunsigned\tid;\n"
  270. "\tchar *str;\n"
  271. "} MESSAGE;\n\n");
  272. fprintf(fmsg, "MESSAGE __MSGTAB[] = {\n");
  273. }
  274. if (opt.c && !(fc = fopen( opt.c, "w" ))) {
  275. Error( "can't open -c file %s for writing\n", opt.c );
  276. return( -1 );
  277. }
  278. if (fc) {
  279. fprintf(fc, "#include <stdio.h>\n\n");
  280. fprintf(fc, "typedef struct _message {\n"
  281. "\tunsigned int\tid;\n"
  282. "\tchar *str;\n"
  283. "} MESSAGE;\n\n");
  284. fprintf(fc, "MESSAGE __MSGTAB[] = {\n");
  285. }
  286. while ((cchLine = ReadLine ( f )) != EOF) // process lines
  287. {
  288. nLineCur++;
  289. if (buf[0] == '\0' || buf[0] == '#')
  290. {
  291. continue;
  292. }
  293. else if (HandleDirectives()) // Directive
  294. {
  295. continue;
  296. }
  297. else if (buf[0] == '[' && buf[1] == '[') // Old style leadbyte tbl
  298. {
  299. fprintf(stderr,
  300. "WARNING: Ignore leadbyte table, use !codepage instead: %s\n",
  301. buf);
  302. continue;
  303. }
  304. else if (buf[0] == '<') // <<NMSG>> or <<FMSG>>
  305. {
  306. if (!strcmp( "<<NMSG>>", buf )) // near msgs follow
  307. {
  308. if (asmstate == 0)
  309. {
  310. if (fasm)
  311. {
  312. if (!didnear) {
  313. didnear++;
  314. if (opt.use32)
  315. fprintf( fasm, dir32);
  316. fprintf( fasm, n1 );
  317. fprintf( fasm, n2 );
  318. fprintf( fasm, n3 );
  319. fprintf( fasm, n4 );
  320. fprintf( fasm, n5 );
  321. }
  322. fprintf( fasm,
  323. "MSG segment\n" );
  324. if (opt.def)
  325. {
  326. fprintf( fasm, "\tpublic\t%s\n", opt.def );
  327. fprintf( fasm, "%s\tequ\t$\n", opt.def );
  328. }
  329. asmstate = 1;
  330. } // if (fasm)
  331. } // if (asmstate == 0)
  332. else if (asmstate == 1)
  333. {
  334. Error( "already in nmsg\n" );
  335. }
  336. else if (asmstate == 2 && !opt.use32)
  337. {
  338. if (fasm)
  339. {
  340. fprintf( fasm, "FAR_MSG ends\n\n" );
  341. if (!didnear)
  342. {
  343. didnear++;
  344. fprintf( fasm, n1 );
  345. fprintf( fasm, n2 );
  346. fprintf( fasm, n3 );
  347. fprintf( fasm, n4 );
  348. fprintf( fasm, n5 );
  349. }
  350. fprintf( fasm, "MSG segment\n" );
  351. asmstate = 1;
  352. } // if (fasm)
  353. } // else if (asmstate == 2 ...)
  354. else
  355. {
  356. Error( "internal error\n" );
  357. return( -1 );
  358. }
  359. } // if near msg
  360. else if (!strcmp( "<<FMSG>>", buf ))//far msgs follow
  361. {
  362. if (asmstate == 0)
  363. {
  364. if (fasm)
  365. {
  366. if (!didfar)
  367. {
  368. didfar++;
  369. if (opt.use32)
  370. {
  371. fprintf( fasm, dir32);
  372. fprintf( fasm, n1 );
  373. fprintf( fasm, n2 );
  374. fprintf( fasm, n3 );
  375. fprintf( fasm, n4 );
  376. fprintf( fasm, n5 );
  377. fprintf( fasm, "MSG segment\n" );
  378. }
  379. else
  380. {
  381. fprintf( fasm, f1 );
  382. fprintf( fasm, f2 );
  383. fprintf( fasm, f3 );
  384. fprintf( fasm, f4 );
  385. fprintf( fasm, f5 );
  386. fprintf( fasm, "FAR_MSG segment\n" );
  387. }
  388. } // if (!didfar)
  389. if (opt.def)
  390. {
  391. fprintf( fasm, "\tpublic\t%s\n", opt.def );
  392. fprintf( fasm, "%s\tequ\t$\n", opt.def );
  393. }
  394. asmstate = 2;
  395. } // if (fasm)
  396. } // if (asmstate == 0)
  397. else if (asmstate == 1 && !opt.use32)
  398. {
  399. if (fasm)
  400. {
  401. fprintf( fasm, "MSG ends\n\n" );
  402. if (!didfar)
  403. {
  404. didfar++;
  405. fprintf( fasm, f1 );
  406. fprintf( fasm, f2 );
  407. fprintf( fasm, f3 );
  408. fprintf( fasm, f4 );
  409. fprintf( fasm, f5 );
  410. }
  411. fprintf( fasm, "FAR_MSG segment\n" );
  412. asmstate = 2;
  413. }
  414. } // else if (asmstate == 1 ...)
  415. else if (asmstate == 2)
  416. {
  417. Error( "already in far_msg\n" );
  418. }
  419. else
  420. {
  421. Error( "internal error\n" );
  422. return( -1 );
  423. }
  424. } // far message
  425. else // Not near, not far
  426. {
  427. Error( "ignoring bad line: %s\n", buf );
  428. }
  429. } // if (.. < ) near/far message
  430. else if (buf[0] != '\r' && buf[0] != '\n') // something to do
  431. {
  432. char *pSymbol;
  433. char *pNumber;
  434. char *pMessage;
  435. if (!ParseLine(&pSymbol, &pNumber, &pMessage))
  436. {
  437. fprintf( stderr, "%s(%d): error in line: \"%s\"\n", opt.txt, nLineCur, buf);
  438. continue;
  439. }
  440. if (pSymbol && opt.h)
  441. {
  442. fprintf( fh, "#define\t%s\t%s\n", pSymbol, pNumber );
  443. }
  444. if (opt.rc)
  445. {
  446. fprintf(frc, "\t%s, \"%s\"\n", pNumber, pMessage);
  447. }
  448. if (pSymbol && opt.x)
  449. {
  450. fprintf( fx, "#define\t%s\t\"", pSymbol );
  451. msg_fputs( pMessage
  452. , opt.hex ? MSGSTYLE_C_HEX : MSGSTYLE_COPY
  453. , fx );
  454. putc( '\"', fx );
  455. putc( '\n', fx );
  456. }
  457. if (pSymbol && opt.inc)
  458. {
  459. fprintf( finc, "%s\t=\t%s\n", pSymbol, pNumber);
  460. }
  461. if (opt.msg)
  462. {
  463. fprintf( fmsg, "{%s, \"", pNumber );
  464. msg_fputs( pMessage, opt.hex ? MSGSTYLE_C_HEX : MSGSTYLE_COPY, fmsg );
  465. fprintf( fmsg, "\"}," );
  466. if (opt.hex)
  467. fprintf( fmsg, " // \"%s\"", pMessage );
  468. putc ( '\n', fmsg );
  469. }
  470. if (opt.c)
  471. {
  472. fprintf( fc, "{%s, \"", pNumber );
  473. msg_fputs( pMessage, opt.hex ? MSGSTYLE_C_HEX : MSGSTYLE_COPY, fc );
  474. fprintf( fc, "\"}," );
  475. if (opt.hex)
  476. fprintf( fc, " // \"%s\"", pMessage );
  477. putc ( '\n', fc );
  478. }
  479. if (opt.err)
  480. {
  481. fprintf(ferr, "%s\t\"%s\"\n", pNumber, pMessage);
  482. } // opt.err
  483. if (fasm) // write asmfile
  484. {
  485. if (asmstate == 0)
  486. {
  487. if (!didnear)
  488. {
  489. didnear++;
  490. if (opt.use32)
  491. fprintf( fasm, dir32);
  492. fprintf( fasm, n1 );
  493. fprintf( fasm, n2 );
  494. fprintf( fasm, n3 );
  495. fprintf( fasm, n4 );
  496. fprintf( fasm, n5 );
  497. }
  498. fprintf( fasm, "MSG segment\n" );
  499. if (opt.def)
  500. {
  501. fprintf( fasm, "\tpublic\t%s\n", opt.def );
  502. fprintf( fasm, "%s\tequ\t$\n", opt.def );
  503. }
  504. asmstate = 1;
  505. } // if (asmstate == 0)
  506. fprintf( fasm, "\tdw\t%s\n\tdb\t", pNumber );
  507. instring = 0;
  508. for (p = pMessage, length = 0; *p; p++, length++)
  509. {
  510. // allocate message
  511. if (*p == '\\')
  512. {
  513. // C escape sequence
  514. switch (*++p)
  515. {
  516. case 'r':
  517. case 'n':
  518. case 't':
  519. case 'f':
  520. case 'v':
  521. case 'b':
  522. case '\'':
  523. case '"':
  524. case '\\':
  525. case 'x':
  526. if (instring) {
  527. putc( '"', fasm );
  528. putc( ',', fasm );
  529. instring = 0;
  530. }
  531. if (*p == 'x') {
  532. p++;
  533. if (*p && *(p+1))
  534. fprintf ( fasm, "0%c%ch", *p, *(p+1));
  535. else
  536. puts ("Error in Hex Constant");
  537. p++;
  538. }
  539. else if (*p == 'r')
  540. fprintf( fasm, "13" );
  541. else if (*p == 'n')
  542. fprintf( fasm, "10" );
  543. else if (*p == 't')
  544. fprintf( fasm, "9" );
  545. else if (*p == 'f')
  546. fprintf( fasm, "12" );
  547. else if (*p == 'v')
  548. fprintf( fasm, "11" );
  549. else if (*p == 'b')
  550. fprintf( fasm, "8" );
  551. else if (*p == '\'')
  552. fprintf( fasm, "39" );
  553. else if (*p == '"')
  554. fprintf( fasm, "34" );
  555. else if (*p == '\\')
  556. fprintf( fasm, "92" );
  557. putc( ',', fasm );
  558. break;
  559. case '\0':
  560. //not an error, warning ...
  561. fprintf(stderr, ex, buf);
  562. p--;
  563. break;
  564. default:
  565. if (!instring) {
  566. putc( '"', fasm );
  567. instring = 1;
  568. }
  569. putc( *p, fasm );
  570. break;
  571. } // switch
  572. } //if (*p == '\\')
  573. else if (instring) // keep building string
  574. {
  575. putc( *p, fasm );
  576. if (IsLeadByte(*p))
  577. putc( *++p, fasm );
  578. }
  579. else // start building string
  580. {
  581. putc( '"', fasm );
  582. instring = 1;
  583. putc( *p, fasm );
  584. if (IsLeadByte(*p))
  585. putc( *++p, fasm );
  586. }
  587. } // for
  588. if (instring) // close string
  589. {
  590. putc( '"', fasm );
  591. putc( ',', fasm );
  592. }
  593. putc( '0', fasm );
  594. putc( '\n', fasm );
  595. // calculate padding
  596. // depends on msg length
  597. if (opt.min || opt.max)
  598. {
  599. if (opt.min)
  600. if (length <= 10)
  601. factor = 1.01;
  602. else if (length <= 20)
  603. factor = 0.81;
  604. else if (length <= 30)
  605. factor = 0.61;
  606. else if (length <= 50)
  607. factor = 0.41;
  608. else if (length <= 70)
  609. factor = 0.31;
  610. else
  611. factor = 0.30;
  612. else if (length <= 10)
  613. factor = 2.00;
  614. else if (length <= 20)
  615. factor = 1.00;
  616. else if (length <= 30)
  617. factor = 0.80;
  618. else if (length <= 50)
  619. factor = 0.60;
  620. else if (length <= 70)
  621. factor = 0.40;
  622. else
  623. factor = 0.30;
  624. result = (double)length * factor;
  625. if (asmstate == 1 || opt.use32)
  626. {
  627. npad += (int)result;
  628. if (result > (float)((int)result))
  629. npad++;
  630. }
  631. else if (asmstate == 2)
  632. {
  633. fpad += (int)result;
  634. if (result > (float)((int)result))
  635. fpad++;
  636. }
  637. } // if (opt.min || opt.max)
  638. } // if (fasm)...
  639. } // Something to do
  640. } // while read line
  641. if (fmsg) { // finish up -msg stuff
  642. fprintf(fmsg, "{0, NULL}\n};\n");
  643. }
  644. if (fc) // finish up -c stuff
  645. {
  646. fprintf(fc, "{0, NULL}\n};\n\n");
  647. fprintf(fc, "char * __NMSG_TEXT(\n" );
  648. fprintf(fc, "unsigned msgId\n" );
  649. fprintf(fc, ") {\n" );
  650. fprintf(fc, " MESSAGE *pMsg = __MSGTAB;\n" );
  651. fprintf(fc, "\n" );
  652. fprintf(fc, " for (;pMsg->id; pMsg++) {\n" );
  653. fprintf(fc, " if (pMsg->id == msgId)\n" );
  654. fprintf(fc, " break;\n" );
  655. fprintf(fc, " }\n" );
  656. fprintf(fc, " return pMsg->str;\n" );
  657. fprintf(fc, "}\n" );
  658. } // if (fc)
  659. if (fasm) // finish up asm file
  660. {
  661. if (asmstate == 1 || opt.use32)
  662. fprintf( fasm, "MSG ends\n\n");
  663. else if (asmstate == 2)
  664. fprintf( fasm, "FAR_MSG ends\n\n");
  665. if (npad) { // add near padding
  666. fprintf( fasm, "PAD segment\n\tdb\t%d dup(0)\n", npad );
  667. fprintf( fasm, "PAD ends\n\n" );
  668. }
  669. if (fpad) { // add far padding
  670. fprintf( fasm, "FAR_PAD segment\n\tdb\t%d dup(0)\n", fpad );
  671. fprintf( fasm, "FAR_PAD ends\n\n" );
  672. }
  673. fprintf( fasm, "\tend\n" );
  674. fclose( fasm );
  675. } // if (fasm)
  676. if (fh)
  677. {
  678. if (opt.c)
  679. fprintf (fh, "\n#define GET_MSG(x) __NMSG_TEXT(x)\n");
  680. fclose( fh );
  681. }
  682. if (frc) {
  683. fprintf(frc, "END\n");
  684. fclose( frc );
  685. }
  686. if (fx) fclose( fx );
  687. if (finc) fclose( finc );
  688. if (fmsg) fclose( fmsg );
  689. if (fc) fclose( fc );
  690. if (ferr) fclose( ferr );
  691. fclose( f );
  692. return(cError ? 1 : 0);
  693. } // main()
  694. void
  695. parseCommandLine(
  696. unsigned argc,
  697. char **argv,
  698. STATUS *opt
  699. )
  700. {
  701. // skip argv[0]
  702. argc--; argv++;
  703. while (argc && **argv == '-') // process options
  704. {
  705. if (!strcmp("-err", *argv)) // Create .err file
  706. {
  707. argc--; argv++;
  708. if (!argc)
  709. Error("no -err file given\n");
  710. else if (opt->err)
  711. Error("extra -err for %s ignored\n", *argv);
  712. else
  713. {
  714. opt->err = *argv;
  715. argc--; argv++;
  716. }
  717. } // -err
  718. else if (!strcmp( "-h", *argv )) { // create .h file
  719. argc--; argv++;
  720. if (!argc)
  721. Error( "no -h file given\n" );
  722. else if (opt->h) {
  723. Error( "extra -h file %s ignored\n", *argv );
  724. argc--; argv++;
  725. }
  726. else { // remember -h file
  727. opt->h = *argv;
  728. argc--; argv++;
  729. }
  730. }
  731. else if (!strcmp("-rc", *argv)) { // Create .rc file
  732. argc--; argv++;
  733. if (!argc)
  734. Error("no -rc file given\n");
  735. else if (opt->rc)
  736. Error("extra -rc for %s ignored\n", *argv);
  737. else
  738. {
  739. opt->rc = *argv;
  740. argc--; argv++;
  741. }
  742. }
  743. else if (!strcmp( "-x", *argv )) { // create .h file
  744. argc--; argv++;
  745. if (!argc)
  746. Error( "no -x file given\n" );
  747. else if (opt->x) {
  748. Error( "extra -x file %s ignored\n", *argv );
  749. argc--; argv++;
  750. }
  751. else { // remember -x file
  752. opt->x = *argv;
  753. argc--; argv++;
  754. }
  755. }
  756. else if (!strcmp( "-inc", *argv )) { // create .inc file
  757. argc--; argv++;
  758. if (!argc)
  759. Error( "no -inc file given\n" );
  760. else if (opt->inc) {
  761. Error( "extra -inc file %s ignored\n", *argv );
  762. argc--; argv++;
  763. }
  764. else { // remember -inc file
  765. opt->inc = *argv;
  766. argc--; argv++;
  767. }
  768. }
  769. else if (!strcmp( "-msg", *argv )) { // create .h file with struct
  770. argc--; argv++;
  771. if (!argc)
  772. Error( "no -msg file given\n" );
  773. else if (opt->msg) {
  774. Error( "extra -msg file %s ignored\n", *argv );
  775. argc--; argv++;
  776. }
  777. else { // remember -msg file
  778. opt->msg = *argv;
  779. argc--; argv++;
  780. }
  781. }
  782. else if (!strcmp( "-c", *argv )) { // create .c file with struct and function
  783. argc--; argv++;
  784. if (!argc)
  785. Error( "no -c file given\n" );
  786. else if (opt->c) {
  787. Error( "extra -c file %s ignored\n", *argv );
  788. argc--; argv++;
  789. }
  790. else { // remember -c file
  791. opt->c = *argv;
  792. argc--; argv++;
  793. }
  794. }
  795. else if (!strcmp( "-asm", *argv )) { // create .asm file
  796. argc--; argv++;
  797. if (!argc)
  798. Error( "no -asm file given\n" );
  799. else if (opt->asm) {
  800. Error( "extra -asm file %s ignored\n", *argv );
  801. argc--;
  802. argv++;
  803. }
  804. else { // remember -asm file
  805. opt->asm = *argv;
  806. argc--; argv++;
  807. }
  808. }
  809. else if (!strcmp( "-def", *argv )) {
  810. argc--; argv++;
  811. if (!argc)
  812. Error( "no -def string given\n" );
  813. else {
  814. opt->def = *argv;
  815. argc--; argv++;
  816. }
  817. }
  818. else if (!strcmp( "-min", *argv )) { // minimum padding
  819. argc--; argv++;
  820. if (opt->min)
  821. Error( "redundant -min\n" );
  822. opt->min = 1;
  823. }
  824. else if (!strcmp( "-max", *argv )) { // maximum padding
  825. argc--; argv++;
  826. if (opt->max)
  827. Error( "redundant -max\n" );
  828. opt->max = 1;
  829. }
  830. else if (!strcmp( "-386", *argv)) { // 32-bit segments
  831. argc--; argv++;
  832. if (opt->use32)
  833. Error( "redundant -386\n" );
  834. opt->use32 = 1;
  835. }
  836. else if (!strcmp( "-hex", *argv)) { // hex rendering of text
  837. argc--; argv++;
  838. if (opt->hex)
  839. Error( "redundant -hex\n" );
  840. opt->hex = 1;
  841. }
  842. else {
  843. Error( "unknown option %s ignored\n", *argv );
  844. argc--;
  845. argv++;
  846. }
  847. } // while
  848. if (!argc) { // no arguments
  849. Error( usage, szVersionNo, szCopyRightYrs );
  850. exit( -1 );
  851. }
  852. if (argc != 1) // extra arguments
  853. Error( "ignoring extra arguments\n" );
  854. opt->txt = *argv;
  855. } // ParseCommandLine()
  856. //
  857. // Read One line into global buf
  858. //
  859. int
  860. ReadLine (FILE * fp)
  861. {
  862. int i = 0;
  863. int ch;
  864. static char szBuffer[INBUFSIZE];
  865. while ((ch = getc( fp )) != EOF && ch != '\r' && ch != '\n' && ch != '\x1A')
  866. if (i < INBUFSIZE-1)
  867. szBuffer[i++] = (char)ch;
  868. if (ch == EOF && i == 0)
  869. return EOF;
  870. if (ch == '\r')
  871. getc ( fp ); // Flush line feed
  872. szBuffer[i] = '\0';
  873. // Skip initial space
  874. for (buf = szBuffer; *buf && isspace(*buf); buf++)
  875. i--;
  876. return i;
  877. }
  878. void
  879. __cdecl
  880. Error(
  881. char *fmt,
  882. ...
  883. ) {
  884. va_list args;
  885. va_start (args, fmt);
  886. vfprintf(stderr, fmt, args);
  887. ++cError;
  888. }
  889. ///// msg_fputs
  890. //
  891. // Purpose:
  892. //
  893. // Send string to file in the given format.
  894. //
  895. //////////////////////////////////////////////////////////////////////////
  896. void msg_fputs(
  897. unsigned char * pch,
  898. int style,
  899. FILE * fp
  900. ) {
  901. char chbuf[8];
  902. unsigned char bInDBCS = FALSE;
  903. static int bPrevWasHex = FALSE;
  904. switch (style)
  905. {
  906. case MSGSTYLE_COPY:
  907. fputs (pch, fp);
  908. break;
  909. case MSGSTYLE_C_HEX:
  910. for (;*pch; pch++) {
  911. // If a lead byte table was specified, we use hex
  912. // only for double-byte characters, and for hex
  913. // digits after hex output. This later is because
  914. // hex constants terminate only when a non-hex digit
  915. // (like a \) is encountered.
  916. //
  917. if (!bUseLeadByteTable
  918. || bInDBCS || IsLeadByte(*pch)
  919. || (bPrevWasHex && isxdigit(*pch))) {
  920. sprintf (chbuf, "\\x%2.2x", *pch);
  921. fputs (chbuf, fp);
  922. bInDBCS = bInDBCS ? FALSE : IsLeadByte(*pch);
  923. bPrevWasHex = TRUE;
  924. }
  925. else {
  926. fputc(*pch, fp);
  927. bPrevWasHex = FALSE;
  928. }
  929. }
  930. break;
  931. case MSGSTYLE_ASM_TEXT: // UNDONE
  932. case MSGSTYLE_ASM_BYTE: // UNDONE
  933. break;
  934. }
  935. }
  936. ///// SetCodePage
  937. //
  938. // Purpose:
  939. //
  940. // Switch to the specified codepage so that we can recognize a lead
  941. // byte using IsLeadByte().
  942. //
  943. // Parameters:
  944. // const char *pszCodePage: Points to a buffer containing the codepage
  945. // in the the format of ".xxx" where xxx is
  946. // the codepage number.
  947. //
  948. // Note:
  949. // This function replaces the old SetLeadByteTable(), which fills
  950. // the global lead byte table with values the user supplied in the
  951. // message file.
  952. //
  953. ///////////////////////////////////////////////////////////////////////////
  954. void
  955. SetCodePage(const char *pszCodePage)
  956. {
  957. unsigned i;
  958. unsigned uCodePage;
  959. if (!setlocale(LC_ALL, pszCodePage)) // Switch to new locale
  960. {
  961. //__setmbctable(atoi(pszCodePage+1)); // Failed, use undoc'ed call
  962. // Failed, use internal codepage table
  963. uCodePage = atoi(pszCodePage+1);
  964. for (i = 0; cpTable[i].uCodePage; i++)
  965. {
  966. if (cpTable[i].uCodePage == uCodePage) // Found
  967. {
  968. pCP = &cpTable[i];
  969. break;
  970. }
  971. } // for
  972. if (0 == cpTable[i].uCodePage)
  973. fprintf(stderr, "WARNING: unknown codepage: %s\n", pszCodePage+1);
  974. }
  975. bUseLeadByteTable = TRUE;
  976. }
  977. #ifdef VERBOSE
  978. #define DB(x) x
  979. #else
  980. #define DB(x)
  981. #endif
  982. BOOL
  983. IsLeadByte(unsigned by)
  984. {
  985. unsigned byIndex;
  986. DB(printf("IsLeadByte(0x%02x) ==> ",by));
  987. if (!bUseLeadByteTable)
  988. {
  989. DB(printf("FALSE\n"));
  990. return FALSE;
  991. }
  992. if (!pCP)
  993. #ifdef NT_BUILD
  994. puts("Codepage support not implemented");
  995. #else
  996. return _ismbblead(by);
  997. #endif
  998. for (byIndex = 0; pCP->byLead[byIndex]; byIndex += 2)
  999. {
  1000. if (pCP->byLead[byIndex] <= by && by <= pCP->byLead[byIndex+1])
  1001. {
  1002. DB(printf("TRUE\n"));
  1003. return TRUE;
  1004. }
  1005. }
  1006. DB(printf("FALSE\n"));
  1007. return FALSE;
  1008. }
  1009. ///// ParseLine
  1010. //
  1011. // Purpose:
  1012. //
  1013. // Break the input line to 3 fields: symbol, number, and message.
  1014. // The symbol field is optional: if the first non-blank char in
  1015. // the line is a digit, then the symbol field is set to NULL.
  1016. //
  1017. // Assumption:
  1018. // We assume that the input line has the following formats:
  1019. // [<White space> SYMBOL] <White space> NUMBER <White space> MESSAGE
  1020. // where as the text between the square brackets ([]) is optional.
  1021. //
  1022. // Parameters:
  1023. // char **ppSymbol: Points to the buffer containing the symbol.
  1024. // *ppSymbol == NULL if there is no symbol.
  1025. // char **ppNumber: Points to the buffer containing the number.
  1026. // char **ppMessage: Points to the buffer containing the message.
  1027. //
  1028. // Use Global:
  1029. // buf Contains the line to examine.
  1030. //
  1031. // Return Value:
  1032. // TRUE: The line is a valid line.
  1033. // FALSE: The line is not a valid line.
  1034. //
  1035. ///////////////////////////////////////////////////////////////////////////
  1036. BOOL
  1037. ParseLine(char **ppSymbol, char **ppNumber, char **ppMessage)
  1038. {
  1039. unsigned char *pBuf = buf;
  1040. #define SKIP_BLANKS() for ( ; *pBuf && isspace(*pBuf); pBuf++)
  1041. #define SKIP_TO_BLANKS() for ( ; *pBuf && !isspace(*pBuf); pBuf++)
  1042. #define CHECK_NULL() if (!*pBuf) return FALSE;
  1043. SKIP_BLANKS(); // Skip initial blanks
  1044. CHECK_NULL(); // Blank line?
  1045. if (!isdigit(*pBuf)) // Symbol?
  1046. {
  1047. *ppSymbol = pBuf;
  1048. SKIP_TO_BLANKS();
  1049. CHECK_NULL();
  1050. *pBuf++ = '\0';
  1051. SKIP_BLANKS();
  1052. CHECK_NULL();
  1053. }
  1054. else
  1055. *ppSymbol = NULL;
  1056. *ppNumber = pBuf;
  1057. SKIP_TO_BLANKS();
  1058. CHECK_NULL();
  1059. *pBuf++ = '\0';
  1060. SKIP_BLANKS();
  1061. CHECK_NULL();
  1062. *ppMessage = pBuf;
  1063. // Handle quoted message: if the message does not begin with quote, it is
  1064. // extended to end of line. If it begins with a double quote character,
  1065. // then it is extended to the closing quote, or end of line, whichever
  1066. // comes first. While scanning for closing quote, we ignore the '\"',
  1067. // which is the literal quote character.
  1068. if ('\"' == *pBuf) // quoted message
  1069. {
  1070. *ppMessage = ++pBuf;
  1071. while (*pBuf && '\"' != *pBuf)
  1072. {
  1073. if ('\\' == *pBuf || IsLeadByte(*pBuf))
  1074. pBuf++;
  1075. if (*pBuf)
  1076. pBuf++;
  1077. } // while
  1078. *pBuf = '\0';
  1079. } // if ... quoted message
  1080. return TRUE;
  1081. }
  1082. ///// HandleDirectives
  1083. //
  1084. // Purpose:
  1085. //
  1086. // Determine if a line contains a directive, then carry out the
  1087. // directive's command.
  1088. //
  1089. // Use Global:
  1090. // buf Contains the line to examine.
  1091. //
  1092. // Return Value:
  1093. // TRUE: The line is a directive.
  1094. // FALSE: The line is not a directive
  1095. //
  1096. ///////////////////////////////////////////////////////////////////////////
  1097. BOOL
  1098. HandleDirectives(void)
  1099. {
  1100. register unsigned char *pBuf = buf;
  1101. unsigned char *pEnd;
  1102. for ( ; *pBuf && isspace(*pBuf); pBuf++) // Skip leading spaces
  1103. ;
  1104. if (!_strnicmp("!codepage", pBuf, 9)) // Change the codepage
  1105. {
  1106. for (pBuf += 9; *pBuf && isspace(*pBuf); pBuf++) // Skip spaces
  1107. ;
  1108. *--pBuf = '.';
  1109. for (pEnd = pBuf + 1; *pEnd && isdigit(*pEnd); pEnd++)
  1110. ;
  1111. *pEnd = '\0';
  1112. SetCodePage(pBuf);
  1113. return TRUE;
  1114. }
  1115. else if ('!' == *pBuf)
  1116. {
  1117. Error("Unrecognized directive: '%s'\n", pBuf);
  1118. return TRUE;
  1119. }
  1120. return FALSE;
  1121. } // HandleDirectives