1 /* $Id: batch.c,v 1.4 2001/02/28 22:33:23 ekohl Exp $
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 ("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 ("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 ("%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 ("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 ("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 ("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
));