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.

1187 lines
42 KiB

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