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.

1623 lines
46 KiB

  1. // Exec.C - Contains routines that have do to with execing programs
  2. //
  3. // Copyright (c) 1988-1991, Microsoft Corporation. All Rights Reserved.
  4. //
  5. // Purpose:
  6. // Contains routines that spawn programs ...
  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. // 10-May-1993 HV Add include file mbstring.h
  13. // Change the str* functions to STR*
  14. // 06-Oct-1992 GBS Removed extern for _pgmptr
  15. // 10-Aug-1992 GBS Change file parsing in execLine to use splitpath
  16. // 19-Aug-1992 SS Remove Quotes from cd argument.
  17. // 08-Jun-1992 SS add IDE feedback support
  18. // 08-Jun-1992 SS Port to DOSX32
  19. // 16-May-1991 SB Created from routines that existed elsewhere
  20. #include "precomp.h"
  21. #pragma hdrstop
  22. #define SLASH '\\'
  23. #define PUBLIC
  24. #define QUOTE '\"'
  25. extern BOOL processInline(char *, char **, STRINGLIST **, BOOL);
  26. #ifdef _M_IX86
  27. extern UCHAR fRunningUnderChicago;
  28. #else
  29. #define fRunningUnderChicago FALSE
  30. #endif
  31. char * getComSpec(void);
  32. BOOL iterateCommand(char*, STRINGLIST*, UCHAR, UCHAR, char *, unsigned*);
  33. void removeQuotes(int, char **);
  34. void touch(char*, BOOL);
  35. //buffer for path of .cmd/.bat
  36. extern char * makeStr;
  37. extern char * shellName;
  38. char szCmdLineBuf[MAXCMDLINELENGTH];
  39. char *szNmakeProgName;
  40. // buildArgumentVector -- builds an argument vector from a command line
  41. //
  42. // Scope:
  43. // Local.
  44. //
  45. // Purpose:
  46. // It builds an argument vector for a command line. This argument vector can
  47. // be used by spawnvX routines. The algorithm is explained in the notes below.
  48. //
  49. // Input:
  50. // argc -- The number of arguments created in the argument vector
  51. // argv -- The actual argument vector created
  52. // (Ignored if NULL)
  53. // cmdline -- The command line whose vector is required
  54. //
  55. // Output:
  56. // Returns the number of arguments and the argument vector as parameters
  57. //
  58. // Errors/Warnings:
  59. // Assumes:
  60. // That the behaviour of cmd.exe i.e. parses quotes but does not disturb them.
  61. // Assumes that the SpawnVX routines will handle quotes as well as escaped
  62. // chars.
  63. //
  64. // Modifies Globals:
  65. // Uses Globals:
  66. // Notes:
  67. // Scan the cmdline from left to the end building the argument vector along
  68. // the way. Whitespace delimits arguments except for the first argument for
  69. // which the switch char '/' is also allowed. Backslash can be used to escape
  70. // a char and so ignore the character following it. Parse the quotes along
  71. // the way. If an argument begins with a double-quote then all characters till
  72. // an unescaped double-quote are part of that argument. Likewise, if an
  73. // unescaped Doublequote occurs within an argument then the above follows. If
  74. // the end of the command line comes before the closing quote then the
  75. // argument goes as far as that.
  76. void
  77. buildArgumentVector(
  78. unsigned *argc,
  79. char **argv,
  80. char *cmdline
  81. )
  82. {
  83. char *p; // current loc in cmdline
  84. char *end; // end of command line
  85. BOOL fFirstTime = TRUE; // true if 1st argument
  86. // 11-May-1993 HV _mbschr() bug: return NULL
  87. // end = _tcschr(p = cmdline, '\0');
  88. // Work around:
  89. end = p = cmdline;
  90. while (*end)
  91. end++;
  92. for (*argc = 0; p < end; ++*argc) {
  93. p += _tcsspn(p, " \t"); // skip whitespace
  94. if (p >= end)
  95. break;
  96. if (argv)
  97. *argv++ = p;
  98. if (*p == '\"') {
  99. // If the word begins with double-quote, find the next
  100. // occurrence of double-quote which is not preceded by backslash
  101. // (same escape as C runtime), or end of string, whichever is
  102. // first. From there, find the next whitespace character.
  103. for (++p; p < end; p = _tcsinc(p)) {
  104. if (*p == '\\')
  105. ++p; // skip escaped character
  106. else if (*p == '\"')
  107. break;
  108. }
  109. if (p >= end)
  110. continue;
  111. ++p;
  112. p = _tcspbrk(p, " \t");
  113. } else {
  114. // For the first word on the command line, accept the switch
  115. // character and whitespace as terminators. Otherwise, just
  116. // whitespace.
  117. p = _tcspbrk(p, " \t\"/");
  118. for (;p && p < end;p = _tcspbrk(p+1, " \t\"/")) {
  119. if (*p == '/' && !fFirstTime)
  120. continue; // after 1st word '/' is !terminator
  121. else break;
  122. }
  123. if (p && *p == '\"') {
  124. for (p++;p < end;p++) { // inside quote so skip to next one
  125. if (*p == '\"')
  126. break;
  127. }
  128. p = _tcspbrk(p, " \t"); // after quote go to first whitespace
  129. }
  130. if (fFirstTime) {
  131. fFirstTime = FALSE;
  132. // If switch char terminates the word, replace it with 0,
  133. // re-allocate the word on the heap, restore the switch and set
  134. // p just before the switch. It would be easier to shift
  135. // everything right but then we have to worry about overflow.
  136. if (p && *p == '/' && argv) {
  137. *p = '\0';
  138. argv[-1] = makeString(argv[-1]);
  139. *p-- = '/';
  140. }
  141. }
  142. }
  143. if (!p)
  144. p = end;
  145. // Now, p points to end of command line argument
  146. if (argv)
  147. *p++ = '\0';
  148. }
  149. if (argv)
  150. *argv = NULL;
  151. }
  152. PUBLIC int
  153. doCommands(
  154. char *name,
  155. STRINGLIST *s,
  156. STRINGLIST *t,
  157. UCHAR buildFlags,
  158. char *pFirstDep
  159. )
  160. {
  161. STRINGLIST *temp;
  162. int rc;
  163. temp = makeNewStrListElement();
  164. temp->text = makeString(name);
  165. rc = doCommandsEx (temp, s, t, buildFlags, pFirstDep);
  166. free_stringlist(temp);
  167. return rc;
  168. }
  169. PUBLIC int
  170. doCommandsEx(
  171. STRINGLIST *nameList,
  172. STRINGLIST *s,
  173. STRINGLIST *t,
  174. UCHAR buildFlags,
  175. char *pFirstDep
  176. )
  177. {
  178. char *u, *v;
  179. UCHAR cFlags;
  180. unsigned status = 0;
  181. int retryCount = 0;
  182. char c;
  183. char *Cmd;
  184. char *pLine;
  185. BOOL fExpanded;
  186. char *pCmd;
  187. size_t cbLine;
  188. #ifdef DEBUG_ALL
  189. if (fDebug) {
  190. printf("* doCommands:");
  191. DumpList(nameList);
  192. DumpList(s);
  193. DumpList(t);
  194. }
  195. #endif
  196. #ifdef DEBUG_ALL
  197. printf("DEBUG: doCommands 1\n");
  198. #endif
  199. ++numCommands;
  200. if (ON(gFlags, F1_QUESTION_STATUS))
  201. return(0);
  202. if (ON(gFlags, F1_TOUCH_TARGETS)) {
  203. STRINGLIST *pName;
  204. for (pName = nameList; pName; pName = pName->next) {
  205. touch(pName->text, (USHORT) ON(buildFlags, F2_NO_EXECUTE));
  206. }
  207. return(0);
  208. }
  209. #ifdef DEBUG_ALL
  210. printf("DEBUG: doCommands 2\n");
  211. #endif
  212. for (; s; s = s->next) {
  213. fExpanded = processInline(s->text, &Cmd, &t,
  214. ON(buildFlags, F2_DUMP_INLINE));
  215. cFlags = 0;
  216. errorLevel = 0;
  217. u = Cmd;
  218. for (v = u; *v; v = _tcsinc(v)) {
  219. if (*v == ESCH) ++v;
  220. else if (*v == '$') {
  221. if (*++v == '$')
  222. continue;
  223. // commented out 15-Apr-93 by JonM. This code forces recursive nmake to be
  224. // executed even if -n, but it's hosed (the -n is not passed to the recursive
  225. // nmake), and the whole thing sounds like a bad idea anyway, so I'm going to
  226. // turn it off.
  227. // if (!_tcsncmp(v, "(MAKE)", 6)) {
  228. // SET(cFlags, C_EXECUTE);
  229. // break;
  230. // }
  231. }
  232. }
  233. #ifdef DEBUG_ALL
  234. printf("DEBUG: doCommands 2.1\n");
  235. #endif
  236. for (c = *u; c == '!' ||
  237. c == '-' ||
  238. c == '@' ||
  239. c == ESCH ||
  240. WHITESPACE(c); u = _tcsinc(u), c = *u) {
  241. switch (c) {
  242. case ESCH:
  243. if (c = *++u, WHITESPACE(c))
  244. c = ' '; // keep going
  245. else
  246. c = ESCH;
  247. break;
  248. case '!':
  249. SET(cFlags, C_ITERATE);
  250. break;
  251. case '-':
  252. SET(cFlags, C_IGNORE);
  253. ++u;
  254. if (_istdigit(*u)) {
  255. char *pNumber = u;
  256. errorLevel = _tcstoul(u, &u, 10);
  257. if (errno == ERANGE) {
  258. *u = '\0';
  259. makeError(line, CONST_TOO_BIG, pNumber);
  260. }
  261. while(_istspace(*u))
  262. u++;
  263. } else
  264. errorLevel = UINT_MAX;
  265. --u;
  266. break;
  267. case '@':
  268. if (
  269. OFF(flags, F2_NO_EXECUTE)) {
  270. SET(cFlags, C_SILENT);
  271. }
  272. break;
  273. }
  274. if (c == ESCH)
  275. break; // stop parsing for cmd-line options
  276. }
  277. #ifdef DEBUG_ALL
  278. printf("DEBUG: doCommands 2.2\n");
  279. #endif
  280. if (ON(cFlags, C_ITERATE) &&
  281. iterateCommand(u, t, buildFlags, cFlags, pFirstDep, &status)
  282. ) {
  283. // The macros used by the command have to be freed & so we do so
  284. v = u;
  285. #ifdef DEBUG_ALL
  286. printf("DEBUG: doCommands 2.21\n");
  287. #endif
  288. if (_tcschr(u, '$'))
  289. u = expandMacros(u, &t);
  290. #ifdef DEBUG_ALL
  291. printf("DEBUG: doCommands 2.22\n");
  292. #endif
  293. if (v != u)
  294. FREE(u);
  295. if (OFF(buildFlags, F2_IGNORE_EXIT_CODES) &&
  296. fOptionK &&
  297. status &&
  298. status > errorLevel)
  299. {
  300. break;
  301. }
  302. continue;
  303. }
  304. v = u;
  305. #ifdef DEBUG_ALL
  306. printf("DEBUG: doCommands 2.23\n");
  307. #endif
  308. if (!fExpanded && _tcschr(u, '$'))
  309. u = expandMacros(u, &t);
  310. #ifdef DEBUG_ALL
  311. printf("DEBUG: doCommands 2.24\n");
  312. #endif
  313. cbLine = _tcslen(u) + 1;
  314. pLine = (char *) rallocate (__max(cbLine, MAXCMDLINELENGTH));
  315. _tcscpy(pLine, u);
  316. // by this time $< has already been expanded.
  317. // in order to allow processing of long commands that are due to
  318. // batch-mode rules, use a buffer that may be larger than MAXCMDLINELENGTH
  319. // Later we'll attempt to execute the long command directly, instead of
  320. // passing it to the shell.
  321. // Note: the macros expanded by ZFormat are not normally found in the
  322. // command block of a batch-mode rule, so it should be safe to use
  323. // max(cbLine, MAXCMDLINELENGTH) as a limit for ZFormat
  324. if (ZFormat (pLine, __max(cbLine, MAXCMDLINELENGTH), u, pFirstDep))
  325. makeError(0, COMMAND_TOO_LONG, u);
  326. retry:
  327. status = execLine(pLine,
  328. (BOOL)(ON(buildFlags, F2_NO_EXECUTE)
  329. || (OFF(buildFlags,F2_NO_ECHO)
  330. && OFF(cFlags,C_SILENT))),
  331. (BOOL)((OFF(buildFlags, F2_NO_EXECUTE)
  332. )
  333. || ON(cFlags, C_EXECUTE)),
  334. (BOOL)ON(cFlags, C_IGNORE), &pCmd);
  335. if (OFF(buildFlags, F2_IGNORE_EXIT_CODES)) {
  336. if (status == STATUS_PENDING) {
  337. // Hack for ntvdm problem returning correct error code.
  338. if (retryCount < 10) {
  339. retryCount++;
  340. goto retry;
  341. }
  342. }
  343. if (status && status > errorLevel) {
  344. if (!fOptionK)
  345. makeError(0, BAD_RETURN_CODE, pCmd, status);
  346. }
  347. }
  348. if (v != u)
  349. FREE(u);
  350. FREE(Cmd);
  351. FREE(pLine);
  352. if (OFF(buildFlags, F2_IGNORE_EXIT_CODES) &&
  353. fOptionK &&
  354. status &&
  355. status > errorLevel)
  356. {
  357. break;
  358. }
  359. }
  360. #ifdef DEBUG_ALL
  361. printf("DEBUG: doCommands 3\n");
  362. #endif
  363. if (OFF(buildFlags, F2_IGNORE_EXIT_CODES) && fOptionK &&
  364. (status > errorLevel))
  365. return(status);
  366. else
  367. return(0);
  368. }
  369. // expandCommandLine -- expands %name% strings in the Command Line
  370. //
  371. // Purpose:
  372. // The function expands '%name%' type strings in the Command Line. Its main
  373. // job is to assist FEmulateCommand() in emulating set for OS/2.
  374. //
  375. // Modifies: buf -- The Command Line available globally
  376. //
  377. // Output:
  378. // Returns -- the position of 'name=value' part in the Command Line.
  379. // -- Null when no '=' is found so that FEmulateCommand() can pass the
  380. // line to the shell to signal syntax error.
  381. // Note:
  382. // The shell does not give a syntax error for unmatched '%' and assumes it
  383. // as just another character in this case. This behaviour is duplicated
  384. // by expandCommandLine()
  385. char *
  386. expandCommandLine(
  387. void
  388. )
  389. {
  390. char *Buf; // Buffer for expanded string
  391. char *pBuf;
  392. char *EnvBuf; // getenv returned string copy
  393. char *posName, // position of 'name=string' in Buf or buf
  394. *p, // points into buf
  395. *pEnv; // points into Env
  396. char ExpandName[MAXNAME]; // %name% string
  397. char *pExpandName;
  398. Buf = (char *)malloc(MAXCMDLINELENGTH);
  399. if (!Buf) {
  400. makeError(0, OUT_OF_MEMORY);
  401. return NULL;
  402. }
  403. EnvBuf = (char *)malloc(MAXCMDLINELENGTH);
  404. if (!EnvBuf) {
  405. makeError(0, OUT_OF_MEMORY);
  406. free(Buf);
  407. return NULL;
  408. }
  409. pBuf = Buf;
  410. _tcscpy(pBuf, "set");
  411. p = szCmdLineBuf + 3; // go beyond 'set'
  412. pBuf +=3;
  413. /* Skip whitespace */
  414. for (;;p++) {
  415. if (!(WHITESPACE(*p)))
  416. break; // argc>1 � this will happen
  417. else *pBuf++ = *p;
  418. }
  419. if (!_tcschr(p, '=')) {
  420. free(EnvBuf);
  421. free(Buf);
  422. return(""); // Syntax error so pass to the shell
  423. } else {
  424. posName = pBuf; // fixes position of Name in Buf
  425. }
  426. // Now we look for environment variables and expand if required
  427. for (;*p != '=';p++)
  428. *pBuf++ = *p;
  429. for (;*p;) {
  430. if (*p == '%') {
  431. pExpandName = &ExpandName[0];
  432. while (*++p != '%' && *p)
  433. *pExpandName++ = *p;
  434. *pExpandName = '\0';
  435. if (!*p++) { // unmatched %;so don't expand
  436. *pBuf='\0'; // from the environment; like set
  437. _tcscat(Buf, ExpandName);
  438. pBuf += _tcslen(ExpandName);
  439. break; // Done precessing quit #43290
  440. } else { // matched %;so expand from the environment
  441. EnvBuf[0] = '\0';
  442. if ((pEnv = getenv(ExpandName)) != (char *)NULL) {
  443. *pBuf='\0';
  444. // If the expanded command line is too long
  445. // just say that we can't expand it!!! #43290
  446. size_t len = _tcslen(pEnv) + _tcslen(Buf) + 1;
  447. if (len > MAXCMDLINELENGTH) {
  448. free(EnvBuf);
  449. free(Buf);
  450. return NULL;
  451. }
  452. _tcscat(EnvBuf, pEnv);
  453. _tcscat(Buf,EnvBuf);
  454. pBuf += _tcslen(EnvBuf);
  455. }
  456. }
  457. } else
  458. *pBuf++ = *p++;
  459. }
  460. *pBuf = '\0';
  461. _tcscpy(szCmdLineBuf, Buf);
  462. *posName = '\0';
  463. posName = szCmdLineBuf + _tcslen(Buf); // Offset into buf
  464. free(EnvBuf);
  465. free(Buf);
  466. return(posName);
  467. }
  468. // expandEnvVars -- expands %name% strings in szArg
  469. //
  470. // Returns -- szNew: the resulting expanded string
  471. // (szNew should be FREEd by the caller)
  472. //
  473. char *
  474. expandEnvVars(
  475. char *szArg
  476. )
  477. {
  478. char *pchLeft = NULL;
  479. char *pchRight = NULL;
  480. char *pchStart = szArg;
  481. char *szNew = makeString("");
  482. while (*pchStart) {
  483. pchLeft = _tcschr(pchStart, '%');
  484. if (pchLeft) {
  485. pchRight = _tcschr(pchLeft + 1, '%');
  486. }
  487. if (pchLeft && pchRight) {
  488. char *szEnv;
  489. *pchLeft = '\0';
  490. *pchRight = '\0';
  491. szNew = reallocString(szNew, pchStart);
  492. if (szEnv = getenv(pchLeft + 1)) {
  493. szNew = reallocString(szNew, szEnv);
  494. }
  495. else {
  496. // no matching env var was found
  497. // append the %..% string literary
  498. *pchLeft = '%';
  499. szNew = reallocString(szNew, pchLeft);
  500. szNew = reallocString(szNew, "%");
  501. }
  502. *pchLeft = '%';
  503. *pchRight = '%';
  504. pchStart = pchRight + 1;
  505. pchLeft = NULL;
  506. pchRight = NULL;
  507. }
  508. else {
  509. szNew = reallocString(szNew, pchStart);
  510. pchStart += _tcslen(pchStart);
  511. }
  512. }
  513. return szNew;
  514. }
  515. // FEmulateCommand - look for certain commands and emulate them
  516. //
  517. // Emulate $(MAKE), cd, chdir, and <drive letter>:.
  518. // Also emulates 'set'.
  519. //
  520. // RETURNS: TRUE if command emulated, FALSE if not.
  521. //
  522. // Note:
  523. // In set emulation if a syntax error is discovered then it lets the
  524. // shell handle it. It does this by returning FALSE.
  525. BOOL
  526. FEmulateCommand(
  527. int argc,
  528. char **argv,
  529. int *pStatus
  530. )
  531. {
  532. char *pArg0 = argv[0];
  533. char *pArg1 = argv[1];
  534. if (_istalpha(*pArg0) && pArg0[1] == ':' && !pArg0[2]) {
  535. // If "<drive letter>:" then change drives. Ignore everything after
  536. // the drive letter, just like the shell does.
  537. _chdrive(_totupper(*pArg0) - 'A' + 1);
  538. *pStatus = 0;
  539. return(TRUE);
  540. }
  541. if (!_tcsicmp(pArg0, "set")) {
  542. char *pNameVal; // the "name=value" string
  543. // If "set" then pass it to the shell and if "set string" then put it
  544. // into the environment. Let the shell handle the syntax errors.
  545. if (argc == 1) {
  546. return(FALSE); // pass it to the shell
  547. }
  548. // expandCommandLine cannot handle lines > MAXCMDLINELENGTH
  549. // In that case szCmdLineBuf will be empty
  550. if (!szCmdLineBuf[0])
  551. return (FALSE);
  552. pNameVal = expandCommandLine();
  553. if (pNameVal == NULL)
  554. {
  555. // Expanded commad line too long
  556. return FALSE;
  557. }
  558. if (!*pNameVal) {
  559. // If there is a syntax error let the shell handle it
  560. return(FALSE);
  561. }
  562. if ((*pStatus = PutEnv(makeString(pNameVal))) == -1) {
  563. makeError(currentLine, OUT_OF_ENV_SPACE);
  564. }
  565. } else {
  566. // If "cd foo" or "chdir foo", do a chdir() else in protect mode this
  567. // would be a no-op. Ignore everything after 1st arg, just like the
  568. // shell does.
  569. char *szArg;
  570. if (!_tcsnicmp(pArg0, "cd", 2)) {
  571. pArg0 += 2;
  572. } else if (!_tcsnicmp(pArg0, "chdir", 5)) {
  573. pArg0 += 5;
  574. } else {
  575. return(FALSE);
  576. }
  577. // At this point, a prefix of argv[0] matches cd or chdir and pArg0
  578. // points to the next char. Check for a path separator in argv[0]
  579. // (e.g., cd..\foo) or else use the next arg if present.
  580. // if there are more than two arguments then let the shell handle it
  581. if (argc > 2) {
  582. return(FALSE);
  583. }
  584. // Remove quotes, if any from the argument
  585. removeQuotes(argc, argv);
  586. if (!*pArg0 && pArg1) {
  587. // Under certain circumstances the C RunTime does not help us
  588. // e.g. 'd:', in this case let the shell do it ...
  589. if (isalpha(*pArg1) && pArg1[1] == ':' && !pArg1[2]) {
  590. return(FALSE);
  591. }
  592. szArg = expandEnvVars(pArg1); // [VS98 2251]
  593. *pStatus = _chdir(szArg);
  594. FREE (szArg);
  595. } else if (*pArg0 == '.' || PATH_SEPARATOR(*pArg0)) {
  596. szArg = expandEnvVars(pArg0); // [VS98 2251]
  597. *pStatus = _chdir(szArg);
  598. FREE (szArg);
  599. } else {
  600. // Unrecognized syntax--we can't emulate.
  601. return(FALSE);
  602. }
  603. }
  604. // If error, simulate a return code of 1.
  605. if (*pStatus != 0) {
  606. *pStatus = 1;
  607. }
  608. return(TRUE);
  609. }
  610. #ifdef WIN95
  611. int __cdecl
  612. cmpSzPsz(
  613. const void *sz,
  614. const void *psz
  615. )
  616. {
  617. const char *sz1 = (char *) sz;
  618. const char *sz2 = *(char **) psz;
  619. return(_tcsicmp(sz1, sz2));
  620. }
  621. BOOL
  622. FInternalCommand(
  623. const char *szName
  624. )
  625. {
  626. const char * const *pszInternal;
  627. static const char * const rgszInternal[] =
  628. {
  629. "BREAK",
  630. "CALL",
  631. "CD",
  632. "CHDIR",
  633. "CLS",
  634. "COPY",
  635. "CTTY",
  636. "DATE",
  637. "DEL",
  638. "DIR",
  639. "DIR.",
  640. "ECHO",
  641. "ECHO.",
  642. "ERASE",
  643. "EXIT",
  644. "FOR",
  645. "GOTO",
  646. "IF",
  647. "MD",
  648. "MKDIR",
  649. "PATH",
  650. "PAUSE",
  651. "PROMPT",
  652. "RD",
  653. "REM",
  654. "REN",
  655. "RENAME",
  656. "RMDIR",
  657. "SET",
  658. "SHIFT",
  659. "TIME",
  660. "TYPE",
  661. "VER",
  662. "VERIFY",
  663. "VOL"
  664. };
  665. pszInternal = (const char * const *) bsearch(szName,
  666. rgszInternal,
  667. sizeof(rgszInternal) / sizeof(rgszInternal[0]),
  668. sizeof(rgszInternal[0]),
  669. &cmpSzPsz);
  670. return(pszInternal != NULL);
  671. }
  672. #endif // WIN95
  673. // redirect -- handles redirection of input or output.
  674. //
  675. // arguments: dir - READ => input,
  676. // WRITE => output,
  677. // APPEND => append to end of the file.
  678. //
  679. // p - pointer to buffer that has the filename as
  680. // well as the rest of the command string.
  681. //
  682. // return value FALSE => error (freopen fails)
  683. // TRUE => normal return.
  684. //
  685. // the freopen() call sets up the redirection. the rest of the
  686. // command string is then copied forward.
  687. BOOL
  688. redirect(
  689. char *name,
  690. unsigned which
  691. )
  692. {
  693. char *p;
  694. char c = '\0';
  695. BOOL fStatus;
  696. char *mode;
  697. FILE *stream;
  698. FILE *newFile;
  699. while (WHITESPACE(*name)) {
  700. name++;
  701. }
  702. if (p = _tcspbrk(name, " \t<>\r")) {
  703. c = *p;
  704. *p = '\0';
  705. }
  706. if (which == READ) {
  707. mode = "r";
  708. stream = stdin;
  709. } else {
  710. stream = stdout;
  711. if (which == WRITE) {
  712. mode = "w";
  713. } else {
  714. mode = "a";
  715. }
  716. }
  717. newFile = freopen(name, mode, stream);
  718. fStatus = (newFile != NULL);
  719. if (fStatus && (which == APPEND)) {
  720. if (_lseek(_fileno(newFile), 0L, SEEK_END) == -1)
  721. return FALSE;
  722. }
  723. while (*name) {
  724. *name++ = ' ';
  725. }
  726. if (p) {
  727. *p = c;
  728. }
  729. return(fStatus);
  730. }
  731. BOOL
  732. FDoRedirection(
  733. char *p,
  734. int *oldIn,
  735. int *oldOut
  736. )
  737. {
  738. BOOL in = FALSE;
  739. BOOL out = FALSE;
  740. BOOL fReturn = FALSE;
  741. char *q;
  742. unsigned which;
  743. char *save = NULL;
  744. while (q = _tcspbrk(p, "<>|")) {
  745. switch (*q) {
  746. case '<':
  747. if (in) {
  748. fReturn = TRUE;
  749. break;
  750. }
  751. if (!save) {
  752. save = makeString(p);
  753. }
  754. *q++ = ' ';
  755. p = q;
  756. in = TRUE;
  757. *oldIn = _dup(_fileno(stdin));
  758. if ((*oldIn == -1) || !redirect(q, READ)) {
  759. fReturn = TRUE;
  760. break;
  761. }
  762. break;
  763. case '>':
  764. if (out) {
  765. fReturn = TRUE;
  766. break;
  767. }
  768. if (!save) {
  769. save = makeString(p);
  770. }
  771. *q++ = ' ';
  772. p = q;
  773. out = TRUE;
  774. if (*q == '>') {
  775. *q++ = ' ';
  776. which = APPEND;
  777. } else {
  778. which = WRITE;
  779. }
  780. *oldOut = _dup(_fileno(stdout));
  781. if ((*oldOut == -1) || !redirect(q, which)) {
  782. fReturn = TRUE;
  783. break;
  784. }
  785. break;
  786. case '|':
  787. fReturn = TRUE;
  788. break;
  789. default :
  790. makeError(0, BUILD_INTERNAL);
  791. }
  792. if (fReturn) {
  793. break;
  794. }
  795. }
  796. if (fReturn) {
  797. if (save != NULL) {
  798. _tcscpy(p, save);
  799. FREE(save);
  800. }
  801. if (in && (*oldIn != -1)) {
  802. if (_dup2(*oldIn, _fileno(stdin)) == -1) {
  803. makeError(0, BUILD_INTERNAL);
  804. }
  805. _close(*oldIn);
  806. *oldIn = -1;
  807. }
  808. if (out && (*oldOut != -1)) {
  809. if (_dup2(*oldOut, _fileno(stdout)) == -1) {
  810. makeError(0, BUILD_INTERNAL);
  811. }
  812. _close(*oldOut);
  813. *oldOut = -1;
  814. }
  815. }
  816. return(fReturn);
  817. }
  818. BOOL
  819. FSearchForExecutableExt(
  820. const char *szFilename,
  821. const char *szExt,
  822. BOOL fHasPath,
  823. char *szPath
  824. )
  825. {
  826. char szFullName[_MAX_PATH] = {0};
  827. strncat(szFullName, szFilename, sizeof(szFullName)-1);
  828. strncat(szFullName, szExt, sizeof(szFullName)-strlen(szFullName)-1);
  829. if (fHasPath) {
  830. if (_access(szFullName, 0) == 0) {
  831. szPath[0] = '\0';
  832. strncat(szPath, szFullName, _MAX_PATH);
  833. return(TRUE);
  834. }
  835. return(FALSE);
  836. }
  837. _searchenv(szFullName, "PATH", szPath);
  838. return(szPath[0] != '\0');
  839. }
  840. BOOL
  841. FSearchForExecutable(char *szFullName, char *szPath, BOOL *fBat)
  842. {
  843. char szDrive[_MAX_DRIVE];
  844. char szDir[_MAX_DIR];
  845. char szFileName[_MAX_FNAME];
  846. char szNoExt[_MAX_PATH];
  847. BOOL fHasPath;
  848. char *szEndQuote;
  849. BOOL fHasQuotes = FALSE;
  850. // Ignore any given extension. This is what COMMAND.COM does,
  851. char *szToPass = szFullName;
  852. if (*szFullName == QUOTE) {
  853. // get rid of any number of quotes at the beginning and at the end of the
  854. // string. This allows handling names enclosed in multiple quotes that are
  855. // accepted by the shell (DS 14300)
  856. szEndQuote = _tcsdec(szFullName, szFullName + _tcslen(szFullName));
  857. if (szEndQuote) {
  858. if (QUOTE == *szEndQuote) {
  859. while (QUOTE == *szToPass)
  860. szToPass ++;
  861. while (szEndQuote > szToPass) {
  862. char *szPrev = _tcsdec (szToPass, szEndQuote);
  863. if (szPrev) {
  864. if (QUOTE != *szPrev)
  865. break;
  866. }
  867. szEndQuote = szPrev;
  868. }
  869. if (szEndQuote) {
  870. *szEndQuote = '\0';
  871. fHasQuotes = TRUE;
  872. }
  873. }
  874. }
  875. }
  876. _splitpath(szToPass, szDrive, szDir, szFileName, NULL);
  877. _makepath(szNoExt, szDrive, szDir, szFileName, NULL);
  878. fHasPath = (szDrive[0] != '\0') || (szDir[0] != '\0');
  879. *fBat = FALSE;
  880. // Search for .COM file
  881. if (FSearchForExecutableExt(szNoExt, ".com", fHasPath, szPath)) {
  882. goto success;
  883. }
  884. // Search for .EXE file
  885. if (FSearchForExecutableExt(szNoExt, ".exe", fHasPath, szPath)) {
  886. goto success;
  887. }
  888. // Search for .BAT file
  889. if (FSearchForExecutableExt(szNoExt, ".bat", fHasPath, szPath)) {
  890. *fBat = TRUE;
  891. goto success;
  892. }
  893. return(FALSE);
  894. success:
  895. if (fHasQuotes) {
  896. size_t size = _tcslen(szPath);
  897. memmove(szPath+1, szPath, size);
  898. *szPath = '"';
  899. *(szPath + size + 1) = '"';
  900. *(szPath + size + 2) = '\0';
  901. *szEndQuote = '"';
  902. }
  903. return TRUE;
  904. }
  905. // execLine -- execute a command line
  906. //
  907. // Scope: Global (build.c, rpn.c)
  908. //
  909. // Purpose:
  910. // Parses the command line for redirection characters and redirects stdin and
  911. // stdout if "<", ">", or ">>" are seen. If any of the following occur,
  912. // restore the original stdin and stdout, pass the command to the shell, and
  913. // invoke the shell:
  914. // - the command line contains "|" (pipe)
  915. // - a syntax error occurs in parsing the command line
  916. // - an error occurs in redirection
  917. // Otherwise, attempt to invoke the command directly, then restore the
  918. // original stdin and stdout. If this invocation failed because of
  919. // file-not-found then pass the command to the shell and invoke the shell.
  920. //
  921. // Input: line -- The command line to be executed
  922. // echoCmd -- determines if the command line is to be echoed
  923. // doCmd -- determines if the command is to be actually executed
  924. // ignoreReturn -- determines if NMAKE is to ignore the return code on
  925. // execution
  926. // ppCmd -- if non-null then on error returns command executed
  927. //
  928. // Output: Returns ... return code from child process
  929. // ... -1 if error occurs
  930. //
  931. // Notes:
  932. // 1/ Quoted strings can have redir chars "<>" which will be skipped over.
  933. // 2/ Unmatched quotes cause error; redir chars are replaced by space char.
  934. // 3/ Dup stdin file handle then redirect it. If we have to use the shell,
  935. // restore the original command line.
  936. // 4/ Emulate certain commands such as "cd" to help prevent some makefiles
  937. // from breaking when ported from DOS to OS/2.
  938. //
  939. // Algorithm for spawning commands:
  940. // If we can't handle the syntax, let the shell do everything. Otherwise,
  941. // first check to see if the command (without extension) is a DOS built-in &
  942. // if it is, call the shell to execute it (this is how cmd.exe behaves)
  943. // If it's not a built-in, we check to see if it has a .cmd or a .bat
  944. // extension (depending on whether we're in DOS or OS/2). If it does, we
  945. // call system() to execute it.
  946. // If it has some other extension, we ignore the extension and go looking for
  947. // a .cmd or .bat file. If we find it, we execute it with system().
  948. // Otherwise, we try to spawn it (without extension). If the spawn fails,
  949. // we issue an unknown program error.
  950. int
  951. execLine(
  952. char *line,
  953. BOOL echoCmd,
  954. BOOL doCmd,
  955. BOOL ignoreReturn,
  956. char **ppCmd
  957. )
  958. {
  959. char **argv;
  960. BOOL fUseShell;
  961. BOOL fLongCommand;
  962. int status;
  963. unsigned argc;
  964. if (!shellName) {
  965. shellName = getComSpec();
  966. }
  967. switch (*line) {
  968. case '@':
  969. // Turn off echo if it was on. This handles the case where the "@"
  970. // was in a macro.
  971. //
  972. line++;
  973. if (doCmd)
  974. echoCmd = 0;
  975. break;
  976. case '-':
  977. ignoreReturn = TRUE;
  978. ++line;
  979. if (_istdigit(*line)) {
  980. char * pNumber = line;
  981. errorLevel = _tcstoul(line, &line, 10);
  982. if (errno == ERANGE) {
  983. *line = '\0';
  984. makeError(0, CONST_TOO_BIG, pNumber); // Todo: replace 0 with line number
  985. }
  986. while(_istspace(*line))
  987. line++;
  988. } else
  989. errorLevel = UINT_MAX;
  990. break;
  991. }
  992. // handle null command ...
  993. if (!line[0])
  994. return(0);
  995. #if 0
  996. // 10/10/96: disabled to allow execution of long
  997. // commands that are produced by batch-mode rules
  998. // copy command line into buffer
  999. if (_tcslen(line) < MAXCMDLINELENGTH)
  1000. _tcscpy(szCmdLineBuf, line);
  1001. else
  1002. makeError(0, COMMAND_TOO_LONG, line);
  1003. #endif
  1004. fLongCommand = _tcslen(line) >= MAXCMDLINELENGTH;
  1005. if (!fLongCommand)
  1006. _tcscpy(szCmdLineBuf, line);
  1007. else
  1008. *szCmdLineBuf = '\0';
  1009. // Allocate a copy of the command line on the heap because in a
  1010. // recursive call to doMake(), argv pointers will be allocated from
  1011. // the static buffer which will then be trashed. For buildArg...().
  1012. pCmdLineCopy = makeString(line);
  1013. // If -n then echo command if not '$(MAKE)'
  1014. if (echoCmd) {
  1015. printf("\t%s\n", pCmdLineCopy);
  1016. fflush(stdout);
  1017. }
  1018. // Build arg vector. This is a waste on Windows NT since we're probably
  1019. // going to use the shell, except we have to check for cd, $(MAKE),
  1020. // etc. so we take advantage of the parsing code.
  1021. buildArgumentVector(&argc, NULL, pCmdLineCopy);
  1022. if (argc == 0) {
  1023. return(0); // for case when macro command is null
  1024. }
  1025. // allocate argv. Leave space for extra arguments
  1026. // (like "cmd", "/k", quotes) that may be added later
  1027. argv = (char **) rallocate((argc + 5) * sizeof (char *));
  1028. buildArgumentVector(&argc, argv, pCmdLineCopy);
  1029. // 11-May-1993 HV The _mbsicmp() does not like NULL pointer
  1030. // so I have to check before calling it.
  1031. if (argv[0] && makeStr && !_tcsicmp(argv[0], makeStr)) {
  1032. if(!szNmakeProgName) {
  1033. szNmakeProgName = _pgmptr;
  1034. if( _tcspbrk( szNmakeProgName," " )) { // If the program name has an embedded space in it
  1035. // Let's put quotes around it
  1036. szNmakeProgName = (char *)rallocate(_tcslen(szNmakeProgName)+3);
  1037. *szNmakeProgName = QUOTE; // First quote
  1038. *(szNmakeProgName+1) = '\0';
  1039. _tcscat( szNmakeProgName, _pgmptr ); // copy the full program name (self)
  1040. _tcscat( szNmakeProgName, "\""); // Final quote and \0
  1041. }
  1042. }
  1043. argv[0]=szNmakeProgName;
  1044. }
  1045. if (!doCmd) { // don't execute command if doCmd false
  1046. // For -n, emulate if possible.
  1047. if (FEmulateCommand(argc, argv, &status)) {
  1048. if (status && ppCmd) {
  1049. *ppCmd = makeString(*argv);
  1050. }
  1051. return(status); // return status
  1052. }
  1053. return(0);
  1054. }
  1055. // Try emulating the command if appropriate. If not, and we should not
  1056. // use the shell, try spawning command directly.
  1057. // Check status when emulating
  1058. if (FEmulateCommand(argc, argv, &status)) {
  1059. // Command has been emulated. Don't execute it again.
  1060. fUseShell = FALSE;
  1061. } else if (!fRunningUnderChicago && !fLongCommand) {
  1062. // Use the shell for Windows NT unless the command is too long
  1063. fUseShell = TRUE;
  1064. #ifdef WIN95
  1065. } else if (fRunningUnderChicago && FInternalCommand(argv[0])) {
  1066. // Under Windows 95 or MS-DOS, use the shell for internal commands
  1067. fUseShell = TRUE;
  1068. #endif // WIN95
  1069. } else {
  1070. int oldIn = -1; // Old stdin file handle
  1071. int oldOut = -1; // Old stdout file handle
  1072. // Under Windows 95 or MS-DOS, COMMAND.COM doesn't return child return
  1073. // codes. Try spawning the child application directly.
  1074. // This code is also now used if the line is too long to be handled by
  1075. // the NT command interpreter.
  1076. fUseShell = FDoRedirection(line, &oldIn, &oldOut);
  1077. if (!fUseShell) {
  1078. char szPath[_MAX_PATH];
  1079. char szQuotedPath[_MAX_PATH];
  1080. BOOL fBat;
  1081. if (oldIn != -1 || oldOut != -1) { // If there was a redirection
  1082. // Need to re-build the argument vector without the
  1083. // redirection characters
  1084. FREE(pCmdLineCopy);
  1085. pCmdLineCopy = makeString(line);
  1086. buildArgumentVector(&argc, argv, pCmdLineCopy);
  1087. }
  1088. if (!FSearchForExecutable(argv[0], szPath, &fBat)) {
  1089. /* If not found, set up an error since COMMAND will
  1090. * return 0. This risks future incompatibility if new
  1091. * COMMAND.COM internal commands are added.
  1092. */
  1093. if (fRunningUnderChicago) {
  1094. errno = ENOENT;
  1095. status = -1;
  1096. } else {
  1097. fUseShell = TRUE;
  1098. }
  1099. } else if (fBat) {
  1100. // If .bat extension, use COMMAND.COM.
  1101. // UNDONE: CreateProcess is supposed to handle this. Try it.
  1102. fUseShell = TRUE;
  1103. } else {
  1104. // Spawn command directly.
  1105. // DevStudio#8911, cannot use quotes in szPath
  1106. if (*szPath == QUOTE && *(szPath + _tcslen(szPath) - 1) == QUOTE) {
  1107. // unquote the path.
  1108. size_t cb = _tcslen(szPath);
  1109. memmove(szPath, szPath + 1, cb);
  1110. *(szPath + cb - 2) = '\0';
  1111. }
  1112. #if 0
  1113. {
  1114. int i;
  1115. printf("Spawning \"%s\" directly\n", szPath);
  1116. for (i = 0; i < argc; i++) {
  1117. printf ( "Arg[%d] = \"%s\"\n", i, argv[i] );
  1118. }
  1119. }
  1120. #endif
  1121. // DS 14300: Use full path for argv[0]
  1122. // otherwise a shell command may be invoked
  1123. // instead of an intended executable with the
  1124. // same name. Enclosing quotes are needed if
  1125. // string has embedded spaces
  1126. argv[0] = szPath;
  1127. if (_tcschr (argv[0], ' ')) {
  1128. *szQuotedPath = QUOTE;
  1129. _tcscpy (szQuotedPath+1, szPath);
  1130. _tcscat (szQuotedPath, "\"");
  1131. argv[0] = szQuotedPath;
  1132. }
  1133. status = (int)_spawnvp(P_WAIT, szPath, argv); // REVIEW:WIN64 cast
  1134. }
  1135. }
  1136. if (oldIn != -1) {
  1137. if (_dup2(oldIn, _fileno(stdin)) == -1) {
  1138. makeError(0, BUILD_INTERNAL);
  1139. }
  1140. _close(oldIn);
  1141. }
  1142. if (oldOut != -1) {
  1143. if (_dup2(oldOut, _fileno(stdout)) == -1) {
  1144. makeError(0, BUILD_INTERNAL);
  1145. }
  1146. _close(oldOut);
  1147. }
  1148. }
  1149. if (fUseShell) {
  1150. int i;
  1151. BOOL fExtraQuote = FALSE;
  1152. // copy command line into buffer
  1153. if (_tcslen(line) < MAXCMDLINELENGTH)
  1154. _tcscpy(szCmdLineBuf, line);
  1155. else
  1156. makeError(0, COMMAND_TOO_LONG, line);
  1157. // Workaround for cmd bug (DevStudio #11253):
  1158. // IF argv[0] (before we rearrange with cmd.exe /c) is quoted AND
  1159. // any of the other argv[1...n] args have quotes AND
  1160. // running on NT
  1161. // THEN we add an extra quote before argv[0] and one after argv[n].
  1162. if ((*argv[0] == QUOTE) &&
  1163. (*(argv[0] + _tcslen(argv[0]) - 1) == QUOTE) &&
  1164. !fRunningUnderChicago) {
  1165. for (i = argc - 1; i >= 1; i--) {
  1166. if( _tcspbrk( argv[i],"\"" )) {
  1167. fExtraQuote = TRUE;
  1168. break;
  1169. }
  1170. }
  1171. }
  1172. if (fExtraQuote) {
  1173. argv[argc++] = "\"";
  1174. argv[argc] = NULL;
  1175. }
  1176. for (i = argc; i >= 0; i--) {
  1177. argv[i+2] = argv[i];
  1178. }
  1179. argv[0] = shellName;
  1180. argv[1] = fExtraQuote ? "/c \"" : "/c";
  1181. #if 0
  1182. printf("Shelling \"%s\"\n", szCmdLineBuf);
  1183. for (i = 0; i < argc + 2; i++) {
  1184. printf ( "Arg[%d] = \"%s\"\n", i, argv[i] );
  1185. }
  1186. #endif
  1187. status = (int)_spawnvp(P_WAIT, argv[0], (const char * const *) argv); // REVIEW:WIN64 cast
  1188. }
  1189. // Check for errors spawning command (distinct from errors *returned*
  1190. // from a successfully spawned command).
  1191. if (status == -1) {
  1192. if (ignoreReturn) {
  1193. status = 0;
  1194. } else {
  1195. switch (errno) {
  1196. case 0:
  1197. // We (ie: nmake) didn't fail, but the spawned program did.
  1198. break;
  1199. case ENOENT:
  1200. makeError(0, CANT_FIND_PROGRAM, argv[0]);
  1201. break;
  1202. case ENOMEM:
  1203. makeError(0, EXEC_NO_MEM, fUseShell ? argv[2] : argv[0]);
  1204. break;
  1205. default:
  1206. // Done to flag possibly erroneous decision made here [SB]
  1207. makeError(0, SPAWN_FAILED_ERROR, _strerror(NULL));
  1208. }
  1209. }
  1210. }
  1211. if (status && ppCmd) {
  1212. *ppCmd = makeString(fUseShell ? argv[2] : argv[0]);
  1213. }
  1214. FREE(argv);
  1215. FREE(pCmdLineCopy);
  1216. return(status);
  1217. }
  1218. // getComSpec()
  1219. //
  1220. // actions: Attempts to find system shell.
  1221. //
  1222. // First look for COMSPEC. If not found, look for COMMAND.COM or CMD.EXE
  1223. // in the current directory then the path. If not found, fatal error.
  1224. // It would make sense to give an error if COMSPEC is not defined but
  1225. // test suites are easier if no user-defined environment variables are
  1226. // required.
  1227. char *
  1228. getComSpec()
  1229. {
  1230. char *szShell;
  1231. char szPath[_MAX_PATH];
  1232. if ((szShell = getenv("COMSPEC")) != NULL) {
  1233. return(szShell);
  1234. }
  1235. if (fRunningUnderChicago) {
  1236. szShell = "COMMAND.COM";
  1237. } else {
  1238. szShell = "CMD.EXE";
  1239. }
  1240. _searchenv(szShell, "PATH", szPath);
  1241. if (szPath[0] == '\0') {
  1242. makeError(0, NO_COMMAND_COM);
  1243. }
  1244. return(makeString(szPath));
  1245. }
  1246. BOOL
  1247. iterateCommand(
  1248. char *u,
  1249. STRINGLIST *t,
  1250. UCHAR buildFlags,
  1251. UCHAR cFlags,
  1252. char *pFirstDep,
  1253. unsigned *status
  1254. )
  1255. {
  1256. BOOL parens;
  1257. char c = '\0';
  1258. char *v;
  1259. STRINGLIST *p = NULL,
  1260. *q;
  1261. char *pLine;
  1262. char *pCmd;
  1263. for (v = u; *v ; ++v) {
  1264. parens = FALSE;
  1265. if (*v == '$') {
  1266. if (*(v+1) == '(') {
  1267. ++v;
  1268. parens = TRUE;
  1269. }
  1270. if (*(v+1) == '?') {
  1271. if (parens
  1272. && !(_tcschr("DFBR", *(v+2)) && *(v+3) == ')')
  1273. && *(v+2) != ')')
  1274. continue;
  1275. p = dollarQuestion;
  1276. c = '?';
  1277. break;
  1278. }
  1279. if (*++v == '*' && *(v+1) == '*') {
  1280. if (parens
  1281. && !(_tcschr("DFBR", *(v+2)) && *(v+3) == ')')
  1282. && *(v+2) != ')')
  1283. continue;
  1284. p = dollarStarStar;
  1285. c = '*';
  1286. break;
  1287. }
  1288. }
  1289. }
  1290. if (!*v) {
  1291. return(FALSE);
  1292. }
  1293. v = u;
  1294. q = p;
  1295. while (p) {
  1296. macros = t;
  1297. if (c == '*') {
  1298. p = dollarStarStar->next;
  1299. dollarStarStar->next = NULL;
  1300. } else {
  1301. p = dollarQuestion->next;
  1302. dollarQuestion->next = NULL;
  1303. }
  1304. u = expandMacros(v, &macros);
  1305. expandExtmake(CmdLine, u, pFirstDep);
  1306. pLine = CmdLine;
  1307. *status = execLine(pLine,
  1308. (BOOL)(ON(buildFlags, F2_NO_EXECUTE)
  1309. || (OFF(buildFlags,F2_NO_ECHO)
  1310. && OFF(cFlags,C_SILENT))),
  1311. (BOOL)((OFF(buildFlags, F2_NO_EXECUTE)
  1312. )
  1313. || ON(cFlags, C_EXECUTE)),
  1314. (BOOL)ON(cFlags, C_IGNORE), &pCmd);
  1315. if (OFF(buildFlags, F2_IGNORE_EXIT_CODES)) {
  1316. if (*status && *status > errorLevel)
  1317. if (!fOptionK)
  1318. makeError(0, BAD_RETURN_CODE, pCmd, *status);
  1319. }
  1320. if (c == '*')
  1321. dollarStarStar = dollarStarStar->next = p;
  1322. else
  1323. dollarQuestion = dollarQuestion->next = p;
  1324. FREE(u);
  1325. if (OFF(buildFlags, F2_IGNORE_EXIT_CODES) &&
  1326. fOptionK &&
  1327. *status &&
  1328. *status > errorLevel)
  1329. {
  1330. break;
  1331. }
  1332. }
  1333. if (c == '*')
  1334. dollarStarStar = q;
  1335. else
  1336. dollarQuestion = q;
  1337. return(TRUE);
  1338. }
  1339. void
  1340. removeQuotes(
  1341. int argc,
  1342. char **argv
  1343. )
  1344. {
  1345. char *t,
  1346. *L_string;
  1347. for (; argc--; argv++) {
  1348. L_string = *argv;
  1349. for (t = L_string; *t;) {
  1350. if (*t == SLASH || *t == ESCH) {
  1351. if (t[1] == QUOTE)
  1352. *(L_string)++ = *(t++);
  1353. *(L_string++) = *(t++);
  1354. continue;
  1355. }
  1356. if (*t == QUOTE)
  1357. ++t;
  1358. else {
  1359. if (_istlead(* (unsigned char *)t))
  1360. *(L_string++) = *(t++);
  1361. *(L_string++) = *(t++);
  1362. }
  1363. }
  1364. *L_string = '\0';
  1365. }
  1366. }
  1367. void
  1368. touch(
  1369. char *s,
  1370. BOOL minusN
  1371. )
  1372. {
  1373. int fd;
  1374. char c;
  1375. FILE * L_file;
  1376. makeMessage(TOUCHING_TARGET, s);
  1377. if (!minusN &&
  1378. ((L_file = FILEOPEN(s, "r+b")) != NULL)) {
  1379. fd = _fileno(L_file);
  1380. if (_read(fd, &c, 1) > 0) {
  1381. if (_lseek(fd, 0L, SEEK_SET)!=-1) {
  1382. _write(fd, &c, 1);
  1383. }
  1384. }
  1385. _close(fd);
  1386. }
  1387. }