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.

683 lines
19 KiB

  1. // INLINE.C - contains routines used to handle processing of in-line files
  2. //
  3. // Copyright (c) 1989-1990, Microsoft Corporation. All rights reserved.
  4. //
  5. // Purpose:
  6. // This module contains the in-line file handling routines of NMAKE.
  7. //
  8. // Revision History:
  9. // 04-Feb-2000 BTF Ported to Win64
  10. // 15-Nov-1993 JdR Major speed improvements
  11. // 15-Oct-1993 HV Use tchar.h instead of mbstring.h directly, change STR*() to _ftcs*()
  12. // 01-Jun-1993 HV Use UngetTxtChr() instead of ungetc()
  13. // 10-May-1993 HV Add include file mbstring.h
  14. // Change the str* functions to STR*
  15. // 02-Feb-1990 SB change fopen() to FILEOPEN()
  16. // 03-Jan-1990 SB removed unitiallized variable
  17. // 04-Dec-1989 SB Removed to unreferenced variables in makeInlineFiles()
  18. // 01-Dec-1989 SB Changed realloc() to REALLOC()
  19. // 22-Nov-1989 SB Changed free() to FREE()
  20. // 07-Nov-1989 SB Length of action word not evaluated correct for multiple
  21. // inline files for the same command
  22. // 06-Nov-1989 SB allow macros in action word for inline files
  23. // 24-Sep-1989 SB added processInline(), createInline()
  24. // 20-Sep-1989 SB Created from routines previously scattered in the sources.
  25. //
  26. // Notes:
  27. // Sections with 'NOTE:' inside comments marks important/incomplete items.
  28. // NOTE: Function headers yet to be completed; other comments are incomplete
  29. #include "precomp.h"
  30. #pragma hdrstop
  31. void processEschIn(char *);
  32. // NOTE: This may go soon (use nextInlineFile ?)
  33. void parseInlineFileList(char *);
  34. // NOTE: The next one has to go soon
  35. void appendScript(SCRIPTLIST**,SCRIPTLIST*);
  36. void delInlineSymbol(char*);
  37. char * nextInlineFile(char **);
  38. // NOTE: Probably needs a new name
  39. void replaceLtLt(char **, char *);
  40. void createInline(FILE *, const char *, char **, BOOL);
  41. char * getLine(char *, int);
  42. void echoLine(char *, const char *, BOOL);
  43. // NOTE: delScriptFiles() from nmake.c not yet brought in here
  44. extern FILE * createDosTmp(char *);
  45. char * makeInlineFiles(char *, char **, char **);
  46. BOOL processInline(char *, char **, STRINGLIST **, BOOL);
  47. // makeInlineFiles - creates memory images for in-line files
  48. //
  49. // Scope: Global.
  50. //
  51. // Purpose: This is the function that handles dynamic in-line files
  52. //
  53. // Input: s - Input command line string after first << (pts to char Buffer)
  54. //
  55. // Output: Returns ...
  56. //
  57. // Errors/Warnings:
  58. // SYNTAX_UNEXPECTED_TOKEN - The makefile cannot end without the in-line file
  59. // ending.
  60. // CANT_READ_FILE - When the makefile is unreadable.
  61. // SYNTAX_KEEP_INLINE_FILE - An inline file should end
  62. // OUT_OF_MEMORY - On failing to extend in-memory in-line file.
  63. //
  64. // Uses Globals:
  65. // file - global stream
  66. // line - lexer's line count
  67. //
  68. // Notes:
  69. // Usage notes and other important notes
  70. char *
  71. makeInlineFiles(
  72. char *s,
  73. char **begin,
  74. char **end
  75. )
  76. {
  77. char rgchBuf[MAXBUF];
  78. char *t;
  79. unsigned size;
  80. BOOL fPastCmd = FALSE; // If seen line past Cmd line
  81. // used when rgchBuf is insuff for in-memory-inline file
  82. char *szTmpBuf = NULL;
  83. _tcscpy(rgchBuf, "<<"); // to help parseInlineFileList
  84. if (!getLine(rgchBuf+2,MAXBUF - 2)) {
  85. if (feof(file))
  86. makeError(line, SYNTAX_UNEXPECTED_TOKEN, "EOF");
  87. makeError(line, CANT_READ_FILE);
  88. }
  89. parseInlineFileList(rgchBuf);
  90. for (;scriptFileList;scriptFileList = scriptFileList->next) {
  91. for (;;) {
  92. for (t = rgchBuf;;) {
  93. *s++ = *t++;
  94. if (s == *end) {
  95. if (!szTmpBuf) { /* Increase size of s */
  96. szTmpBuf = (char *) allocate(MAXBUF<<1);
  97. _tcsncpy(szTmpBuf, *begin, MAXBUF);
  98. s = szTmpBuf + MAXBUF;
  99. size = MAXBUF << 1;
  100. *end = szTmpBuf + size;
  101. } else {
  102. void *pv;
  103. if ((size + MAXBUF < size) || !(pv = REALLOC(szTmpBuf,size+MAXBUF))) {
  104. makeError(line, MACRO_TOO_LONG);
  105. } else {
  106. szTmpBuf = (char *) pv;
  107. }
  108. s = szTmpBuf + size;
  109. size += MAXBUF;
  110. *end = szTmpBuf + size;
  111. }
  112. *begin = szTmpBuf;
  113. }
  114. if (!*t)
  115. break;
  116. }
  117. if (fPastCmd && rgchBuf[0] == '<' && rgchBuf[1] == '<') {
  118. //We don't care about action specified here; could be a macro
  119. if (scriptFileList->next) {
  120. if (!getLine(rgchBuf, MAXBUF)) {
  121. if (feof(file))
  122. makeError(line, SYNTAX_UNEXPECTED_TOKEN, "EOF");
  123. makeError(line, CANT_READ_FILE);
  124. }
  125. }
  126. break;
  127. }
  128. fPastCmd = TRUE;
  129. if (!getLine(rgchBuf,MAXBUF)) {
  130. if (feof(file))
  131. makeError(line, SYNTAX_UNEXPECTED_TOKEN, "EOF");
  132. makeError(line,CANT_READ_FILE);
  133. }
  134. }
  135. }
  136. *s = '\0';
  137. return(s);
  138. }
  139. // processEschIn - Handles Esch characters in Script File lines
  140. //
  141. // Scope: Global.
  142. //
  143. // Purpose:
  144. // Inline file lines are handled for escape characters. If a line contains an
  145. // escaped newline then append the next line to it.
  146. //
  147. // Input: buf - the command line to be processed for ESCH characters
  148. //
  149. // Errors/Warnings:
  150. // SYNTAX_UNEXPECTED_TOKEN - The makefile cannot end without the in-line file
  151. // ending.
  152. // CANT_READ_FILE - When the makefile is unreadable.
  153. //
  154. // Assumes:
  155. // If the newline is escaped the newline is last char in 'pGlobalbuf'. Safe
  156. // to do so because we got 'pGlobalBuf' via fgets(). ????
  157. //
  158. // Modifies Globals:
  159. // line - if newline was Escaped update line
  160. // file - the makefile being processed
  161. // buf - gets next line appended if newline was Escaped (indirectly)
  162. //
  163. // Uses Globals:
  164. // buf - Indirectly
  165. void
  166. processEschIn(
  167. char *pGlobalBuf
  168. )
  169. {
  170. char *p, *q;
  171. p = pGlobalBuf;
  172. while (p = _tcschr(p, '\n')) {
  173. if (p > pGlobalBuf) {
  174. char * pprev = _tcsdec(pGlobalBuf, p);
  175. if (pprev) {
  176. if (*pprev != ESCH) {
  177. break;
  178. }
  179. }
  180. }
  181. p++;
  182. if (!(q = fgets(p, (int)(size_t) (MAXBUF - (p - pGlobalBuf)), file))) {
  183. if (feof(file)) {
  184. makeError(line, SYNTAX_UNEXPECTED_TOKEN, "EOF");
  185. }
  186. makeError(line, CANT_READ_FILE);
  187. }
  188. line++;
  189. }
  190. }
  191. // parseInlineFileList - Parses file list and makes list of Inline files
  192. //
  193. // Scope: Global.
  194. //
  195. // Purpose:
  196. // To handle multiple inline files, the names of the files are to be stored
  197. // in a list. This function creates the list by parsing the command file
  198. //
  199. // Input: buf - the line to be parsed
  200. //
  201. // Modifies Globals:
  202. // scriptFileList -- the list of script files.
  203. void
  204. parseInlineFileList(
  205. char *buf
  206. )
  207. {
  208. char *token;
  209. processEschIn(buf);
  210. token = nextInlineFile(&buf); //next inline file
  211. while (token != NULL) {
  212. SCRIPTLIST *newScript;
  213. newScript = makeNewScriptListElement();
  214. newScript->sFile = makeString(token);
  215. appendScript(&scriptFileList, newScript);
  216. token = nextInlineFile(&buf); // next inline file
  217. }
  218. }
  219. // appendScript -- appends an element to the tail of a scriptlist
  220. //
  221. // Purpose:
  222. // Traverse to the end of the list and append element there.
  223. //
  224. // Input:
  225. // list -- the list to append to
  226. // element -- the element inserted
  227. //
  228. // Modifies:
  229. // the global list
  230. void
  231. appendScript(
  232. SCRIPTLIST **list,
  233. SCRIPTLIST *element
  234. )
  235. {
  236. for (; *list; list = &(*list)->next)
  237. ;
  238. *list = element;
  239. }
  240. char tok[MAXNAME];
  241. // Space not included in the following macro as it is now a valid
  242. // character for filenames [DS 14966]
  243. #define NAME_CHAR(c) (c) != '>' && (c) != '<' && \
  244. (c) != '^' && (c) != ',' && (c) != '\t' && \
  245. (c) != '\n'
  246. // nextInlineFile - gets next Inline file name from command line
  247. //
  248. // Scope: Local.
  249. //
  250. // Purpose:
  251. // The command line syntax is complex. This function returns the next Inline
  252. // file in the command line part passed to it. As a side effect it changes the
  253. // pointer to just after this inline file name.
  254. //
  255. // Input: str - address of the part of command line under consideration.
  256. //
  257. // Output: Returns the next inline filename.
  258. //
  259. // Modifies Globals:
  260. // Global - How and why modified
  261. //
  262. // Uses Globals:
  263. // tok - the address of this static array is returned.
  264. char *
  265. nextInlineFile(
  266. char **str
  267. )
  268. {
  269. char *t = tok, *pStr = *str;
  270. BOOL fFound = FALSE; // '<<' not found
  271. BOOL fQuoted = FALSE; // found '\"'
  272. while (!fFound) {
  273. if (!(pStr = _tcschr(pStr, '<'))) {
  274. return(NULL);
  275. }
  276. if (*++pStr == '<') {
  277. fFound = TRUE;
  278. }
  279. }
  280. // Since '<<' has been found we definitely have another Inline File
  281. pStr++;
  282. while (*pStr && NAME_CHAR(*pStr)) {
  283. if (*pStr == '\"') {
  284. fQuoted = !fQuoted;
  285. }
  286. if (*pStr == ' ' && !fQuoted) {
  287. break;
  288. }
  289. if (*pStr == '$' && pStr[1] == '(') {
  290. *t = '$';
  291. *++t = '(';
  292. while (*++pStr != '\n' && *pStr != ')') {
  293. *t++ = *pStr;
  294. }
  295. if (*pStr == '\n') {
  296. break;
  297. }
  298. } else {
  299. *t = *pStr;
  300. ++t; ++pStr;
  301. }
  302. }
  303. *t = '\0';
  304. *str = pStr;
  305. return(tok);
  306. }
  307. // processInline - Brief description of the function
  308. //
  309. // Output: Returns ... TRUE if cmdline returned is expanded
  310. BOOL
  311. processInline(
  312. char *szCmd,
  313. char **szCmdLine,
  314. STRINGLIST **pMacroList,
  315. BOOL fDump
  316. )
  317. {
  318. char *szInline, *szUnexpInline; // Inline name, unexpanded
  319. char *pCmdLine; // The executable line
  320. FILE *infile; // The inline file
  321. char *begInBlock, *inBlock, *pInBlock; // inline block
  322. char szTmp[MAXNAME + 2]; // add 2 to allow space for quotes
  323. STRINGLIST *newString;
  324. int iKeywordLen;
  325. if (begInBlock = _tcschr(szCmd, '\n')) {
  326. *begInBlock = '\0';
  327. *szCmdLine = expandMacros(szCmd, pMacroList);
  328. *begInBlock = '\n';
  329. begInBlock++;
  330. // if not expanded, allocate a copy
  331. if (*szCmdLine == szCmd)
  332. *szCmdLine = makeString(szCmd);
  333. } else {
  334. *szCmdLine = makeString(szCmd);
  335. return(FALSE);
  336. }
  337. pCmdLine = *szCmdLine;
  338. //expand macros in the inline file ...
  339. pInBlock = inBlock = expandMacros(begInBlock, pMacroList);
  340. while (szUnexpInline = nextInlineFile(&pCmdLine)) {
  341. BOOL fKeep = FALSE; // default is NOKEEP
  342. char *newline;
  343. // CAVIAR 3410 -- the inline filename has already been expaned
  344. // by the time we get here... we just need to dup the name
  345. // so that it is preserved long enough to delete it later... [rm]
  346. //
  347. // szInline = removeMacros(szUnexpInline);
  348. szInline = makeString(szUnexpInline);
  349. if (!*szInline) {
  350. char *nmTmp;
  351. if ((nmTmp = getenv("TMP")) != NULL && *nmTmp) {
  352. assert(_tcslen(nmTmp) <= MAXNAME);
  353. _tcsncpy(szTmp, nmTmp, MAXNAME);
  354. } else
  355. szTmp[0] = '\0';
  356. if (!(infile = createDosTmp(szTmp)))
  357. makeError(line, CANT_MAKE_INLINE, szTmp);
  358. if (_tcschr(szTmp, ' ') && !_tcschr(szTmp, '"')) {
  359. // if the filename (str) contains spaces
  360. // and is unquoted, quote it, so that we can
  361. // feed it properly to the command interpreter! [VS98 1931]
  362. size_t size = _tcslen(szTmp);
  363. memmove(szTmp+1, szTmp, size);
  364. *szTmp = '"';
  365. *(szTmp + size + 1) = '"';
  366. *(szTmp + size + 2) = '\0';
  367. }
  368. replaceLtLt(szCmdLine, szTmp);
  369. FREE(szInline);
  370. szInline = makeString(szTmp);
  371. } else if (!(infile = FILEOPEN(szInline, "w")))
  372. makeError(line, CANT_MAKE_INLINE, szInline);
  373. else
  374. delInlineSymbol(*szCmdLine);
  375. pCmdLine = *szCmdLine; // Because szCmdLine changed
  376. createInline(infile, szInline, &pInBlock, fDump);
  377. // Add handling of KEEP and NOKEEP here
  378. // iKeywordLen is length of word after << on that line
  379. newline = _tcschr(pInBlock , '\n');
  380. iKeywordLen = newline ? ((int) (newline - pInBlock)) : _tcslen(pInBlock);
  381. if (iKeywordLen > 3 && !_tcsnicmp(pInBlock, "keep", 4)) {
  382. pInBlock +=4;
  383. fKeep = (BOOL)TRUE;
  384. } else if (iKeywordLen > 5 && !_tcsnicmp(pInBlock, "nokeep", 6))
  385. pInBlock += 6;
  386. else if (iKeywordLen)
  387. makeError(line, SYNTAX_KEEP_INLINE_FILE);
  388. if (*pInBlock == '\n')
  389. pInBlock++;
  390. fclose(infile);
  391. // Add the file to list to be deleted; except for "KEEP"
  392. if (!fKeep) {
  393. newString = makeNewStrListElement();
  394. newString->text = makeString(szInline);
  395. appendItem(&delList, newString);
  396. }
  397. FREE(szInline);
  398. }
  399. if (inBlock != begInBlock)
  400. FREE(inBlock);
  401. return(TRUE);
  402. }
  403. void
  404. replaceLtLt(
  405. char **source,
  406. char *str
  407. )
  408. {
  409. char *szBuf;
  410. char *p, *q;
  411. void *pv;
  412. // Don't subtract two for the << and forget to add 1 for the null termination.
  413. szBuf = (char *) malloc(_tcslen(*source) - 1 + _tcslen(str));
  414. if (!szBuf) {
  415. makeError(0, OUT_OF_MEMORY);
  416. }
  417. for (p = *source, q = szBuf;;++p,++q)
  418. if (*p != '<')
  419. *q = *p;
  420. else if (*(p+1) != '<') {
  421. *q = '<';
  422. } else {
  423. *q = '\0';
  424. _tcscat(_tcscat(szBuf, str), p+2);
  425. pv = REALLOC(*source, _tcslen(szBuf) + 1);
  426. if (pv) {
  427. *source = (char *) pv;
  428. } else {
  429. makeError(0, OUT_OF_MEMORY);
  430. }
  431. _tcscpy(*source, szBuf);
  432. break;
  433. }
  434. free(szBuf);
  435. }
  436. void
  437. createInline(
  438. FILE *file,
  439. const char *szFName,
  440. char **szString,
  441. BOOL fDump
  442. )
  443. {
  444. char *t, *u;
  445. BOOL fFirstLine = TRUE;
  446. while (t = _tcschr(*szString, '\n'))
  447. if (!_tcsncmp(*szString, "<<", 2)) {
  448. *szString += 2;
  449. break;
  450. } else {
  451. // [msdev96 #3036]
  452. // "nmake /n" should somehow show the contents of
  453. // response files (esp. temp ones that are deleted
  454. // right after use). In order to preserve the batch
  455. // file format of the output (at least in common
  456. // cases), we use a syntax like
  457. // "echo. command >> resp_file" (the dot after
  458. // the "echo" command is useful for echo'ing
  459. // empty strings.)
  460. //
  461. // A new switch has been added for this
  462. // purpose ("nmake /u" dumps inline files)
  463. if (fDump) {
  464. *t = '\0';
  465. echoLine(*szString, szFName, !fFirstLine);
  466. *t = '\n';
  467. }
  468. for (u = *szString; u <= t; u++)
  469. fputc(*u, file);
  470. *szString = u;
  471. fFirstLine = FALSE;
  472. }
  473. if (!t && !_tcsncmp(*szString, "<<", 2))
  474. *szString += 2;
  475. }
  476. // echoLine
  477. //
  478. // Usage: echoLine (szLine, szFName, fAppend)
  479. //
  480. // Description:
  481. // prints an "echo szLine >> szFName"-like command
  482. // uses ">>" if fAppend is TRUE, ">" otherwise
  483. void
  484. echoLine(char *szLine, const char *szFName, BOOL fAppend)
  485. {
  486. // use a 1024-byte buffer to split long lines, so that "echo"
  487. // commands can be handled by the command interpreter
  488. static char L_buf[1024];
  489. BOOL fBlankLine = TRUE;
  490. char *pch;
  491. char *szCur = szLine;
  492. size_t cbBuf;
  493. for (pch = szLine; *pch; pch = _tcsinc (pch)) {
  494. if (!_istspace((unsigned char)*pch)) {
  495. fBlankLine = FALSE;
  496. break;
  497. }
  498. }
  499. if (fBlankLine) {
  500. printf("\techo. %s %s\n",
  501. fAppend ? ">>" : ">",
  502. szFName);
  503. return;
  504. }
  505. // calculate available buffer length for szLine
  506. // assuming space for "\techo. ", " >> " and szFName
  507. cbBuf = sizeof(L_buf) - 11 - _tcslen( szFName ) - 1;
  508. while (*szCur) {
  509. size_t iLast;
  510. _tcsncpy (L_buf, szCur, cbBuf);
  511. iLast = _tcslen (L_buf);
  512. if (cbBuf < _tcslen (szCur)) {
  513. // find index of character next to the
  514. // last occurence of white space in buffer
  515. for (pch = L_buf; *pch; pch = _tcsinc(pch)) {
  516. if (_istspace((unsigned char)*pch)) {
  517. iLast = (size_t) (pch - L_buf + 1);
  518. }
  519. }
  520. }
  521. L_buf[iLast] = 0;
  522. printf("\techo %s %s %s\n",
  523. L_buf,
  524. fAppend ? ">>" : ">",
  525. szFName);
  526. szCur += iLast;
  527. fAppend = TRUE;
  528. }
  529. }
  530. void
  531. delInlineSymbol(
  532. char *s
  533. )
  534. {
  535. char *p = _tcschr(s, '<');
  536. while (p[1] != '<')
  537. p = _tcschr(p+1, '<');
  538. // "<<" found
  539. _tcscpy(p, p+2);
  540. }
  541. // getLine - get next line processing NMAKE conditionals enroute
  542. //
  543. // Scope: Local
  544. //
  545. // Purpose:
  546. // This function handles directives in inline files. This function gets the
  547. // next line of input ... managing conditionals on the way.
  548. //
  549. // Input:
  550. // pchLine - pointer to buffer where line is copied
  551. // n - size of buffer
  552. //
  553. // Output:
  554. // Returns ... NULL, on EOF
  555. // ... non-zero on success
  556. //
  557. // Uses Globals:
  558. // line - lexer's line count
  559. // colZero - if starting from colZero, needed by lgetc()
  560. //
  561. // Notes:
  562. // Similar to fgets() without stream
  563. //
  564. // Implementation Notes:
  565. // lgetc() handles directives while getting the next character. It handles
  566. // directives when the global colZero is TRUE.
  567. char *
  568. getLine(
  569. char *pchLine,
  570. int n
  571. )
  572. {
  573. char *end = pchLine + n;
  574. int c;
  575. while (c = lgetc()) {
  576. switch (c) {
  577. case EOF:
  578. *pchLine = '\0';
  579. return(NULL);
  580. default:
  581. *pchLine++ = (char)c;
  582. break;
  583. }
  584. if (pchLine == end) {
  585. pchLine[-1] = '\0';
  586. UngetTxtChr(c, file);
  587. return(pchLine);
  588. } else if (c == '\n') {
  589. colZero = TRUE;
  590. ++line;
  591. *pchLine = '\0';
  592. return(pchLine);
  593. } else
  594. colZero = FALSE; // the last character was not a '\n' and
  595. // we are not at the beginning of the file
  596. }
  597. return(pchLine);
  598. }