3 * BATCH.C - batch file processor for CMD.EXE.
8 * ??/??/?? (Evan Jeffrey)
11 * 15 Jul 1995 (Tim Norman)
14 * 08 Aug 1995 (Matt Rains)
15 * i have cleaned up the source code. changes now bring this
16 * source into guidelines for recommended programming practice.
18 * i have added some constants to help making changes easier.
20 * 29 Jan 1996 (Steffan Kaiser)
21 * made a few cosmetic changes
23 * 05 Feb 1996 (Tim Norman)
24 * changed to comply with new first/rest calling scheme
26 * 14 Jun 1997 (Steffen Kaiser)
27 * bug fixes. added error level expansion %?. ctrl-break handling
29 * 16 Jul 1998 (Hans B Pufal)
30 * Totally reorganised in conjunction with COMMAND.C (cf) to
31 * implement proper BATCH file nesting and other improvements.
33 * 16 Jul 1998 (John P Price <linux-guru@gcfl.net>)
34 * Seperated commands into individual files.
36 * 19 Jul 1998 (Hans B Pufal) [HBP_001]
37 * Preserve state of echo flag across batch calls.
39 * 19 Jul 1998 (Hans B Pufal) [HBP_002]
40 * Implementation of FOR command
42 * 20-Jul-1998 (John P Price <linux-guru@gcfl.net>)
43 * added error checking after malloc calls
45 * 27-Jul-1998 (John P Price <linux-guru@gcfl.net>)
46 * added config.h include
48 * 02-Aug-1998 (Hans B Pufal) [HBP_003]
49 * Fixed bug in ECHO flag restoration at exit from batch file
51 * 26-Jan-1999 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
52 * Replaced CRT io functions by Win32 io functions.
55 * 23-Feb-2001 (Carl Nettelblad <cnettel@hem.passagen.es>)
56 * Fixes made to get "for" working.
71 /* The stack of current batch contexts.
72 * NULL when no batch is active
74 LPBATCH_CONTEXT bc = NULL;
76 BOOL bEcho = TRUE; /* The echo flag */
80 /* Buffer for reading Batch file lines */
81 TCHAR textline[BATCH_BUFFSIZE];
85 * Returns a pointer to the n'th parameter of the current batch file.
86 * If no such parameter exists returns pointer to empty string.
87 * If no batch file is current, returns NULL
91 LPTSTR FindArg (INT n)
96 DebugPrintf (_T("FindArg: (%d)\n"), n);
105 /* Step up the strings till we reach the end */
106 /* or the one we want */
108 pp += _tcslen (pp) + 1;
115 * Batch_params builds a parameter list in newlay allocated memory.
116 * The parameters consist of null terminated strings with a final
117 * NULL character signalling the end of the parameters.
121 LPTSTR BatchParams (LPTSTR s1, LPTSTR s2)
123 LPTSTR dp = (LPTSTR)malloc ((_tcslen(s1) + _tcslen(s2) + 3) * sizeof (TCHAR));
125 /* JPP 20-Jul-1998 added error checking */
128 error_out_of_memory();
134 s1 = stpcpy (dp, s1);
142 if (_istspace (*s2) || _tcschr (_T(",;"), *s2))
146 while (*s2 && _tcschr (_T(" ,;"), *s2))
151 if ((*s2 == _T('"')) || (*s2 == _T('\'')))
157 while (*s2 && (*s2 != st));
171 * If a batch file is current, exits it, freeing the context block and
172 * chaining back to the previous one.
174 * If no new batch context is found, sets ECHO back ON.
176 * If the parameter is non-null or not empty, it is printed as an exit
180 VOID ExitBatch (LPTSTR msg)
183 DebugPrintf (_T("ExitBatch: (\'%s\')\n"), msg);
188 LPBATCH_CONTEXT t = bc;
192 CloseHandle (bc->hBatchFile);
193 bc->hBatchFile = INVALID_HANDLE_VALUE;
205 /* Preserve echo state across batch calls */
213 ConOutPrintf (_T("%s\n"), msg);
218 * Start batch file execution
220 * The firstword parameter is the full filename of the batch file.
224 BOOL Batch (LPTSTR fullname, LPTSTR firstword, LPTSTR param)
228 hFile = CreateFile (fullname, GENERIC_READ, FILE_SHARE_READ, NULL,
229 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL |
230 FILE_FLAG_SEQUENTIAL_SCAN, NULL);
233 DebugPrintf (_T("Batch: (\'%s\', \'%s\', \'%s\') hFile = %x\n"),
234 fullname, firstword, param, hFile);
237 if (hFile == INVALID_HANDLE_VALUE)
239 ConErrPrintf (_T("Error opening batch file\n"));
243 /* Kill any and all FOR contexts */
244 while (bc && bc->forvar)
249 /* No curent batch file, create a new context */
250 LPBATCH_CONTEXT n = (LPBATCH_CONTEXT)malloc (sizeof(BATCH_CONTEXT));
254 error_out_of_memory ();
261 else if (bc->hBatchFile != INVALID_HANDLE_VALUE)
263 /* Then we are transferring to another batch */
264 CloseHandle (bc->hBatchFile);
265 bc->hBatchFile = INVALID_HANDLE_VALUE;
269 bc->hBatchFile = hFile;
270 bc->bEcho = bEcho; /* Preserve echo across batch calls */
274 bc->forvar = _T('\0');
276 bc->params = BatchParams (firstword, param);
279 DebugPrintf (_T("Batch: returns TRUE\n"));
287 * Read and return the next executable line form the current batch file
289 * If no batch file is current or no further executable lines are found
292 * Here we also look out for FOR bcontext structures which trigger the
293 * FOR expansion code.
295 * Set eflag to 0 if line is not to be echoed else 1
298 LPTSTR ReadBatchLine (LPBOOL bLocalEcho)
308 DebugPrintf (_T("ReadBatchLine ()\n"));
314 if (CheckCtrlBreak (BREAK_BATCHFILE))
325 /* If its a FOR context... */
328 LPTSTR sp = bc->forproto; /* pointer to prototype command */
329 LPTSTR dp = textline; /* Place to expand protoype */
330 LPTSTR fv = FindArg (0); /* Next list element */
332 /* End of list so... */
333 if ((fv == NULL) || (*fv == _T('\0')))
335 /* just exit this context */
340 if (_tcscspn (fv, _T("?*")) == _tcslen (fv))
342 /* element is wild file */
343 bc->shiftlevel++; /* No use it and shift list */
347 /* Wild file spec, find first (or next) file name */
350 /* First already done so do next */
352 fv = FindNextFile (bc->hFind, bc->ffind) ? bc->ffind->cFileName : NULL;
356 /* For first find, allocate a find first block */
357 if ((bc->ffind = (LPWIN32_FIND_DATA)malloc (sizeof (WIN32_FIND_DATA))) == NULL)
359 error_out_of_memory();
363 bc->hFind = FindFirstFile (fv, bc->ffind);
365 fv = !(bc->hFind==INVALID_HANDLE_VALUE) ? bc->ffind->cFileName : NULL;
370 /* Null indicates no more files.. */
371 free (bc->ffind); /* free the buffer */
373 bc->shiftlevel++; /* On to next list element */
378 /* At this point, fv points to parameter string */
381 if ((*sp == _T('%')) && (*(sp + 1) == bc->forvar))
384 dp = stpcpy (dp, fv);
401 if (!FileGetString (bc->hBatchFile, textline, sizeof (textline)))
404 DebugPrintf (_T("ReadBatchLine(): Reached EOF!\n"));
406 /* End of file.... */
416 DebugPrintf (_T("ReadBatchLine(): textline: \'%s\'\n"), textline);
419 /* Strip leading spaces and trailing space/control chars */
420 for (first = textline; _istspace (*first); first++)
423 for (ip = first + _tcslen (first) - 1; _istspace (*ip) || _istcntrl (*ip); ip--)
428 /* ignore labels and empty lines */
429 if (*first == _T(':') || *first == 0)
432 if (*first == _T('@'))
434 /* don't echo this line */
437 while (_istspace (*first));