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

1458 lines
44 KiB

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