blob: 3748f2194656a5df7cce49f08892419dcfb6bcac [file] [log] [blame]
Dave Pickles74f440e1999-06-06 15:24:04 +00001/*
Dan Kegel39857442006-09-03 21:13:08 -07002 * CMD - Wine-compatible command line interface - batch interface.
Dave Pickles74f440e1999-06-06 15:24:04 +00003 *
Alexandre Julliard0799c1a2002-03-09 23:29:33 +00004 * Copyright (C) 1999 D A Pickles
Jason Edmeades1ee75382007-09-11 21:43:08 +01005 * Copyright (C) 2007 J Edmeades
Dave Pickles74f440e1999-06-06 15:24:04 +00006 *
Alexandre Julliard0799c1a2002-03-09 23:29:33 +00007 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
Jonathan Ernst360a3f92006-05-18 14:49:52 +020019 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
Dave Pickles74f440e1999-06-06 15:24:04 +000020 */
21
Dave Pickles74f440e1999-06-06 15:24:04 +000022#include "wcmd.h"
Jason Edmeadesb6924112007-09-11 21:43:03 +010023#include "wine/debug.h"
24
25WINE_DEFAULT_DEBUG_CHANNEL(cmd);
Dave Pickles74f440e1999-06-06 15:24:04 +000026
Dave Pickles74f440e1999-06-06 15:24:04 +000027extern int echo_mode;
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +010028extern WCHAR quals[MAX_PATH], param1[MAX_PATH], param2[MAX_PATH];
Dave Pickles5f8f4f71999-06-26 10:24:08 +000029extern BATCH_CONTEXT *context;
Dave Picklesebecf502000-08-01 22:02:18 +000030extern DWORD errorlevel;
Dave Pickles74f440e1999-06-06 15:24:04 +000031
Dave Pickles74f440e1999-06-06 15:24:04 +000032/****************************************************************************
33 * WCMD_batch
34 *
35 * Open and execute a batch file.
36 * On entry *command includes the complete command line beginning with the name
37 * of the batch file (if a CALL command was entered the CALL has been removed).
38 * *file is the name of the file, which might not exist and may not have the
Dave Pickles5f8f4f71999-06-26 10:24:08 +000039 * .BAT suffix on. Called is 1 for a CALL, 0 otherwise.
Dave Pickles74f440e1999-06-06 15:24:04 +000040 *
41 * We need to handle recursion correctly, since one batch program might call another.
Dave Pickles5f8f4f71999-06-26 10:24:08 +000042 * So parameters for this batch file are held in a BATCH_CONTEXT structure.
Jason Edmeades327d7ef2007-02-23 22:19:25 +000043 *
44 * To support call within the same batch program, another input parameter is
45 * a label to goto once opened.
Dave Pickles74f440e1999-06-06 15:24:04 +000046 */
47
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +010048void WCMD_batch (WCHAR *file, WCHAR *command, int called, WCHAR *startLabel, HANDLE pgmHandle) {
Dave Pickles74f440e1999-06-06 15:24:04 +000049
Stefan Leichtera127ad12003-03-25 00:33:56 +000050#define WCMD_BATCH_EXT_SIZE 5
51
Alexandre Julliardabfe1052007-03-01 12:43:19 +010052 HANDLE h = INVALID_HANDLE_VALUE;
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +010053 WCHAR string[MAXSTRING];
54 static const WCHAR extension_batch[][WCMD_BATCH_EXT_SIZE] = {{'.','b','a','t','\0'},
55 {'.','c','m','d','\0'}};
56 static const WCHAR extension_exe[WCMD_BATCH_EXT_SIZE] = {'.','e','x','e','\0'};
Alexandre Julliardabfe1052007-03-01 12:43:19 +010057 unsigned int i;
58 BATCH_CONTEXT *prev_context;
Dave Pickles74f440e1999-06-06 15:24:04 +000059
Jason Edmeades327d7ef2007-02-23 22:19:25 +000060 if (startLabel == NULL) {
Rob Shearman271eaf52008-02-25 09:00:07 +000061 for(i=0; (i<sizeof(extension_batch)/(WCMD_BATCH_EXT_SIZE * sizeof(WCHAR))) &&
Jason Edmeades327d7ef2007-02-23 22:19:25 +000062 (h == INVALID_HANDLE_VALUE); i++) {
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +010063 strcpyW (string, file);
64 CharLower (string);
65 if (strstrW (string, extension_batch[i]) == NULL) strcatW (string, extension_batch[i]);
66 h = CreateFile (string, GENERIC_READ, FILE_SHARE_READ,
67 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
Vincent Béron95571e62005-03-28 10:01:31 +000068 }
Jason Edmeades327d7ef2007-02-23 22:19:25 +000069 if (h == INVALID_HANDLE_VALUE) {
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +010070 strcpyW (string, file);
Jason Edmeades327d7ef2007-02-23 22:19:25 +000071 CharLower (string);
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +010072 if (strstrW (string, extension_exe) == NULL) strcatW (string, extension_exe);
Lance Jacksond56217e2008-01-03 18:03:44 -060073 if (GetFileAttributes (string) != INVALID_FILE_ATTRIBUTES) {
Jason Edmeades327d7ef2007-02-23 22:19:25 +000074 WCMD_run_program (command, 0);
75 } else {
76 SetLastError (ERROR_FILE_NOT_FOUND);
77 WCMD_print_error ();
78 }
79 return;
80 }
81 } else {
82 DuplicateHandle(GetCurrentProcess(), pgmHandle,
83 GetCurrentProcess(), &h,
84 0, FALSE, DUPLICATE_SAME_ACCESS);
Dave Pickles74f440e1999-06-06 15:24:04 +000085 }
86
87/*
Dave Pickles5f8f4f71999-06-26 10:24:08 +000088 * Create a context structure for this batch file.
89 */
90
91 prev_context = context;
92 context = (BATCH_CONTEXT *)LocalAlloc (LMEM_FIXED, sizeof (BATCH_CONTEXT));
93 context -> h = h;
94 context -> command = command;
Jason Edmeades612dc9d2007-03-08 00:50:37 +000095 memset(context -> shift_count, 0x00, sizeof(context -> shift_count));
Dave Pickles5f8f4f71999-06-26 10:24:08 +000096 context -> prev_context = prev_context;
Jason Edmeadesc3666482007-02-20 17:49:08 +000097 context -> skip_rest = FALSE;
Dave Pickles5f8f4f71999-06-26 10:24:08 +000098
Jason Edmeades327d7ef2007-02-23 22:19:25 +000099 /* If processing a call :label, 'goto' the label in question */
100 if (startLabel) {
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100101 strcpyW(param1, startLabel);
Jason Edmeades929a92f2007-06-15 20:59:22 +0100102 WCMD_goto(NULL);
Jason Edmeades327d7ef2007-02-23 22:19:25 +0000103 }
104
Dave Pickles5f8f4f71999-06-26 10:24:08 +0000105/*
Dave Pickles74f440e1999-06-06 15:24:04 +0000106 * Work through the file line by line. Specific batch commands are processed here,
107 * the rest are handled by the main command processor.
108 */
109
Jason Edmeadesa88a6c82007-06-15 20:59:19 +0100110 while (context -> skip_rest == FALSE) {
111 CMD_LIST *toExecute = NULL; /* Commands left to be executed */
112 if (WCMD_ReadAndParseLine(NULL, &toExecute, h) == NULL)
113 break;
Jason Edmeades54d890c2007-06-15 20:59:28 +0100114 WCMD_process_commands(toExecute, FALSE, NULL, NULL);
Jason Edmeadesa88a6c82007-06-15 20:59:19 +0100115 WCMD_free_commands(toExecute);
116 toExecute = NULL;
Dave Pickles74f440e1999-06-06 15:24:04 +0000117 }
118 CloseHandle (h);
Dave Pickles5f8f4f71999-06-26 10:24:08 +0000119
120/*
121 * If invoked by a CALL, we return to the context of our caller. Otherwise return
122 * to the caller's caller.
123 */
124
125 LocalFree ((HANDLE)context);
126 if ((prev_context != NULL) && (!called)) {
Jason Edmeades85c5dcb2007-07-24 23:36:30 +0100127 prev_context -> skip_rest = TRUE;
Dave Pickles5f8f4f71999-06-26 10:24:08 +0000128 context = prev_context;
129 }
Jason Edmeades85c5dcb2007-07-24 23:36:30 +0100130 context = prev_context;
Dave Pickles74f440e1999-06-06 15:24:04 +0000131}
132
Dave Pickles74f440e1999-06-06 15:24:04 +0000133/*******************************************************************
134 * WCMD_parameter - extract a parameter from a command line.
135 *
Jason Edmeades118f3a62007-09-11 21:43:04 +0100136 * Returns the 'n'th delimited parameter on the command line (zero-based).
Dave Pickles74f440e1999-06-06 15:24:04 +0000137 * Parameter is in static storage overwritten on the next call.
Dave Pickles5f8f4f71999-06-26 10:24:08 +0000138 * Parameters in quotes (and brackets) are handled.
Dave Pickles036a9f71999-07-10 11:36:45 +0000139 * Also returns a pointer to the location of the parameter in the command line.
Dave Pickles74f440e1999-06-06 15:24:04 +0000140 */
141
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100142WCHAR *WCMD_parameter (WCHAR *s, int n, WCHAR **where) {
Dave Pickles74f440e1999-06-06 15:24:04 +0000143
Alexandre Julliardabfe1052007-03-01 12:43:19 +0100144 int i = 0;
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100145 static WCHAR param[MAX_PATH];
146 WCHAR *p;
Dave Pickles74f440e1999-06-06 15:24:04 +0000147
Jason Edmeadesa7c59062007-02-27 23:21:59 +0000148 if (where != NULL) *where = NULL;
Dave Pickles74f440e1999-06-06 15:24:04 +0000149 p = param;
150 while (TRUE) {
151 switch (*s) {
Jason Edmeades118f3a62007-09-11 21:43:04 +0100152 case ' ': /* Skip leading spaces */
Dave Pickles74f440e1999-06-06 15:24:04 +0000153 s++;
154 break;
155 case '"':
Jason Edmeadesa7c59062007-02-27 23:21:59 +0000156 if (where != NULL && i==n) *where = s;
Dave Pickles74f440e1999-06-06 15:24:04 +0000157 s++;
158 while ((*s != '\0') && (*s != '"')) {
159 *p++ = *s++;
160 }
161 if (i == n) {
162 *p = '\0';
163 return param;
164 }
Dave Pickles036a9f71999-07-10 11:36:45 +0000165 if (*s == '"') s++;
Dave Pickles74f440e1999-06-06 15:24:04 +0000166 param[0] = '\0';
167 i++;
Dave Pickles036a9f71999-07-10 11:36:45 +0000168 p = param;
Dave Pickles74f440e1999-06-06 15:24:04 +0000169 break;
Jason Edmeades54d890c2007-06-15 20:59:28 +0100170 /* The code to handle bracketed parms is removed because it should no longer
171 be necessary after the multiline support has been added and the for loop
172 set of data is now parseable individually. */
Dave Pickles74f440e1999-06-06 15:24:04 +0000173 case '\0':
174 return param;
175 default:
Jason Edmeadese7dc3f12007-02-20 18:02:29 +0000176 /* Only return where if it is for the right parameter */
177 if (where != NULL && i==n) *where = s;
Jason Edmeades118f3a62007-09-11 21:43:04 +0100178 while ((*s != '\0') && (*s != ' ') && (*s != ',') && (*s != '=')) {
Dave Pickles74f440e1999-06-06 15:24:04 +0000179 *p++ = *s++;
180 }
Jason Edmeades118f3a62007-09-11 21:43:04 +0100181 if (i == n && (p!=param)) {
Dave Pickles74f440e1999-06-06 15:24:04 +0000182 *p = '\0';
183 return param;
184 }
Jason Edmeades118f3a62007-09-11 21:43:04 +0100185 /* Skip double delimiters, eg. dir a.a,,,,,b.b */
186 if (p != param) {
Dave Pickles74f440e1999-06-06 15:24:04 +0000187 param[0] = '\0';
188 i++;
Jason Edmeades118f3a62007-09-11 21:43:04 +0100189 } else {
Francois Gouget8a18e0e2008-04-07 13:01:02 +0200190 s++; /* Skip delimiter */
Jason Edmeades118f3a62007-09-11 21:43:04 +0100191 }
Dave Pickles036a9f71999-07-10 11:36:45 +0000192 p = param;
Dave Pickles74f440e1999-06-06 15:24:04 +0000193 }
194 }
195}
196
Dave Picklesebecf502000-08-01 22:02:18 +0000197/****************************************************************************
Dave Pickles74f440e1999-06-06 15:24:04 +0000198 * WCMD_fgets
199 *
200 * Get one line from a batch file. We can't use the native f* functions because
Dave Pickles5f8f4f71999-06-26 10:24:08 +0000201 * of the filename syntax differences between DOS and Unix. Also need to lose
202 * the LF (or CRLF) from the line.
Dave Pickles74f440e1999-06-06 15:24:04 +0000203 */
204
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100205WCHAR *WCMD_fgets (WCHAR *s, int noChars, HANDLE h) {
Dave Pickles74f440e1999-06-06 15:24:04 +0000206
Alexandre Julliardabfe1052007-03-01 12:43:19 +0100207 DWORD bytes;
208 BOOL status;
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100209 WCHAR *p;
Dave Pickles74f440e1999-06-06 15:24:04 +0000210
211 p = s;
212 do {
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100213 status = WCMD_ReadFile (h, s, 1, &bytes, NULL);
Dave Pickles5f8f4f71999-06-26 10:24:08 +0000214 if ((status == 0) || ((bytes == 0) && (s == p))) return NULL;
Dave Pickles74f440e1999-06-06 15:24:04 +0000215 if (*s == '\n') bytes = 0;
Dave Pickles5f8f4f71999-06-26 10:24:08 +0000216 else if (*s != '\r') {
217 s++;
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100218 noChars--;
Dave Pickles5f8f4f71999-06-26 10:24:08 +0000219 }
220 *s = '\0';
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100221 } while ((bytes == 1) && (noChars > 1));
Dave Pickles74f440e1999-06-06 15:24:04 +0000222 return p;
223}
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000224
Jason Edmeades409368e2007-02-27 23:19:24 +0000225/* WCMD_splitpath - copied from winefile as no obvious way to use it otherwise */
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100226void WCMD_splitpath(const WCHAR* path, WCHAR* drv, WCHAR* dir, WCHAR* name, WCHAR* ext)
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000227{
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100228 const WCHAR* end; /* end of processed string */
229 const WCHAR* p; /* search pointer */
230 const WCHAR* s; /* copy pointer */
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000231
232 /* extract drive name */
233 if (path[0] && path[1]==':') {
234 if (drv) {
235 *drv++ = *path++;
236 *drv++ = *path++;
237 *drv = '\0';
238 }
239 } else if (drv)
240 *drv = '\0';
241
242 /* search for end of string or stream separator */
243 for(end=path; *end && *end!=':'; )
244 end++;
245
246 /* search for begin of file extension */
247 for(p=end; p>path && *--p!='\\' && *p!='/'; )
248 if (*p == '.') {
249 end = p;
250 break;
251 }
252
253 if (ext)
254 for(s=end; (*ext=*s++); )
255 ext++;
256
257 /* search for end of directory name */
258 for(p=end; p>path; )
259 if (*--p=='\\' || *p=='/') {
260 p++;
261 break;
262 }
263
264 if (name) {
265 for(s=p; s<end; )
266 *name++ = *s++;
267
268 *name = '\0';
269 }
270
271 if (dir) {
272 for(s=path; s<p; )
273 *dir++ = *s++;
274
275 *dir = '\0';
276 }
277}
278
279/****************************************************************************
280 * WCMD_HandleTildaModifiers
281 *
282 * Handle the ~ modifiers when expanding %0-9 or (%a-z in for command)
283 * %~xxxxxV (V=0-9 or A-Z)
284 * Where xxxx is any combination of:
285 * ~ - Removes quotes
286 * f - Fully qualified path (assumes current dir if not drive\dir)
287 * d - drive letter
288 * p - path
289 * n - filename
290 * x - file extension
291 * s - path with shortnames
292 * a - attributes
293 * t - date/time
294 * z - size
295 * $ENVVAR: - Searches ENVVAR for (contents of V) and expands to fully
296 * qualified path
297 *
298 * To work out the length of the modifier:
299 *
300 * Note: In the case of %0-9 knowing the end of the modifier is easy,
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100301 * but in a for loop, the for end WCHARacter may also be a modifier
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000302 * eg. for %a in (c:\a.a) do echo XXX
303 * where XXX = %~a (just ~)
304 * %~aa (~ and attributes)
305 * %~aaxa (~, attributes and extension)
306 * BUT %~aax (~ and attributes followed by 'x')
307 *
308 * Hence search forwards until find an invalid modifier, and then
309 * backwards until find for variable or 0-9
310 */
Jason Edmeadesb6924112007-09-11 21:43:03 +0100311void WCMD_HandleTildaModifiers(WCHAR **start, WCHAR *forVariable, WCHAR *forValue, BOOL justFors) {
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000312
313#define NUMMODIFIERS 11
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100314 static const WCHAR validmodifiers[NUMMODIFIERS] = {
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000315 '~', 'f', 'd', 'p', 'n', 'x', 's', 'a', 't', 'z', '$'
316 };
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100317 static const WCHAR space[] = {' ', '\0'};
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000318
319 WIN32_FILE_ATTRIBUTE_DATA fileInfo;
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100320 WCHAR outputparam[MAX_PATH];
321 WCHAR finaloutput[MAX_PATH];
322 WCHAR fullfilename[MAX_PATH];
323 WCHAR thisoutput[MAX_PATH];
324 WCHAR *pos = *start+1;
325 WCHAR *firstModifier = pos;
326 WCHAR *lastModifier = NULL;
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000327 int modifierLen = 0;
328 BOOL finished = FALSE;
329 int i = 0;
330 BOOL exists = TRUE;
331 BOOL skipFileParsing = FALSE;
332 BOOL doneModifier = FALSE;
333
Jason Edmeadesb6924112007-09-11 21:43:03 +0100334 /* Search forwards until find invalid character modifier */
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000335 while (!finished) {
336
Jason Edmeadesb6924112007-09-11 21:43:03 +0100337 /* Work on the previous character */
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000338 if (lastModifier != NULL) {
339
340 for (i=0; i<NUMMODIFIERS; i++) {
341 if (validmodifiers[i] == *lastModifier) {
342
343 /* Special case '$' to skip until : found */
344 if (*lastModifier == '$') {
345 while (*pos != ':' && *pos) pos++;
346 if (*pos == 0x00) return; /* Invalid syntax */
347 pos++; /* Skip ':' */
348 }
349 break;
350 }
351 }
352
353 if (i==NUMMODIFIERS) {
354 finished = TRUE;
355 }
356 }
357
358 /* Save this one away */
359 if (!finished) {
360 lastModifier = pos;
361 pos++;
362 }
363 }
364
Jason Edmeadesb6924112007-09-11 21:43:03 +0100365 while (lastModifier > firstModifier) {
366 WINE_TRACE("Looking backwards for parameter id: %s / %s\n",
367 wine_dbgstr_w(lastModifier), wine_dbgstr_w(forVariable));
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000368
Jason Edmeadesb6924112007-09-11 21:43:03 +0100369 if (!justFors && context && (*lastModifier >= '0' || *lastModifier <= '9')) {
370 /* Its a valid parameter identifier - OK */
371 break;
372
373 } else if (forVariable && *lastModifier == *(forVariable+1)) {
374 /* Its a valid parameter identifier - OK */
375 break;
376
377 } else {
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000378 lastModifier--;
379 }
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000380 }
Jason Edmeadesb6924112007-09-11 21:43:03 +0100381 if (lastModifier == firstModifier) return; /* Invalid syntax */
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000382
383 /* Extract the parameter to play with */
384 if ((*lastModifier >= '0' && *lastModifier <= '9')) {
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100385 strcpyW(outputparam, WCMD_parameter (context -> command,
Jason Edmeades612dc9d2007-03-08 00:50:37 +0000386 *lastModifier-'0' + context -> shift_count[*lastModifier-'0'], NULL));
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000387 } else {
Jason Edmeadesb6924112007-09-11 21:43:03 +0100388 strcpyW(outputparam, forValue);
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000389 }
390
391 /* So now, firstModifier points to beginning of modifiers, lastModifier
392 points to the variable just after the modifiers. Process modifiers
393 in a specific order, remembering there could be duplicates */
394 modifierLen = lastModifier - firstModifier;
395 finaloutput[0] = 0x00;
396
397 /* Useful for debugging purposes: */
398 /*printf("Modifier string '%*.*s' and variable is %c\n Param starts as '%s'\n",
399 (modifierLen), (modifierLen), firstModifier, *lastModifier,
400 outputparam);*/
401
402 /* 1. Handle '~' : Strip surrounding quotes */
403 if (outputparam[0]=='"' &&
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100404 memchrW(firstModifier, '~', modifierLen) != NULL) {
405 int len = strlenW(outputparam);
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000406 if (outputparam[len-1] == '"') {
407 outputparam[len-1]=0x00;
408 len = len - 1;
409 }
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100410 memmove(outputparam, &outputparam[1], (len * sizeof(WCHAR))-1);
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000411 }
412
413 /* 2. Handle the special case of a $ */
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100414 if (memchrW(firstModifier, '$', modifierLen) != NULL) {
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000415 /* Special Case: Search envar specified in $[envvar] for outputparam
416 Note both $ and : are guaranteed otherwise check above would fail */
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100417 WCHAR *start = strchrW(firstModifier, '$') + 1;
418 WCHAR *end = strchrW(firstModifier, ':');
419 WCHAR env[MAX_PATH];
420 WCHAR fullpath[MAX_PATH];
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000421
422 /* Extract the env var */
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100423 memcpy(env, start, (end-start) * sizeof(WCHAR));
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000424 env[(end-start)] = 0x00;
425
Francois Gouget44b52b12008-01-16 12:20:50 +0100426 /* If env var not found, return empty string */
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000427 if ((GetEnvironmentVariable(env, fullpath, MAX_PATH) == 0) ||
428 (SearchPath(fullpath, outputparam, NULL,
429 MAX_PATH, outputparam, NULL) == 0)) {
430 finaloutput[0] = 0x00;
431 outputparam[0] = 0x00;
432 skipFileParsing = TRUE;
433 }
434 }
435
436 /* After this, we need full information on the file,
437 which is valid not to exist. */
438 if (!skipFileParsing) {
439 if (GetFullPathName(outputparam, MAX_PATH, fullfilename, NULL) == 0)
440 return;
441
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100442 exists = GetFileAttributesExW(fullfilename, GetFileExInfoStandard,
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000443 &fileInfo);
444
445 /* 2. Handle 'a' : Output attributes */
446 if (exists &&
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100447 memchrW(firstModifier, 'a', modifierLen) != NULL) {
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000448
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100449 WCHAR defaults[] = {'-','-','-','-','-','-','-','-','-','\0'};
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000450 doneModifier = TRUE;
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100451 strcpyW(thisoutput, defaults);
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000452 if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
453 thisoutput[0]='d';
454 if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
455 thisoutput[1]='r';
456 if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)
457 thisoutput[2]='a';
458 if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
459 thisoutput[3]='h';
460 if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)
461 thisoutput[4]='s';
462 if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED)
463 thisoutput[5]='c';
464 /* FIXME: What are 6 and 7? */
465 if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
466 thisoutput[8]='l';
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100467 strcatW(finaloutput, thisoutput);
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000468 }
469
470 /* 3. Handle 't' : Date+time */
471 if (exists &&
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100472 memchrW(firstModifier, 't', modifierLen) != NULL) {
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000473
474 SYSTEMTIME systime;
475 int datelen;
476
477 doneModifier = TRUE;
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100478 if (finaloutput[0] != 0x00) strcatW(finaloutput, space);
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000479
480 /* Format the time */
481 FileTimeToSystemTime(&fileInfo.ftLastWriteTime, &systime);
482 GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &systime,
483 NULL, thisoutput, MAX_PATH);
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100484 strcatW(thisoutput, space);
485 datelen = strlenW(thisoutput);
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000486 GetTimeFormat(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &systime,
487 NULL, (thisoutput+datelen), MAX_PATH-datelen);
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100488 strcatW(finaloutput, thisoutput);
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000489 }
490
491 /* 4. Handle 'z' : File length */
492 if (exists &&
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100493 memchrW(firstModifier, 'z', modifierLen) != NULL) {
Francois Gouget7b0cde82007-03-06 14:26:24 +0100494 /* FIXME: Output full 64 bit size (sprintf does not support I64 here) */
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000495 ULONG/*64*/ fullsize = /*(fileInfo.nFileSizeHigh << 32) +*/
496 fileInfo.nFileSizeLow;
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100497 static const WCHAR fmt[] = {'%','u','\0'};
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000498
499 doneModifier = TRUE;
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100500 if (finaloutput[0] != 0x00) strcatW(finaloutput, space);
501 wsprintf(thisoutput, fmt, fullsize);
502 strcatW(finaloutput, thisoutput);
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000503 }
504
Francois Gouget7b0cde82007-03-06 14:26:24 +0100505 /* 4. Handle 's' : Use short paths (File doesn't have to exist) */
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100506 if (memchrW(firstModifier, 's', modifierLen) != NULL) {
507 if (finaloutput[0] != 0x00) strcatW(finaloutput, space);
Francois Gouget7b0cde82007-03-06 14:26:24 +0100508 /* Don't flag as doneModifier - %~s on its own is processed later */
Rob Shearman1d6922b2008-02-25 09:00:14 +0000509 GetShortPathName(outputparam, outputparam,
510 sizeof(outputparam)/sizeof(outputparam[0]));
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000511 }
512
Francois Gouget7b0cde82007-03-06 14:26:24 +0100513 /* 5. Handle 'f' : Fully qualified path (File doesn't have to exist) */
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000514 /* Note this overrides d,p,n,x */
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100515 if (memchrW(firstModifier, 'f', modifierLen) != NULL) {
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000516 doneModifier = TRUE;
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100517 if (finaloutput[0] != 0x00) strcatW(finaloutput, space);
518 strcatW(finaloutput, fullfilename);
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000519 } else {
520
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100521 WCHAR drive[10];
522 WCHAR dir[MAX_PATH];
523 WCHAR fname[MAX_PATH];
524 WCHAR ext[MAX_PATH];
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000525 BOOL doneFileModifier = FALSE;
526
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100527 if (finaloutput[0] != 0x00) strcatW(finaloutput, space);
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000528
529 /* Split into components */
Jason Edmeades409368e2007-02-27 23:19:24 +0000530 WCMD_splitpath(fullfilename, drive, dir, fname, ext);
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000531
532 /* 5. Handle 'd' : Drive Letter */
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100533 if (memchrW(firstModifier, 'd', modifierLen) != NULL) {
534 strcatW(finaloutput, drive);
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000535 doneModifier = TRUE;
536 doneFileModifier = TRUE;
537 }
538
539 /* 6. Handle 'p' : Path */
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100540 if (memchrW(firstModifier, 'p', modifierLen) != NULL) {
541 strcatW(finaloutput, dir);
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000542 doneModifier = TRUE;
543 doneFileModifier = TRUE;
544 }
545
546 /* 7. Handle 'n' : Name */
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100547 if (memchrW(firstModifier, 'n', modifierLen) != NULL) {
548 strcatW(finaloutput, fname);
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000549 doneModifier = TRUE;
550 doneFileModifier = TRUE;
551 }
552
553 /* 8. Handle 'x' : Ext */
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100554 if (memchrW(firstModifier, 'x', modifierLen) != NULL) {
555 strcatW(finaloutput, ext);
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000556 doneModifier = TRUE;
557 doneFileModifier = TRUE;
558 }
559
560 /* If 's' but no other parameter, dump the whole thing */
561 if (!doneFileModifier &&
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100562 memchrW(firstModifier, 's', modifierLen) != NULL) {
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000563 doneModifier = TRUE;
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100564 if (finaloutput[0] != 0x00) strcatW(finaloutput, space);
565 strcatW(finaloutput, outputparam);
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000566 }
567 }
568 }
569
570 /* If No other modifier processed, just add in parameter */
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100571 if (!doneModifier) strcpyW(finaloutput, outputparam);
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000572
573 /* Finish by inserting the replacement into the string */
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100574 pos = WCMD_strdupW(lastModifier+1);
575 strcpyW(*start, finaloutput);
576 strcatW(*start, pos);
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000577 free(pos);
578}
Jason Edmeades327d7ef2007-02-23 22:19:25 +0000579
580/*******************************************************************
581 * WCMD_call - processes a batch call statement
582 *
583 * If there is a leading ':', calls within this batch program
584 * otherwise launches another program.
585 */
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100586void WCMD_call (WCHAR *command) {
Jason Edmeades327d7ef2007-02-23 22:19:25 +0000587
588 /* Run other program if no leading ':' */
589 if (*command != ':') {
590 WCMD_run_program(command, 1);
591 } else {
592
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100593 WCHAR gotoLabel[MAX_PATH];
Jason Edmeades327d7ef2007-02-23 22:19:25 +0000594
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100595 strcpyW(gotoLabel, param1);
Jason Edmeades327d7ef2007-02-23 22:19:25 +0000596
597 if (context) {
598
599 LARGE_INTEGER li;
600
601 /* Save the current file position, call the same file,
602 restore position */
603 li.QuadPart = 0;
Francois Gougete46b99d2007-03-04 01:59:59 +0100604 li.u.LowPart = SetFilePointer(context -> h, li.u.LowPart,
605 &li.u.HighPart, FILE_CURRENT);
Jason Edmeades327d7ef2007-02-23 22:19:25 +0000606
607 WCMD_batch (param1, command, 1, gotoLabel, context->h);
608
Francois Gougete46b99d2007-03-04 01:59:59 +0100609 SetFilePointer(context -> h, li.u.LowPart,
610 &li.u.HighPart, FILE_BEGIN);
Jason Edmeades327d7ef2007-02-23 22:19:25 +0000611 } else {
Jason Edmeades5cc492c2007-06-03 22:07:39 +0100612 WCMD_output_asis( WCMD_LoadMessage(WCMD_CALLINSCRIPT));
Jason Edmeades327d7ef2007-02-23 22:19:25 +0000613 }
614 }
615}