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 malloc 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 <ekohl@abo.rhein-zeitung.de>)
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
65 /* The stack of current batch contexts.
66 * NULL when no batch is active
68 LPBATCH_CONTEXT bc
= NULL
;
70 BOOL bEcho
= TRUE
; /* The echo flag */
74 /* Buffer for reading Batch file lines */
75 TCHAR textline
[BATCH_BUFFSIZE
];
79 * Returns a pointer to the n'th parameter of the current batch file.
80 * If no such parameter exists returns pointer to empty string.
81 * If no batch file is current, returns NULL
85 LPTSTR
FindArg (INT n
)
90 DebugPrintf (_T("FindArg: (%d)\n"), n
);
99 /* Step up the strings till we reach the end */
100 /* or the one we want */
102 pp
+= _tcslen (pp
) + 1;
109 * Batch_params builds a parameter list in newlay allocated memory.
110 * The parameters consist of null terminated strings with a final
111 * NULL character signalling the end of the parameters.
115 LPTSTR
BatchParams (LPTSTR s1
, LPTSTR s2
)
117 LPTSTR dp
= (LPTSTR
)malloc ((_tcslen(s1
) + _tcslen(s2
) + 3) * sizeof (TCHAR
));
119 /* JPP 20-Jul-1998 added error checking */
122 error_out_of_memory();
128 s1
= _stpcpy (dp
, s1
);
136 if (_istspace (*s2
) || _tcschr (_T(",;"), *s2
))
140 while (*s2
&& _tcschr (_T(" ,;"), *s2
))
145 if ((*s2
== _T('"')) || (*s2
== _T('\'')))
151 while (*s2
&& (*s2
!= st
));
165 * If a batch file is current, exits it, freeing the context block and
166 * chaining back to the previous one.
168 * If no new batch context is found, sets ECHO back ON.
170 * If the parameter is non-null or not empty, it is printed as an exit
174 VOID
ExitBatch (LPTSTR msg
)
177 DebugPrintf (_T("ExitBatch: (\'%s\')\n"), msg
);
182 LPBATCH_CONTEXT t
= bc
;
186 CloseHandle (bc
->hBatchFile
);
187 bc
->hBatchFile
= INVALID_HANDLE_VALUE
;
199 /* Preserve echo state across batch calls */
207 ConOutPrintf (_T("%s\n"), msg
);
212 * Start batch file execution
214 * The firstword parameter is the full filename of the batch file.
218 BOOL
Batch (LPTSTR fullname
, LPTSTR firstword
, LPTSTR param
)
222 hFile
= CreateFile (fullname
, GENERIC_READ
, FILE_SHARE_WRITE
| FILE_SHARE_READ
| FILE_SHARE_DELETE
, NULL
,
223 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
|
224 FILE_FLAG_SEQUENTIAL_SCAN
, NULL
);
227 DebugPrintf (_T("Batch: (\'%s\', \'%s\', \'%s\') hFile = %x\n"),
228 fullname
, firstword
, param
, hFile
);
231 if (hFile
== INVALID_HANDLE_VALUE
)
233 ConErrResPuts(STRING_BATCH_ERROR
);
237 /* Kill any and all FOR contexts */
238 while (bc
&& bc
->forvar
)
243 /* No curent batch file, create a new context */
244 LPBATCH_CONTEXT n
= (LPBATCH_CONTEXT
)malloc (sizeof(BATCH_CONTEXT
));
248 error_out_of_memory ();
254 bc
->In
[0] = _T('\0');
255 bc
->Out
[0] = _T('\0');
256 bc
->Err
[0] = _T('\0');
258 else if (bc
->hBatchFile
!= INVALID_HANDLE_VALUE
)
260 /* Then we are transferring to another batch */
261 CloseHandle (bc
->hBatchFile
);
262 bc
->hBatchFile
= INVALID_HANDLE_VALUE
;
266 bc
->hBatchFile
= hFile
;
267 SetFilePointer (bc
->hBatchFile
, 0, NULL
, FILE_BEGIN
);
268 bc
->bEcho
= bEcho
; /* Preserve echo across batch calls */
272 bc
->forvar
= _T('\0');
274 bc
->params
= BatchParams (firstword
, param
);
277 DebugPrintf (_T("Batch: returns TRUE\n"));
283 VOID
AddBatchRedirection(TCHAR
* ifn
, TCHAR
* ofn
, TCHAR
* efn
)
290 _tcscpy(bc
->Out
,ofn
);
292 _tcscpy(bc
->Err
,efn
);
297 * Read and return the next executable line form the current batch file
299 * If no batch file is current or no further executable lines are found
302 * Here we also look out for FOR bcontext structures which trigger the
303 * FOR expansion code.
305 * Set eflag to 0 if line is not to be echoed else 1
308 LPTSTR
ReadBatchLine (LPBOOL bLocalEcho
)
318 DebugPrintf (_T("ReadBatchLine ()\n"));
324 if (CheckCtrlBreak (BREAK_BATCHFILE
))
335 /* If its a FOR context... */
338 LPTSTR sp
= bc
->forproto
; /* pointer to prototype command */
339 LPTSTR dp
= textline
; /* Place to expand protoype */
340 LPTSTR fv
= FindArg (0); /* Next list element */
342 /* End of list so... */
343 if ((fv
== NULL
) || (*fv
== _T('\0')))
345 /* just exit this context */
350 if (_tcscspn (fv
, _T("?*")) == _tcslen (fv
))
352 /* element is wild file */
353 bc
->shiftlevel
++; /* No use it and shift list */
357 /* Wild file spec, find first (or next) file name */
360 /* First already done so do next */
362 fv
= FindNextFile (bc
->hFind
, bc
->ffind
) ? bc
->ffind
->cFileName
: NULL
;
366 /* For first find, allocate a find first block */
367 if ((bc
->ffind
= (LPWIN32_FIND_DATA
)malloc (sizeof (WIN32_FIND_DATA
))) == NULL
)
369 error_out_of_memory();
373 bc
->hFind
= FindFirstFile (fv
, bc
->ffind
);
375 fv
= !(bc
->hFind
==INVALID_HANDLE_VALUE
) ? bc
->ffind
->cFileName
: NULL
;
380 /* Null indicates no more files.. */
381 free (bc
->ffind
); /* free the buffer */
383 bc
->shiftlevel
++; /* On to next list element */
388 /* At this point, fv points to parameter string */
391 if ((*sp
== _T('%')) && (*(sp
+ 1) == bc
->forvar
))
394 dp
= _stpcpy (dp
, fv
);
411 if (!FileGetString (bc
->hBatchFile
, textline
, sizeof (textline
) / sizeof (textline
[0])))
414 DebugPrintf (_T("ReadBatchLine(): Reached EOF!\n"));
416 /* End of file.... */
425 DebugPrintf (_T("ReadBatchLine(): textline: \'%s\'\n"), textline
);
428 /* Strip leading spaces and trailing space/control chars */
429 for (first
= textline
; _istspace (*first
); first
++)
432 for (ip
= first
+ _tcslen (first
) - 1; _istspace (*ip
) || _istcntrl (*ip
); ip
--)
437 /* ignore labels and empty lines */
438 if (*first
== _T(':') || *first
== 0)
441 if (*first
== _T('@'))
443 /* don't echo this line */
446 while (_istspace (*first
));