Source code of Windows XP (NT5)
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.

582 lines
13 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. mapmsg.c
  5. Abstract:
  6. This utility will create an input file for MC from specially
  7. formatted include files. This is used to create DLL's which can be
  8. used by the message utilities to get message text to display.
  9. The format of the header files is:
  10. :
  11. :
  12. #define <basename> <basenumber>
  13. :
  14. :
  15. #define <errornum> <basenumber> + <number> /* text of message */
  16. /*
  17. Example:
  18. #define NETBASE 1000
  19. #define NerrFOO NETBASE+1 /* A FOO has been encountered at %1 * /
  20. /*
  21. The mapping tries to be generous about whitespace and parenthesis.
  22. It will also handle comments across several lines. Some important points:
  23. - all continuations must begin with [WS]'*'
  24. any whitespace at the beginning of a message is removed
  25. unless the -p command line option is specified.
  26. - #define .....
  27. /*
  28. * FOO
  29. */
  30. /* is handled correctly.
  31. The command line to MAPMSG is:
  32. mapmsg [-p] [-a appendfile] <system name> <basename> <inputfile>
  33. Example:
  34. mapmsg NET NERRBASE neterr.h > neterr.mc
  35. The <system name> is the 3 character name required by the mkmsg
  36. input. The output is written to stdout. If the append file
  37. is given, the output is appropriately appended to an existing
  38. mkmsgf source file.
  39. An optional @X, X: {E, W, I, P} can be the 1st non-WS chars of the
  40. comment field. The letter (E, W, I, or P) will be the message type.
  41. See MKMSGF documentation for an explaination of the message types.
  42. The default type is E.
  43. The @X must appear on the same line as the #define.
  44. Examples:
  45. #define NerrFOO NETBASE+1 /* @I A FOO has been encountered * /
  46. /*
  47. #define NERR_Foo NETBASE + 2 /* @P
  48. The prompt text: %0 */
  49. /*
  50. The resulting entry in the message file input file will be
  51. NETnnnnI: A FOO has been encountered
  52. Use the DOS message file source convention of XXXnnnn?: for
  53. placeholder messages.
  54. Author:
  55. This was ported from the Lanman utility that was used to create input
  56. files for mkmsgf by:
  57. Dan Hinsley (danhi) 29-Jul-1991
  58. Revision History:
  59. Ronald Meijer (ronaldm) 17-Mar-1993
  60. Added -p option to preserve leading white space characters
  61. --*/
  62. #include <windows.h>
  63. #include <stdio.h>
  64. #include <string.h>
  65. #include <ctype.h>
  66. #include <stdlib.h>
  67. #include "mapmsg.h"
  68. #define USAGE "syntax: mapmsg [-p] [-a appendfile] <system name> <basename> <inputfile>\n"
  69. int Append = FALSE; /* was the -a switch specified */
  70. int Preserve = FALSE; /* TRUE if the -p switch is set */
  71. int
  72. __cdecl main(
  73. int argc,
  74. PCHAR * argv
  75. )
  76. {
  77. int Base;
  78. // Check for -p[reserve whitespace] option
  79. if (argc > 1)
  80. {
  81. if (_stricmp(argv[1], "-p") == 0)
  82. {
  83. ++argv;
  84. --argc;
  85. Preserve = TRUE;
  86. }
  87. }
  88. if (argc == 6)
  89. {
  90. if (_stricmp(argv[1], "-a") != 0)
  91. {
  92. fprintf(stderr, USAGE);
  93. return(1);
  94. }
  95. if (freopen(argv[2], "r+", stdout) == NULL)
  96. {
  97. fprintf(stderr, "Cannot open '%s'\n", argv[2]);
  98. return(1);
  99. }
  100. argv += 2;
  101. argc -= 2;
  102. Append = TRUE;
  103. }
  104. /* check for valid command line */
  105. if (argc != 4)
  106. {
  107. fprintf(stderr, USAGE);
  108. return(1);
  109. }
  110. if (freopen(argv[3], "r", stdin) == NULL)
  111. {
  112. fprintf(stderr, "Cannot open '%s'\n", argv[3]);
  113. return(1);
  114. }
  115. if (GetBase(argv[2], &Base))
  116. {
  117. fprintf(stderr, "Cannot locate definition of <basename> in '%s'\n", argv[3]);
  118. return(1);
  119. }
  120. /* now process the rest of the file and map it */
  121. MapMessage(Base, argv[2]);
  122. return(0);
  123. }
  124. int
  125. GetBase(
  126. PCHAR String,
  127. int * pBase
  128. )
  129. /*++
  130. Routine Description:
  131. GetBase - find the line defining the value of the base number.
  132. Arguments:
  133. String is the string to match.
  134. pBase is a pointer of where to put the value.
  135. Return Value:
  136. Return 0 if string found, 1 if not.
  137. Notes:
  138. The global variable, chBuff is used w/in this routine.
  139. The pattern to look for is:
  140. [WS] #define [WS] <string> [WS | '('] <number> .....
  141. --*/
  142. {
  143. PCHAR p;
  144. size_t len;
  145. len = strlen(String);
  146. while(gets(chBuff))
  147. {
  148. p = chBuff;
  149. SKIPWHITE(p);
  150. if (strncmp(p, "#define", 7) == 0)
  151. {
  152. p += 7;
  153. SKIPWHITE(p);
  154. if (strncmp(String, p, len) == 0 && strcspn(p, " \t") == len)
  155. {
  156. /* found the definition ... skip to number */
  157. p += len;
  158. SKIP_W_P(p);
  159. if ( !isdigit(*p))
  160. {
  161. ReportError(chBuff, "Bad <base> definition");
  162. }
  163. *pBase = atoi(p);
  164. return(0);
  165. }
  166. }
  167. }
  168. return(1);
  169. }
  170. VOID
  171. MapMessage(
  172. int Base,
  173. PCHAR BaseName
  174. )
  175. /*++
  176. Routine Description:
  177. MapMessage - map the definition lines.
  178. Arguments:
  179. Base is the base number
  180. BaseName is the text form of base
  181. Return Value:
  182. None
  183. Notes:
  184. The global variable, chBuff is used w/in this routine.
  185. Make sure that the numbers are strictly increasing.
  186. --*/
  187. {
  188. CHAR auxbuff[BUFSIZ];
  189. int num;
  190. int first = TRUE;
  191. int next;
  192. PCHAR text;
  193. CHAR define[41];
  194. PCHAR p;
  195. CHAR type;
  196. /* Make certain the buffer is always null-terminated */
  197. define[sizeof(define)-1] = '\0';
  198. /* print the header */
  199. if (!Append)
  200. {
  201. printf(";//\n");
  202. printf(";// Net error file for basename %s = %d\n", BaseName, Base);
  203. printf(";//\n");
  204. }
  205. else
  206. {
  207. /* get last number and position to end of file */
  208. first = FALSE;
  209. next = 0;
  210. if (fseek(stdout, 0L, SEEK_END) == -1) {
  211. return;
  212. }
  213. }
  214. /* for each line of the proper format */
  215. while (GetNextLine(BaseName, chBuff, define, &num, &text, &type))
  216. {
  217. num += Base;
  218. if (first)
  219. {
  220. first = FALSE;
  221. next = num;
  222. }
  223. /* make sure that the numbers are monotonically increasing */
  224. if (num > next)
  225. {
  226. if (next == num - 1)
  227. {
  228. fprintf(stderr, "(warning) Missing error number %d\n", next);
  229. }
  230. else
  231. {
  232. fprintf(stderr, "(warning) Missing error numbers %d - %d\n",
  233. next, num-1);
  234. }
  235. next = num;
  236. }
  237. else if (num < next)
  238. {
  239. ReportError(chBuff, "Error numbers not strictly increasing");
  240. }
  241. /* rule out comment start alone on def line */
  242. if (text && *text == 0)
  243. {
  244. ReportError(chBuff, "Bad comment format");
  245. }
  246. /*
  247. * catch the cases where there is no open comment
  248. * or the open comment just contains a @X
  249. */
  250. if (text == NULL)
  251. {
  252. text = gets(auxbuff);
  253. SKIPWHITE(text);
  254. if ((type == '\0') && (strncmp(text, "/*", 2) == 0))
  255. {
  256. if (text[2] == 0)
  257. {
  258. gets(auxbuff);
  259. }
  260. else
  261. {
  262. text += 1;
  263. }
  264. strcpy(chBuff, text);
  265. text = chBuff;
  266. SKIPWHITE(text);
  267. if (*text++ != '*')
  268. {
  269. ReportError(chBuff, "Comment continuation requires '*'");
  270. }
  271. }
  272. else if ((type) && (*text == '*'))
  273. {
  274. if (text[1] == 0)
  275. {
  276. gets(auxbuff);
  277. }
  278. strcpy(chBuff, text);
  279. text = chBuff;
  280. SKIPWHITE(text);
  281. if (*text++ != '*')
  282. {
  283. ReportError(chBuff, "Comment continuation requires '*'");
  284. }
  285. }
  286. else
  287. {
  288. ReportError(chBuff, "Bad comment format");
  289. }
  290. }
  291. /* Strip off trailing trailing close comment */
  292. while (strstr(text, "*/") == NULL)
  293. {
  294. /* multi-line message ... comment MUST
  295. * be continued with '*'
  296. */
  297. p = gets(auxbuff);
  298. SKIPWHITE(p);
  299. if (*p != '*')
  300. {
  301. ReportError(auxbuff, "Comment continuation requires '*'");
  302. }
  303. if (*++p == '/')
  304. {
  305. break;
  306. }
  307. // abort if the current text length + add text + "\n" is > the max
  308. if (strlen(text) + strlen(p) + 1 > MAXMSGTEXTLEN)
  309. {
  310. ReportError(text, "\nMessage text length too long");
  311. }
  312. strcat(text, "\n");
  313. //
  314. // Get rid of leading spaces on continuation line,
  315. // unless -p specified
  316. //
  317. if (!Preserve)
  318. {
  319. SKIPWHITE(p);
  320. }
  321. strcat(text, p);
  322. }
  323. if ((p=strstr(text, "*/")) != NULL)
  324. {
  325. *p = 0;
  326. }
  327. TrimTrailingSpaces(text);
  328. //
  329. // Get rid of leading spaces on first line, unless -p specified
  330. //
  331. p = text;
  332. if (!Preserve) {
  333. SKIPWHITE(p);
  334. if (!p) {
  335. p = text;
  336. }
  337. }
  338. printf("MessageId=%04d SymbolicName=%s\nLanguage=English\n"
  339. "%s\n.\n", num, define, p);
  340. ++next;
  341. }
  342. }
  343. int
  344. GetNextLine(
  345. PCHAR BaseName,
  346. PCHAR pInputBuffer,
  347. PCHAR pDefineName,
  348. int * pNumber,
  349. PCHAR * pText,
  350. PCHAR pType
  351. )
  352. /*++
  353. Routine Description:
  354. GetNextLine - get the next line of the proper format, and parse out
  355. the error number.
  356. The format is assumed to be:
  357. [WS] #define [WS] <name> [WS | '('] <basename> [WS | ')'] \
  358. '+' [WS | '('] <number> [WS | ')'] '/*' [WS] [@X] [WS] <text>
  359. Arguments:
  360. BaseName is the basename.
  361. pInputBuffer is a pointer to an input buffer
  362. pDefineName is a pointer to where the manifest constant name pointer goes
  363. pNumber is a pointer to where the <number> goes.
  364. pText is a pointer to where the text pointer goes.
  365. pType is a pointer to the message type (set to 0 if no @X on line).
  366. Return Value:
  367. Returns 0 at end of file, non-zero otherwise.
  368. --*/
  369. {
  370. size_t len = strlen(BaseName);
  371. PCHAR savep = pInputBuffer;
  372. PCHAR startdefine;
  373. while (gets(savep))
  374. {
  375. pInputBuffer = savep;
  376. SKIPWHITE(pInputBuffer);
  377. if (strncmp(pInputBuffer, "#define", 7) == 0)
  378. {
  379. pInputBuffer += 7;
  380. SKIPWHITE(pInputBuffer);
  381. /* get manifest constant name */
  382. startdefine = pInputBuffer;
  383. pInputBuffer += strcspn(pInputBuffer, " \t");
  384. *pInputBuffer = '\0';
  385. pInputBuffer++;
  386. strncpy(pDefineName, startdefine, 40);
  387. SKIP_W_P(pInputBuffer);
  388. /* match <basename?> */
  389. if (strncmp(BaseName, pInputBuffer, len) == 0 &&
  390. strcspn(pInputBuffer, " \t)+") == len)
  391. {
  392. pInputBuffer += len;
  393. SKIP_W_P(pInputBuffer);
  394. if (*pInputBuffer == '+')
  395. {
  396. ++pInputBuffer;
  397. SKIP_W_P(pInputBuffer);
  398. /* the number !! */
  399. if (!isdigit(*pInputBuffer))
  400. {
  401. ReportError(savep, "Bad error file format");
  402. }
  403. *pNumber = atoi(pInputBuffer);
  404. SKIP_NOT_W_P(pInputBuffer);
  405. SKIP_W_P(pInputBuffer);
  406. if (strncmp(pInputBuffer, "/*", 2))
  407. {
  408. *pText = NULL;
  409. *pType = '\0';
  410. return(1);
  411. }
  412. pInputBuffer += 2;
  413. SKIPWHITE(pInputBuffer);
  414. if (*pInputBuffer == '@')
  415. {
  416. *pType = *(pInputBuffer+1);
  417. pInputBuffer += 2;
  418. SKIPWHITE(pInputBuffer);
  419. }
  420. else
  421. {
  422. *pType = '\0';
  423. }
  424. if (*pInputBuffer)
  425. {
  426. *pText = pInputBuffer;
  427. }
  428. else
  429. {
  430. *pText = NULL;
  431. }
  432. return(1);
  433. }
  434. }
  435. }
  436. }
  437. return(0);
  438. }
  439. void
  440. ReportError(
  441. PCHAR pLineNumber,
  442. PCHAR Message
  443. )
  444. /*++
  445. Routine Description:
  446. ReportError - report a fatal error.
  447. Arguments:
  448. pLineNumber is the offending input line.
  449. Message is a description of what is wrong.
  450. Return Value:
  451. None
  452. --*/
  453. {
  454. fprintf(stderr, "\a%s:%s\n", Message, pLineNumber);
  455. exit(1);
  456. }
  457. void
  458. TrimTrailingSpaces(
  459. PCHAR Text
  460. )
  461. /*++
  462. Routine Description:
  463. TrimTrailingSpaces - strip off the end spaces.
  464. Arguments:
  465. Text - the text to remove spaces from
  466. Return Value:
  467. None
  468. --*/
  469. {
  470. PCHAR p;
  471. /* strip off trailing space */
  472. while (((p=strrchr(Text, ' ')) && p[1] == 0) ||
  473. ((p=strrchr(Text, '\t')) && p[1] == 0))
  474. {
  475. *p = 0;
  476. }
  477. }