2 * BATCH.C - batch file processor for CMD.EXE.
7 * ??/??/?? (Evan Jeffrey)
10 * 15 Jul 1995 (Tim Norman)
13 * 08 Aug 1995 (Matt Rains)
14 * i have cleaned up the source code. changes now bring this
15 * source into guidelines for recommended programming practice.
17 * i have added some constants to help making changes easier.
19 * 29 Jan 1996 (Steffan Kaiser)
20 * made a few cosmetic changes
22 * 05 Feb 1996 (Tim Norman)
23 * changed to comply with new first/rest calling scheme
25 * 14 Jun 1997 (Steffen Kaiser)
26 * bug fixes. added error level expansion %?. ctrl-break handling
28 * 16 Jul 1998 (Hans B Pufal)
29 * Totally reorganised in conjunction with COMMAND.C (cf) to
30 * implement proper BATCH file nesting and other improvements.
32 * 16 Jul 1998 (John P Price <linux-guru@gcfl.net>)
33 * Seperated commands into individual files.
35 * 19 Jul 1998 (Hans B Pufal) [HBP_001]
36 * Preserve state of echo flag across batch calls.
38 * 19 Jul 1998 (Hans B Pufal) [HBP_002]
39 * Implementation of FOR command
41 * 20-Jul-1998 (John P Price <linux-guru@gcfl.net>)
42 * added error checking after cmd_alloc calls
44 * 27-Jul-1998 (John P Price <linux-guru@gcfl.net>)
45 * added config.h include
47 * 02-Aug-1998 (Hans B Pufal) [HBP_003]
48 * Fixed bug in ECHO flag restoration at exit from batch file
50 * 26-Jan-1999 Eric Kohl
51 * Replaced CRT io functions by Win32 io functions.
54 * 23-Feb-2001 (Carl Nettelblad <cnettel@hem.passagen.es>)
55 * Fixes made to get "for" working.
57 * 02-Apr-2005 (Magnus Olsen) <magnus@greatlord.com>)
58 * Remove all hardcode string to En.rc
64 /* The stack of current batch contexts.
65 * NULL when no batch is active
67 LPBATCH_CONTEXT bc
= NULL
;
69 BOOL bEcho
= TRUE
; /* The echo flag */
73 /* Buffer for reading Batch file lines */
74 TCHAR textline
[BATCH_BUFFSIZE
];
78 * Returns a pointer to the n'th parameter of the current batch file.
79 * If no such parameter exists returns pointer to empty string.
80 * If no batch file is current, returns NULL
84 LPTSTR
FindArg(TCHAR Char
, BOOL
*IsParam0
)
87 INT n
= Char
- _T('0');
89 TRACE ("FindArg: (%d)\n", n
);
94 n
= bc
->shiftlevel
[n
];
98 /* Step up the strings till we reach the end */
99 /* or the one we want */
101 pp
+= _tcslen (pp
) + 1;
108 * Batch_params builds a parameter list in newlay allocated memory.
109 * The parameters consist of null terminated strings with a final
110 * NULL character signalling the end of the parameters.
114 LPTSTR
BatchParams (LPTSTR s1
, LPTSTR s2
)
116 LPTSTR dp
= (LPTSTR
)cmd_alloc ((_tcslen(s1
) + _tcslen(s2
) + 3) * sizeof (TCHAR
));
118 /* JPP 20-Jul-1998 added error checking */
121 error_out_of_memory();
127 s1
= _stpcpy (dp
, s1
);
135 BOOL inquotes
= FALSE
;
137 /* Find next parameter */
138 while (_istspace(*s2
) || (*s2
&& _tcschr(_T(",;="), *s2
)))
146 if (!inquotes
&& (_istspace(*s2
) || _tcschr(_T(",;="), *s2
)))
148 inquotes
^= (*s2
== _T('"'));
161 * If a batch file is current, exits it, freeing the context block and
162 * chaining back to the previous one.
164 * If no new batch context is found, sets ECHO back ON.
166 * If the parameter is non-null or not empty, it is printed as an exit
172 TRACE ("ExitBatch\n");
176 CloseHandle (bc
->hBatchFile
);
177 bc
->hBatchFile
= INVALID_HANDLE_VALUE
;
181 cmd_free(bc
->raw_params
);
184 cmd_free(bc
->params
);
186 UndoRedirection(bc
->RedirList
, NULL
);
187 FreeRedirection(bc
->RedirList
);
189 /* Preserve echo state across batch calls */
193 cmd_endlocal(_T(""));
200 * Start batch file execution
202 * The firstword parameter is the full filename of the batch file.
206 INT
Batch (LPTSTR fullname
, LPTSTR firstword
, LPTSTR param
, PARSED_COMMAND
*Cmd
)
209 LPFOR_CONTEXT saved_fc
;
215 hFile
= CreateFile (fullname
, GENERIC_READ
, FILE_SHARE_WRITE
| FILE_SHARE_READ
| FILE_SHARE_DELETE
, NULL
,
216 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
|
217 FILE_FLAG_SEQUENTIAL_SCAN
, NULL
);
219 TRACE ("Batch: (\'%s\', \'%s\', \'%s\') hFile = %x\n",
220 debugstr_aw(fullname
), debugstr_aw(firstword
), debugstr_aw(param
), hFile
);
222 if (hFile
== INVALID_HANDLE_VALUE
)
224 ConErrResPuts(STRING_BATCH_ERROR
);
228 if (bc
!= NULL
&& Cmd
== bc
->current
)
230 /* Then we are transferring to another batch */
231 CloseHandle (bc
->hBatchFile
);
232 bc
->hBatchFile
= INVALID_HANDLE_VALUE
;
234 cmd_free (bc
->params
);
236 cmd_free (bc
->raw_params
);
237 AddBatchRedirection(&Cmd
->Redirections
);
241 struct _SETLOCAL
*setlocal
= NULL
;
245 /* This is a CALL. CALL will set errorlevel to our return value, so
246 * in order to keep the value of errorlevel unchanged in the case
247 * of calling an empty batch file, we must return that same value. */
252 /* If a batch file runs another batch file as part of a compound command
253 * (e.g. "x.bat & somethingelse") then the first file gets terminated. */
255 /* Get its SETLOCAL stack so it can be migrated to the new context */
256 setlocal
= bc
->setlocal
;
261 /* Create a new context. This function will not
262 * return until this context has been exited */
265 bc
->RedirList
= NULL
;
266 bc
->setlocal
= setlocal
;
269 GetFullPathName(fullname
, sizeof(bc
->BatchFilePath
) / sizeof(TCHAR
), bc
->BatchFilePath
, NULL
);
271 bc
->hBatchFile
= hFile
;
272 SetFilePointer (bc
->hBatchFile
, 0, NULL
, FILE_BEGIN
);
273 bc
->bEcho
= bEcho
; /* Preserve echo across batch calls */
274 for (i
= 0; i
< 10; i
++)
275 bc
->shiftlevel
[i
] = i
;
277 bc
->params
= BatchParams (firstword
, param
);
279 // Allocate enough memory to hold the params and copy them over without modifications
281 bc
->raw_params
= cmd_dup(param
);
282 if (bc
->raw_params
== NULL
)
284 error_out_of_memory();
288 /* Check if this is a "CALL :label" */
289 if (*firstword
== _T(':'))
292 /* If we are calling from inside a FOR, hide the FOR variables */
296 /* If we have created a new context, don't return
297 * until this batch file has completed. */
298 while (bc
== &new && !bExit
)
300 Cmd
= ParseCommand(NULL
);
305 /* Echo batch file line */
306 if (bEcho
&& !bDisableBatchEcho
&& Cmd
->Type
!= C_QUIET
)
309 ConOutChar(_T('\n'));
312 ConOutChar(_T('\n'));
316 ret
= ExecuteCommand(Cmd
);
320 TRACE ("Batch: returns TRUE\n");
326 VOID
AddBatchRedirection(REDIRECTION
**RedirList
)
328 REDIRECTION
**ListEnd
;
330 /* Prepend the list to the batch context's list */
333 ListEnd
= &(*ListEnd
)->Next
;
334 *ListEnd
= bc
->RedirList
;
335 bc
->RedirList
= *RedirList
;
337 /* Null out the pointer so that the list will not be cleared prematurely.
338 * These redirections should persist until the batch file exits. */
343 * Read and return the next executable line form the current batch file
345 * If no batch file is current or no further executable lines are found
348 * Set eflag to 0 if line is not to be echoed else 1
351 LPTSTR
ReadBatchLine ()
353 TRACE ("ReadBatchLine ()\n");
356 if (CheckCtrlBreak (BREAK_BATCHFILE
))
363 if (!FileGetString (bc
->hBatchFile
, textline
, sizeof (textline
) / sizeof (textline
[0]) - 1))
365 TRACE ("ReadBatchLine(): Reached EOF!\n");
366 /* End of file.... */
371 TRACE ("ReadBatchLine(): textline: \'%s\'\n", debugstr_aw(textline
));
373 if (textline
[_tcslen(textline
) - 1] != _T('\n'))
374 _tcscat(textline
, _T("\n"));