Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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