Windows NT 4.0 source code leak
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.

1151 lines
35 KiB

4 years ago
  1. /*** LEXER.C -- gets tokens from input, returns them to parse() in parser.c ***
  2. *
  3. * Copyright (c) 1988-1990, Microsoft Corporation. All rights reserved.
  4. *
  5. * Purpose:
  6. * This module contains the lexical routines of nmake
  7. *
  8. * Revision History:
  9. * 15-Oct-1993 HV Use tchar.h instead of mbstring.h directly, change STR*() to _ftcs*()
  10. * 01-Jun-1993 HV Use UngetTxtChr() instead of ungetc()
  11. * 01-Jun-1993 HV Change #ifdef KANJI to _MBCS
  12. * 10-May-1993 HV Add include file mbstring.h
  13. * Change the str* functions to STR*
  14. * 06-Apr-1993 HV Change createDosTmp() to use _makepath()
  15. * 22-Mar-1993 HV Rewrite getPath() to make use of the new _splitpath() and
  16. * _makepath() functions.
  17. * 04-Aug-1992 SS CAVIAR 2266: expand INCLUDE macro in processIncludeFile()
  18. * 08-Jun-1992 SS Port to DOSX32
  19. * 02-Feb-1990 SB change fopen() to FILEOPEN()
  20. * 01-Dec-1989 SB Changed realloc() to REALLOC()
  21. * 22-Nov-1989 SB Changed free() to FREE()
  22. * 19-Oct-1989 SB searchHandle passed around as extra param
  23. * 08-Oct-1989 SB handle OS/2 1.2 quoted filenames
  24. * 04-Sep-1989 SB temporary filename generated has a trailing '.' for LINK.EXE
  25. * 24-Aug-1989 SB Allow $* and $@ in dependency lines
  26. * 18-Aug-1989 SB Added fclose() return code check
  27. * 31-Jul-1989 SB Added lookahead to the lexer for \ on dependency lines
  28. * 06-Jul-1989 SB Remove escaping abilities of '^' in command lines totally
  29. * 29-Jun-1989 SB Add duplicateInline() to detect duplicate inline filenames
  30. * and issue error if duplicates are found
  31. * 26-Jun-1989 SB Modify ParseScriptFileList() and add nextInlineFile() to
  32. * handle complex syntax of Inline file command line.
  33. * 15-Jun-1989 SB issue error for usage of inline file in an inference rule
  34. * 18-May-1989 SB Added getPath(), changed processIncludeFile() to have C like
  35. * processing of include files
  36. * 16-May-1989 SB expand macros in include file names; handle '\' processing
  37. * in same way for macros and dependency lines
  38. * 15-May-1989 SB Changed nameStates to 16x14
  39. * 13-May-1989 SB don't remove ESCH on reading cmd block
  40. * 24-Apr-1989 SB made FILEINFO as void * and corrected regression in parsing
  41. * inline file names
  42. * 14-Apr-1989 SB inline file names are correctly expanded now
  43. * 06-Apr-1989 SB ren removeFirstLtLt() as delInlineSymbol().
  44. * 05-Apr-1989 SB made all funcs NEAR; Reqd to make all function calls NEAR
  45. * 22-Mar-1989 SB removed unlinkTmpFiles() function; not needed
  46. * 19-Jan-1989 SB added function removeFirstLtLt() to remove '<<' appearing
  47. * in -n output
  48. * 30-Dec-1988 SB Fixed GP fault for KEEP/NOKEEP in parseScriptFileList()
  49. * and makeScriptFileList()
  50. * 21-Dec-1988 SB Added parseScriptFileList() and appendScript() to allow
  51. * handling of multiple script files inside a makefile
  52. * Improved KEEP/NOKEEP so that each file can have its own
  53. * action
  54. * 16-Dec-1988 SB Added to makeScriptFile() for KEEP/NOKEEP
  55. * 14-Dec-1988 SB Added tmpScriptFile so that a delete command can be
  56. * added for unnamed script files for Z option
  57. * 13-Dec-1988 SB Added processEschIn() to improve response files
  58. * 5-Oct-1988 RB Strip trailing whitespace from macro defs, build lines.
  59. * 22-Sep-1988 RB Fix skipComments() to not parse \\nl.
  60. * 20-Sep-1988 RB Error if named script file creation fails.
  61. * Count line numbers in script files.
  62. * 18-Sep-1988 RB Handle mktemp() small limit.
  63. * 17-Aug-1988 RB Clean up.
  64. * 14-Jul-1988 rj Fixed handling of ^ before !, @, or -.
  65. * 8-Jul-1988 rj Added handler to ignore ^ inside quotes.
  66. * Made \ carry comments over lines.
  67. * Made ^ carry comments over lines.
  68. * 27-Jun-1988 rj Fixed bug with handling of response files.
  69. * 16-Jun-1988 rj Finished up ESCH.
  70. * 15-Jun-1988 rj Added support for ESCH escape: modified skipWhiteSpace
  71. * (adding some redundancy in setting colZero), getString,
  72. * getName; removed \\nl escape.
  73. * 13-Jun-1988 rj Fixed backslashes to work as in nmake, with addition of
  74. * double-backslash escape. (v1.5)
  75. *
  76. *******************************************************************************/
  77. #include "nmake.h"
  78. #include "nmmsg.h"
  79. #include "proto.h"
  80. #include "globals.h"
  81. #include "grammar.h"
  82. #define COMMENT(A,B,C) (((A) == ';' && B && C) || ((A) == '#'))
  83. #ifdef _MBCS
  84. #define GET(A) A ? GetTxtChr(file) : lgetc()
  85. #else
  86. #define GET(A) A ? getc(file) : lgetc()
  87. #endif
  88. extern char * NEAR makeInlineFiles(char*, char**, char**);
  89. extern void NEAR removeTrailChars(char *);
  90. LOCAL void NEAR skipComments(UCHAR);
  91. LOCAL void NEAR getString(UCHAR,char*,char*);
  92. LOCAL void NEAR getName(char*,char*);
  93. LOCAL UCHAR NEAR determineTokenFor(int,char*,char*);
  94. LOCAL void NEAR popFileStack(void);
  95. LOCAL UCHAR NEAR include(int);
  96. LOCAL char * NEAR getPath(const char *);
  97. extern UCHAR NEAR nameStates[18][15];
  98. extern UCHAR NEAR stringStates[13][14];
  99. extern STRINGLIST * NEAR targetList;
  100. /* --------------------------------------------------------------------
  101. * getToken()
  102. *
  103. * arguments: init global boolean value -- TRUE if tools.ini is the
  104. * file being lexed
  105. * n size of s[]
  106. * expected kind of token expected by parser -- only
  107. * needed when parser wants a whole string
  108. * (meaning everything left on the current line)
  109. * -- this way getToken() doesn't break strings
  110. * into their separate tokens
  111. *
  112. * actions: if no tokens have been read from current file,
  113. * returns some kind of newline to initialize the parser
  114. * (if 1st char in file is whitespace, returns NEWLINESPACE
  115. * else returns NEWLINE -- w/o actually getting a token
  116. * from the input)
  117. * if the parser wants a whole string, reads rest of line
  118. * into s and returns STRING
  119. * if at end of file, return ACCEPT (which is the last
  120. * symbol on the parser's stack)
  121. * if input char is newline
  122. * if followed by whitespace, return NEWLINESPACE
  123. * if the next char is [ and we're reading tools.ini
  124. * pretend that we've reached end of file and
  125. * return ACCEPT
  126. * otherwise return NEWLINE
  127. * if input char is colon
  128. * if following char is also colon,
  129. * (put both chars in s) return DOUBLECOLON
  130. * otherwise return SINGLECOLON
  131. * if input char is semicolon return SEMICOLON
  132. * if input char is equals return EQUALS
  133. * if input char is exclamation handle directives
  134. * (not yet implemented)
  135. * otherwise char must be part of a name, so gather
  136. * the rest of the identifier and return NAME
  137. *
  138. * returns: token type: NEWLINE NEWLINESPACE NAME EQUALS COLON
  139. * SEMICOLON STRING ACCEPT
  140. *
  141. * modifies: buf by modifying *s, which points somewhere into buf
  142. * line global line count
  143. * fname will change when !include is handled
  144. * colZero global flag set if at column zero of a file
  145. *
  146. * The lexer has to keep track of whether or not it is at the beginning
  147. * of a line in the makefile (i.e. in column zero) so that it will know
  148. * whether to ignore comments. If init is TRUE, meaning that we are
  149. * lexing tools.ini, then we have to treat lines beginning with ';' as
  150. * comment lines. If the parser expects a string, only comments beginning
  151. * in column zero are ignored; all others are returned as part of the
  152. * string. Comments are stripped from macro values (strings that are
  153. * part of macro definitions).
  154. *
  155. * The user can specify a macro definition or a build line that
  156. * spans several lines (using the \<newline> to "continue" the lines) while
  157. * interspersing comment lines with the text.
  158. */
  159. UCHAR NEAR
  160. getToken(n,expected)
  161. unsigned n; /* size of s[] */
  162. UCHAR expected; /* STRING means get line */
  163. { /* w/o checking for #;:= */
  164. char *s;
  165. char *end;
  166. int c;
  167. s = buf;
  168. end = buf + n;
  169. if (firstToken) { /* global var */
  170. ++line;
  171. firstToken = FALSE; /* parser needs to*/
  172. /* see some kind */
  173. c = lgetc(); /* of newline to */
  174. if (colZero = (BOOL) !WHITESPACE(c)) { /* initialize it */
  175. if (c == EOF)
  176. return(determineTokenFor(c,s,end));
  177. else
  178. UngetTxtChr(c,file);
  179. return(NEWLINE);
  180. }
  181. return(NEWLINESPACE);
  182. }
  183. if (expected == STRING || expected == VALUE) { /* get everything */
  184. getString(expected,s,end); /* up to \n */
  185. return(expected);
  186. } /* were/are we */
  187. c = skipWhiteSpace(FROMLOCAL); /* past col 0? */
  188. *s++ = (char) c; /* save the letter*/
  189. *s = '\0'; /* terminate s */
  190. return(determineTokenFor(c,s,end));
  191. }
  192. /* -----------------------------------------------------------------
  193. * determineTokenFor()
  194. *
  195. * arguments: c current input character
  196. * s buffer to place token in for return to parser
  197. * end end of the token return buffer
  198. *
  199. * returns: token type: NEWLINE NEWLINESPACE NAME EQUALS COLON
  200. * SEMICOLON ACCEPT
  201. *
  202. * modifies: buf by modifying *s, which points somewhere into buf
  203. * line global line count
  204. * fname will change when include is handled
  205. * init global flag - set if parsing tools.ini
  206. * colZero global flag set if at column zero of a file
  207. *
  208. */
  209. LOCAL UCHAR NEAR
  210. determineTokenFor(c,s,end)
  211. int c;
  212. char *s;
  213. char *end;
  214. {
  215. switch (c) {
  216. case EOF: if (!feof(file))
  217. makeError(line,LEXER+FATAL_ERR);
  218. if (incTop) popFileStack();
  219. else if (ifTop >= 0) /* all directives not processed*/
  220. makeError(line,SYNTAX_EOF_NO_DIRECTIVE);
  221. else return(ACCEPT);
  222. case '\n': ++line;
  223. colZero = TRUE;
  224. c = lgetc();
  225. if (COMMENT(c,TRUE,init)) {
  226. skipComments(FROMLOCAL);
  227. ++line;
  228. colZero = TRUE; /* manis - 11/13/87 */
  229. c = lgetc();
  230. }
  231. if (colZero = (BOOL) !WHITESPACE(c)) {
  232. if (c == EOF)
  233. return(determineTokenFor(c,s,end));
  234. else
  235. //save for next token
  236. UngetTxtChr(c,file);
  237. return(NEWLINE);
  238. }
  239. return(NEWLINESPACE);
  240. case ':': colZero = FALSE;
  241. if ((c = lgetc()) == ':') {
  242. *s++ = (char) c;
  243. *s = '\0';
  244. return(DOUBLECOLON);
  245. }
  246. UngetTxtChr(c,file);
  247. return(COLON);
  248. case ';': colZero = FALSE;
  249. return(SEMICOLON);
  250. case '=': colZero = FALSE;
  251. return(EQUALS);
  252. case '[': if (init && colZero)
  253. return(ACCEPT);
  254. case ESCH: UngetTxtChr(c, file); /* getName has to get esch */
  255. s--; /* so we don't double the caret */
  256. default: getName(s,end);
  257. if (colZero && !_ftcsicmp(buf, "include")) {
  258. colZero = FALSE;
  259. if ((c = skipWhiteSpace(FROMLOCAL)) != ':'
  260. && c != '=') {
  261. if (init)
  262. makeError(line, SYNTAX_UNEXPECTED_TOKEN, s);
  263. return(include(c));
  264. }
  265. UngetTxtChr(c,file);
  266. }
  267. else colZero = FALSE;
  268. return(NAME);
  269. }
  270. }
  271. /* ----------------------------------------------------------------------------
  272. * skipWhiteSpace()
  273. *
  274. * arguments: c current input character
  275. * init global boolean value -- TRUE if we're lexing tools.ini
  276. * colZero global boolean value -- TRUE if the current
  277. * input char is at the beginning of the line
  278. *
  279. * actions: reads and discards characters until it gets a
  280. * non-whitespace char that isn't part of a comment
  281. * or hits the end of the line (NEWLINE and NEWLINESPACE
  282. * are valid tokens and shouldn't be skipped w/ whitespace)
  283. * backslash-newline ('\\''\n') is treated as whitespace
  284. * comments are treated as whitespace
  285. * escaped whitespace is treated as whitespace (v1.5)
  286. *
  287. * modifies: colZero global boolean value to :
  288. * TRUE if by skipping whitespace and comments we're
  289. * at the beginning of a line
  290. * else if we skipped characters and are not at the
  291. * beginning of a line, FALSE
  292. * else if we did not skip any characters, leave
  293. * colZero unchanged
  294. *
  295. * returns: c the current non-whitespace input char
  296. */
  297. int NEAR
  298. skipWhiteSpace(stream)
  299. UCHAR stream;
  300. {
  301. int c;
  302. do {
  303. c = GET(stream);
  304. if (WHITESPACE(c) || c == ESCH) {
  305. if (c == ESCH) {
  306. c = GET(stream);
  307. if (!WHITESPACE(c)) { /* push char back out, return esch*/
  308. UngetTxtChr(c, file);
  309. c = ESCH;
  310. break;
  311. }
  312. }
  313. colZero = FALSE; /* we've moved past col 0 */
  314. }
  315. if (c == '\\')
  316. c = skipBackSlash(c, stream);
  317. } while(WHITESPACE(c));
  318. if (COMMENT(c,colZero,init)) {
  319. skipComments(stream); /* current char is always */
  320. c = '\n'; /* \n after comments */
  321. colZero = TRUE; /* always in col 0 after */
  322. } /* a comment */
  323. return(c); /* true if we're in col 0 */
  324. }
  325. /* ----------------------------------------------------------------------------
  326. * skipComments()
  327. *
  328. * arguments: c pointer to current input character
  329. * init global boolean value -- TRUE if tools.ini is the
  330. * file being lexed
  331. *
  332. * actions: reads and discards characters until it hits the end of
  333. * the line
  334. * checks to see if 1st char on next line is comment,
  335. * and if so, discards that line, too
  336. * DO NOT parse backslash-newline. That would break our
  337. * precedence of comments over escaped newlines, the reverse
  338. * of Xenix.
  339. *
  340. * modifies: line global line count
  341. * colZero
  342. *
  343. */
  344. LOCAL void NEAR
  345. skipComments(stream)
  346. UCHAR stream;
  347. {
  348. int c;
  349. for (;;) {
  350. colZero = FALSE; /* manis 11/13/87 */
  351. do
  352. c = GET(stream);
  353. while (c != EOF && c != '\n');
  354. if (c == EOF) return;
  355. colZero = TRUE;
  356. c = GET(stream);
  357. if (!COMMENT(c,TRUE,init)) { /* if next line comment, */
  358. UngetTxtChr(c,file); /* go around again */
  359. return;
  360. }
  361. ++line;
  362. }
  363. }
  364. /* -------------------------------------------------------------------------
  365. * skipBackSlash() - skips backslash-newline sequences
  366. *
  367. *
  368. * arguments: c current input char
  369. * stream flag to determine if chars are to be got
  370. * from the raw stream or thru' lgetc()
  371. *
  372. *
  373. */
  374. int NEAR
  375. skipBackSlash(c,stream)
  376. int c;
  377. UCHAR stream;
  378. {
  379. while (c == '\\') { /* treat \newline as space*/
  380. if ((c = GET(stream)) == '\n') { /* and consume it too */
  381. colZero = TRUE; /* manis - 11/13-87 */
  382. ++line; /* adjust line count */
  383. c = GET(stream); /* skip over newline */
  384. if (COMMENT(c,TRUE,init)) { /* skip comment line after*/
  385. skipComments(stream); /* continuation char */
  386. ++line; /* manis - 11/13/87 */
  387. c = GET(stream);
  388. }
  389. }
  390. else {
  391. UngetTxtChr(c,file);
  392. c = '\\';
  393. return(c);
  394. }
  395. }
  396. return(c);
  397. }
  398. /* ----------------------------------------------------------------------------
  399. * getString()
  400. *
  401. * arguments: type says which kind of token we're getting,
  402. * a build STRING, or macro VALUE
  403. * (we strip comments from VALUEs, but not
  404. * from STRINGs)
  405. * s pointer to buffer that will hold string
  406. * init global boolean value -- TRUE if tools.ini is the
  407. * file being lexed
  408. * colZero global boolean value -- true if we 're in
  409. * 1st position of line when invoked
  410. * end pointer to end of s[]
  411. *
  412. * actions: gets all chars up to the end of line or end of file
  413. * and stores them in s[]
  414. * backslash followed by newline is replaced by a single
  415. * space, and getString() continues getting characters
  416. * comments beginning in column 0 are ignored, as are
  417. * comments anywhere on a VALUE line
  418. *
  419. * modifies: buf by modifying *s
  420. * line global line count
  421. * colZero thru' calls to lgetc()
  422. *
  423. * When build strings or macro values are continued on the next line w/
  424. * a backslash before the newline, leading whitespace after the newline
  425. * is omitted. This is for xmake compatibility.
  426. *
  427. * The continuation character is backslash immediately before newline.
  428. *
  429. * The only difference between build strings and macro values is that
  430. * comments are stripped from macro values and not from build strings.
  431. *
  432. * Modifications:
  433. *
  434. * 06-Jul-1989 SB remove escaping in command lines
  435. * 15-Jun-1988 rj Added escape functionality. Escape char., before
  436. * certain characters, causes those characters to bypass
  437. * the normal mechanism determining their type; they are
  438. * placed directly into the string. Some characters cause
  439. * the escape character itself to be placed into the
  440. * string.
  441. */
  442. LOCAL void NEAR
  443. getString(type,s,end)
  444. UCHAR type; /* build string or*/
  445. char *s; /* macro value? */
  446. char *end;
  447. {
  448. int c; /* buffer */
  449. UCHAR state, input;
  450. int tempC;
  451. unsigned size; /* whenever state */
  452. char *begin; /* is 0, we're in*/
  453. /* column zero */
  454. BOOL parsechar; /* flag to examine char. type */
  455. BOOL inQuotes = (BOOL) FALSE; /* flag when inside quote marks */
  456. begin = s;
  457. c = lgetc();
  458. if (type == STRING) state = (UCHAR) 2;
  459. else if (WHITESPACE(c)) {
  460. state = (UCHAR) 2;
  461. c = skipWhiteSpace(FROMLOCAL);
  462. }
  463. else if (c == ESCH) {
  464. c = lgetc();
  465. if (WHITESPACE(c)) {
  466. state = (UCHAR) 2;
  467. c = skipWhiteSpace(FROMLOCAL);
  468. }
  469. else {
  470. UngetTxtChr(c, file);
  471. c = ESCH;
  472. }
  473. }
  474. else state = (UCHAR) 1; /* default state */
  475. for (;;c = lgetc()) {
  476. if (c == '\"') inQuotes = (BOOL) !inQuotes;
  477. parsechar = 1; /* Default is examine character. */
  478. if (c == ESCH && !inQuotes && type == VALUE) {
  479. c = lgetc();
  480. switch (c) {
  481. case '$': case ESCH: /* Special characters; must */
  482. case '{': case '}': /* not elide esch from string */
  483. case '(': case ')':
  484. case '!': case '-': case '@':
  485. *s++ = ESCH;
  486. if (s == end) {
  487. if (string == NULL) { /* Increase size of s */
  488. string = allocate(MAXBUF<<1);
  489. _ftcsncpy(string,begin,MAXBUF);
  490. s = string + MAXBUF;
  491. size = MAXBUF << 1;
  492. end = string + size;
  493. }
  494. else {
  495. if ((size + MAXBUF < size) /* overflow error */
  496. || !(string = REALLOC(string,size+MAXBUF)))
  497. makeError(line, MACRO_TOO_LONG);
  498. s = string + size;
  499. size += MAXBUF;
  500. end = string + size;
  501. }
  502. begin = string;
  503. }
  504. case '#': case '\n': /* elide esch right now! */
  505. case '\\': case '\"':
  506. input = DEFAULT_;
  507. parsechar = 0; /* DON'T examine character*/
  508. break;
  509. default:
  510. break; /* DO examine character. */
  511. }
  512. }
  513. else if (c == ESCH) {
  514. c = lgetc();
  515. UngetTxtChr(c, file);
  516. c = ESCH;
  517. }
  518. if (parsechar) {
  519. switch (c) {
  520. case '#': input = COMMENT_; break;
  521. case '=': input = EQUALS_; break;
  522. case ':': input = COLON_; break;
  523. case '$': input = DOLLAR_; break;
  524. case '(': input = OPENPAREN_; break;
  525. case ')': input = CLOSEPAREN_; break;
  526. case '\\': input = BACKSLASH_; break;
  527. case '\n':
  528. case EOF: input = NEWLINE_; break;
  529. case ' ':
  530. case '\t': input = WHITESPACE_; break;
  531. case '*': input = STAR_; break;
  532. case '@':
  533. case '<':
  534. case '?': input = SPECIAL1_; break;
  535. case 'F':
  536. case 'D':
  537. case 'B':
  538. case 'R': input = SPECIAL2_; break;
  539. case ';': input = (UCHAR) (!state && init ? COMMENT_ : DEFAULT_);
  540. break; /* Handle comments in tools.ini */
  541. default: input = (UCHAR) (MACRO_CHAR(c) ? MACROCHAR_:DEFAULT_);
  542. break;
  543. }
  544. }
  545. if (input == SPECIAL1_ && type == STRING && c == '<') {
  546. if ((tempC = lgetc()) == '<') { /* << means start */
  547. s = makeInlineFiles(s, &begin, &end); /* an inline file */
  548. input = NEWLINE_;
  549. c = '\n';
  550. }
  551. else {
  552. UngetTxtChr(tempC,file);
  553. }
  554. state = stringStates[state][input];
  555. }
  556. else if (input == COMMENT_) { /* Handle comments*/
  557. if (!state) {
  558. inQuotes = (BOOL) FALSE;
  559. skipComments(FROMLOCAL);
  560. ++line;
  561. continue;
  562. }
  563. else if (type == VALUE) state = OK; /* don't elide from command */
  564. else state = stringStates[state][input];
  565. }
  566. else state = stringStates[state][input];
  567. if (state == OK) { /* Accept end of string */
  568. inQuotes = (BOOL) FALSE;
  569. UngetTxtChr(c,file);
  570. /*
  571. * Strip trailing whitespace from string. Easier to do it here,
  572. * else we have to treat a multi-string value (OBJS=a b c) as
  573. * separate tokens. [RB]
  574. */
  575. while (s > begin && _istspace(s[-1]))
  576. --s;
  577. *s = '\0';
  578. if (string) {
  579. if (s = REALLOC(string,s-string+1))
  580. string = s;
  581. }
  582. else string = makeString(begin);
  583. return;
  584. }
  585. else if (ON(state,ERROR_MASK)) /* Error code from table */
  586. makeError(line,(state&~ERROR_MASK)+FATAL_ERR,c);
  587. if (!state) { /* Col 0; we just hit \nl */
  588. *--s = ' '; /* so treat it like white-*/
  589. ++s; ++line; /* space; overwrite the */
  590. colZero = TRUE; /* backslash with a space.*/
  591. c = lgetc();
  592. colZero = FALSE;
  593. if (WHITESPACE(c)) {
  594. state = 2;
  595. do {
  596. c = lgetc();
  597. } while (WHITESPACE(c));
  598. }
  599. UngetTxtChr(c,file);
  600. }
  601. else { /* Keep storing string */
  602. *s++ = (char) c;
  603. if (s == end) {
  604. if (!string) { /* Increase size of s */
  605. string = allocate(MAXBUF<<1);
  606. _ftcsncpy(string,begin,MAXBUF);
  607. s = string + MAXBUF;
  608. size = MAXBUF << 1;
  609. end = string + size;
  610. }
  611. else {
  612. if ((size + MAXBUF < size) /* overflow error */
  613. || !(string = REALLOC(string,size+MAXBUF)))
  614. makeError(line, MACRO_TOO_LONG);
  615. s = string + size;
  616. size += MAXBUF;
  617. end = string + size;
  618. }
  619. }
  620. }
  621. }
  622. }
  623. /* ----------------------------------------------------------------------------
  624. * getName()
  625. *
  626. * arguments: s pointer into buffer that will hold string
  627. * (s is pointing to buf+1 when passed, because
  628. * the caller, getToken(), has already seen and
  629. * saved one char)
  630. * init global boolean value -- TRUE if tools.ini is the
  631. * file being lexed
  632. * used by routine called - lgetc()
  633. * end pointer to end of s[]
  634. *
  635. * actions: gets all chars up to first token delimiter and stores
  636. * them in s[] (delimiters are ' ', '\t', '\n' and (when
  637. * not inside a macro invocation) ':' and '='
  638. * note that backslash-newline is treated as a space,
  639. * which is a delimiter
  640. * if the current input char is '$' this must be a macro
  641. * invocation
  642. * if the macro name is in parentheses
  643. * get all chars up to and including close paren
  644. * (if ')' not found, error)
  645. *
  646. * We check the syntax within the name here -- thus errors in macro
  647. * invocation syntax will be caught. Special macros cannot be used
  648. * as part of names, with the exception of the dynamic dependency macros.
  649. *
  650. * We can probably never overrun our buffer, because it would be extremely
  651. * difficult for the user to get a name with 1024 characters or more into
  652. * his makefile.
  653. *
  654. * we never end up in column zero, because we push the delimiter back
  655. * out on the input
  656. *
  657. * uses state table defined in table.h, defs from grammar.h
  658. *
  659. * modifies: line (possibly) thru' call to lgetc()
  660. * file (possibly) if lgetc() finds a !include
  661. * fName (possibly) if lgetc() finds a !include
  662. */
  663. LOCAL void NEAR
  664. getName(s,end)
  665. char *s;
  666. char *end; /* pts to end of s*/
  667. {
  668. int c;
  669. UCHAR state,
  670. input;
  671. BOOL seenBackSlash = FALSE;
  672. BOOL fQuoted = FALSE;
  673. char *beg = s - 1;
  674. BOOL parsechar; /* flag to examine char. type */
  675. switch (*(s-1)) {
  676. case '$': state = (UCHAR) 2; break;
  677. case '{': state = (UCHAR) 8; break;
  678. case '"': fQuoted = TRUE; state = (UCHAR)16; break;
  679. default: state = (UCHAR) 0; break;
  680. }
  681. for (;;) {
  682. c = lgetc();
  683. parsechar = 1; /* Default is examine char. */
  684. if (c == ESCH) {
  685. c = lgetc();
  686. switch (c) {
  687. case '{': case '}': /* Special characters; must */
  688. case '(': case ')': /* not elide esch from string */
  689. case '$': case ESCH:
  690. *s++ = ESCH;
  691. case '#': case '\n': case '\\': /* elide esch right now! */
  692. input = DEFAULT_;
  693. parsechar = 0; /* DON'T examine character*/
  694. break;
  695. default:
  696. break; /* DO examine character. */
  697. }
  698. }
  699. if (parsechar) {
  700. switch (c) {
  701. case '#' : input = COMMENT_; break;
  702. case '=' : input = EQUALS_; break;
  703. case ';' : input = SEMICOLON_; break;
  704. case ':' : input = COLON_; break;
  705. case '$' : input = DOLLAR_; break;
  706. case '(' : input = OPENPAREN_; break;
  707. case ')' : input = CLOSEPAREN_; break;
  708. case '{' : input = OPENCURLY_; break;
  709. case '}' : input = CLOSECURLY_; break;
  710. case ' ' :
  711. case '\t': input = (UCHAR)((fQuoted)
  712. ? DEFAULT_ : WHITESPACE_);
  713. break;
  714. case '\n':
  715. case EOF : input = NEWLINE_; break;
  716. case '\\': input = BKSLSH_; break;
  717. case '"' : input = QUOTE_; break;
  718. //Add support for $* and $@ on the dependency line
  719. default : if (ON(actionFlags, A_DEPENDENT))
  720. input = (UCHAR)((MACRO_CHAR(c) || c == '*' || c == '@')
  721. ?MACROCHAR_:DEFAULT_);
  722. else
  723. input = (UCHAR)(MACRO_CHAR(c)?MACROCHAR_:DEFAULT_);
  724. break;
  725. }
  726. }
  727. state = nameStates[state][input];
  728. //Cheat lex table to think that you are handling quoted string case
  729. if (fQuoted && state == 1)
  730. state = 16;
  731. //seenBackSlash is used to provide lookahead when \ is seen on a
  732. //dependency line
  733. if (seenBackSlash)
  734. //if \ followed by \n then use it as a continuation
  735. if (input == NEWLINE_) {
  736. ++line;
  737. colZero = TRUE;
  738. c = lgetc();
  739. colZero = FALSE;
  740. if (WHITESPACE(c)) {
  741. state = OK;
  742. do {
  743. c = lgetc();
  744. } while (WHITESPACE(c));
  745. }
  746. else
  747. state = (UCHAR)((s == buf + 1) ? BEG : DEF);
  748. }
  749. else
  750. *s++ = '\\';
  751. seenBackSlash = FALSE;
  752. if (state == OK) {
  753. if (s >= end)
  754. makeError(line,NAME_TOO_LONG);
  755. UngetTxtChr(c,file);
  756. *s = '\0';
  757. removeTrailChars(beg);
  758. return;
  759. }
  760. else if (ON(state,ERROR_MASK))
  761. makeError(line,(state&~ERROR_MASK)+FATAL_ERR,c);
  762. if (state == BKS) {
  763. //set lookahead flag
  764. seenBackSlash = TRUE;
  765. }
  766. else
  767. *s++ = (char) c;
  768. }
  769. }
  770. /*** createDosTmp -- Creates a unique temporary file. ******************
  771. *
  772. * Scope:
  773. * Global.
  774. *
  775. * Purpose:
  776. * To create a unique temporary file by calling _mktemp() but it gets
  777. * over _mktemp() limitation to be able to create more files.
  778. *
  779. * Input:
  780. * path -- The buffer initially contain the directory to store the temp
  781. * file. On exit, if success, the temp file is appended to it.
  782. * In case of failure, the its contents is undetermined.
  783. *
  784. * Output:
  785. * If successful, temporary file name is appended to path and
  786. * the function returns the file pointer, else NULL.
  787. *
  788. * Errors/Warnings:
  789. *
  790. * Assumes:
  791. *
  792. * Modifies Globals:
  793. * None.
  794. *
  795. * Uses Globals:
  796. * None.
  797. *
  798. * Notes:
  799. *
  800. * History:
  801. * 06-Apr-1993 HV Change createDosTmp() to use _makepath()
  802. *
  803. *******************************************************************************/
  804. FILE * NEAR
  805. createDosTmp(char *path)
  806. {
  807. FILE *fd;
  808. static char template[] = "nmXXXXXX";
  809. static char szExtension[] = "."; // just a dot to make Mr.
  810. // linker happy.
  811. // CONSIDER: is the size of szDir too big for the stack? We can
  812. // make it a little smaller if we take the risk.
  813. char szDir[_MAX_PATH];
  814. // CONSIDER: The path supplied by the caller might contain both
  815. // the drive and probably some level of directories
  816. // (e.g. c:\win\tmp) Right now, _makepath happily takes
  817. // the whole thing as the directory component, but that
  818. // might change in the future. In such case, we should
  819. // first break up path to drive/dir compents before we
  820. // construct the full pathname of the template. There
  821. // is something to watch out: for "c:\win\tmp", _splitpath
  822. // will split as: "c:", "\win\", and "tmp", which is not
  823. // what we want. To fix it, append a backslash to the
  824. // end before calling _splitpath. "c:\win\tmp\" will
  825. // be broken up correctly to "c:" and "\win\tmp\"
  826. if (!path || !*path) // If path is empty, use "."
  827. _ftcscpy(szDir, ".");
  828. else
  829. _ftcscpy(szDir, path);
  830. // Construct the full pathname. _mktemp() doesn't seem to like
  831. // template with trailing dot (".") so instead of specifying "."
  832. // for the extension, we defer it later and _ftcscat the dot to
  833. // its tail.
  834. _makepath(path, NULL, szDir, template, NULL);
  835. if(_mktemp(path) == NULL)
  836. {
  837. /*
  838. * Mktemp() has a limit of 27 files per template. If it fails, assume
  839. * the limit has overflowed and increment the second letter of the
  840. * template.
  841. */
  842. if (template[1] == 'z')
  843. template[1] = 'a';
  844. else
  845. ++template[1]; // ASSUMPTION: that this will work with DBCS
  846. _makepath(path, NULL, szDir, template, NULL);
  847. if(_mktemp(path) == NULL)
  848. return(NULL);
  849. }
  850. // add a trailing "." for the linker's sake
  851. _ftcscat(path, szExtension);
  852. // Open the file and return the file's descriptor.
  853. return(fd = FILEOPEN(path, "w"));
  854. } // createDosTmp
  855. LOCAL void NEAR
  856. popFileStack()
  857. {
  858. if (fclose(file) == EOF)
  859. makeError(0, ERROR_CLOSING_FILE, fName);
  860. FREE(fName);
  861. file = incStack[--incTop].file;
  862. fName = incStack[incTop].name;
  863. line = incStack[incTop].line;
  864. }
  865. /* -----------------------------------------------------------------
  866. * include() -- handle include files
  867. *
  868. * arguments: c first non-whitespace char after the string
  869. * INCLUDE on the line...
  870. * colZero global boolean value, set if currently at
  871. * column zero of a file.
  872. *
  873. * modifies: line global line count - if include file opened
  874. * file global pointer to current file
  875. * fName global pointer to name of current file
  876. * colZero global boolean value, changed if include
  877. * file opened and char from colZero is returned
  878. */
  879. LOCAL UCHAR NEAR
  880. include(c)
  881. int c;
  882. {
  883. unsigned n;
  884. char *s;
  885. if (c == '\n' || c == EOF)
  886. makeError(line,SYNTAX_NO_NAME);
  887. *buf = (char) c;
  888. if (!fgets(buf+1,MAXBUF - 1,file)) {
  889. if (feof(file))
  890. makeError(line,SYNTAX_UNEXPECTED_TOKEN,"EOF");
  891. makeError(line,CANT_READ_FILE);
  892. }
  893. n = _ftcslen(buf) - 1;
  894. if (buf[n] == '\n') buf[n] = '\0';
  895. s = buf;
  896. while (WHITESPACE(*s)) ++s;
  897. return(processIncludeFile(s));
  898. }
  899. /* -------------------------------------------------------------------
  900. * processIncludeFile() -- checks for include file and switches state
  901. *
  902. * arguments: s buffer that has include file name
  903. * colZero global boolean value, set if currently at
  904. * column zero of a file.
  905. * init global boolean - set if tools.ini is being lexed
  906. * used by lgetc() which is called from here...
  907. *
  908. *
  909. * modifies: line global line count - if include file opened
  910. * file global pointer to current file
  911. * fName global pointer to name of current file
  912. * colZero global boolean value, changed if include
  913. * file opened and char from colZero is returned
  914. */
  915. UCHAR NEAR
  916. processIncludeFile(s)
  917. char *s;
  918. {
  919. MACRODEF *m;
  920. void *findBuf = _alloca(resultbuf_size);
  921. NMHANDLE searchHandle;
  922. char *t,
  923. *p,
  924. *u;
  925. int c = 0;
  926. int i;
  927. if (!*s || *s == '#')
  928. makeError(line,SYNTAX_NO_NAME);
  929. if (t = _ftcspbrk(s," \t#")) {
  930. if (*t == '#') c = *t;
  931. *t = '\0';
  932. if (!c) {
  933. for (u = t; *++u;) { /* check for extra*/
  934. if (*u == '#') break; /* text on line */
  935. else if (!WHITESPACE(*u))
  936. makeError(line,SYNTAX_UNEXPECTED_TOKEN,u);
  937. }
  938. }
  939. }
  940. else t = s + _ftcslen(s);
  941. if (*s == '<' && *(t-1) == '>') {
  942. char * pt;
  943. *--t = '\0';
  944. p = removeMacros(++s);
  945. p = p == s ? makeString(s) : p;
  946. t = (m = findMacro("INCLUDE")) ? m->values->text : (char*) NULL;
  947. if (t != NULL) //expand INCLUDE macro before passing it on
  948. {
  949. char * pt1;
  950. pt1= makeString(t);
  951. pt = removeMacros(pt1);
  952. if (pt != pt1)
  953. FREE (pt1); //we've got a new string, free old one
  954. }
  955. else
  956. pt = NULL;
  957. if (!(u = searchPath(pt, p, findBuf, &searchHandle)))
  958. makeError(line, CANT_OPEN_FILE, p);
  959. if (pt) FREE (pt);
  960. FREE(p);
  961. s = u;
  962. }
  963. else {
  964. if (*s == '"' && *(t-1) == '"') {
  965. *--t = '\0';
  966. ++s;
  967. }
  968. p = removeMacros(s);
  969. p = p == s ? makeString(s) : p;
  970. if (!findFirst(p,&findBuf, &searchHandle))
  971. if (!_ftcspbrk(p, "\\/:")) {
  972. //use C sematics for include
  973. for (i = incTop;i >= 0;i--) {
  974. t = (i == incTop) ? fName : incStack[i].name;
  975. if (!(t = getPath(t)))
  976. continue;
  977. u = (char *)allocate(_ftcslen(t) + 1 + _ftcslen(p) + 1);
  978. _ftcscat(_ftcscat(_ftcscpy(u, t), "\\"), p);
  979. if (findFirst(u, &findBuf, &searchHandle)) {
  980. s = u;
  981. FREE(t);
  982. break;
  983. }
  984. FREE(t);
  985. FREE(u);
  986. }
  987. FREE(p);
  988. if (i < 0)
  989. makeError(line,CANT_OPEN_FILE,s);
  990. }
  991. else
  992. makeError(line,CANT_OPEN_FILE,p);
  993. }
  994. for (i = 0; i < incTop; ++i) { /* test for cycles*/
  995. if (!_ftcsicmp(s,incStack[i].name))
  996. makeError(line,CYCLE_IN_INCLUDES,s);
  997. }
  998. incStack[incTop].file = file; /* push info on */
  999. incStack[incTop].line = line; /* stack */
  1000. incStack[incTop++].name = fName;
  1001. currentLine = 0;
  1002. if (!(file = FILEOPEN(s,"rt"))) /* read, text mode*/
  1003. makeError(line,CANT_OPEN_FILE,s);
  1004. fName = makeString(s);
  1005. line = 1;
  1006. colZero = TRUE; /* parser needs to*/
  1007. c = lgetc(); /* see some kind */
  1008. if (colZero = (BOOL) !WHITESPACE(c)) { /* of newline to */
  1009. UngetTxtChr(c,file); /* initialize it */
  1010. return(NEWLINE); /* for this file */
  1011. }
  1012. return(NEWLINESPACE);
  1013. }
  1014. #if 0 // The old getPath() is bracketted between the #if 0/#endif pair
  1015. LOCAL char * NEAR
  1016. getPath(s)
  1017. char *s;
  1018. {
  1019. char *path = (char *)allocate(_ftcslen(s));
  1020. char *t = _ftcsrchr(s, '\\'),
  1021. *u;
  1022. int n;
  1023. if (t && (u = _ftcsrchr(s, '/')) > t)
  1024. t = u;
  1025. if (!t)
  1026. n = s[1] == ':' ? 2 : 0;
  1027. else
  1028. n = t - s;
  1029. _ftcsncpy(path, s, n);
  1030. path[n] = '\0';
  1031. return(path);
  1032. }
  1033. #endif
  1034. /*** getPath -- return the drive/directory parts of a full path ***************
  1035. *
  1036. * Scope:
  1037. * Local
  1038. *
  1039. * Purpose:
  1040. * This function returns the drive/directory parts of a full path. Space is
  1041. * allocated for the resulting string, so the caller is responsible for freeing
  1042. * it after use.
  1043. *
  1044. * Input:
  1045. * pszFullPath -- The full pathname.
  1046. *
  1047. * Output:
  1048. *
  1049. * Errors/Warnings:
  1050. *
  1051. * Assumes:
  1052. * Pathnames use MS-DOS file naming convension.
  1053. *
  1054. * Modifies Globals:
  1055. * None.
  1056. *
  1057. * Uses Globals:
  1058. * None.
  1059. *
  1060. * Notes:
  1061. * To allocate temporary memory for the drive and path components, I have used
  1062. * _MAX_DRIVE and _MAX_DIR. Under Windows NT there are two possibilities:
  1063. * 1. These two parameters can be so large that the stack will be overflow
  1064. * 2. They are not large enough (?)
  1065. *
  1066. * History:
  1067. * 22-Mar-1993 HV Rewrite getPath() to make use of the new _splitpath() and
  1068. * _makepath() functions.
  1069. *******************************************************************************/
  1070. LOCAL char * NEAR
  1071. getPath(const char *pszFullPath)
  1072. {
  1073. // HV: Are _MAX_DRIVE and _MAX_DIR good size under NT? Does it
  1074. // work under Windows NT?
  1075. char szDrive[_MAX_DRIVE];
  1076. char szDir[_MAX_DIR];
  1077. char *szPath;
  1078. char *pszSlash;
  1079. // Separate the components of the fullpath
  1080. _splitpath(pszFullPath, szDrive, szDir, NULL, NULL);
  1081. // Allocate just enough memory to hold the drive/path combo then
  1082. // Glue just the drive and dir component back together.
  1083. szPath = (char *) rallocate(_ftcslen(szDrive) + _ftcslen(szDir) + 1);
  1084. _makepath(szPath, szDrive, szDir, NULL, NULL);
  1085. // Eliminate the trailing slash/blackslash to retain compatibility with
  1086. // the older version of getPath()
  1087. pszSlash = szPath + _ftcslen(szPath) - 1;
  1088. if ('\\' == *pszSlash || '/' == *pszSlash)
  1089. *pszSlash = '\0';
  1090. return szPath;
  1091. }