1 /*
2 * CMD - Wine-compatible command line interface - built-in functions.
3 *
4 * Copyright (C) 1999 D A Pickles
5 * Copyright (C) 2007 J Edmeades
6 *
7 * 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
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 */
21
22 /*
23 * NOTES:
24 * On entry to each function, global variables quals, param1, param2 contain
25 * the qualifiers (uppercased and concatenated) and parameters entered, with
26 * environment-variable and batch parameter substitution already done.
27 */
28
29 /*
30 * FIXME:
31 * - No support for pipes, shell parameters
32 * - Lots of functionality missing from builtins
33 * - Messages etc need international support
34 */
35
36 #define WIN32_LEAN_AND_MEAN
37
38 #include "wcmd.h"
39 #include <shellapi.h>
40 #include "wine/debug.h"
41
42 WINE_DEFAULT_DEBUG_CHANNEL(cmd);
43
44 static void WCMD_part_execute(CMD_LIST **commands, WCHAR *firstcmd, WCHAR *variable,
45 WCHAR *value, BOOL isIF, BOOL conditionTRUE);
46
47 struct env_stack *saved_environment;
48 struct env_stack *pushd_directories;
49
50 extern HINSTANCE hinst;
51 extern WCHAR inbuilt[][10];
52 extern int echo_mode, verify_mode, defaultColor;
53 extern WCHAR quals[MAX_PATH], param1[MAX_PATH], param2[MAX_PATH];
54 extern BATCH_CONTEXT *context;
55 extern DWORD errorlevel;
56
57 static const WCHAR dotW[] = {'.','\0'};
58 static const WCHAR dotdotW[] = {'.','.','\0'};
59 static const WCHAR slashW[] = {'\\','\0'};
60 static const WCHAR starW[] = {'*','\0'};
61 static const WCHAR equalW[] = {'=','\0'};
62 static const WCHAR fslashW[] = {'/','\0'};
63 static const WCHAR onW[] = {'O','N','\0'};
64 static const WCHAR offW[] = {'O','F','F','\0'};
65 static const WCHAR parmY[] = {'/','Y','\0'};
66 static const WCHAR parmNoY[] = {'/','-','Y','\0'};
67 static const WCHAR nullW[] = {'\0'};
68
69 /**************************************************************************
70 * WCMD_ask_confirm
71 *
72 * Issue a message and ask 'Are you sure (Y/N)', waiting on a valid
73 * answer.
74 *
75 * Returns True if Y (or A) answer is selected
76 * If optionAll contains a pointer, ALL is allowed, and if answered
77 * set to TRUE
78 *
79 */
80 static BOOL WCMD_ask_confirm (WCHAR *message, BOOL showSureText, BOOL *optionAll) {
81
82 WCHAR msgbuffer[MAXSTRING];
83 WCHAR Ybuffer[MAXSTRING];
84 WCHAR Nbuffer[MAXSTRING];
85 WCHAR Abuffer[MAXSTRING];
86 WCHAR answer[MAX_PATH] = {'\0'};
87 DWORD count = 0;
88
89 /* Load the translated 'Are you sure', plus valid answers */
90 LoadString (hinst, WCMD_CONFIRM, msgbuffer, sizeof(msgbuffer)/sizeof(WCHAR));
91 LoadString (hinst, WCMD_YES, Ybuffer, sizeof(Ybuffer)/sizeof(WCHAR));
92 LoadString (hinst, WCMD_NO, Nbuffer, sizeof(Nbuffer)/sizeof(WCHAR));
93 LoadString (hinst, WCMD_ALL, Abuffer, sizeof(Abuffer)/sizeof(WCHAR));
94
95 /* Loop waiting on a Y or N */
96 while (answer[0] != Ybuffer[0] && answer[0] != Nbuffer[0]) {
97 static const WCHAR startBkt[] = {' ','(','\0'};
98 static const WCHAR endBkt[] = {')','?','\0'};
99
100 WCMD_output_asis (message);
101 if (showSureText) {
102 WCMD_output_asis (msgbuffer);
103 }
104 WCMD_output_asis (startBkt);
105 WCMD_output_asis (Ybuffer);
106 WCMD_output_asis (fslashW);
107 WCMD_output_asis (Nbuffer);
108 if (optionAll) {
109 WCMD_output_asis (fslashW);
110 WCMD_output_asis (Abuffer);
111 }
112 WCMD_output_asis (endBkt);
113 WCMD_ReadFile (GetStdHandle(STD_INPUT_HANDLE), answer,
114 sizeof(answer)/sizeof(WCHAR), &count, NULL);
115 answer[0] = toupperW(answer[0]);
116 }
117
118 /* Return the answer */
119 return ((answer[0] == Ybuffer[0]) ||
120 (optionAll && (answer[0] == Abuffer[0])));
121 }
122
123 /****************************************************************************
124 * WCMD_clear_screen
125 *
126 * Clear the terminal screen.
127 */
128
129 void WCMD_clear_screen (void) {
130
131 /* Emulate by filling the screen from the top left to bottom right with
132 spaces, then moving the cursor to the top left afterwards */
133 CONSOLE_SCREEN_BUFFER_INFO consoleInfo;
134 HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
135
136 if (GetConsoleScreenBufferInfo(hStdOut, &consoleInfo))
137 {
138 COORD topLeft;
139 DWORD screenSize;
140
141 screenSize = consoleInfo.dwSize.X * (consoleInfo.dwSize.Y + 1);
142
143 topLeft.X = 0;
144 topLeft.Y = 0;
145 FillConsoleOutputCharacter(hStdOut, ' ', screenSize, topLeft, &screenSize);
146 SetConsoleCursorPosition(hStdOut, topLeft);
147 }
148 }
149
150 /****************************************************************************
151 * WCMD_change_tty
152 *
153 * Change the default i/o device (ie redirect STDin/STDout).
154 */
155
156 void WCMD_change_tty (void) {
157
158 WCMD_output (WCMD_LoadMessage(WCMD_NYI));
159
160 }
161
162 /****************************************************************************
163 * WCMD_copy
164 *
165 * Copy a file or wildcarded set.
166 * FIXME: Add support for a+b+c type syntax
167 */
168
169 void WCMD_copy (void) {
170
171 WIN32_FIND_DATA fd;
172 HANDLE hff;
173 BOOL force, status;
174 WCHAR outpath[MAX_PATH], srcpath[MAX_PATH], copycmd[4];
175 DWORD len;
176 static const WCHAR copyCmdW[] = {'C','O','P','Y','C','M','D','\0'};
177 BOOL copyToDir = FALSE;
178 BOOL copyFromDir = FALSE;
179 WCHAR srcspec[MAX_PATH];
180 DWORD attribs;
181 WCHAR drive[10];
182 WCHAR dir[MAX_PATH];
183 WCHAR fname[MAX_PATH];
184 WCHAR ext[MAX_PATH];
185
186 if (param1[0] == 0x00) {
187 WCMD_output (WCMD_LoadMessage(WCMD_NOARG));
188 return;
189 }
190
191 /* Convert source into full spec */
192 WINE_TRACE("Copy source (supplied): '%s'\n", wine_dbgstr_w(param1));
193 GetFullPathName (param1, sizeof(srcpath)/sizeof(WCHAR), srcpath, NULL);
194 if (srcpath[strlenW(srcpath) - 1] == '\\')
195 srcpath[strlenW(srcpath) - 1] = '\0';
196
197 if ((strchrW(srcpath,'*') == NULL) && (strchrW(srcpath,'?') == NULL)) {
198 attribs = GetFileAttributes(srcpath);
199 } else {
200 attribs = 0;
201 }
202 strcpyW(srcspec, srcpath);
203
204 /* If a directory, then add \* on the end when searching */
205 if (attribs & FILE_ATTRIBUTE_DIRECTORY) {
206 strcatW(srcpath, slashW);
207 copyFromDir = TRUE;
208 strcatW(srcspec, slashW);
209 strcatW(srcspec, starW);
210 } else {
211 WCMD_splitpath(srcpath, drive, dir, fname, ext);
212 strcpyW(srcpath, drive);
213 strcatW(srcpath, dir);
214 }
215
216 WINE_TRACE("Copy source (calculated): path: '%s'\n", wine_dbgstr_w(srcpath));
217
218 /* If no destination supplied, assume current directory */
219 WINE_TRACE("Copy destination (supplied): '%s'\n", wine_dbgstr_w(param2));
220 if (param2[0] == 0x00) {
221 strcpyW(param2, dotW);
222 }
223
224 GetFullPathName (param2, sizeof(outpath)/sizeof(WCHAR), outpath, NULL);
225 if (outpath[strlenW(outpath) - 1] == '\\')
226 outpath[strlenW(outpath) - 1] = '\0';
227 attribs = GetFileAttributes(outpath);
228 if (attribs != INVALID_FILE_ATTRIBUTES && (attribs & FILE_ATTRIBUTE_DIRECTORY)) {
229 strcatW (outpath, slashW);
230 copyToDir = TRUE;
231 }
232 WINE_TRACE("Copy destination (calculated): '%s'(%d)\n",
233 wine_dbgstr_w(outpath), copyToDir);
234
235 /* /-Y has the highest priority, then /Y and finally the COPYCMD env. variable */
236 if (strstrW (quals, parmNoY))
237 force = FALSE;
238 else if (strstrW (quals, parmY))
239 force = TRUE;
240 else {
241 /* By default, we will force the overwrite in batch mode and ask for
242 * confirmation in interactive mode. */
243 force = !!context;
244
245 /* If COPYCMD is set, then we force the overwrite with /Y and ask for
246 * confirmation with /-Y. If COPYCMD is neither of those, then we use the
247 * default behavior. */
248 len = GetEnvironmentVariable (copyCmdW, copycmd, sizeof(copycmd)/sizeof(WCHAR));
249 if (len && len < (sizeof(copycmd)/sizeof(WCHAR))) {
250 if (!lstrcmpiW (copycmd, parmY))
251 force = TRUE;
252 else if (!lstrcmpiW (copycmd, parmNoY))
253 force = FALSE;
254 }
255 }
256
257 /* Loop through all source files */
258 WINE_TRACE("Searching for: '%s'\n", wine_dbgstr_w(srcspec));
259 hff = FindFirstFile (srcspec, &fd);
260 if (hff != INVALID_HANDLE_VALUE) {
261 do {
262 WCHAR outname[MAX_PATH];
263 WCHAR srcname[MAX_PATH];
264 BOOL overwrite = force;
265
266 /* Destination is either supplied filename, or source name in
267 supplied destination directory */
268 strcpyW(outname, outpath);
269 if (copyToDir) strcatW(outname, fd.cFileName);
270 strcpyW(srcname, srcpath);
271 strcatW(srcname, fd.cFileName);
272
273 WINE_TRACE("Copying from : '%s'\n", wine_dbgstr_w(srcname));
274 WINE_TRACE("Copying to : '%s'\n", wine_dbgstr_w(outname));
275
276 /* Skip . and .., and directories */
277 if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
278 overwrite = FALSE;
279 WINE_TRACE("Skipping directories\n");
280 }
281
282 /* Prompt before overwriting */
283 else if (!overwrite) {
284 attribs = GetFileAttributes(outname);
285 if (attribs != INVALID_FILE_ATTRIBUTES) {
286 WCHAR buffer[MAXSTRING];
287 wsprintf(buffer, WCMD_LoadMessage(WCMD_OVERWRITE), outname);
288 overwrite = WCMD_ask_confirm(buffer, FALSE, NULL);
289 }
290 else overwrite = TRUE;
291 }
292
293 /* Do the copy as appropriate */
294 if (overwrite) {
295 status = CopyFile (srcname, outname, FALSE);
296 if (!status) WCMD_print_error ();
297 }
298
299 } while (FindNextFile(hff, &fd) != 0);
300 FindClose (hff);
301 } else {
302 status = ERROR_FILE_NOT_FOUND;
303 WCMD_print_error ();
304 }
305 }
306
307 /****************************************************************************
308 * WCMD_create_dir
309 *
310 * Create a directory.
311 *
312 * this works recursively. so mkdir dir1\dir2\dir3 will create dir1 and dir2 if
313 * they do not already exist.
314 */
315
316 static BOOL create_full_path(WCHAR* path)
317 {
318 int len;
319 WCHAR *new_path;
320 BOOL ret = TRUE;
321
322 new_path = HeapAlloc(GetProcessHeap(),0,(strlenW(path) * sizeof(WCHAR))+1);
323 strcpyW(new_path,path);
324
325 while ((len = strlenW(new_path)) && new_path[len - 1] == '\\')
326 new_path[len - 1] = 0;
327
328 while (!CreateDirectory(new_path,NULL))
329 {
330 WCHAR *slash;
331 DWORD last_error = GetLastError();
332 if (last_error == ERROR_ALREADY_EXISTS)
333 break;
334
335 if (last_error != ERROR_PATH_NOT_FOUND)
336 {
337 ret = FALSE;
338 break;
339 }
340
341 if (!(slash = strrchrW(new_path,'\\')) && ! (slash = strrchrW(new_path,'/')))
342 {
343 ret = FALSE;
344 break;
345 }
346
347 len = slash - new_path;
348 new_path[len] = 0;
349 if (!create_full_path(new_path))
350 {
351 ret = FALSE;
352 break;
353 }
354 new_path[len] = '\\';
355 }
356 HeapFree(GetProcessHeap(),0,new_path);
357 return ret;
358 }
359
360 void WCMD_create_dir (void) {
361
362 if (param1[0] == 0x00) {
363 WCMD_output (WCMD_LoadMessage(WCMD_NOARG));
364 return;
365 }
366 if (!create_full_path(param1)) WCMD_print_error ();
367 }
368
369 /****************************************************************************
370 * WCMD_delete
371 *
372 * Delete a file or wildcarded set.
373 *
374 * Note on /A:
375 * - Testing shows /A is repeatable, eg. /a-r /ar matches all files
376 * - Each set is a pattern, eg /ahr /as-r means
377 * readonly+hidden OR nonreadonly system files
378 * - The '-' applies to a single field, ie /a:-hr means read only
379 * non-hidden files
380 */
381
382 BOOL WCMD_delete (WCHAR *command, BOOL expectDir) {
383
384 int argno = 0;
385 int argsProcessed = 0;
386 WCHAR *argN = command;
387 BOOL foundAny = FALSE;
388 static const WCHAR parmA[] = {'/','A','\0'};
389 static const WCHAR parmQ[] = {'/','Q','\0'};
390 static const WCHAR parmP[] = {'/','P','\0'};
391 static const WCHAR parmS[] = {'/','S','\0'};
392 static const WCHAR parmF[] = {'/','F','\0'};
393
394 /* If not recursing, clear error flag */
395 if (expectDir) errorlevel = 0;
396
397 /* Loop through all args */
398 while (argN) {
399 WCHAR *thisArg = WCMD_parameter (command, argno++, &argN);
400 WCHAR argCopy[MAX_PATH];
401
402 if (argN && argN[0] != '/') {
403
404 WIN32_FIND_DATA fd;
405 HANDLE hff;
406 WCHAR fpath[MAX_PATH];
407 WCHAR *p;
408 BOOL handleParm = TRUE;
409 BOOL found = FALSE;
410 static const WCHAR anyExt[]= {'.','*','\0'};
411
412 strcpyW(argCopy, thisArg);
413 WINE_TRACE("del: Processing arg %s (quals:%s)\n",
414 wine_dbgstr_w(argCopy), wine_dbgstr_w(quals));
415 argsProcessed++;
416
417 /* If filename part of parameter is * or *.*, prompt unless
418 /Q supplied. */
419 if ((strstrW (quals, parmQ) == NULL) && (strstrW (quals, parmP) == NULL)) {
420
421 WCHAR drive[10];
422 WCHAR dir[MAX_PATH];
423 WCHAR fname[MAX_PATH];
424 WCHAR ext[MAX_PATH];
425
426 /* Convert path into actual directory spec */
427 GetFullPathName (argCopy, sizeof(fpath)/sizeof(WCHAR), fpath, NULL);
428 WCMD_splitpath(fpath, drive, dir, fname, ext);
429
430 /* Only prompt for * and *.*, not *a, a*, *.a* etc */
431 if ((strcmpW(fname, starW) == 0) &&
432 (*ext == 0x00 || (strcmpW(ext, anyExt) == 0))) {
433 BOOL ok;
434 WCHAR question[MAXSTRING];
435 static const WCHAR fmt[] = {'%','s',' ','\0'};
436
437 /* Note: Flag as found, to avoid file not found message */
438 found = TRUE;
439
440 /* Ask for confirmation */
441 wsprintf(question, fmt, fpath);
442 ok = WCMD_ask_confirm(question, TRUE, NULL);
443
444 /* Abort if answer is 'N' */
445 if (!ok) continue;
446 }
447 }
448
449 /* First, try to delete in the current directory */
450 hff = FindFirstFile (argCopy, &fd);
451 if (hff == INVALID_HANDLE_VALUE) {
452 handleParm = FALSE;
453 } else {
454 found = TRUE;
455 }
456
457 /* Support del <dirname> by just deleting all files dirname\* */
458 if (handleParm && (strchrW(argCopy,'*') == NULL) && (strchrW(argCopy,'?') == NULL)
459 && (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
460 WCHAR modifiedParm[MAX_PATH];
461 static const WCHAR slashStar[] = {'\\','*','\0'};
462
463 strcpyW(modifiedParm, argCopy);
464 strcatW(modifiedParm, slashStar);
465 FindClose(hff);
466 found = TRUE;
467 WCMD_delete(modifiedParm, FALSE);
468
469 } else if (handleParm) {
470
471 /* Build the filename to delete as <supplied directory>\<findfirst filename> */
472 strcpyW (fpath, argCopy);
473 do {
474 p = strrchrW (fpath, '\\');
475 if (p != NULL) {
476 *++p = '\0';
477 strcatW (fpath, fd.cFileName);
478 }
479 else strcpyW (fpath, fd.cFileName);
480 if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
481 BOOL ok = TRUE;
482 WCHAR *nextA = strstrW (quals, parmA);
483
484 /* Handle attribute matching (/A) */
485 if (nextA != NULL) {
486 ok = FALSE;
487 while (nextA != NULL && !ok) {
488
489 WCHAR *thisA = (nextA+2);
490 BOOL stillOK = TRUE;
491
492 /* Skip optional : */
493 if (*thisA == ':') thisA++;
494
495 /* Parse each of the /A[:]xxx in turn */
496 while (*thisA && *thisA != '/') {
497 BOOL negate = FALSE;
498 BOOL attribute = FALSE;
499
500 /* Match negation of attribute first */
501 if (*thisA == '-') {
502 negate=TRUE;
503 thisA++;
504 }
505
506 /* Match attribute */
507 switch (*thisA) {
508 case 'R': attribute = (fd.dwFileAttributes & FILE_ATTRIBUTE_READONLY);
509 break;
510 case 'H': attribute = (fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN);
511 break;
512 case 'S': attribute = (fd.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM);
513 break;
514 case 'A': attribute = (fd.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE);
515 break;
516 default:
517 WCMD_output (WCMD_LoadMessage(WCMD_SYNTAXERR));
518 }
519
520 /* Now check result, keeping a running boolean about whether it
521 matches all parsed attributes so far */
522 if (attribute && !negate) {
523 stillOK = stillOK;
524 } else if (!attribute && negate) {
525 stillOK = stillOK;
526 } else {
527 stillOK = FALSE;
528 }
529 thisA++;
530 }
531
532 /* Save the running total as the final result */
533 ok = stillOK;
534
535 /* Step on to next /A set */
536 nextA = strstrW (nextA+1, parmA);
537 }
538 }
539
540 /* /P means prompt for each file */
541 if (ok && strstrW (quals, parmP) != NULL) {
542 WCHAR question[MAXSTRING];
543
544 /* Ask for confirmation */
545 wsprintf(question, WCMD_LoadMessage(WCMD_DELPROMPT), fpath);
546 ok = WCMD_ask_confirm(question, FALSE, NULL);
547 }
548
549 /* Only proceed if ok to */
550 if (ok) {
551
552 /* If file is read only, and /F supplied, delete it */
553 if (fd.dwFileAttributes & FILE_ATTRIBUTE_READONLY &&
554 strstrW (quals, parmF) != NULL) {
555 SetFileAttributes(fpath, fd.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY);
556 }
557
558 /* Now do the delete */
559 if (!DeleteFile (fpath)) WCMD_print_error ();
560 }
561
562 }
563 } while (FindNextFile(hff, &fd) != 0);
564 FindClose (hff);
565 }
566
567 /* Now recurse into all subdirectories handling the parameter in the same way */
568 if (strstrW (quals, parmS) != NULL) {
569
570 WCHAR thisDir[MAX_PATH];
571 int cPos;
572
573 WCHAR drive[10];
574 WCHAR dir[MAX_PATH];
575 WCHAR fname[MAX_PATH];
576 WCHAR ext[MAX_PATH];
577
578 /* Convert path into actual directory spec */
579 GetFullPathName (argCopy, sizeof(thisDir)/sizeof(WCHAR), thisDir, NULL);
580 WCMD_splitpath(thisDir, drive, dir, fname, ext);
581
582 strcpyW(thisDir, drive);
583 strcatW(thisDir, dir);
584 cPos = strlenW(thisDir);
585
586 WINE_TRACE("Searching recursively in '%s'\n", wine_dbgstr_w(thisDir));
587
588 /* Append '*' to the directory */
589 thisDir[cPos] = '*';
590 thisDir[cPos+1] = 0x00;
591
592 hff = FindFirstFile (thisDir, &fd);
593
594 /* Remove residual '*' */
595 thisDir[cPos] = 0x00;
596
597 if (hff != INVALID_HANDLE_VALUE) {
598 DIRECTORY_STACK *allDirs = NULL;
599 DIRECTORY_STACK *lastEntry = NULL;
600
601 do {
602 if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
603 (strcmpW(fd.cFileName, dotdotW) != 0) &&
604 (strcmpW(fd.cFileName, dotW) != 0)) {
605
606 DIRECTORY_STACK *nextDir;
607 WCHAR subParm[MAX_PATH];
608
609 /* Work out search parameter in sub dir */
610 strcpyW (subParm, thisDir);
611 strcatW (subParm, fd.cFileName);
612 strcatW (subParm, slashW);
613 strcatW (subParm, fname);
614 strcatW (subParm, ext);
615 WINE_TRACE("Recursive, Adding to search list '%s'\n", wine_dbgstr_w(subParm));
616
617 /* Allocate memory, add to list */
618 nextDir = HeapAlloc(GetProcessHeap(),0,sizeof(DIRECTORY_STACK));
619 if (allDirs == NULL) allDirs = nextDir;
620 if (lastEntry != NULL) lastEntry->next = nextDir;
621 lastEntry = nextDir;
622 nextDir->next = NULL;
623 nextDir->dirName = HeapAlloc(GetProcessHeap(),0,
624 (strlenW(subParm)+1) * sizeof(WCHAR));
625 strcpyW(nextDir->dirName, subParm);
626 }
627 } while (FindNextFile(hff, &fd) != 0);
628 FindClose (hff);
629
630 /* Go through each subdir doing the delete */
631 while (allDirs != NULL) {
632 DIRECTORY_STACK *tempDir;
633
634 tempDir = allDirs->next;
635 found |= WCMD_delete (allDirs->dirName, FALSE);
636
637 HeapFree(GetProcessHeap(),0,allDirs->dirName);
638 HeapFree(GetProcessHeap(),0,allDirs);
639 allDirs = tempDir;
640 }
641 }
642 }
643 /* Keep running total to see if any found, and if not recursing
644 issue error message */
645 if (expectDir) {
646 if (!found) {
647 errorlevel = 1;
648 WCMD_output (WCMD_LoadMessage(WCMD_FILENOTFOUND), argCopy);
649 }
650 }
651 foundAny |= found;
652 }
653 }
654
655 /* Handle no valid args */
656 if (argsProcessed == 0) {
657 WCMD_output (WCMD_LoadMessage(WCMD_NOARG));
658 }
659
660 return foundAny;
661 }
662
663 /****************************************************************************
664 * WCMD_echo
665 *
666 * Echo input to the screen (or not). We don't try to emulate the bugs
667 * in DOS (try typing "ECHO ON AGAIN" for an example).
668 */
669
670 void WCMD_echo (const WCHAR *command) {
671
672 int count;
673
674 if ((command[0] == '.') && (command[1] == 0)) {
675 WCMD_output (newline);
676 return;
677 }
678 if (command[0]==' ')
679 command++;
680 count = strlenW(command);
681 if (count == 0) {
682 if (echo_mode) WCMD_output (WCMD_LoadMessage(WCMD_ECHOPROMPT), onW);
683 else WCMD_output (WCMD_LoadMessage(WCMD_ECHOPROMPT), offW);
684 return;
685 }
686 if (lstrcmpiW(command, onW) == 0) {
687 echo_mode = 1;
688 return;
689 }
690 if (lstrcmpiW(command, offW) == 0) {
691 echo_mode = 0;
692 return;
693 }
694 WCMD_output_asis (command);
695 WCMD_output (newline);
696
697 }
698
699 /**************************************************************************
700 * WCMD_for
701 *
702 * Batch file loop processing.
703 *
704 * On entry: cmdList contains the syntax up to the set
705 * next cmdList and all in that bracket contain the set data
706 * next cmdlist contains the DO cmd
707 * following that is either brackets or && entries (as per if)
708 *
709 */
710
711 void WCMD_for (WCHAR *p, CMD_LIST **cmdList) {
712
713 WIN32_FIND_DATA fd;
714 HANDLE hff;
715 int i;
716 const WCHAR inW[] = {'i', 'n', ' ', '\0'};
717 const WCHAR doW[] = {'d', 'o', ' ', '\0'};
718 CMD_LIST *setStart, *thisSet, *cmdStart, *cmdEnd;
719 WCHAR variable[4];
720 WCHAR *firstCmd;
721 int thisDepth;
722
723 WCHAR *curPos = p;
724 BOOL expandDirs = FALSE;
725 BOOL useNumbers = FALSE;
726 BOOL doRecursive = FALSE;
727 BOOL doFileset = FALSE;
728 LONG numbers[3] = {0,0,0}; /* Defaults to 0 in native */
729 int itemNum;
730 CMD_LIST *thisCmdStart;
731
732
733 /* Handle optional qualifiers (multiple are allowed) */
734 while (*curPos && *curPos == '/') {
735 WINE_TRACE("Processing qualifier at %s\n", wine_dbgstr_w(curPos));
736 curPos++;
737 switch (toupperW(*curPos)) {
738 case 'D': curPos++; expandDirs = TRUE; break;
739 case 'L': curPos++; useNumbers = TRUE; break;
740
741 /* Recursive is special case - /R can have an optional path following it */
742 /* filenamesets are another special case - /F can have an optional options following it */
743 case 'R':
744 case 'F':
745 {
746 BOOL isRecursive = (*curPos == 'R');
747
748 if (isRecursive) doRecursive = TRUE;
749 else doFileset = TRUE;
750
751 /* Skip whitespace */
752 curPos++;
753 while (*curPos && *curPos==' ') curPos++;
754
755 /* Next parm is either qualifier, path/options or variable -
756 only care about it if it is the path/options */
757 if (*curPos && *curPos != '/' && *curPos != '%') {
758 if (isRecursive) WINE_FIXME("/R needs to handle supplied root\n");
759 else WINE_FIXME("/F needs to handle options\n");
760 }
761 break;
762 }
763 default:
764 WINE_FIXME("for qualifier '%c' unhandled\n", *curPos);
765 curPos++;
766 }
767
768 /* Skip whitespace between qualifiers */
769 while (*curPos && *curPos==' ') curPos++;
770 }
771
772 /* Skip whitespace before variable */
773 while (*curPos && *curPos==' ') curPos++;
774
775 /* Ensure line continues with variable */
776 if (!*curPos || *curPos != '%') {
777 WCMD_output (WCMD_LoadMessage(WCMD_SYNTAXERR));
778 return;
779 }
780
781 /* Variable should follow */
782 i = 0;
783 while (curPos[i] && curPos[i]!=' ') i++;
784 memcpy(&variable[0], curPos, i*sizeof(WCHAR));
785 variable[i] = 0x00;
786 WINE_TRACE("Variable identified as %s\n", wine_dbgstr_w(variable));
787 curPos = &curPos[i];
788
789 /* Skip whitespace before IN */
790 while (*curPos && *curPos==' ') curPos++;
791
792 /* Ensure line continues with IN */
793 if (!*curPos || lstrcmpiW (curPos, inW)) {
794 WCMD_output (WCMD_LoadMessage(WCMD_SYNTAXERR));
795 return;
796 }
797
798 /* Save away where the set of data starts and the variable */
799 thisDepth = (*cmdList)->bracketDepth;
800 *cmdList = (*cmdList)->nextcommand;
801 setStart = (*cmdList);
802
803 /* Skip until the close bracket */
804 WINE_TRACE("Searching %p as the set\n", *cmdList);
805 while (*cmdList &&
806 (*cmdList)->command != NULL &&
807 (*cmdList)->bracketDepth > thisDepth) {
808 WINE_TRACE("Skipping %p which is part of the set\n", *cmdList);
809 *cmdList = (*cmdList)->nextcommand;
810 }
811
812 /* Skip the close bracket, if there is one */
813 if (*cmdList) *cmdList = (*cmdList)->nextcommand;
814
815 /* Syntax error if missing close bracket, or nothing following it
816 and once we have the complete set, we expect a DO */
817 WINE_TRACE("Looking for 'do' in %p\n", *cmdList);
818 if ((*cmdList == NULL) ||
819 (CompareString (LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT,
820 (*cmdList)->command, 3, doW, -1) != 2)) {
821 WCMD_output (WCMD_LoadMessage(WCMD_SYNTAXERR));
822 return;
823 }
824
825 /* Save away the starting position for the commands (and offset for the
826 first one */
827 cmdStart = *cmdList;
828 cmdEnd = *cmdList;
829 firstCmd = (*cmdList)->command + 3; /* Skip 'do ' */
830 itemNum = 0;
831
832 thisSet = setStart;
833 /* Loop through all set entries */
834 while (thisSet &&
835 thisSet->command != NULL &&
836 thisSet->bracketDepth >= thisDepth) {
837
838 /* Loop through all entries on the same line */
839 WCHAR *item;
840 WCHAR *itemStart;
841
842 WINE_TRACE("Processing for set %p\n", thisSet);
843 i = 0;
844 while (*(item = WCMD_parameter (thisSet->command, i, &itemStart))) {
845
846 /*
847 * If the parameter within the set has a wildcard then search for matching files
848 * otherwise do a literal substitution.
849 */
850 static const WCHAR wildcards[] = {'*','?','\0'};
851 thisCmdStart = cmdStart;
852
853 itemNum++;
854 WINE_TRACE("Processing for item %d '%s'\n", itemNum, wine_dbgstr_w(item));
855
856 if (!useNumbers && !doFileset) {
857 if (strpbrkW (item, wildcards)) {
858 hff = FindFirstFile (item, &fd);
859 if (hff != INVALID_HANDLE_VALUE) {
860 do {
861 BOOL isDirectory = FALSE;
862
863 if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) isDirectory = TRUE;
864
865 /* Handle as files or dirs appropriately, but ignore . and .. */
866 if (isDirectory == expandDirs &&
867 (strcmpW(fd.cFileName, dotdotW) != 0) &&
868 (strcmpW(fd.cFileName, dotW) != 0))
869 {
870 thisCmdStart = cmdStart;
871 WINE_TRACE("Processing FOR filename %s\n", wine_dbgstr_w(fd.cFileName));
872 WCMD_part_execute (&thisCmdStart, firstCmd, variable,
873 fd.cFileName, FALSE, TRUE);
874 }
875
876 } while (FindNextFile(hff, &fd) != 0);
877 FindClose (hff);
878 }
879 } else {
880 WCMD_part_execute(&thisCmdStart, firstCmd, variable, item, FALSE, TRUE);
881 }
882
883 } else if (useNumbers) {
884 /* Convert the first 3 numbers to signed longs and save */
885 if (itemNum <=3) numbers[itemNum-1] = atolW(item);
886 /* else ignore them! */
887
888 /* Filesets - either a list of files, or a command to run and parse the output */
889 } else if (doFileset && *itemStart != '"') {
890
891 HANDLE input;
892 WCHAR temp_file[MAX_PATH];
893
894 WINE_TRACE("Processing for filespec from item %d '%s'\n", itemNum,
895 wine_dbgstr_w(item));
896
897 /* If backquote or single quote, we need to launch that command
898 and parse the results - use a temporary file */
899 if (*itemStart == '`' || *itemStart == '\'') {
900
901 WCHAR temp_path[MAX_PATH], temp_cmd[MAXSTRING];
902 static const WCHAR redirOut[] = {'>','%','s','\0'};
903 static const WCHAR cmdW[] = {'C','M','D','\0'};
904
905 /* Remove trailing character */
906 itemStart[strlenW(itemStart)-1] = 0x00;
907
908 /* Get temp filename */
909 GetTempPath (sizeof(temp_path)/sizeof(WCHAR), temp_path);
910 GetTempFileName (temp_path, cmdW, 0, temp_file);
911
912 /* Execute program and redirect output */
913 wsprintf (temp_cmd, redirOut, (itemStart+1), temp_file);
914 WCMD_execute (itemStart, temp_cmd, NULL, NULL, NULL);
915
916 /* Open the file, read line by line and process */
917 input = CreateFile (temp_file, GENERIC_READ, FILE_SHARE_READ,
918 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
919 } else {
920
921 /* Open the file, read line by line and process */
922 input = CreateFile (item, GENERIC_READ, FILE_SHARE_READ,
923 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
924 }
925
926 /* Process the input file */
927 if (input == INVALID_HANDLE_VALUE) {
928 WCMD_print_error ();
929 WCMD_output (WCMD_LoadMessage(WCMD_READFAIL), item);
930 errorlevel = 1;
931 return; /* FOR loop aborts at first failure here */
932
933 } else {
934
935 WCHAR buffer[MAXSTRING] = {'\0'};
936 WCHAR *where, *parm;
937
938 while (WCMD_fgets (buffer, sizeof(buffer)/sizeof(WCHAR), input)) {
939
940 /* Skip blank lines*/
941 parm = WCMD_parameter (buffer, 0, &where);
942 WINE_TRACE("Parsed parameter: %s from %s\n", wine_dbgstr_w(parm),
943 wine_dbgstr_w(buffer));
944
945 if (where) {
946 /* FIXME: The following should be moved into its own routine and
947 reused for the string literal parsing below */
948 thisCmdStart = cmdStart;
949 WCMD_part_execute(&thisCmdStart, firstCmd, variable, parm, FALSE, TRUE);
950 cmdEnd = thisCmdStart;
951 }
952
953 buffer[0] = 0x00;
954
955 }
956 CloseHandle (input);
957 }
958
959 /* Delete the temporary file */
960 if (*itemStart == '`' || *itemStart == '\'') {
961 DeleteFile (temp_file);
962 }
963
964 /* Filesets - A string literal */
965 } else if (doFileset && *itemStart == '"') {
966 WCHAR buffer[MAXSTRING] = {'\0'};
967 WCHAR *where, *parm;
968
969 /* Skip blank lines, and re-extract parameter now string has quotes removed */
970 strcpyW(buffer, item);
971 parm = WCMD_parameter (buffer, 0, &where);
972 WINE_TRACE("Parsed parameter: %s from %s\n", wine_dbgstr_w(parm),
973 wine_dbgstr_w(buffer));
974
975 if (where) {
976 /* FIXME: The following should be moved into its own routine and
977 reused for the string literal parsing below */
978 thisCmdStart = cmdStart;
979 WCMD_part_execute(&thisCmdStart, firstCmd, variable, parm, FALSE, TRUE);
980 cmdEnd = thisCmdStart;
981 }
982 }
983
984 WINE_TRACE("Post-command, cmdEnd = %p\n", cmdEnd);
985 cmdEnd = thisCmdStart;
986 i++;
987 }
988
989 /* Move onto the next set line */
990 thisSet = thisSet->nextcommand;
991 }
992
993 /* If /L is provided, now run the for loop */
994 if (useNumbers) {
995 WCHAR thisNum[20];
996 static const WCHAR fmt[] = {'%','d','\0'};
997
998 WINE_TRACE("FOR /L provided range from %d to %d step %d\n",
999 numbers[0], numbers[2], numbers[1]);
1000 for (i=numbers[0];
1001 (numbers[1]<0)? i>numbers[2] : i<numbers[2];
1002 i=i + numbers[1]) {
1003
1004 sprintfW(thisNum, fmt, i);
1005 WINE_TRACE("Processing FOR number %s\n", wine_dbgstr_w(thisNum));
1006
1007 thisCmdStart = cmdStart;
1008 WCMD_part_execute(&thisCmdStart, firstCmd, variable, thisNum, FALSE, TRUE);
1009 cmdEnd = thisCmdStart;
1010 }
1011 }
1012
1013 /* When the loop ends, either something like a GOTO or EXIT /b has terminated
1014 all processing, OR it should be pointing to the end of && processing OR
1015 it should be pointing at the NULL end of bracket for the DO. The return
1016 value needs to be the NEXT command to execute, which it either is, or
1017 we need to step over the closing bracket */
1018 *cmdList = cmdEnd;
1019 if (cmdEnd && cmdEnd->command == NULL) *cmdList = cmdEnd->nextcommand;
1020 }
1021
1022
1023 /*****************************************************************************
1024 * WCMD_part_execute
1025 *
1026 * Execute a command, and any && or bracketed follow on to the command. The
1027 * first command to be executed may not be at the front of the
1028 * commands->thiscommand string (eg. it may point after a DO or ELSE)
1029 */
1030 void WCMD_part_execute(CMD_LIST **cmdList, WCHAR *firstcmd, WCHAR *variable,
1031 WCHAR *value, BOOL isIF, BOOL conditionTRUE) {
1032
1033 CMD_LIST *curPosition = *cmdList;
1034 int myDepth = (*cmdList)->bracketDepth;
1035
1036 WINE_TRACE("cmdList(%p), firstCmd(%p), with '%s'='%s', doIt(%d)\n",
1037 cmdList, wine_dbgstr_w(firstcmd),
1038 wine_dbgstr_w(variable), wine_dbgstr_w(value),
1039 conditionTRUE);
1040
1041 /* Skip leading whitespace between condition and the command */
1042 while (firstcmd && *firstcmd && (*firstcmd==' ' || *firstcmd=='\t')) firstcmd++;
1043
1044 /* Process the first command, if there is one */
1045 if (conditionTRUE && firstcmd && *firstcmd) {
1046 WCHAR *command = WCMD_strdupW(firstcmd);
1047 WCMD_execute (firstcmd, (*cmdList)->redirects, variable, value, cmdList);
1048 HeapFree(GetProcessHeap(), 0, command);
1049 }
1050
1051
1052 /* If it didn't move the position, step to next command */
1053 if (curPosition == *cmdList) *cmdList = (*cmdList)->nextcommand;
1054
1055 /* Process any other parts of the command */
1056 if (*cmdList) {
1057 BOOL processThese = TRUE;
1058
1059 if (isIF) processThese = conditionTRUE;
1060
1061 while (*cmdList) {
1062 const WCHAR ifElse[] = {'e','l','s','e',' ','\0'};
1063
1064 /* execute all appropriate commands */
1065 curPosition = *cmdList;
1066
1067 WINE_TRACE("Processing cmdList(%p) - delim(%d) bd(%d / %d)\n",
1068 *cmdList,
1069 (*cmdList)->prevDelim,
1070 (*cmdList)->bracketDepth, myDepth);
1071
1072 /* Execute any statements appended to the line */
1073 /* FIXME: Only if previous call worked for && or failed for || */
1074 if ((*cmdList)->prevDelim == CMD_ONFAILURE ||
1075 (*cmdList)->prevDelim != CMD_ONSUCCESS) {
1076 if (processThese && (*cmdList)->command) {
1077 WCMD_execute ((*cmdList)->command, (*cmdList)->redirects, variable,
1078 value, cmdList);
1079 }
1080 if (curPosition == *cmdList) *cmdList = (*cmdList)->nextcommand;
1081
1082 /* Execute any appended to the statement with (...) */
1083 } else if ((*cmdList)->bracketDepth > myDepth) {
1084 if (processThese) {
1085 *cmdList = WCMD_process_commands(*cmdList, TRUE, variable, value);
1086 WINE_TRACE("Back from processing commands, (next = %p)\n", *cmdList);
1087 }
1088 if (curPosition == *cmdList) *cmdList = (*cmdList)->nextcommand;
1089
1090 /* End of the command - does 'ELSE ' follow as the next command? */
1091 } else {
1092 if (isIF && CompareString (LOCALE_USER_DEFAULT,
1093 NORM_IGNORECASE | SORT_STRINGSORT,
1094 (*cmdList)->command, 5, ifElse, -1) == 2) {
1095
1096 /* Swap between if and else processing */
1097 processThese = !processThese;
1098
1099 /* Process the ELSE part */
1100 if (processThese) {
1101 WCHAR *cmd = ((*cmdList)->command) + strlenW(ifElse);
1102
1103 /* Skip leading whitespace between condition and the command */
1104 while (*cmd && (*cmd==' ' || *cmd=='\t')) cmd++;
1105 if (*cmd) {
1106 WCMD_execute (cmd, (*cmdList)->redirects, variable, value, cmdList);
1107 }
1108 }
1109 if (curPosition == *cmdList) *cmdList = (*cmdList)->nextcommand;
1110 } else {
1111 WINE_TRACE("Found end of this IF statement (next = %p)\n", *cmdList);
1112 break;
1113 }
1114 }
1115 }
1116 }
1117 return;
1118 }
1119
1120 /**************************************************************************
1121 * WCMD_give_help
1122 *
1123 * Simple on-line help. Help text is stored in the resource file.
1124 */
1125
1126 void WCMD_give_help (WCHAR *command) {
1127
1128 int i;
1129
1130 command = WCMD_strtrim_leading_spaces(command);
1131 if (strlenW(command) == 0) {
1132 WCMD_output_asis (WCMD_LoadMessage(WCMD_ALLHELP));
1133 }
1134 else {
1135 for (i=0; i<=WCMD_EXIT; i++) {
1136 if (CompareString (LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT,
1137 param1, -1, inbuilt[i], -1) == 2) {
1138 WCMD_output_asis (WCMD_LoadMessage(i));
1139 return;
1140 }
1141 }
1142 WCMD_output (WCMD_LoadMessage(WCMD_NOCMDHELP), param1);
1143 }
1144 return;
1145 }
1146
1147 /****************************************************************************
1148 * WCMD_go_to
1149 *
1150 * Batch file jump instruction. Not the most efficient algorithm ;-)
1151 * Prints error message if the specified label cannot be found - the file pointer is
1152 * then at EOF, effectively stopping the batch file.
1153 * FIXME: DOS is supposed to allow labels with spaces - we don't.
1154 */
1155
1156 void WCMD_goto (CMD_LIST **cmdList) {
1157
1158 WCHAR string[MAX_PATH];
1159
1160 /* Do not process any more parts of a processed multipart or multilines command */
1161 if (cmdList) *cmdList = NULL;
1162
1163 if (param1[0] == 0x00) {
1164 WCMD_output (WCMD_LoadMessage(WCMD_NOARG));
1165 return;
1166 }
1167 if (context != NULL) {
1168 WCHAR *paramStart = param1;
1169 static const WCHAR eofW[] = {':','e','o','f','\0'};
1170
1171 /* Handle special :EOF label */
1172 if (lstrcmpiW (eofW, param1) == 0) {
1173 context -> skip_rest = TRUE;
1174 return;
1175 }
1176
1177 /* Support goto :label as well as goto label */
1178 if (*paramStart == ':') paramStart++;
1179
1180 SetFilePointer (context -> h, 0, NULL, FILE_BEGIN);
1181 while (WCMD_fgets (string, sizeof(string)/sizeof(WCHAR), context -> h)) {
1182 if ((string[0] == ':') && (lstrcmpiW (&string[1], paramStart) == 0)) return;
1183 }
1184 WCMD_output (WCMD_LoadMessage(WCMD_NOTARGET));
1185 }
1186 return;
1187 }
1188
1189 /*****************************************************************************
1190 * WCMD_pushd
1191 *
1192 * Push a directory onto the stack
1193 */
1194
1195 void WCMD_pushd (WCHAR *command) {
1196 struct env_stack *curdir;
1197 WCHAR *thisdir;
1198 static const WCHAR parmD[] = {'/','D','\0'};
1199
1200 if (strchrW(command, '/') != NULL) {
1201 SetLastError(ERROR_INVALID_PARAMETER);
1202 WCMD_print_error();
1203 return;
1204 }
1205
1206 curdir = LocalAlloc (LMEM_FIXED, sizeof (struct env_stack));
1207 thisdir = LocalAlloc (LMEM_FIXED, 1024 * sizeof(WCHAR));
1208 if( !curdir || !thisdir ) {
1209 LocalFree(curdir);
1210 LocalFree(thisdir);
1211 WINE_ERR ("out of memory\n");
1212 return;
1213 }
1214
1215 /* Change directory using CD code with /D parameter */
1216 strcpyW(quals, parmD);
1217 GetCurrentDirectoryW (1024, thisdir);
1218 errorlevel = 0;
1219 WCMD_setshow_default(command);
1220 if (errorlevel) {
1221 LocalFree(curdir);
1222 LocalFree(thisdir);
1223 return;
1224 } else {
1225 curdir -> next = pushd_directories;
1226 curdir -> strings = thisdir;
1227 if (pushd_directories == NULL) {
1228 curdir -> u.stackdepth = 1;
1229 } else {
1230 curdir -> u.stackdepth = pushd_directories -> u.stackdepth + 1;
1231 }
1232 pushd_directories = curdir;
1233 }
1234 }
1235
1236
1237 /*****************************************************************************
1238 * WCMD_popd
1239 *
1240 * Pop a directory from the stack
1241 */
1242
1243 void WCMD_popd (void) {
1244 struct env_stack *temp = pushd_directories;
1245
1246 if (!pushd_directories)
1247 return;
1248
1249 /* pop the old environment from the stack, and make it the current dir */
1250 pushd_directories = temp->next;
1251 SetCurrentDirectoryW(temp->strings);
1252 LocalFree (temp->strings);
1253 LocalFree (temp);
1254 }
1255
1256 /****************************************************************************
1257 * WCMD_if
1258 *
1259 * Batch file conditional.
1260 *
1261 * On entry, cmdlist will point to command containing the IF, and optionally
1262 * the first command to execute (if brackets not found)
1263 * If &&'s were found, this may be followed by a record flagged as isAmpersand
1264 * If ('s were found, execute all within that bracket
1265 * Command may optionally be followed by an ELSE - need to skip instructions
1266 * in the else using the same logic
1267 *
1268 * FIXME: Much more syntax checking needed!
1269 */
1270
1271 void WCMD_if (WCHAR *p, CMD_LIST **cmdList) {
1272
1273 int negate = 0, test = 0;
1274 WCHAR condition[MAX_PATH], *command, *s;
1275 static const WCHAR notW[] = {'n','o','t','\0'};
1276 static const WCHAR errlvlW[] = {'e','r','r','o','r','l','e','v','e','l','\0'};
1277 static const WCHAR existW[] = {'e','x','i','s','t','\0'};
1278 static const WCHAR defdW[] = {'d','e','f','i','n','e','d','\0'};
1279 static const WCHAR eqeqW[] = {'=','=','\0'};
1280
1281 if (!lstrcmpiW (param1, notW)) {
1282 negate = 1;
1283 strcpyW (condition, param2);
1284 }
1285 else {
1286 strcpyW (condition, param1);
1287 }
1288 WINE_TRACE("Condition: %s\n", wine_dbgstr_w(condition));
1289
1290 if (!lstrcmpiW (condition, errlvlW)) {
1291 if (errorlevel >= atoiW(WCMD_parameter (p, 1+negate, NULL))) test = 1;
1292 WCMD_parameter (p, 2+negate, &command);
1293 }
1294 else if (!lstrcmpiW (condition, existW)) {
1295 if (GetFileAttributes(WCMD_parameter (p, 1+negate, NULL)) != INVALID_FILE_ATTRIBUTES) {
1296 test = 1;
1297 }
1298 WCMD_parameter (p, 2+negate, &command);
1299 }
1300 else if (!lstrcmpiW (condition, defdW)) {
1301 if (GetEnvironmentVariable(WCMD_parameter (p, 1+negate, NULL), NULL, 0) > 0) {
1302 test = 1;
1303 }
1304 WCMD_parameter (p, 2+negate, &command);
1305 }
1306 else if ((s = strstrW (p, eqeqW))) {
1307 s += 2;
1308 if (!lstrcmpiW (condition, WCMD_parameter (s, 0, NULL))) test = 1;
1309 WCMD_parameter (s, 1, &command);
1310 }
1311 else {
1312 WCMD_output (WCMD_LoadMessage(WCMD_SYNTAXERR));
1313 return;
1314 }
1315
1316 /* Process rest of IF statement which is on the same line
1317 Note: This may process all or some of the cmdList (eg a GOTO) */
1318 WCMD_part_execute(cmdList, command, NULL, NULL, TRUE, (test != negate));
1319 }
1320
1321 /****************************************************************************
1322 * WCMD_move
1323 *
1324 * Move a file, directory tree or wildcarded set of files.
1325 */
1326
1327 void WCMD_move (void) {
1328
1329 int status;
1330 WIN32_FIND_DATA fd;
1331 HANDLE hff;
1332 WCHAR input[MAX_PATH];
1333 WCHAR output[MAX_PATH];
1334 WCHAR drive[10];
1335 WCHAR dir[MAX_PATH];
1336 WCHAR fname[MAX_PATH];
1337 WCHAR ext[MAX_PATH];
1338
1339 if (param1[0] == 0x00) {
1340 WCMD_output (WCMD_LoadMessage(WCMD_NOARG));
1341 return;
1342 }
1343
1344 /* If no destination supplied, assume current directory */
1345 if (param2[0] == 0x00) {
1346 strcpyW(param2, dotW);
1347 }
1348
1349 /* If 2nd parm is directory, then use original filename */
1350 /* Convert partial path to full path */
1351 GetFullPathName (param1, sizeof(input)/sizeof(WCHAR), input, NULL);
1352 GetFullPathName (param2, sizeof(output)/sizeof(WCHAR), output, NULL);
1353 WINE_TRACE("Move from '%s'('%s') to '%s'\n", wine_dbgstr_w(input),
1354 wine_dbgstr_w(param1), wine_dbgstr_w(output));
1355
1356 /* Split into components */
1357 WCMD_splitpath(input, drive, dir, fname, ext);
1358
1359 hff = FindFirstFile (input, &fd);
1360 while (hff != INVALID_HANDLE_VALUE) {
1361 WCHAR dest[MAX_PATH];
1362 WCHAR src[MAX_PATH];
1363 DWORD attribs;
1364
1365 WINE_TRACE("Processing file '%s'\n", wine_dbgstr_w(fd.cFileName));
1366
1367 /* Build src & dest name */
1368 strcpyW(src, drive);
1369 strcatW(src, dir);
1370
1371 /* See if dest is an existing directory */
1372 attribs = GetFileAttributes(output);
1373 if (attribs != INVALID_FILE_ATTRIBUTES &&
1374 (attribs & FILE_ATTRIBUTE_DIRECTORY)) {
1375 strcpyW(dest, output);
1376 strcatW(dest, slashW);
1377 strcatW(dest, fd.cFileName);
1378 } else {
1379 strcpyW(dest, output);
1380 }
1381
1382 strcatW(src, fd.cFileName);
1383
1384 WINE_TRACE("Source '%s'\n", wine_dbgstr_w(src));
1385 WINE_TRACE("Dest '%s'\n", wine_dbgstr_w(dest));
1386
1387 /* Check if file is read only, otherwise move it */
1388 attribs = GetFileAttributes(src);
1389 if ((attribs != INVALID_FILE_ATTRIBUTES) &&
1390 (attribs & FILE_ATTRIBUTE_READONLY)) {
1391 SetLastError(ERROR_ACCESS_DENIED);
1392 status = 0;
1393 } else {
1394 BOOL ok = TRUE;
1395
1396 /* If destination exists, prompt unless /Y supplied */
1397 if (GetFileAttributes(dest) != INVALID_FILE_ATTRIBUTES) {
1398 BOOL force = FALSE;
1399 WCHAR copycmd[MAXSTRING];
1400 int len;
1401
1402 /* /-Y has the highest priority, then /Y and finally the COPYCMD env. variable */
1403 if (strstrW (quals, parmNoY))
1404 force = FALSE;
1405 else if (strstrW (quals, parmY))
1406 force = TRUE;
1407 else {
1408 const WCHAR copyCmdW[] = {'C','O','P','Y','C','M','D','\0'};
1409 len = GetEnvironmentVariable (copyCmdW, copycmd, sizeof(copycmd)/sizeof(WCHAR));
1410 force = (len && len < (sizeof(copycmd)/sizeof(WCHAR))
1411 && ! lstrcmpiW (copycmd, parmY));
1412 }
1413
1414 /* Prompt if overwriting */
1415 if (!force) {
1416 WCHAR question[MAXSTRING];
1417 WCHAR yesChar[10];
1418
1419 strcpyW(yesChar, WCMD_LoadMessage(WCMD_YES));
1420
1421 /* Ask for confirmation */
1422 wsprintf(question, WCMD_LoadMessage(WCMD_OVERWRITE), dest);
1423 ok = WCMD_ask_confirm(question, FALSE, NULL);
1424
1425 /* So delete the destination prior to the move */
1426 if (ok) {
1427 if (!DeleteFile (dest)) {
1428 WCMD_print_error ();
1429 errorlevel = 1;
1430 ok = FALSE;
1431 }
1432 }
1433 }
1434 }
1435
1436 if (ok) {
1437 status = MoveFile (src, dest);
1438 } else {
1439 status = 1; /* Anything other than 0 to prevent error msg below */
1440 }
1441 }
1442
1443 if (!status) {
1444 WCMD_print_error ();
1445 errorlevel = 1;
1446 }
1447
1448 /* Step on to next match */
1449 if (FindNextFile(hff, &fd) == 0) {
1450 FindClose(hff);
1451 hff = INVALID_HANDLE_VALUE;
1452 break;
1453 }
1454 }
1455 }
1456
1457 /****************************************************************************
1458 * WCMD_pause
1459 *
1460 * Wait for keyboard input.
1461 */
1462
1463 void WCMD_pause (void) {
1464
1465 DWORD count;
1466 WCHAR string[32];
1467
1468 WCMD_output (anykey);
1469 WCMD_ReadFile (GetStdHandle(STD_INPUT_HANDLE), string,
1470 sizeof(string)/sizeof(WCHAR), &count, NULL);
1471 }
1472
1473 /****************************************************************************
1474 * WCMD_remove_dir
1475 *
1476 * Delete a directory.
1477 */
1478
1479 void WCMD_remove_dir (WCHAR *command) {
1480
1481 int argno = 0;
1482 int argsProcessed = 0;
1483 WCHAR *argN = command;
1484 static const WCHAR parmS[] = {'/','S','\0'};
1485 static const WCHAR parmQ[] = {'/','Q','\0'};
1486
1487 /* Loop through all args */
1488 while (argN) {
1489 WCHAR *thisArg = WCMD_parameter (command, argno++, &argN);
1490 if (argN && argN[0] != '/') {
1491 WINE_TRACE("rd: Processing arg %s (quals:%s)\n", wine_dbgstr_w(thisArg),
1492 wine_dbgstr_w(quals));
1493 argsProcessed++;
1494
1495 /* If subdirectory search not supplied, just try to remove
1496 and report error if it fails (eg if it contains a file) */
1497 if (strstrW (quals, parmS) == NULL) {
1498 if (!RemoveDirectory (thisArg)) WCMD_print_error ();
1499
1500 /* Otherwise use ShFileOp to recursively remove a directory */
1501 } else {
1502
1503 SHFILEOPSTRUCT lpDir;
1504
1505 /* Ask first */
1506 if (strstrW (quals, parmQ) == NULL) {
1507 BOOL ok;
1508 WCHAR question[MAXSTRING];
1509 static const WCHAR fmt[] = {'%','s',' ','\0'};
1510
1511 /* Ask for confirmation */
1512 wsprintf(question, fmt, thisArg);
1513 ok = WCMD_ask_confirm(question, TRUE, NULL);
1514
1515 /* Abort if answer is 'N' */
1516 if (!ok) return;
1517 }
1518
1519 /* Do the delete */
1520 lpDir.hwnd = NULL;
1521 lpDir.pTo = NULL;
1522 lpDir.pFrom = thisArg;
1523 lpDir.fFlags = FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI;
1524 lpDir.wFunc = FO_DELETE;
1525 if (SHFileOperation(&lpDir)) WCMD_print_error ();
1526 }
1527 }
1528 }
1529
1530 /* Handle no valid args */
1531 if (argsProcessed == 0) {
1532 WCMD_output (WCMD_LoadMessage(WCMD_NOARG));
1533 return;
1534 }
1535
1536 }
1537
1538 /****************************************************************************
1539 * WCMD_rename
1540 *
1541 * Rename a file.
1542 */
1543
1544 void WCMD_rename (void) {
1545
1546 int status;
1547 HANDLE hff;
1548 WIN32_FIND_DATA fd;
1549 WCHAR input[MAX_PATH];
1550 WCHAR *dotDst = NULL;
1551 WCHAR drive[10];
1552 WCHAR dir[MAX_PATH];
1553 WCHAR fname[MAX_PATH];
1554 WCHAR ext[MAX_PATH];
1555 DWORD attribs;
1556
1557 errorlevel = 0;
1558
1559 /* Must be at least two args */
1560 if (param1[0] == 0x00 || param2[0] == 0x00) {
1561 WCMD_output (WCMD_LoadMessage(WCMD_NOARG));
1562 errorlevel = 1;
1563 return;
1564 }
1565
1566 /* Destination cannot contain a drive letter or directory separator */
1567 if ((strchrW(param1,':') != NULL) || (strchrW(param1,'\\') != NULL)) {
1568 SetLastError(ERROR_INVALID_PARAMETER);
1569 WCMD_print_error();
1570 errorlevel = 1;
1571 return;
1572 }
1573
1574 /* Convert partial path to full path */
1575 GetFullPathName (param1, sizeof(input)/sizeof(WCHAR), input, NULL);
1576 WINE_TRACE("Rename from '%s'('%s') to '%s'\n", wine_dbgstr_w(input),
1577 wine_dbgstr_w(param1), wine_dbgstr_w(param2));
1578 dotDst = strchrW(param2, '.');
1579
1580 /* Split into components */
1581 WCMD_splitpath(input, drive, dir, fname, ext);
1582
1583 hff = FindFirstFile (input, &fd);
1584 while (hff != INVALID_HANDLE_VALUE) {
1585 WCHAR dest[MAX_PATH];
1586 WCHAR src[MAX_PATH];
1587 WCHAR *dotSrc = NULL;
1588 int dirLen;
1589
1590 WINE_TRACE("Processing file '%s'\n", wine_dbgstr_w(fd.cFileName));
1591
1592 /* FIXME: If dest name or extension is *, replace with filename/ext
1593 part otherwise use supplied name. This supports:
1594 ren *.fred *.jim
1595 ren jim.* fred.* etc
1596 However, windows has a more complex algorithm supporting eg
1597 ?'s and *'s mid name */
1598 dotSrc = strchrW(fd.cFileName, '.');
1599
1600 /* Build src & dest name */
1601 strcpyW(src, drive);
1602 strcatW(src, dir);
1603 strcpyW(dest, src);
1604 dirLen = strlenW(src);
1605 strcatW(src, fd.cFileName);
1606
1607 /* Build name */
1608 if (param2[0] == '*') {
1609 strcatW(dest, fd.cFileName);
1610 if (dotSrc) dest[dirLen + (dotSrc - fd.cFileName)] = 0x00;
1611 } else {
1612 strcatW(dest, param2);
1613 if (dotDst) dest[dirLen + (dotDst - param2)] = 0x00;
1614 }
1615
1616 /* Build Extension */
1617 if (dotDst && (*(dotDst+1)=='*')) {
1618 if (dotSrc) strcatW(dest, dotSrc);
1619 } else if (dotDst) {
1620 if (dotDst) strcatW(dest, dotDst);
1621 }
1622
1623 WINE_TRACE("Source '%s'\n", wine_dbgstr_w(src));
1624 WINE_TRACE("Dest '%s'\n", wine_dbgstr_w(dest));
1625
1626 /* Check if file is read only, otherwise move it */
1627 attribs = GetFileAttributes(src);
1628 if ((attribs != INVALID_FILE_ATTRIBUTES) &&
1629 (attribs & FILE_ATTRIBUTE_READONLY)) {
1630 SetLastError(ERROR_ACCESS_DENIED);
1631 status = 0;
1632 } else {
1633 status = MoveFile (src, dest);
1634 }
1635
1636 if (!status) {
1637 WCMD_print_error ();
1638 errorlevel = 1;
1639 }
1640
1641 /* Step on to next match */
1642 if (FindNextFile(hff, &fd) == 0) {
1643 FindClose(hff);
1644 hff = INVALID_HANDLE_VALUE;
1645 break;
1646 }
1647 }
1648 }
1649
1650 /*****************************************************************************
1651 * WCMD_dupenv
1652 *
1653 * Make a copy of the environment.
1654 */
1655 static WCHAR *WCMD_dupenv( const WCHAR *env )
1656 {
1657 WCHAR *env_copy;
1658 int len;
1659
1660 if( !env )
1661 return NULL;
1662
1663 len = 0;
1664 while ( env[len] )
1665 len += (strlenW(&env[len]) + 1);
1666
1667 env_copy = LocalAlloc (LMEM_FIXED, (len+1) * sizeof (WCHAR) );
1668 if (!env_copy)
1669 {
1670 WINE_ERR("out of memory\n");
1671 return env_copy;
1672 }
1673 memcpy (env_copy, env, len*sizeof (WCHAR));
1674 env_copy[len] = 0;
1675
1676 return env_copy;
1677 }
1678
1679 /*****************************************************************************
1680 * WCMD_setlocal
1681 *
1682 * setlocal pushes the environment onto a stack
1683 * Save the environment as unicode so we don't screw anything up.
1684 */
1685 void WCMD_setlocal (const WCHAR *s) {
1686 WCHAR *env;
1687 struct env_stack *env_copy;
1688 WCHAR cwd[MAX_PATH];
1689
1690 /* DISABLEEXTENSIONS ignored */
1691
1692 env_copy = LocalAlloc (LMEM_FIXED, sizeof (struct env_stack));
1693 if( !env_copy )
1694 {
1695 WINE_ERR ("out of memory\n");
1696 return;
1697 }
1698
1699 env = GetEnvironmentStringsW ();
1700
1701 env_copy->strings = WCMD_dupenv (env);
1702 if (env_copy->strings)
1703 {
1704 env_copy->next = saved_environment;
1705 saved_environment = env_copy;
1706
1707 /* Save the current drive letter */
1708 GetCurrentDirectory (MAX_PATH, cwd);
1709 env_copy->u.cwd = cwd[0];
1710 }
1711 else
1712 LocalFree (env_copy);
1713
1714 FreeEnvironmentStringsW (env);
1715
1716 }
1717
1718 /*****************************************************************************
1719 * WCMD_endlocal
1720 *
1721 * endlocal pops the environment off a stack
1722 * Note: When searching for '=', search from WCHAR position 1, to handle
1723 * special internal environment variables =C:, =D: etc
1724 */
1725 void WCMD_endlocal (void) {
1726 WCHAR *env, *old, *p;
1727 struct env_stack *temp;
1728 int len, n;
1729
1730 if (!saved_environment)
1731 return;
1732
1733 /* pop the old environment from the stack */
1734 temp = saved_environment;
1735 saved_environment = temp->next;
1736
1737 /* delete the current environment, totally */
1738 env = GetEnvironmentStringsW ();
1739 old = WCMD_dupenv (GetEnvironmentStringsW ());
1740 len = 0;
1741 while (old[len]) {
1742 n = strlenW(&old[len]) + 1;
1743 p = strchrW(&old[len] + 1, '=');
1744 if (p)
1745 {
1746 *p++ = 0;
1747 SetEnvironmentVariableW (&old[len], NULL);
1748 }
1749 len += n;
1750 }
1751 LocalFree (old);
1752 FreeEnvironmentStringsW (env);
1753
1754 /* restore old environment */
1755 env = temp->strings;
1756 len = 0;
1757 while (env[len]) {
1758 n = strlenW(&env[len]) + 1;
1759 p = strchrW(&env[len] + 1, '=');
1760 if (p)
1761 {
1762 *p++ = 0;
1763 SetEnvironmentVariableW (&env[len], p);
1764 }
1765 len += n;
1766 }
1767
1768 /* Restore current drive letter */
1769 if (IsCharAlpha(temp->u.cwd)) {
1770 WCHAR envvar[4];
1771 WCHAR cwd[MAX_PATH];
1772 static const WCHAR fmt[] = {'=','%','c',':','\0'};
1773
1774 wsprintf(envvar, fmt, temp->u.cwd);
1775 if (GetEnvironmentVariable(envvar, cwd, MAX_PATH)) {
1776 WINE_TRACE("Resetting cwd to %s\n", wine_dbgstr_w(cwd));
1777 SetCurrentDirectory(cwd);
1778 }
1779 }
1780
1781 LocalFree (env);
1782 LocalFree (temp);
1783 }
1784
1785 /*****************************************************************************
1786 * WCMD_setshow_attrib
1787 *
1788 * Display and optionally sets DOS attributes on a file or directory
1789 *
1790 */
1791
1792 void WCMD_setshow_attrib (void) {
1793
1794 DWORD count;
1795 HANDLE hff;
1796 WIN32_FIND_DATA fd;
1797 WCHAR flags[9] = {' ',' ',' ',' ',' ',' ',' ',' ','\0'};
1798 WCHAR *name = param1;
1799 DWORD attrib_set=0;
1800 DWORD attrib_clear=0;
1801
1802 if (param1[0] == '+' || param1[0] == '-') {
1803 DWORD attrib = 0;
1804 /* FIXME: the real cmd can handle many more than two args; this should be in a loop */
1805 switch (param1[1]) {
1806 case 'H': case 'h': attrib |= FILE_ATTRIBUTE_HIDDEN; break;
1807 case 'S': case 's': attrib |= FILE_ATTRIBUTE_SYSTEM; break;
1808 case 'R': case 'r': attrib |= FILE_ATTRIBUTE_READONLY; break;
1809 case 'A': case 'a': attrib |= FILE_ATTRIBUTE_ARCHIVE; break;
1810 default:
1811 WCMD_output (WCMD_LoadMessage(WCMD_NYI));
1812 return;
1813 }
1814 switch (param1[0]) {
1815 case '+': attrib_set = attrib; break;
1816 case '-': attrib_clear = attrib; break;
1817 }
1818 name = param2;
1819 }
1820
1821 if (strlenW(name) == 0) {
1822 static const WCHAR slashStarW[] = {'\\','*','\0'};
1823
1824 GetCurrentDirectory (sizeof(param2)/sizeof(WCHAR), name);
1825 strcatW (name, slashStarW);
1826 }
1827
1828 hff = FindFirstFile (name, &fd);
1829 if (hff == INVALID_HANDLE_VALUE) {
1830 WCMD_output (WCMD_LoadMessage(WCMD_FILENOTFOUND), name);
1831 }
1832 else {
1833 do {
1834 if (attrib_set || attrib_clear) {
1835 fd.dwFileAttributes &= ~attrib_clear;
1836 fd.dwFileAttributes |= attrib_set;
1837 if (!fd.dwFileAttributes)
1838 fd.dwFileAttributes |= FILE_ATTRIBUTE_NORMAL;
1839 SetFileAttributesW(name, fd.dwFileAttributes);
1840 } else {
1841 static const WCHAR fmt[] = {'%','s',' ',' ',' ','%','s','\n','\0'};
1842 if (fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) {
1843 flags[0] = 'H';
1844 }
1845 if (fd.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) {
1846 flags[1] = 'S';
1847 }
1848 if (fd.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE) {
1849 flags[2] = 'A';
1850 }
1851 if (fd.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
1852 flags[3] = 'R';
1853 }
1854 if (fd.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY) {
1855 flags[4] = 'T';
1856 }
1857 if (fd.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) {
1858 flags[5] = 'C';
1859 }
1860 WCMD_output (fmt, flags, fd.cFileName);
1861 for (count=0; count < 8; count++) flags[count] = ' ';
1862 }
1863 } while (FindNextFile(hff, &fd) != 0);
1864 }
1865 FindClose (hff);
1866 }
1867
1868 /*****************************************************************************
1869 * WCMD_setshow_default
1870 *
1871 * Set/Show the current default directory
1872 */
1873
1874 void WCMD_setshow_default (WCHAR *command) {
1875
1876 BOOL status;
1877 WCHAR string[1024];
1878 WCHAR cwd[1024];
1879 WCHAR *pos;
1880 WIN32_FIND_DATA fd;
1881 HANDLE hff;
1882 static const WCHAR parmD[] = {'/','D','\0'};
1883
1884 WINE_TRACE("Request change to directory '%s'\n", wine_dbgstr_w(command));
1885
1886 /* Skip /D and trailing whitespace if on the front of the command line */
1887 if (CompareString (LOCALE_USER_DEFAULT,
1888 NORM_IGNORECASE | SORT_STRINGSORT,
1889 command, 2, parmD, -1) == 2) {
1890 command += 2;
1891 while (*command && *command==' ') command++;
1892 }
1893
1894 GetCurrentDirectory (sizeof(cwd)/sizeof(WCHAR), cwd);
1895 if (strlenW(command) == 0) {
1896 strcatW (cwd, newline);
1897 WCMD_output (cwd);
1898 }
1899 else {
1900 /* Remove any double quotes, which may be in the
1901 middle, eg. cd "C:\Program Files"\Microsoft is ok */
1902 pos = string;
1903 while (*command) {
1904 if (*command != '"') *pos++ = *command;
1905 command++;
1906 }
1907 *pos = 0x00;
1908
1909 /* Search for appropriate directory */
1910 WINE_TRACE("Looking for directory '%s'\n", wine_dbgstr_w(string));
1911 hff = FindFirstFile (string, &fd);
1912 while (hff != INVALID_HANDLE_VALUE) {
1913 if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1914 WCHAR fpath[MAX_PATH];
1915 WCHAR drive[10];
1916 WCHAR dir[MAX_PATH];
1917 WCHAR fname[MAX_PATH];
1918 WCHAR ext[MAX_PATH];
1919 static const WCHAR fmt[] = {'%','s','%','s','%','s','\0'};
1920
1921 /* Convert path into actual directory spec */
1922 GetFullPathName (string, sizeof(fpath)/sizeof(WCHAR), fpath, NULL);
1923 WCMD_splitpath(fpath, drive, dir, fname, ext);
1924
1925 /* Rebuild path */
1926 wsprintf(string, fmt, drive, dir, fd.cFileName);
1927
1928 FindClose(hff);
1929 hff = INVALID_HANDLE_VALUE;
1930 break;
1931 }
1932
1933 /* Step on to next match */
1934 if (FindNextFile(hff, &fd) == 0) {
1935 FindClose(hff);
1936 hff = INVALID_HANDLE_VALUE;
1937 break;
1938 }
1939 }
1940
1941 /* Change to that directory */
1942 WINE_TRACE("Really changing to directory '%s'\n", wine_dbgstr_w(string));
1943
1944 status = SetCurrentDirectory (string);
1945 if (!status) {
1946 errorlevel = 1;
1947 WCMD_print_error ();
1948 return;
1949 } else {
1950
1951 /* Save away the actual new directory, to store as current location */
1952 GetCurrentDirectoryW (sizeof(string)/sizeof(WCHAR), string);
1953
1954 /* Restore old directory if drive letter would change, and
1955 CD x:\directory /D (or pushd c:\directory) not supplied */
1956 if ((strstrW(quals, parmD) == NULL) &&
1957 (param1[1] == ':') && (toupper(param1[0]) != toupper(cwd[0]))) {
1958 SetCurrentDirectory(cwd);
1959 }
1960 }
1961
1962 /* Set special =C: type environment variable, for drive letter of
1963 change of directory, even if path was restored due to missing
1964 /D (allows changing drive letter when not resident on that
1965 drive */
1966 if ((string[1] == ':') && IsCharAlpha (string[0])) {
1967 WCHAR env[4];
1968 strcpyW(env, equalW);
1969 memcpy(env+1, string, 2 * sizeof(WCHAR));
1970 env[3] = 0x00;
1971 WINE_TRACE("Setting '%s' to '%s'\n", wine_dbgstr_w(env), wine_dbgstr_w(string));
1972 SetEnvironmentVariable(env, string);
1973 }
1974
1975 }
1976 return;
1977 }
1978
1979 /****************************************************************************
1980 * WCMD_setshow_date
1981 *
1982 * Set/Show the system date
1983 * FIXME: Can't change date yet
1984 */
1985
1986 void WCMD_setshow_date (void) {
1987
1988 WCHAR curdate[64], buffer[64];
1989 DWORD count;
1990 static const WCHAR parmT[] = {'/','T','\0'};
1991
1992 if (strlenW(param1) == 0) {
1993 if (GetDateFormat (LOCALE_USER_DEFAULT, 0, NULL, NULL,
1994 curdate, sizeof(curdate)/sizeof(WCHAR))) {
1995 WCMD_output (WCMD_LoadMessage(WCMD_CURRENTDATE), curdate);
1996 if (strstrW (quals, parmT) == NULL) {
1997 WCMD_output (WCMD_LoadMessage(WCMD_NEWDATE));
1998 WCMD_ReadFile (GetStdHandle(STD_INPUT_HANDLE),
1999 buffer, sizeof(buffer)/sizeof(WCHAR), &count, NULL);
2000 if (count > 2) {
2001 WCMD_output (WCMD_LoadMessage(WCMD_NYI));
2002 }
2003 }
2004 }
2005 else WCMD_print_error ();
2006 }
2007 else {
2008 WCMD_output (WCMD_LoadMessage(WCMD_NYI));
2009 }
2010 }
2011
2012 /****************************************************************************
2013 * WCMD_compare
2014 */
2015 static int WCMD_compare( const void *a, const void *b )
2016 {
2017 int r;
2018 const WCHAR * const *str_a = a, * const *str_b = b;
2019 r = CompareString( LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT,
2020 *str_a, -1, *str_b, -1 );
2021 if( r == CSTR_LESS_THAN ) return -1;
2022 if( r == CSTR_GREATER_THAN ) return 1;
2023 return 0;
2024 }
2025
2026 /****************************************************************************
2027 * WCMD_setshow_sortenv
2028 *
2029 * sort variables into order for display
2030 * Optionally only display those who start with a stub
2031 * returns the count displayed
2032 */
2033 static int WCMD_setshow_sortenv(const WCHAR *s, const WCHAR *stub)
2034 {
2035 UINT count=0, len=0, i, displayedcount=0, stublen=0;
2036 const WCHAR **str;
2037
2038 if (stub) stublen = strlenW(stub);
2039
2040 /* count the number of strings, and the total length */
2041 while ( s[len] ) {
2042 len += (strlenW(&s[len]) + 1);
2043 count++;
2044 }
2045
2046 /* add the strings to an array */
2047 str = LocalAlloc (LMEM_FIXED | LMEM_ZEROINIT, count * sizeof (WCHAR*) );
2048 if( !str )
2049 return 0;
2050 str[0] = s;
2051 for( i=1; i<count; i++ )
2052 str[i] = str[i-1] + strlenW(str[i-1]) + 1;
2053
2054 /* sort the array */
2055 qsort( str, count, sizeof (WCHAR*), WCMD_compare );
2056
2057 /* print it */
2058 for( i=0; i<count; i++ ) {
2059 if (!stub || CompareString (LOCALE_USER_DEFAULT,
2060 NORM_IGNORECASE | SORT_STRINGSORT,
2061 str[i], stublen, stub, -1) == 2) {
2062 /* Don't display special internal variables */
2063 if (str[i][0] != '=') {
2064 WCMD_output_asis(str[i]);
2065 WCMD_output_asis(newline);
2066 displayedcount++;
2067 }
2068 }
2069 }
2070
2071 LocalFree( str );
2072 return displayedcount;
2073 }
2074
2075 /****************************************************************************
2076 * WCMD_setshow_env
2077 *
2078 * Set/Show the environment variables
2079 */
2080
2081 void WCMD_setshow_env (WCHAR *s) {
2082
2083 LPVOID env;
2084 WCHAR *p;
2085 int status;
2086 static const WCHAR parmP[] = {'/','P','\0'};
2087
2088 errorlevel = 0;
2089 if (param1[0] == 0x00 && quals[0] == 0x00) {
2090 env = GetEnvironmentStrings ();
2091 WCMD_setshow_sortenv( env, NULL );
2092 return;
2093 }
2094
2095 /* See if /P supplied, and if so echo the prompt, and read in a reply */
2096 if (CompareString (LOCALE_USER_DEFAULT,
2097 NORM_IGNORECASE | SORT_STRINGSORT,
2098 s, 2, parmP, -1) == 2) {
2099 WCHAR string[MAXSTRING];
2100 DWORD count;
2101
2102 s += 2;
2103 while (*s && *s==' ') s++;
2104 if (*s=='\"')
2105 WCMD_opt_s_strip_quotes(s);
2106
2107 /* If no parameter, or no '=' sign, return an error */
2108 if (!(*s) || ((p = strchrW (s, '=')) == NULL )) {
2109 WCMD_output (WCMD_LoadMessage(WCMD_NOARG));
2110 return;
2111 }
2112
2113 /* Output the prompt */
2114 *p++ = '\0';
2115 if (strlenW(p) != 0) WCMD_output(p);
2116
2117 /* Read the reply */
2118 WCMD_ReadFile (GetStdHandle(STD_INPUT_HANDLE), string,
2119 sizeof(string)/sizeof(WCHAR), &count, NULL);
2120 if (count > 1) {
2121 string[count-1] = '\0'; /* ReadFile output is not null-terminated! */
2122 if (string[count-2] == '\r') string[count-2] = '\0'; /* Under Windoze we get CRLF! */
2123 WINE_TRACE("set /p: Setting var '%s' to '%s'\n", wine_dbgstr_w(s),
2124 wine_dbgstr_w(string));
2125 status = SetEnvironmentVariable (s, string);
2126 }
2127
2128 } else {
2129 DWORD gle;
2130
2131 if (*s=='\"')
2132 WCMD_opt_s_strip_quotes(s);
2133 p = strchrW (s, '=');
2134 if (p == NULL) {
2135 env = GetEnvironmentStrings ();
2136 if (WCMD_setshow_sortenv( env, s ) == 0) {
2137 WCMD_output (WCMD_LoadMessage(WCMD_MISSINGENV), s);
2138 errorlevel = 1;
2139 }
2140 return;
2141 }
2142 *p++ = '\0';
2143
2144 if (strlenW(p) == 0) p = NULL;
2145 status = SetEnvironmentVariable (s, p);
2146 gle = GetLastError();
2147 if ((!status) & (gle == ERROR_ENVVAR_NOT_FOUND)) {
2148 errorlevel = 1;
2149 } else if ((!status)) WCMD_print_error();
2150 }
2151 }
2152
2153 /****************************************************************************
2154 * WCMD_setshow_path
2155 *
2156 * Set/Show the path environment variable
2157 */
2158
2159 void WCMD_setshow_path (WCHAR *command) {
2160
2161 WCHAR string[1024];
2162 DWORD status;
2163 static const WCHAR pathW[] = {'P','A','T','H','\0'};
2164 static const WCHAR pathEqW[] = {'P','A','T','H','=','\0'};
2165
2166 if (strlenW(param1) == 0) {
2167 status = GetEnvironmentVariable (pathW, string, sizeof(string)/sizeof(WCHAR));
2168 if (status != 0) {
2169 WCMD_output_asis ( pathEqW);
2170 WCMD_output_asis ( string);
2171 WCMD_output_asis ( newline);
2172 }
2173 else {
2174 WCMD_output (WCMD_LoadMessage(WCMD_NOPATH));
2175 }
2176 }
2177 else {
2178 if (*command == '=') command++; /* Skip leading '=' */
2179 status = SetEnvironmentVariable (pathW, command);
2180 if (!status) WCMD_print_error();
2181 }
2182 }
2183
2184 /****************************************************************************
2185 * WCMD_setshow_prompt
2186 *
2187 * Set or show the command prompt.
2188 */
2189
2190 void WCMD_setshow_prompt (void) {
2191
2192 WCHAR *s;
2193 static const WCHAR promptW[] = {'P','R','O','M','P','T','\0'};
2194
2195 if (strlenW(param1) == 0) {
2196 SetEnvironmentVariable (promptW, NULL);
2197 }
2198 else {
2199 s = param1;
2200 while ((*s == '=') || (*s == ' ')) s++;
2201 if (strlenW(s) == 0) {
2202 SetEnvironmentVariable (promptW, NULL);
2203 }
2204 else SetEnvironmentVariable (promptW, s);
2205 }
2206 }
2207
2208 /****************************************************************************
2209 * WCMD_setshow_time
2210 *
2211 * Set/Show the system time
2212 * FIXME: Can't change time yet
2213 */
2214
2215 void WCMD_setshow_time (void) {
2216
2217 WCHAR curtime[64], buffer[64];
2218 DWORD count;
2219 SYSTEMTIME st;
2220 static const WCHAR parmT[] = {'/','T','\0'};
2221
2222 if (strlenW(param1) == 0) {
2223 GetLocalTime(&st);
2224 if (GetTimeFormat (LOCALE_USER_DEFAULT, 0, &st, NULL,
2225 curtime, sizeof(curtime)/sizeof(WCHAR))) {
2226 WCMD_output (WCMD_LoadMessage(WCMD_CURRENTTIME), curtime);
2227 if (strstrW (quals, parmT) == NULL) {
2228 WCMD_output (WCMD_LoadMessage(WCMD_NEWTIME));
2229 WCMD_ReadFile (GetStdHandle(STD_INPUT_HANDLE), buffer,
2230 sizeof(buffer)/sizeof(WCHAR), &count, NULL);
2231 if (count > 2) {
2232 WCMD_output (WCMD_LoadMessage(WCMD_NYI));
2233 }
2234 }
2235 }
2236 else WCMD_print_error ();
2237 }
2238 else {
2239 WCMD_output (WCMD_LoadMessage(WCMD_NYI));
2240 }
2241 }
2242
2243 /****************************************************************************
2244 * WCMD_shift
2245 *
2246 * Shift batch parameters.
2247 * Optional /n says where to start shifting (n=0-8)
2248 */
2249
2250 void WCMD_shift (WCHAR *command) {
2251 int start;
2252
2253 if (context != NULL) {
2254 WCHAR *pos = strchrW(command, '/');
2255 int i;
2256
2257 if (pos == NULL) {
2258 start = 0;
2259 } else if (*(pos+1)>='' && *(pos+1)<='8') {
2260 start = (*(pos+1) - '');
2261 } else {
2262 SetLastError(ERROR_INVALID_PARAMETER);
2263 WCMD_print_error();
2264 return;
2265 }
2266
2267 WINE_TRACE("Shifting variables, starting at %d\n", start);
2268 for (i=start;i<=8;i++) {
2269 context -> shift_count[i] = context -> shift_count[i+1] + 1;
2270 }
2271 context -> shift_count[9] = context -> shift_count[9] + 1;
2272 }
2273
2274 }
2275
2276 /****************************************************************************
2277 * WCMD_title
2278 *
2279 * Set the console title
2280 */
2281 void WCMD_title (WCHAR *command) {
2282 SetConsoleTitle(command);
2283 }
2284
2285 /****************************************************************************
2286 * WCMD_type
2287 *
2288 * Copy a file to standard output.
2289 */
2290
2291 void WCMD_type (WCHAR *command) {
2292
2293 int argno = 0;
2294 WCHAR *argN = command;
2295 BOOL writeHeaders = FALSE;
2296
2297 if (param1[0] == 0x00) {
2298 WCMD_output (WCMD_LoadMessage(WCMD_NOARG));
2299 return;
2300 }
2301
2302 if (param2[0] != 0x00) writeHeaders = TRUE;
2303
2304 /* Loop through all args */
2305 errorlevel = 0;
2306 while (argN) {
2307 WCHAR *thisArg = WCMD_parameter (command, argno++, &argN);
2308
2309 HANDLE h;
2310 WCHAR buffer[512];
2311 DWORD count;
2312
2313 if (!argN) break;
2314
2315 WINE_TRACE("type: Processing arg '%s'\n", wine_dbgstr_w(thisArg));
2316 h = CreateFile (thisArg, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
2317 FILE_ATTRIBUTE_NORMAL, NULL);
2318 if (h == INVALID_HANDLE_VALUE) {
2319 WCMD_print_error ();
2320 WCMD_output (WCMD_LoadMessage(WCMD_READFAIL), thisArg);
2321 errorlevel = 1;
2322 } else {
2323 if (writeHeaders) {
2324 static const WCHAR fmt[] = {'\n','%','s','\n','\n','\0'};
2325 WCMD_output(fmt, thisArg);
2326 }
2327 while (WCMD_ReadFile (h, buffer, sizeof(buffer)/sizeof(WCHAR) - 1, &count, NULL)) {
2328 if (count == 0) break; /* ReadFile reports success on EOF! */
2329 buffer[count] = 0;
2330 WCMD_output_asis (buffer);
2331 }
2332 CloseHandle (h);
2333 if (!writeHeaders)
2334 WCMD_output_asis (newline);
2335 }
2336 }
2337 }
2338
2339 /****************************************************************************
2340 * WCMD_more
2341 *
2342 * Output either a file or stdin to screen in pages
2343 */
2344
2345 void WCMD_more (WCHAR *command) {
2346
2347 int argno = 0;
2348 WCHAR *argN = command;
2349 BOOL useinput = FALSE;
2350 WCHAR moreStr[100];
2351 WCHAR moreStrPage[100];
2352 WCHAR buffer[512];
2353 DWORD count;
2354 static const WCHAR moreStart[] = {'-','-',' ','\0'};
2355 static const WCHAR moreFmt[] = {'%','s',' ','-','-','\n','\0'};
2356 static const WCHAR moreFmt2[] = {'%','s',' ','(','%','2','.','2','d','%','%',
2357 ')',' ','-','-','\n','\0'};
2358 static const WCHAR conInW[] = {'C','O','N','I','N','$','\0'};
2359
2360 /* Prefix the NLS more with '-- ', then load the text */
2361 errorlevel = 0;
2362 strcpyW(moreStr, moreStart);
2363 LoadString (hinst, WCMD_MORESTR, &moreStr[3],
2364 (sizeof(moreStr)/sizeof(WCHAR))-3);
2365
2366 if (param1[0] == 0x00) {
2367
2368 /* Wine implements pipes via temporary files, and hence stdin is
2369 effectively reading from the file. This means the prompts for
2370 more are satisfied by the next line from the input (file). To
2371 avoid this, ensure stdin is to the console */
2372 HANDLE hstdin = GetStdHandle(STD_INPUT_HANDLE);
2373 HANDLE hConIn = CreateFile(conInW, GENERIC_READ | GENERIC_WRITE,
2374 FILE_SHARE_READ, NULL, OPEN_EXISTING,
2375 FILE_ATTRIBUTE_NORMAL, 0);
2376 WINE_TRACE("No parms - working probably in pipe mode\n");
2377 SetStdHandle(STD_INPUT_HANDLE, hConIn);
2378
2379 /* Warning: No easy way of ending the stream (ctrl+z on windows) so
2380 once you get in this bit unless due to a pipe, its going to end badly... */
2381 useinput = TRUE;
2382 wsprintf(moreStrPage, moreFmt, moreStr);
2383
2384 WCMD_enter_paged_mode(moreStrPage);
2385 while (WCMD_ReadFile (hstdin, buffer, (sizeof(buffer)/sizeof(WCHAR))-1, &count, NULL)) {
2386 if (count == 0) break; /* ReadFile reports success on EOF! */
2387 buffer[count] = 0;
2388 WCMD_output_asis (buffer);
2389 }
2390 WCMD_leave_paged_mode();
2391
2392 /* Restore stdin to what it was */
2393 SetStdHandle(STD_INPUT_HANDLE, hstdin);
2394 CloseHandle(hConIn);
2395
2396 return;
2397 } else {
2398 BOOL needsPause = FALSE;
2399
2400 /* Loop through all args */
2401 WINE_TRACE("Parms supplied - working through each file\n");
2402 WCMD_enter_paged_mode(moreStrPage);
2403
2404 while (argN) {
2405 WCHAR *thisArg = WCMD_parameter (command, argno++, &argN);
2406 HANDLE h;
2407
2408 if (!argN) break;
2409
2410 if (needsPause) {
2411
2412 /* Wait */
2413 wsprintf(moreStrPage, moreFmt2, moreStr, 100);
2414 WCMD_leave_paged_mode();
2415 WCMD_output_asis(moreStrPage);
2416 WCMD_ReadFile (GetStdHandle(STD_INPUT_HANDLE), buffer,
2417 sizeof(buffer)/sizeof(WCHAR), &count, NULL);
2418 WCMD_enter_paged_mode(moreStrPage);
2419 }
2420
2421
2422 WINE_TRACE("more: Processing arg '%s'\n", wine_dbgstr_w(thisArg));
2423 h = CreateFile (thisArg, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
2424 FILE_ATTRIBUTE_NORMAL, NULL);
2425 if (h == INVALID_HANDLE_VALUE) {
2426 WCMD_print_error ();
2427 WCMD_output (WCMD_LoadMessage(WCMD_READFAIL), thisArg);
2428 errorlevel = 1;
2429 } else {
2430 ULONG64 curPos = 0;
2431 ULONG64 fileLen = 0;
2432 WIN32_FILE_ATTRIBUTE_DATA fileInfo;
2433
2434 /* Get the file size */
2435 GetFileAttributesEx(thisArg, GetFileExInfoStandard, (void*)&fileInfo);
2436 fileLen = (((ULONG64)fileInfo.nFileSizeHigh) << 32) + fileInfo.nFileSizeLow;
2437
2438 needsPause = TRUE;
2439 while (WCMD_ReadFile (h, buffer, (sizeof(buffer)/sizeof(WCHAR))-1, &count, NULL)) {
2440 if (count == 0) break; /* ReadFile reports success on EOF! */
2441 buffer[count] = 0;
2442 curPos += count;
2443
2444 /* Update % count (would be used in WCMD_output_asis as prompt) */
2445 wsprintf(moreStrPage, moreFmt2, moreStr, (int) min(99, (curPos * 100)/fileLen));
2446
2447 WCMD_output_asis (buffer);
2448 }
2449 CloseHandle (h);
2450 }
2451 }
2452
2453 WCMD_leave_paged_mode();
2454 }
2455 }
2456
2457 /****************************************************************************
2458 * WCMD_verify
2459 *
2460 * Display verify flag.
2461 * FIXME: We don't actually do anything with the verify flag other than toggle
2462 * it...
2463 */
2464
2465 void WCMD_verify (WCHAR *command) {
2466
2467 int count;
2468
2469 count = strlenW(command);
2470 if (count == 0) {
2471 if (verify_mode) WCMD_output (WCMD_LoadMessage(WCMD_VERIFYPROMPT), onW);
2472 else WCMD_output (WCMD_LoadMessage(WCMD_VERIFYPROMPT), offW);
2473 return;
2474 }
2475 if (lstrcmpiW(command, onW) == 0) {
2476 verify_mode = 1;
2477 return;
2478 }
2479 else if (lstrcmpiW(command, offW) == 0) {
2480 verify_mode = 0;
2481 return;
2482 }
2483 else WCMD_output (WCMD_LoadMessage(WCMD_VERIFYERR));
2484 }
2485
2486 /****************************************************************************
2487 * WCMD_version
2488 *
2489 * Display version info.
2490 */
2491
2492 void WCMD_version (void) {
2493
2494 WCMD_output (version_string);
2495
2496 }
2497
2498 /****************************************************************************
2499 * WCMD_volume
2500 *
2501 * Display volume info and/or set volume label. Returns 0 if error.
2502 */
2503
2504 int WCMD_volume (int mode, WCHAR *path) {
2505
2506 DWORD count, serial;
2507 WCHAR string[MAX_PATH], label[MAX_PATH], curdir[MAX_PATH];
2508 BOOL status;
2509
2510 if (strlenW(path) == 0) {
2511 status = GetCurrentDirectory (sizeof(curdir)/sizeof(WCHAR), curdir);
2512 if (!status) {
2513 WCMD_print_error ();
2514 return 0;
2515 }
2516 status = GetVolumeInformation (NULL, label, sizeof(label)/sizeof(WCHAR),
2517 &serial, NULL, NULL, NULL, 0);
2518 }
2519 else {
2520 static const WCHAR fmt[] = {'%','s','\\','\0'};
2521 if ((path[1] != ':') || (strlenW(path) != 2)) {
2522 WCMD_output (WCMD_LoadMessage(WCMD_SYNTAXERR));
2523 return 0;
2524 }
2525 wsprintf (curdir, fmt, path);
2526 status = GetVolumeInformation (curdir, label, sizeof(label)/sizeof(WCHAR),
2527 &serial, NULL,
2528 NULL, NULL, 0);
2529 }
2530 if (!status) {
2531 WCMD_print_error ();
2532 return 0;
2533 }
2534 WCMD_output (WCMD_LoadMessage(WCMD_VOLUMEDETAIL),
2535 curdir[0], label, HIWORD(serial), LOWORD(serial));
2536 if (mode) {
2537 WCMD_output (WCMD_LoadMessage(WCMD_VOLUMEPROMPT));
2538 WCMD_ReadFile (GetStdHandle(STD_INPUT_HANDLE), string,
2539 sizeof(string)/sizeof(WCHAR), &count, NULL);
2540 if (count > 1) {
2541 string[count-1] = '\0'; /* ReadFile output is not null-terminated! */
2542 if (string[count-2] == '\r') string[count-2] = '\0'; /* Under Windoze we get CRLF! */
2543 }
2544 if (strlenW(path) != 0) {
2545 if (!SetVolumeLabel (curdir, string)) WCMD_print_error ();
2546 }
2547 else {
2548 if (!SetVolumeLabel (NULL, string)) WCMD_print_error ();
2549 }
2550 }
2551 return 1;
2552 }
2553
2554 /**************************************************************************
2555 * WCMD_exit
2556 *
2557 * Exit either the process, or just this batch program
2558 *
2559 */
2560
2561 void WCMD_exit (CMD_LIST **cmdList) {
2562
2563 static const WCHAR parmB[] = {'/','B','\0'};
2564 int rc = atoiW(param1); /* Note: atoi of empty parameter is 0 */
2565
2566 if (context && lstrcmpiW(quals, parmB) == 0) {
2567 errorlevel = rc;
2568 context -> skip_rest = TRUE;
2569 *cmdList = NULL;
2570 } else {
2571 ExitProcess(rc);
2572 }
2573 }
2574
2575
2576 /*****************************************************************************
2577 * WCMD_assoc
2578 *
2579 * Lists or sets file associations (assoc = TRUE)
2580 * Lists or sets file types (assoc = FALSE)
2581 */
2582 void WCMD_assoc (WCHAR *command, BOOL assoc) {
2583
2584 HKEY key;
2585 DWORD accessOptions = KEY_READ;
2586 WCHAR *newValue;
2587 LONG rc = ERROR_SUCCESS;
2588 WCHAR keyValue[MAXSTRING];
2589 DWORD valueLen = MAXSTRING;
2590 HKEY readKey;
2591 static const WCHAR shOpCmdW[] = {'\\','S','h','e','l','l','\\',
2592 'O','p','e','n','\\','C','o','m','m','a','n','d','\0'};
2593
2594 /* See if parameter includes '=' */
2595 errorlevel = 0;
2596 newValue = strchrW(command, '=');
2597 if (newValue) accessOptions |= KEY_WRITE;
2598
2599 /* Open a key to HKEY_CLASSES_ROOT for enumerating */
2600 if (RegOpenKeyEx(HKEY_CLASSES_ROOT, nullW, 0,
2601 accessOptions, &key) != ERROR_SUCCESS) {
2602 WINE_FIXME("Unexpected failure opening HKCR key: %d\n", GetLastError());
2603 return;
2604 }
2605
2606 /* If no parameters then list all associations */
2607 if (*command == 0x00) {
2608 int index = 0;
2609
2610 /* Enumerate all the keys */
2611 while (rc != ERROR_NO_MORE_ITEMS) {
2612 WCHAR keyName[MAXSTRING];
2613 DWORD nameLen;
2614
2615 /* Find the next value */
2616 nameLen = MAXSTRING;
2617 rc = RegEnumKeyEx(key, index++,
2618 keyName, &nameLen,
2619 NULL, NULL, NULL, NULL);
2620
2621 if (rc == ERROR_SUCCESS) {
2622
2623 /* Only interested in extension ones if assoc, or others
2624 if not assoc */
2625 if ((keyName[0] == '.' && assoc) ||
2626 (!(keyName[0] == '.') && (!assoc)))
2627 {
2628 WCHAR subkey[MAXSTRING];
2629 strcpyW(subkey, keyName);
2630 if (!assoc) strcatW(subkey, shOpCmdW);
2631
2632 if (RegOpenKeyEx(key, subkey, 0,
2633 accessOptions, &readKey) == ERROR_SUCCESS) {
2634
2635 valueLen = sizeof(keyValue)/sizeof(WCHAR);
2636 rc = RegQueryValueEx(readKey, NULL, NULL, NULL,
2637 (LPBYTE)keyValue, &valueLen);
2638 WCMD_output_asis(keyName);
2639 WCMD_output_asis(equalW);
2640 /* If no default value found, leave line empty after '=' */
2641 if (rc == ERROR_SUCCESS) {
2642 WCMD_output_asis(keyValue);
2643 }
2644 WCMD_output_asis(newline);
2645 RegCloseKey(readKey);
2646 }
2647 }
2648 }
2649 }
2650
2651 } else {
2652
2653 /* Parameter supplied - if no '=' on command line, its a query */
2654 if (newValue == NULL) {
2655 WCHAR *space;
2656 WCHAR subkey[MAXSTRING];
2657
2658 /* Query terminates the parameter at the first space */
2659 strcpyW(keyValue, command);
2660 space = strchrW(keyValue, ' ');
2661 if (space) *space=0x00;
2662
2663 /* Set up key name */
2664 strcpyW(subkey, keyValue);
2665 if (!assoc) strcatW(subkey, shOpCmdW);
2666
2667 if (RegOpenKeyEx(key, subkey, 0,
2668 accessOptions, &readKey) == ERROR_SUCCESS) {
2669
2670 rc = RegQueryValueEx(readKey, NULL, NULL, NULL,
2671 (LPBYTE)keyValue, &valueLen);
2672 WCMD_output_asis(command);
2673 WCMD_output_asis(equalW);
2674 /* If no default value found, leave line empty after '=' */
2675 if (rc == ERROR_SUCCESS) WCMD_output_asis(keyValue);
2676 WCMD_output_asis(newline);
2677 RegCloseKey(readKey);
2678
2679 } else {
2680 WCHAR msgbuffer[MAXSTRING];
2681 WCHAR outbuffer[MAXSTRING];
2682
2683 /* Load the translated 'File association not found' */
2684 if (assoc) {
2685 LoadString (hinst, WCMD_NOASSOC, msgbuffer, sizeof(msgbuffer)/sizeof(WCHAR));
2686 } else {
2687 LoadString (hinst, WCMD_NOFTYPE, msgbuffer, sizeof(msgbuffer)/sizeof(WCHAR));
2688 }
2689 wsprintf(outbuffer, msgbuffer, keyValue);
2690 WCMD_output_asis(outbuffer);
2691 errorlevel = 2;
2692 }
2693
2694 /* Not a query - its a set or clear of a value */
2695 } else {
2696
2697 WCHAR subkey[MAXSTRING];
2698
2699 /* Get pointer to new value */
2700 *newValue = 0x00;
2701 newValue++;
2702
2703 /* Set up key name */
2704 strcpyW(subkey, command);
2705 if (!assoc) strcatW(subkey, shOpCmdW);
2706
2707 /* If nothing after '=' then clear value - only valid for ASSOC */
2708 if (*newValue == 0x00) {
2709
2710 if (assoc) rc = RegDeleteKey(key, command);
2711 if (assoc && rc == ERROR_SUCCESS) {
2712 WINE_TRACE("HKCR Key '%s' deleted\n", wine_dbgstr_w(command));
2713
2714 } else if (assoc && rc != ERROR_FILE_NOT_FOUND) {
2715 WCMD_print_error();
2716 errorlevel = 2;
2717
2718 } else {
2719 WCHAR msgbuffer[MAXSTRING];
2720 WCHAR outbuffer[MAXSTRING];
2721
2722 /* Load the translated 'File association not found' */
2723 if (assoc) {
2724 LoadString (hinst, WCMD_NOASSOC, msgbuffer,
2725 sizeof(msgbuffer)/sizeof(WCHAR));
2726 } else {
2727 LoadString (hinst, WCMD_NOFTYPE, msgbuffer,
2728 sizeof(msgbuffer)/sizeof(WCHAR));
2729 }
2730 wsprintf(outbuffer, msgbuffer, keyValue);
2731 WCMD_output_asis(outbuffer);
2732 errorlevel = 2;
2733 }
2734
2735 /* It really is a set value = contents */
2736 } else {
2737 rc = RegCreateKeyEx(key, subkey, 0, NULL, REG_OPTION_NON_VOLATILE,
2738 accessOptions, NULL, &readKey, NULL);
2739 if (rc == ERROR_SUCCESS) {
2740 rc = RegSetValueEx(readKey, NULL, 0, REG_SZ,
2741 (LPBYTE)newValue, strlenW(newValue));
2742 RegCloseKey(readKey);
2743 }
2744
2745 if (rc != ERROR_SUCCESS) {
2746 WCMD_print_error();
2747 errorlevel = 2;
2748 } else {
2749 WCMD_output_asis(command);
2750 WCMD_output_asis(equalW);
2751 WCMD_output_asis(newValue);
2752 WCMD_output_asis(newline);
2753 }
2754 }
2755 }
2756 }
2757
2758 /* Clean up */
2759 RegCloseKey(key);
2760 }
2761
2762 /****************************************************************************
2763 * WCMD_color
2764 *
2765 * Clear the terminal screen.
2766 */
2767
2768 void WCMD_color (void) {
2769
2770 /* Emulate by filling the screen from the top left to bottom right with
2771 spaces, then moving the cursor to the top left afterwards */
2772 CONSOLE_SCREEN_BUFFER_INFO consoleInfo;
2773 HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
2774
2775 if (param1[0] != 0x00 && strlenW(param1) > 2) {
2776 WCMD_output (WCMD_LoadMessage(WCMD_ARGERR));
2777 return;
2778 }
2779
2780 if (GetConsoleScreenBufferInfo(hStdOut, &consoleInfo))
2781 {
2782 COORD topLeft;
2783 DWORD screenSize;
2784 DWORD color = 0;
2785
2786 screenSize = consoleInfo.dwSize.X * (consoleInfo.dwSize.Y + 1);
2787
2788 topLeft.X = 0;
2789 topLeft.Y = 0;
2790
2791 /* Convert the color hex digits */
2792 if (param1[0] == 0x00) {
2793 color = defaultColor;
2794 } else {
2795 color = strtoulW(param1, NULL, 16);
2796 }
2797
2798 /* Fail if fg == bg color */
2799 if (((color & 0xF0) >> 4) == (color & 0x0F)) {
2800 errorlevel = 1;
2801 return;
2802 }
2803
2804 /* Set the current screen contents and ensure all future writes
2805 remain this color */
2806 FillConsoleOutputAttribute(hStdOut, color, screenSize, topLeft, &screenSize);
2807 SetConsoleTextAttribute(hStdOut, color);
2808 }
2809 }
2810
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.