blob: f012eafef536626b2f151ed63a8f0fada63d2388 [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
Alexandre Julliardabfe1052007-03-01 12:43:19 +010050 HANDLE h = INVALID_HANDLE_VALUE;
Alexandre Julliardabfe1052007-03-01 12:43:19 +010051 BATCH_CONTEXT *prev_context;
Dave Pickles74f440e1999-06-06 15:24:04 +000052
Jason Edmeades327d7ef2007-02-23 22:19:25 +000053 if (startLabel == NULL) {
Paul Vriensf4680552010-02-18 17:09:04 +010054 h = CreateFileW (file, GENERIC_READ, FILE_SHARE_READ,
55 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
Jason Edmeades327d7ef2007-02-23 22:19:25 +000056 if (h == INVALID_HANDLE_VALUE) {
Paul Vriensf4680552010-02-18 17:09:04 +010057 SetLastError (ERROR_FILE_NOT_FOUND);
58 WCMD_print_error ();
Jason Edmeades327d7ef2007-02-23 22:19:25 +000059 return;
60 }
61 } else {
62 DuplicateHandle(GetCurrentProcess(), pgmHandle,
63 GetCurrentProcess(), &h,
64 0, FALSE, DUPLICATE_SAME_ACCESS);
Dave Pickles74f440e1999-06-06 15:24:04 +000065 }
66
67/*
Dave Pickles5f8f4f71999-06-26 10:24:08 +000068 * Create a context structure for this batch file.
69 */
70
71 prev_context = context;
Michael Stefaniuccf8571f2008-12-04 05:31:43 +010072 context = LocalAlloc (LMEM_FIXED, sizeof (BATCH_CONTEXT));
Dave Pickles5f8f4f71999-06-26 10:24:08 +000073 context -> h = h;
Paul Vriensf4680552010-02-18 17:09:04 +010074 context->batchfileW = WCMD_strdupW(file);
Dave Pickles5f8f4f71999-06-26 10:24:08 +000075 context -> command = command;
Jason Edmeades612dc9d2007-03-08 00:50:37 +000076 memset(context -> shift_count, 0x00, sizeof(context -> shift_count));
Dave Pickles5f8f4f71999-06-26 10:24:08 +000077 context -> prev_context = prev_context;
Jason Edmeadesc3666482007-02-20 17:49:08 +000078 context -> skip_rest = FALSE;
Dave Pickles5f8f4f71999-06-26 10:24:08 +000079
Jason Edmeades327d7ef2007-02-23 22:19:25 +000080 /* If processing a call :label, 'goto' the label in question */
81 if (startLabel) {
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +010082 strcpyW(param1, startLabel);
Jason Edmeades929a92f2007-06-15 20:59:22 +010083 WCMD_goto(NULL);
Jason Edmeades327d7ef2007-02-23 22:19:25 +000084 }
85
Dave Pickles5f8f4f71999-06-26 10:24:08 +000086/*
Dave Pickles74f440e1999-06-06 15:24:04 +000087 * Work through the file line by line. Specific batch commands are processed here,
88 * the rest are handled by the main command processor.
89 */
90
Jason Edmeadesa88a6c82007-06-15 20:59:19 +010091 while (context -> skip_rest == FALSE) {
92 CMD_LIST *toExecute = NULL; /* Commands left to be executed */
93 if (WCMD_ReadAndParseLine(NULL, &toExecute, h) == NULL)
94 break;
Jason Edmeades54d890c2007-06-15 20:59:28 +010095 WCMD_process_commands(toExecute, FALSE, NULL, NULL);
Jason Edmeadesa88a6c82007-06-15 20:59:19 +010096 WCMD_free_commands(toExecute);
97 toExecute = NULL;
Dave Pickles74f440e1999-06-06 15:24:04 +000098 }
99 CloseHandle (h);
Dave Pickles5f8f4f71999-06-26 10:24:08 +0000100
101/*
102 * If invoked by a CALL, we return to the context of our caller. Otherwise return
103 * to the caller's caller.
104 */
105
Dan Kegel60fe4da2010-02-02 16:56:38 -0800106 HeapFree(GetProcessHeap(), 0, context->batchfileW);
Michael Stefaniuccf8571f2008-12-04 05:31:43 +0100107 LocalFree (context);
Dave Pickles5f8f4f71999-06-26 10:24:08 +0000108 if ((prev_context != NULL) && (!called)) {
Jason Edmeades85c5dcb2007-07-24 23:36:30 +0100109 prev_context -> skip_rest = TRUE;
Dave Pickles5f8f4f71999-06-26 10:24:08 +0000110 context = prev_context;
111 }
Jason Edmeades85c5dcb2007-07-24 23:36:30 +0100112 context = prev_context;
Dave Pickles74f440e1999-06-06 15:24:04 +0000113}
114
Dave Pickles74f440e1999-06-06 15:24:04 +0000115/*******************************************************************
116 * WCMD_parameter - extract a parameter from a command line.
117 *
Jason Edmeades118f3a62007-09-11 21:43:04 +0100118 * Returns the 'n'th delimited parameter on the command line (zero-based).
Dave Pickles74f440e1999-06-06 15:24:04 +0000119 * Parameter is in static storage overwritten on the next call.
Dave Pickles5f8f4f71999-06-26 10:24:08 +0000120 * Parameters in quotes (and brackets) are handled.
Dave Pickles036a9f71999-07-10 11:36:45 +0000121 * Also returns a pointer to the location of the parameter in the command line.
Dave Pickles74f440e1999-06-06 15:24:04 +0000122 */
123
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100124WCHAR *WCMD_parameter (WCHAR *s, int n, WCHAR **where) {
Dave Pickles74f440e1999-06-06 15:24:04 +0000125
Alexandre Julliardabfe1052007-03-01 12:43:19 +0100126 int i = 0;
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100127 static WCHAR param[MAX_PATH];
128 WCHAR *p;
Dave Pickles74f440e1999-06-06 15:24:04 +0000129
Jason Edmeadesa7c59062007-02-27 23:21:59 +0000130 if (where != NULL) *where = NULL;
Dave Pickles74f440e1999-06-06 15:24:04 +0000131 p = param;
132 while (TRUE) {
133 switch (*s) {
Jason Edmeades118f3a62007-09-11 21:43:04 +0100134 case ' ': /* Skip leading spaces */
Colin Fletcher45905b22009-10-14 11:22:34 +0100135 case '\t': /* Treat tabs as spaces */
Dave Pickles74f440e1999-06-06 15:24:04 +0000136 s++;
137 break;
138 case '"':
Jason Edmeadesa7c59062007-02-27 23:21:59 +0000139 if (where != NULL && i==n) *where = s;
Dave Pickles74f440e1999-06-06 15:24:04 +0000140 s++;
141 while ((*s != '\0') && (*s != '"')) {
142 *p++ = *s++;
143 }
144 if (i == n) {
145 *p = '\0';
146 return param;
147 }
Dave Pickles036a9f71999-07-10 11:36:45 +0000148 if (*s == '"') s++;
Dave Pickles74f440e1999-06-06 15:24:04 +0000149 param[0] = '\0';
150 i++;
Dave Pickles036a9f71999-07-10 11:36:45 +0000151 p = param;
Dave Pickles74f440e1999-06-06 15:24:04 +0000152 break;
Jason Edmeades54d890c2007-06-15 20:59:28 +0100153 /* The code to handle bracketed parms is removed because it should no longer
154 be necessary after the multiline support has been added and the for loop
155 set of data is now parseable individually. */
Dave Pickles74f440e1999-06-06 15:24:04 +0000156 case '\0':
157 return param;
158 default:
Jason Edmeadese7dc3f12007-02-20 18:02:29 +0000159 /* Only return where if it is for the right parameter */
160 if (where != NULL && i==n) *where = s;
Colin Fletcher45905b22009-10-14 11:22:34 +0100161 while ((*s != '\0') && (*s != ' ') && (*s != ',') && (*s != '=') && (*s != '\t')) {
Dave Pickles74f440e1999-06-06 15:24:04 +0000162 *p++ = *s++;
163 }
Jason Edmeades118f3a62007-09-11 21:43:04 +0100164 if (i == n && (p!=param)) {
Dave Pickles74f440e1999-06-06 15:24:04 +0000165 *p = '\0';
166 return param;
167 }
Jason Edmeades118f3a62007-09-11 21:43:04 +0100168 /* Skip double delimiters, eg. dir a.a,,,,,b.b */
169 if (p != param) {
Dave Pickles74f440e1999-06-06 15:24:04 +0000170 param[0] = '\0';
171 i++;
Jason Edmeades118f3a62007-09-11 21:43:04 +0100172 } else {
Francois Gouget8a18e0e2008-04-07 13:01:02 +0200173 s++; /* Skip delimiter */
Jason Edmeades118f3a62007-09-11 21:43:04 +0100174 }
Dave Pickles036a9f71999-07-10 11:36:45 +0000175 p = param;
Dave Pickles74f440e1999-06-06 15:24:04 +0000176 }
177 }
178}
179
Dave Picklesebecf502000-08-01 22:02:18 +0000180/****************************************************************************
Dave Pickles74f440e1999-06-06 15:24:04 +0000181 * WCMD_fgets
182 *
183 * Get one line from a batch file. We can't use the native f* functions because
Dave Pickles5f8f4f71999-06-26 10:24:08 +0000184 * of the filename syntax differences between DOS and Unix. Also need to lose
185 * the LF (or CRLF) from the line.
Dave Pickles74f440e1999-06-06 15:24:04 +0000186 */
187
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100188WCHAR *WCMD_fgets (WCHAR *s, int noChars, HANDLE h) {
Dave Pickles74f440e1999-06-06 15:24:04 +0000189
Alexandre Julliardabfe1052007-03-01 12:43:19 +0100190 DWORD bytes;
191 BOOL status;
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100192 WCHAR *p;
Dave Pickles74f440e1999-06-06 15:24:04 +0000193
194 p = s;
195 do {
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100196 status = WCMD_ReadFile (h, s, 1, &bytes, NULL);
Dave Pickles5f8f4f71999-06-26 10:24:08 +0000197 if ((status == 0) || ((bytes == 0) && (s == p))) return NULL;
Dave Pickles74f440e1999-06-06 15:24:04 +0000198 if (*s == '\n') bytes = 0;
Dave Pickles5f8f4f71999-06-26 10:24:08 +0000199 else if (*s != '\r') {
200 s++;
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100201 noChars--;
Dave Pickles5f8f4f71999-06-26 10:24:08 +0000202 }
203 *s = '\0';
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100204 } while ((bytes == 1) && (noChars > 1));
Dave Pickles74f440e1999-06-06 15:24:04 +0000205 return p;
206}
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000207
Jason Edmeades409368e2007-02-27 23:19:24 +0000208/* WCMD_splitpath - copied from winefile as no obvious way to use it otherwise */
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100209void WCMD_splitpath(const WCHAR* path, WCHAR* drv, WCHAR* dir, WCHAR* name, WCHAR* ext)
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000210{
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100211 const WCHAR* end; /* end of processed string */
212 const WCHAR* p; /* search pointer */
213 const WCHAR* s; /* copy pointer */
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000214
215 /* extract drive name */
216 if (path[0] && path[1]==':') {
217 if (drv) {
218 *drv++ = *path++;
219 *drv++ = *path++;
220 *drv = '\0';
221 }
222 } else if (drv)
223 *drv = '\0';
224
225 /* search for end of string or stream separator */
226 for(end=path; *end && *end!=':'; )
227 end++;
228
229 /* search for begin of file extension */
230 for(p=end; p>path && *--p!='\\' && *p!='/'; )
231 if (*p == '.') {
232 end = p;
233 break;
234 }
235
236 if (ext)
237 for(s=end; (*ext=*s++); )
238 ext++;
239
240 /* search for end of directory name */
241 for(p=end; p>path; )
242 if (*--p=='\\' || *p=='/') {
243 p++;
244 break;
245 }
246
247 if (name) {
248 for(s=p; s<end; )
249 *name++ = *s++;
250
251 *name = '\0';
252 }
253
254 if (dir) {
255 for(s=path; s<p; )
256 *dir++ = *s++;
257
258 *dir = '\0';
259 }
260}
261
262/****************************************************************************
263 * WCMD_HandleTildaModifiers
264 *
265 * Handle the ~ modifiers when expanding %0-9 or (%a-z in for command)
266 * %~xxxxxV (V=0-9 or A-Z)
267 * Where xxxx is any combination of:
268 * ~ - Removes quotes
269 * f - Fully qualified path (assumes current dir if not drive\dir)
270 * d - drive letter
271 * p - path
272 * n - filename
273 * x - file extension
274 * s - path with shortnames
275 * a - attributes
276 * t - date/time
277 * z - size
278 * $ENVVAR: - Searches ENVVAR for (contents of V) and expands to fully
279 * qualified path
280 *
281 * To work out the length of the modifier:
282 *
283 * Note: In the case of %0-9 knowing the end of the modifier is easy,
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100284 * but in a for loop, the for end WCHARacter may also be a modifier
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000285 * eg. for %a in (c:\a.a) do echo XXX
286 * where XXX = %~a (just ~)
287 * %~aa (~ and attributes)
288 * %~aaxa (~, attributes and extension)
289 * BUT %~aax (~ and attributes followed by 'x')
290 *
291 * Hence search forwards until find an invalid modifier, and then
292 * backwards until find for variable or 0-9
293 */
Jason Edmeadesb6924112007-09-11 21:43:03 +0100294void WCMD_HandleTildaModifiers(WCHAR **start, WCHAR *forVariable, WCHAR *forValue, BOOL justFors) {
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000295
296#define NUMMODIFIERS 11
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100297 static const WCHAR validmodifiers[NUMMODIFIERS] = {
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000298 '~', 'f', 'd', 'p', 'n', 'x', 's', 'a', 't', 'z', '$'
299 };
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100300 static const WCHAR space[] = {' ', '\0'};
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000301
302 WIN32_FILE_ATTRIBUTE_DATA fileInfo;
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100303 WCHAR outputparam[MAX_PATH];
304 WCHAR finaloutput[MAX_PATH];
305 WCHAR fullfilename[MAX_PATH];
306 WCHAR thisoutput[MAX_PATH];
307 WCHAR *pos = *start+1;
308 WCHAR *firstModifier = pos;
309 WCHAR *lastModifier = NULL;
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000310 int modifierLen = 0;
311 BOOL finished = FALSE;
312 int i = 0;
313 BOOL exists = TRUE;
314 BOOL skipFileParsing = FALSE;
315 BOOL doneModifier = FALSE;
316
Jason Edmeadesb6924112007-09-11 21:43:03 +0100317 /* Search forwards until find invalid character modifier */
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000318 while (!finished) {
319
Jason Edmeadesb6924112007-09-11 21:43:03 +0100320 /* Work on the previous character */
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000321 if (lastModifier != NULL) {
322
323 for (i=0; i<NUMMODIFIERS; i++) {
324 if (validmodifiers[i] == *lastModifier) {
325
326 /* Special case '$' to skip until : found */
327 if (*lastModifier == '$') {
328 while (*pos != ':' && *pos) pos++;
329 if (*pos == 0x00) return; /* Invalid syntax */
330 pos++; /* Skip ':' */
331 }
332 break;
333 }
334 }
335
336 if (i==NUMMODIFIERS) {
337 finished = TRUE;
338 }
339 }
340
341 /* Save this one away */
342 if (!finished) {
343 lastModifier = pos;
344 pos++;
345 }
346 }
347
Jason Edmeadesb6924112007-09-11 21:43:03 +0100348 while (lastModifier > firstModifier) {
349 WINE_TRACE("Looking backwards for parameter id: %s / %s\n",
350 wine_dbgstr_w(lastModifier), wine_dbgstr_w(forVariable));
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000351
Gerald Pfeifer008edf42009-06-15 19:05:33 +0200352 if (!justFors && context && (*lastModifier >= '0' && *lastModifier <= '9')) {
Jason Edmeadesb6924112007-09-11 21:43:03 +0100353 /* Its a valid parameter identifier - OK */
354 break;
355
356 } else if (forVariable && *lastModifier == *(forVariable+1)) {
357 /* Its a valid parameter identifier - OK */
358 break;
359
360 } else {
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000361 lastModifier--;
362 }
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000363 }
Jason Edmeadesb6924112007-09-11 21:43:03 +0100364 if (lastModifier == firstModifier) return; /* Invalid syntax */
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000365
366 /* Extract the parameter to play with */
Dan Kegel60fe4da2010-02-02 16:56:38 -0800367 if (*lastModifier == '0') {
368 strcpyW(outputparam, context->batchfileW);
369 } else if ((*lastModifier >= '1' && *lastModifier <= '9')) {
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100370 strcpyW(outputparam, WCMD_parameter (context -> command,
Jason Edmeades612dc9d2007-03-08 00:50:37 +0000371 *lastModifier-'0' + context -> shift_count[*lastModifier-'0'], NULL));
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000372 } else {
Jason Edmeadesb6924112007-09-11 21:43:03 +0100373 strcpyW(outputparam, forValue);
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000374 }
375
376 /* So now, firstModifier points to beginning of modifiers, lastModifier
377 points to the variable just after the modifiers. Process modifiers
378 in a specific order, remembering there could be duplicates */
379 modifierLen = lastModifier - firstModifier;
380 finaloutput[0] = 0x00;
381
382 /* Useful for debugging purposes: */
383 /*printf("Modifier string '%*.*s' and variable is %c\n Param starts as '%s'\n",
384 (modifierLen), (modifierLen), firstModifier, *lastModifier,
385 outputparam);*/
386
387 /* 1. Handle '~' : Strip surrounding quotes */
388 if (outputparam[0]=='"' &&
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100389 memchrW(firstModifier, '~', modifierLen) != NULL) {
390 int len = strlenW(outputparam);
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000391 if (outputparam[len-1] == '"') {
392 outputparam[len-1]=0x00;
393 len = len - 1;
394 }
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100395 memmove(outputparam, &outputparam[1], (len * sizeof(WCHAR))-1);
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000396 }
397
398 /* 2. Handle the special case of a $ */
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100399 if (memchrW(firstModifier, '$', modifierLen) != NULL) {
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000400 /* Special Case: Search envar specified in $[envvar] for outputparam
401 Note both $ and : are guaranteed otherwise check above would fail */
Austin English3631ee02011-03-09 01:07:20 -0800402 WCHAR *begin = strchrW(firstModifier, '$') + 1;
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100403 WCHAR *end = strchrW(firstModifier, ':');
404 WCHAR env[MAX_PATH];
405 WCHAR fullpath[MAX_PATH];
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000406
407 /* Extract the env var */
Austin English3631ee02011-03-09 01:07:20 -0800408 memcpy(env, begin, (end-begin) * sizeof(WCHAR));
409 env[(end-begin)] = 0x00;
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000410
Francois Gouget44b52b12008-01-16 12:20:50 +0100411 /* If env var not found, return empty string */
Alexandre Julliard79b00722009-12-09 18:52:40 +0100412 if ((GetEnvironmentVariableW(env, fullpath, MAX_PATH) == 0) ||
413 (SearchPathW(fullpath, outputparam, NULL, MAX_PATH, outputparam, NULL) == 0)) {
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000414 finaloutput[0] = 0x00;
415 outputparam[0] = 0x00;
416 skipFileParsing = TRUE;
417 }
418 }
419
420 /* After this, we need full information on the file,
421 which is valid not to exist. */
422 if (!skipFileParsing) {
Alexandre Julliard79b00722009-12-09 18:52:40 +0100423 if (GetFullPathNameW(outputparam, MAX_PATH, fullfilename, NULL) == 0)
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000424 return;
425
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100426 exists = GetFileAttributesExW(fullfilename, GetFileExInfoStandard,
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000427 &fileInfo);
428
429 /* 2. Handle 'a' : Output attributes */
430 if (exists &&
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100431 memchrW(firstModifier, 'a', modifierLen) != NULL) {
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000432
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100433 WCHAR defaults[] = {'-','-','-','-','-','-','-','-','-','\0'};
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000434 doneModifier = TRUE;
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100435 strcpyW(thisoutput, defaults);
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000436 if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
437 thisoutput[0]='d';
438 if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
439 thisoutput[1]='r';
440 if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)
441 thisoutput[2]='a';
442 if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
443 thisoutput[3]='h';
444 if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)
445 thisoutput[4]='s';
446 if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED)
447 thisoutput[5]='c';
448 /* FIXME: What are 6 and 7? */
449 if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
450 thisoutput[8]='l';
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100451 strcatW(finaloutput, thisoutput);
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000452 }
453
454 /* 3. Handle 't' : Date+time */
455 if (exists &&
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100456 memchrW(firstModifier, 't', modifierLen) != NULL) {
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000457
458 SYSTEMTIME systime;
459 int datelen;
460
461 doneModifier = TRUE;
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100462 if (finaloutput[0] != 0x00) strcatW(finaloutput, space);
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000463
464 /* Format the time */
465 FileTimeToSystemTime(&fileInfo.ftLastWriteTime, &systime);
Alexandre Julliard79b00722009-12-09 18:52:40 +0100466 GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &systime,
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000467 NULL, thisoutput, MAX_PATH);
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100468 strcatW(thisoutput, space);
469 datelen = strlenW(thisoutput);
Alexandre Julliard79b00722009-12-09 18:52:40 +0100470 GetTimeFormatW(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &systime,
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000471 NULL, (thisoutput+datelen), MAX_PATH-datelen);
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100472 strcatW(finaloutput, thisoutput);
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000473 }
474
475 /* 4. Handle 'z' : File length */
476 if (exists &&
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100477 memchrW(firstModifier, 'z', modifierLen) != NULL) {
Francois Gouget7b0cde82007-03-06 14:26:24 +0100478 /* FIXME: Output full 64 bit size (sprintf does not support I64 here) */
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000479 ULONG/*64*/ fullsize = /*(fileInfo.nFileSizeHigh << 32) +*/
480 fileInfo.nFileSizeLow;
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100481 static const WCHAR fmt[] = {'%','u','\0'};
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000482
483 doneModifier = TRUE;
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100484 if (finaloutput[0] != 0x00) strcatW(finaloutput, space);
Alexandre Julliard79b00722009-12-09 18:52:40 +0100485 wsprintfW(thisoutput, fmt, fullsize);
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100486 strcatW(finaloutput, thisoutput);
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000487 }
488
Francois Gouget7b0cde82007-03-06 14:26:24 +0100489 /* 4. Handle 's' : Use short paths (File doesn't have to exist) */
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100490 if (memchrW(firstModifier, 's', modifierLen) != NULL) {
491 if (finaloutput[0] != 0x00) strcatW(finaloutput, space);
Francois Gouget7b0cde82007-03-06 14:26:24 +0100492 /* Don't flag as doneModifier - %~s on its own is processed later */
Alexandre Julliard79b00722009-12-09 18:52:40 +0100493 GetShortPathNameW(outputparam, outputparam, sizeof(outputparam)/sizeof(outputparam[0]));
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000494 }
495
Francois Gouget7b0cde82007-03-06 14:26:24 +0100496 /* 5. Handle 'f' : Fully qualified path (File doesn't have to exist) */
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000497 /* Note this overrides d,p,n,x */
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100498 if (memchrW(firstModifier, 'f', modifierLen) != NULL) {
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000499 doneModifier = TRUE;
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100500 if (finaloutput[0] != 0x00) strcatW(finaloutput, space);
501 strcatW(finaloutput, fullfilename);
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000502 } else {
503
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100504 WCHAR drive[10];
505 WCHAR dir[MAX_PATH];
506 WCHAR fname[MAX_PATH];
507 WCHAR ext[MAX_PATH];
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000508 BOOL doneFileModifier = FALSE;
509
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100510 if (finaloutput[0] != 0x00) strcatW(finaloutput, space);
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000511
512 /* Split into components */
Jason Edmeades409368e2007-02-27 23:19:24 +0000513 WCMD_splitpath(fullfilename, drive, dir, fname, ext);
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000514
515 /* 5. Handle 'd' : Drive Letter */
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100516 if (memchrW(firstModifier, 'd', modifierLen) != NULL) {
517 strcatW(finaloutput, drive);
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000518 doneModifier = TRUE;
519 doneFileModifier = TRUE;
520 }
521
522 /* 6. Handle 'p' : Path */
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100523 if (memchrW(firstModifier, 'p', modifierLen) != NULL) {
524 strcatW(finaloutput, dir);
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000525 doneModifier = TRUE;
526 doneFileModifier = TRUE;
527 }
528
529 /* 7. Handle 'n' : Name */
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100530 if (memchrW(firstModifier, 'n', modifierLen) != NULL) {
531 strcatW(finaloutput, fname);
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000532 doneModifier = TRUE;
533 doneFileModifier = TRUE;
534 }
535
536 /* 8. Handle 'x' : Ext */
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100537 if (memchrW(firstModifier, 'x', modifierLen) != NULL) {
538 strcatW(finaloutput, ext);
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000539 doneModifier = TRUE;
540 doneFileModifier = TRUE;
541 }
542
543 /* If 's' but no other parameter, dump the whole thing */
544 if (!doneFileModifier &&
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100545 memchrW(firstModifier, 's', modifierLen) != NULL) {
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000546 doneModifier = TRUE;
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100547 if (finaloutput[0] != 0x00) strcatW(finaloutput, space);
548 strcatW(finaloutput, outputparam);
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000549 }
550 }
551 }
552
553 /* If No other modifier processed, just add in parameter */
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100554 if (!doneModifier) strcpyW(finaloutput, outputparam);
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000555
556 /* Finish by inserting the replacement into the string */
Francois Gougetc48e5e02009-06-15 10:59:31 +0200557 WCMD_strsubstW(*start, lastModifier+1, finaloutput, -1);
Jason Edmeadesfcec39f2007-02-20 17:49:43 +0000558}
Jason Edmeades327d7ef2007-02-23 22:19:25 +0000559
560/*******************************************************************
561 * WCMD_call - processes a batch call statement
562 *
563 * If there is a leading ':', calls within this batch program
564 * otherwise launches another program.
565 */
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100566void WCMD_call (WCHAR *command) {
Jason Edmeades327d7ef2007-02-23 22:19:25 +0000567
568 /* Run other program if no leading ':' */
569 if (*command != ':') {
570 WCMD_run_program(command, 1);
571 } else {
572
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100573 WCHAR gotoLabel[MAX_PATH];
Jason Edmeades327d7ef2007-02-23 22:19:25 +0000574
Jason Edmeadesb8aa5fc2007-06-03 22:07:42 +0100575 strcpyW(gotoLabel, param1);
Jason Edmeades327d7ef2007-02-23 22:19:25 +0000576
577 if (context) {
578
579 LARGE_INTEGER li;
580
581 /* Save the current file position, call the same file,
582 restore position */
583 li.QuadPart = 0;
Francois Gougete46b99d2007-03-04 01:59:59 +0100584 li.u.LowPart = SetFilePointer(context -> h, li.u.LowPart,
585 &li.u.HighPart, FILE_CURRENT);
Jason Edmeades327d7ef2007-02-23 22:19:25 +0000586
587 WCMD_batch (param1, command, 1, gotoLabel, context->h);
588
Francois Gougete46b99d2007-03-04 01:59:59 +0100589 SetFilePointer(context -> h, li.u.LowPart,
590 &li.u.HighPart, FILE_BEGIN);
Jason Edmeades327d7ef2007-02-23 22:19:25 +0000591 } else {
Jason Edmeades5cc492c2007-06-03 22:07:39 +0100592 WCMD_output_asis( WCMD_LoadMessage(WCMD_CALLINSCRIPT));
Jason Edmeades327d7ef2007-02-23 22:19:25 +0000593 }
594 }
595}